/* NAT_pseudo_dev.c - NAT core comp. & driver (Linux kernel module) */ #include /* Code runs in the Linux kernel */ #include /* The code runs as a kernel module */ #include /* NAT pseudo device is a character device */ #include /* Needed for communication with user space */ #include #include /* Code uses the IXA Resource Manager */ #undef LINUX /* Prevents the compiler from complaining */ #include /* Code uses the IXA CCI */ #include #include "NAT_shared_defs.h" #include "NAT_types.h" #include "NAT_scratch_rings.h" #define ME_MASK 0x33 /* System uses microengines 0, 1, 4 and 5 */ #define CONTEXT_MASK 255 /* Context mask -- enable all contexts */ #define HASH_MULT_W0 0x12345678 /* Hash multiplier -- word 0 */ #define HASH_MULT_W1 0x87654321 /* Hash multiplier -- word 1 */ #define HASH_MULT_W2 0x56781234 /* Hash multiplier -- word 2 */ #define HASH_MULT_W3 0x43218765 /* Hash multiplier -- word 3 */ /* Macro to log error message and terminate resource manager */ #define panic(...) { printk("%s: ",NAT_DRIVER_NAME);\\\\ printk(__VA_ARGS__);\\\\ ix_rm_term();\\\\ unregister_chrdev(NAT_major, NAT_DRIVER_NAME);\\\\ return(-1); } /* Macro to clear block of kernel memory */ #define bzero(buf,size) ix_ossl_memset(buf,0,size) /* Macro to convert microengine sequence number */ /* into microengine ID */ #define ME_ID(i) ((i%ME_CL_SZ)|((i/ME_CL_SZ)<<4)) /* Macro to convert SRAM offset and memory channel into */ /* microengine addressing */ #define ME_SRAM_ADDR(offset,memChan) (offset|(memChan<<30)) /* Module parameter -- a UOF file name */ static char * Uof_file; /* Module parameter -- Linux major device number for NAT pseudo device */ static unsigned int NAT_major = NAT_DEF_MAJOR_NUMBER; MODULE_AUTHOR("Internetworking Lab, CS, Purdue University"); MODULE_DESCRIPTION("NAT pseudo-device driver for IXP2XXX"); MODULE_PARM(Uof_file, "s"); MODULE_PARM(NAT_major,"i"); /* Static gateway IP address */ unsigned int gateway_ip= GW_IP; /* Static network interface configuration */ net_if iface_table[PORTS_NUM]={ {0xC0A80002 /* 192.168.0.2 */, 0x01010101,0x01010000 /* 01:01:01:01:01:01 */}, {0x0A000001 /* 10.0.0.1 */, 0x02020202,0x02020000 /* 02:02:02:02:02:02 */} }; /* External procedures */ extern ix_error nat_pkt_handler(ix_buffer_handle,ix_uint32,void*); extern ix_error resolve_arp(unsigned int); /* Local procedures */ static int patch_microblocks(ix_buffer_free_list_info); static int create_scr_rings(); static int init_hash(); static ix_error nat_table_timer(void*); static ix_error exe_init_f(ix_exe_handle,void**); static ix_error exe_fini_f(ix_exe_handle,void*); static ix_error cc_init_f(ix_cc_handle,void**); static ix_error cc_fini_f(ix_cc_handle,void*); static ix_exe_handle exeHandle; static ix_cc_handle ccHandle; static ix_event_handle eveHandle; static int nat_open(struct inode *, struct file *); static int nat_release(struct inode *, struct file *); static int nat_ioctl(struct inode *, struct file *, unsigned int, unsigned long); /* Verbosity level */ int verb=SILENT; /* Pointers to various run-time data structures */ nat_entry *f_nat_table, *r_nat_table; unsigned int *f_index,*r_index; arp_entry *arp_table; unsigned char *f_timer, *r_timer; void *rx_cntr, *tx_cntr; /* Scratch rings */ ix_hw_ring_handle rxToNatRing, txScrRing[4]; /* List of free buffers */ ix_buffer_free_list_handle hwFreeList = 0; /* Operations for the NAT pseudo-device */ static struct file_operations nat_fops = { ioctl: nat_ioctl, open: nat_open, release: nat_release }; static int nat_open(struct inode *inode, struct file *filp) { MOD_INC_USE_COUNT; return 0; } static int nat_release(struct inode *inode, struct file *filp) { MOD_DEC_USE_COUNT; return 0; } static int nat_ioctl(struct inode *inode, struct file *fp, unsigned int cmd, unsigned long buf) { switch (cmd) { case SILENT: verb = SILENT; break; case VERBOSE: verb = VERBOSE; break; case GET_ARP_TABLE: if ((char *)buf != NULL) return copy_to_user((char *)buf,arp_table, ARP_TABLE_SIZE*sizeof(arp_entry)); break; case GET_NAT_TABLE: if ((char *)buf != NULL) return copy_to_user((char *)buf, (void*)f_nat_table, NAT_TABLE_SIZE*sizeof(nat_entry)); break; case GET_TIMER_TABLE: if ((char *)buf != NULL) return copy_to_user((char *)buf, (void*)f_timer, 2*NAT_TABLE_SIZE); break; case GET_RX_COUNTER: if ((char *)buf != NULL) return copy_to_user((char *)buf,rx_cntr, RX_CNTR_SIZE); break; case GET_TX_COUNTER: if ((char *)buf != NULL) return copy_to_user((char *)buf,tx_cntr, TX_CNTR_SIZE); break; case CLR_RX_COUNTER: bzero(rx_cntr,RX_CNTR_SIZE); break; case CLR_TX_COUNTER: bzero(tx_cntr,TX_CNTR_SIZE); break; default: return INVALID_CMD; } return 0; } int init_module() { ix_error err; ix_buffer_free_list_info hwFreeListInfo; int i; if (Uof_file == NULL) { printk("%s: no microcode file specified!\\\\n", NAT_DRIVER_NAME); return -1; } /* Register the pseudo-device with Linux */ if (register_chrdev(NAT_major, NAT_DRIVER_NAME, &nat_fops) < 0) { printk("%s: can't get major number %d\\\\n", NAT_DRIVER_NAME, NAT_major); return -1; } /* Initialize Intel's Resource Manager */ printk("%s: Initializing Resource Manager\\\\n", NAT_DRIVER_NAME); err=ix_rm_init(0); if (err != IX_SUCCESS) { printk("Error: ix_rm_init failed\\\\n"); return -1; } /* Register the exception packet handler */ printk("%s: Setting packet receive mode (to callback)\\\\n", NAT_DRIVER_NAME); err = ix_rm_packet_set_receive_mode(NAT_CC_ID, IX_COMM_ID_MODE_CALLBACK); if (err != IX_SUCCESS) panic("ix_rm_packet_set_receive_mode failed\\\\n"); printk("%s: Registering packet handler\\\\n", NAT_DRIVER_NAME); err = ix_rm_packet_handler_register(NAT_CC_ID, nat_pkt_handler, NULL); if (err != IX_SUCCESS) panic("ix_rm_packet_handler_register failed\\\\n"); /* Allocate a free buffer list */ err = ix_rm_hw_buffer_free_list_create(NUM_BUFFERS, sizeof(ix_hw_buffer_meta), BUF_SIZE, BUF_SRAM_CHAN, BUF_DRAM_CHAN,&hwFreeList); if (err != IX_SUCCESS) panic("ix_rm_hw_buffer_free_list_create failed\\\\n"); /* Read freelist info (it will be needed later) */ err = ix_rm_buffer_free_list_get_info(hwFreeList, &hwFreeListInfo); if (err != IX_SUCCESS) panic("ix_rm_hw_buffer_free_list_get_info failed\\\\n"); /* Allocate RX counters (SRAM, channel 0) */ err = ix_rm_mem_alloc(IX_MEMORY_TYPE_SRAM, 0, RX_CNTR_SIZE, &rx_cntr); if (err != IX_SUCCESS) panic("ix_rm_mem_alloc failed for RX counters\\\\n"); /* Clear RX counters */ bzero(rx_cntr,RX_CNTR_SIZE); /* Allocate TX counters (SRAM, channel 1) */ err = ix_rm_mem_alloc(IX_MEMORY_TYPE_SRAM, 1, TX_CNTR_SIZE, &tx_cntr); if (err != IX_SUCCESS) panic("ix_rm_mem_alloc failed for TX counters\\\\n"); /* Clear TX counters */ bzero(tx_cntr,TX_CNTR_SIZE); /* Allocate the NAT table in DRAM */ err = ix_rm_mem_alloc(IX_MEMORY_TYPE_DRAM, 0, 2*NAT_TABLE_SIZE*sizeof(nat_entry), (void**)&f_nat_table); if (err != IX_SUCCESS) panic("ix_rm_mem_alloc failed for NAT table\\\\n"); /* Clear the NAT table */ bzero((void*)f_nat_table,2*NAT_TABLE_SIZE*sizeof(nat_entry)); /* Set the base address for reverse NAT table */ r_nat_table=f_nat_table+NAT_TABLE_SIZE; /* Allocate the NAT index table in DRAM */ err = ix_rm_mem_alloc(IX_MEMORY_TYPE_DRAM, 0, 2*NAT_TABLE_SIZE*sizeof(unsigned int), (void**)&f_index); if (err != IX_SUCCESS) panic("ix_rm_mem_alloc failed for NAT index table\\\\n"); /* Clear the NAT index table */ bzero((void*)f_index,2*NAT_TABLE_SIZE*sizeof(unsigned int)); /* Set the base for the reverse NAT index table */ r_index=f_index+NAT_TABLE_SIZE; /* Allocate the timer table in SRAM */ err = ix_rm_mem_alloc(IX_MEMORY_TYPE_SRAM, 0, NAT_TABLE_SIZE, (void**)&f_timer); if (err != IX_SUCCESS) panic("ix_rm_mem_alloc failed for timer table\\\\n"); /* Clear the timer table */ bzero((void*)f_timer,2*NAT_TABLE_SIZE); /* Set the base address for the reverse timer table */ r_timer=f_timer+NAT_TABLE_SIZE; /* Allocate the ARP table in DRAM */ err = ix_rm_mem_alloc(IX_MEMORY_TYPE_SRAM, 1, ARP_TABLE_SIZE*sizeof(arp_entry), (void**)&arp_table); if (err != IX_SUCCESS) panic("ix_rm_mem_alloc failed for ARP table\\\\n"); /* Clear the ARP table */ bzero((void*)arp_table,ARP_TABLE_SIZE*sizeof(arp_entry)); /* Reset the microengines */ printk("%s: Resetting all microengines\\\\n", NAT_DRIVER_NAME); ix_rm_ueng_reset_all(); /* Get the microcode from the UOF file */ printk("%s: Setting ucode\\\\n", NAT_DRIVER_NAME); err = ix_rm_ueng_set_ucode(Uof_file); if (err != IX_SUCCESS) panic("ix_rm_ueng_set_ucode failed\\\\n"); /* Patch the microcode symbols before actually */ /* loading microcode. */ if (patch_microblocks(hwFreeListInfo) < 0) return(-1); /* Create Scratch rings */ if (create_scr_rings() < 0) return(-1); /* Load microcode into microengines */ printk("%s: Loading ucode\\\\n", NAT_DRIVER_NAME); err = ix_rm_ueng_load(); if (err != IX_SUCCESS) panic("ix_rm_ueng_load failed\\\\n"); /* Initialize hash unit */ if (init_hash() < 0) return(-1); /* Start the assigned microengines */ for (i=0;i>i)&0x1) { printk("%s: Starting ME%i\\\\n",NAT_DRIVER_NAME, i); err = ix_rm_ueng_start(ME_ID(i),CONTEXT_MASK); if (err != IX_SUCCESS) panic("ix_rm_ueng_start failed for ME %i\\\\n",i); } } /* Resolve an ARP entry for the gateway */ if (resolve_arp(GW_IP) != IX_SUCCESS) panic("can't resolve ARP entry for the gateway\\\\n"); /* Create an execution engine (i.e., a kernel thread) for the */ /* NAT timer aging procedure */ err=ix_cci_init(); /* Initialize Intel's CCI */ if (err != IX_SUCCESS) panic("ix_cci_init failed\\\\n"); printk("%s: Creating timer thread\\\\n",NAT_DRIVER_NAME); err=ix_cci_exe_run(NULL,exe_init_f,exe_fini_f,"NAT timer", &exeHandle); if (err != IX_SUCCESS) { ix_cci_fini(); panic("ix_cci_exe_run failed\\\\n"); } return 0; } /* Cleanup */ void cleanup_module() { ix_error err; int i; /* Stop each of the assigned microengines */ for (i=0;i>i)&0x1) { printk("%s: Stopping ME%i\\\\n",NAT_DRIVER_NAME,i); err = ix_rm_ueng_stop(ME_ID(i)); if (err != IX_SUCCESS) printk( "%s: ix_rm_ueng_stop failed for ME %i\\\\n", NAT_DRIVER_NAME,i); } } /* Unregister the packet handler */ ix_rm_packet_handler_unregister(NAT_CC_ID); /* Terminate the timer thread */ printk("%s: Stopping timer thread\\\\n",NAT_DRIVER_NAME); ix_cci_exe_shutdown(exeHandle); ix_cci_fini(); /* Terminate the Resource Manager */ ix_rm_term(); /* unregister pseudo-device */ unregister_chrdev(NAT_major, NAT_DRIVER_NAME); } /* NAT table management: periodically go through the timer table */ /* and update (age) each of the timers */ ix_error nat_table_timer(void* dummy) { int i; for (i=0;i<2*NAT_TABLE_SIZE;i++) if (f_nat_table[i].valid) f_timer[i]=f_timer[i]>>1; return(IX_SUCCESS); } ix_error exe_init_f(ix_exe_handle exeHandle,void** ppContext) { ix_cc_init_context dummy; return ix_cci_cc_create(exeHandle,cc_init_f,cc_fini_f, (void*)&dummy,&ccHandle); } ix_error exe_fini_f(ix_exe_handle exeHandle,void* pContext) { return ix_cci_cc_destroy(ccHandle); } ix_error cc_init_f(ix_cc_handle ccHandle,void** ppContext) { /* Age each timer every AGING_INTERVAL */ return ix_cci_cc_add_event_handler(ccHandle,AGING_INTERVAL, nat_table_timer,IX_EVENT_TYPE_PERIODIC,1,&eveHandle); } ix_error cc_fini_f(ix_cc_handle ccHandle,void* pContext) { return ix_cci_cc_remove_event_handler(ccHandle,eveHandle); } /******************************************************************/ /* Patch the microcode symbols before actually loading microcode. */ /* */ /* The imported variables that must be patched are: */ /* BUF_FREE_LIST0 -- get from freelist allocation, */ /* used by all microblocks */ /* BUF_SRAM_BASE -- get from freelist allocation, */ /* used by all microblocks */ /* DL_REL_BASE -- compute from freelist allocation */ /* parameters, used by all microblocks */ /* FREE_LIST_ID -- get from freelist allocation, */ /* used by RX microblock only */ /* PACKET_COUNTERS_SRAM_BASE -- get from memory allocation for */ /* RX counters, used by */ /* RX microblock only */ /* PACKET_TX_COUNTER_BASE -- get from memory allocation for */ /* TX counters, used by */ /* TX microblock only */ /* ARP_TABLE_BASE -- get from memory allocation, */ /* used by NAT microblock only */ /* NAT_TABLE_BASE -- get from memory allocation, */ /* used by NAT microblock only */ /* TIMER_TABLE_BASE -- get from memory allocation, */ /* used by NAT microblock only */ /* GATEWAY_IP_ADDR -- gateway IP address, hardcoded, */ /* used by NAT microblock only */ /* IF0_IP, IF1_IP, */ /* IF0_ETH_W0, IF0_ETH_W1, */ /* IF1_ETH_W0, IF1_ETH_W1 -- interface settings from interface */ /* table, used by NAT microblock only */ /******************************************************************/ int patch_microblocks(ix_buffer_free_list_info hwFreeListInfo) { ix_error err; ix_imported_symbol importSymbols[15]; ix_uint32 memChan; ix_uint32 offset; /* Set common symbols */ importSymbols[0].m_Name="BUF_FREE_LIST0"; importSymbols[0].m_Value = hwFreeListInfo.m_FreeListInfo; importSymbols[1].m_Name="BUF_SRAM_BASE"; err = ix_rm_get_phys_offset( hwFreeListInfo.m_pMetaBaseAddress, NULL,&memChan,&offset,NULL); if (err != IX_SUCCESS) panic("ix_rm_get_phys_offset failed for %s\\\\n", importSymbols[1].m_Name); importSymbols[1].m_Value = ME_SRAM_ADDR(offset,memChan); importSymbols[2].m_Name = "DL_REL_BASE"; err = ix_rm_get_phys_offset(hwFreeListInfo.m_pDataBaseAddress, NULL,&memChan,&offset,NULL); if (err != IX_SUCCESS) panic("ix_rm_get_phys_offset failed for %s\\\\n", importSymbols[2].m_Name); importSymbols[2].m_Value = offset - ((importSymbols[1].m_Value*hwFreeListInfo.m_DataElementSize)/ hwFreeListInfo.m_MetaElementSize); /* Set RX specific symbols */ importSymbols[3].m_Name="PACKET_COUNTERS_SRAM_BASE"; err = ix_rm_get_phys_offset(rx_cntr,NULL,&memChan,&offset,NULL); if (err != IX_SUCCESS) panic("ix_rm_get_phys_offset failed for %s\\\\n", importSymbols[3].m_Name); importSymbols[3].m_Value = ME_SRAM_ADDR(offset,memChan); importSymbols[4].m_Name="FREE_LIST_ID"; importSymbols[4].m_Value = hwFreeListInfo.m_FreeListInfo1; /* Patch ME 0x00 -- RX microblock */ err = ix_rm_ueng_patch_symbols(0x00,5,importSymbols); if (err != IX_SUCCESS) panic("ix_rm_ueng_patch_symbols failed for RX microblock\\\\n"); /* Set NAT specific symbols */ importSymbols[3].m_Name="NAT_TABLE_BASE"; err = ix_rm_get_phys_offset((void*)f_nat_table, NULL,&memChan,&offset,NULL); if (err != IX_SUCCESS) panic("ix_rm_get_phys_offset failed for %s\\\\n", importSymbols[3].m_Name); importSymbols[3].m_Value = offset; importSymbols[4].m_Name="ARP_TABLE_BASE"; err = ix_rm_get_phys_offset((void*)arp_table, NULL,&memChan,&offset,NULL); if (err != IX_SUCCESS) panic("ix_rm_get_phys_offset failed for %s\\\\n", importSymbols[4].m_Name); importSymbols[4].m_Value = ME_SRAM_ADDR(offset,memChan); importSymbols[5].m_Name="GATEWAY_IP_ADDR"; importSymbols[5].m_Value=gateway_ip; importSymbols[6].m_Name="IF0_IP"; importSymbols[6].m_Value=iface_table[0].ip_addr; importSymbols[7].m_Name="IF0_ETH_W0"; importSymbols[7].m_Value=iface_table[0].eth_w0; importSymbols[8].m_Name="IF0_ETH_W1"; importSymbols[8].m_Value=iface_table[0].eth_w1; importSymbols[9].m_Name="IF1_IP"; importSymbols[9].m_Value=iface_table[1].ip_addr; importSymbols[10].m_Name="IF1_ETH_W0"; importSymbols[10].m_Value=iface_table[1].eth_w0; importSymbols[11].m_Name="IF1_ETH_W1"; importSymbols[11].m_Value=iface_table[1].eth_w1; importSymbols[12].m_Name="TIMER_TABLE_BASE"; err = ix_rm_get_phys_offset((void*)f_timer, NULL,&memChan,&offset,NULL); if (err != IX_SUCCESS) panic("ix_rm_get_phys_offset failed for %s\\\\n", importSymbols[12].m_Name); importSymbols[12].m_Value = ME_SRAM_ADDR(offset,memChan); /* Patch ME 0x01 -- NAT microblock */ err = ix_rm_ueng_patch_symbols(0x01,13,importSymbols); if (err != IX_SUCCESS) panic("ix_rm_ueng_patch_symbols failed for NAT microblock\\\\n"); /* Patch ME 0x11 -- NAT microblock */ err = ix_rm_ueng_patch_symbols(0x11,13,importSymbols); if (err != IX_SUCCESS) panic("ix_rm_ueng_patch_symbols failed for NAT microblock\\\\n"); /* Set counter base for TX */ importSymbols[3].m_Name="PACKET_TX_COUNTER_BASE"; err = ix_rm_get_phys_offset((void*)tx_cntr, NULL,&memChan,&offset,NULL); if (err != IX_SUCCESS) panic("ix_rm_get_phys_offset failed for %s\\\\n", importSymbols[3].m_Name); importSymbols[3].m_Value = ME_SRAM_ADDR(offset,memChan); /* Patch ME 0x10 -- TX microblock */ err = ix_rm_ueng_patch_symbols(0x10,4,importSymbols); if (err != IX_SUCCESS) panic("ix_rm_ueng_patch_symbols failed for TX microblock\\\\n"); return(1); } /* Function to create RX and TX Scratch memory rings */ int create_scr_rings() { ix_error err; err = ix_rm_hw_scratch_ring_create(0, (PKT_RX_TO_NAT_SCR_RING_SIZE>>9), PKT_RX_TO_NAT_SCR_RING, &rxToNatRing); if (err != IX_SUCCESS) panic("ix_rm_hw_scratch_ring_create failed for Rx->Nat ring\\\\n"); err = ix_rm_hw_scratch_ring_create(0, (PACKET_TX_SCR_RING_0_SIZE>>9), PACKET_TX_SCR_RING_0, &txScrRing[0]); if (err != IX_SUCCESS) panic("ix_rm_hw_scratch_ring_create failed for TX 0 ring\\\\n"); err = ix_rm_hw_scratch_ring_create(0, (PACKET_TX_SCR_RING_1_SIZE>>9), PACKET_TX_SCR_RING_1, &txScrRing[1]); if (err != IX_SUCCESS) panic("ix_rm_hw_scratch_ring_create failed for TX 1 ring\\\\n"); err = ix_rm_hw_scratch_ring_create(0, (PACKET_TX_SCR_RING_2_SIZE>>9), PACKET_TX_SCR_RING_2, &txScrRing[2]); if (err != IX_SUCCESS) panic("ix_rm_hw_scratch_ring_create failed for TX 2 ring\\\\n"); err = ix_rm_hw_scratch_ring_create(0, (PACKET_TX_SCR_RING_3_SIZE>>9), PACKET_TX_SCR_RING_3, &txScrRing[3]); if (err != IX_SUCCESS) panic("ix_rm_hw_scratch_ring_create failed for TX 3 ring\\\\n"); return(1); } /* Function to initialize the 128-bit and 48-bit hash multipliers */ int init_hash() { ix_error err; ix_hash_multiplier_128 hash128m; ix_hash_multiplier_48 hash48m; hash128m.m_LW0=HASH_MULT_W0; hash128m.m_LW1=HASH_MULT_W1; hash128m.m_LW2=HASH_MULT_W2; hash128m.m_LW3=HASH_MULT_W3; printk("%s: Setting hash 128 multiplier to 0x%08X%08X%08X%08X\\\\n", NAT_DRIVER_NAME, (unsigned int)hash128m.m_LW3, (unsigned int)hash128m.m_LW2, (unsigned int)hash128m.m_LW1, (unsigned int)hash128m.m_LW0); err=ix_rm_hash_128_multiplier_set(&hash128m); if (err != IX_SUCCESS) panic("ix_rm_hash_128_multiplier_set failed\\\\n"); if (err != IX_SUCCESS) panic("ix_rm_hash_64_multiplier_set failed\\\\n"); hash48m.m_LW0=HASH_MULT_W0; hash48m.m_LW1=HASH_MULT_W1; printk("%s: Setting hash 48 multiplier to 0x%08X%08X\\\\n", NAT_DRIVER_NAME, (unsigned int)hash48m.m_LW1, (unsigned int)hash48m.m_LW0); err=ix_rm_hash_48_multiplier_set(&hash48m); if (err != IX_SUCCESS) panic("ix_rm_hash_48_multiplier_set failed\\\\n"); return(1); }