From 1048c7b452f060c290d973d8a50ec4d178c937e6 Mon Sep 17 00:00:00 2001 From: blogic Date: Fri, 2 Nov 2012 20:07:02 +0000 Subject: [lantiq] move files/ -> files-3.3/ git-svn-id: svn://svn.openwrt.org/openwrt/trunk@34060 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- target/linux/lantiq/files-3.3/net/ipv4/svip_nat.c | 1569 +++++++++++++++++++++ 1 file changed, 1569 insertions(+) create mode 100644 target/linux/lantiq/files-3.3/net/ipv4/svip_nat.c (limited to 'target/linux/lantiq/files-3.3/net') diff --git a/target/linux/lantiq/files-3.3/net/ipv4/svip_nat.c b/target/linux/lantiq/files-3.3/net/ipv4/svip_nat.c new file mode 100644 index 000000000..04a0d223a --- /dev/null +++ b/target/linux/lantiq/files-3.3/net/ipv4/svip_nat.c @@ -0,0 +1,1569 @@ +/****************************************************************************** + + Copyright (c) 2009 + Lantiq Deutschland GmbH + Am Campeon 3; 81726 Munich, Germany + + THE DELIVERY OF THIS SOFTWARE AS WELL AS THE HEREBY GRANTED NON-EXCLUSIVE, + WORLDWIDE LICENSE TO USE, COPY, MODIFY, DISTRIBUTE AND SUBLICENSE THIS + SOFTWARE IS FREE OF CHARGE. + + THE LICENSED SOFTWARE IS PROVIDED "AS IS" AND INFINEON EXPRESSLY DISCLAIMS + ALL REPRESENTATIONS AND WARRANTIES, WHETHER EXPRESS OR IMPLIED, INCLUDING + WITHOUT LIMITATION, WARRANTIES OR REPRESENTATIONS OF WORKMANSHIP, + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, THAT THE + OPERATING OF THE LICENSED SOFTWARE WILL BE ERROR FREE OR FREE OF ANY THIRD + PARTY CLAIMS, INCLUDING WITHOUT LIMITATION CLAIMS OF THIRD PARTY INTELLECTUAL + PROPERTY INFRINGEMENT. + + EXCEPT FOR ANY LIABILITY DUE TO WILFUL ACTS OR GROSS NEGLIGENCE AND EXCEPT + FOR ANY PERSONAL INJURY INFINEON SHALL IN NO EVENT BE LIABLE FOR ANY CLAIM + OR DAMAGES OF ANY KIND, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + **************************************************************************** + +Description : This file contains implementation of Custom NAT function +for Infineon's VINETIC-SVIP16 + *******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* just to shut up a warning */ +#include +#include + +#include + +MODULE_AUTHOR("Lantiq Deutschland GmbH"); +MODULE_DESCRIPTION("SVIP Network Address Translation module"); +MODULE_LICENSE("GPL"); + +#define SVIP_NAT_INFO_STR "@(#)SVIP NAT, version "SVIP_NAT_VERSION + +/** maximum voice packet channels possible on the SVIP LC system + (equals maximum number of Codec channels possible) */ +#define SVIP_SYS_CODEC_NUM ((SVIP_SYS_NUM) * (SVIP_CODEC_NUM)) + +/** end UDP port number of the SVIP Linecard System */ +#define SVIP_UDP_TO ((SVIP_UDP_FROM) + (SVIP_SYS_CODEC_NUM) - 1) + +/** end UDP port number of the Master SVIP in SVIP Linecard System */ +#define SVIP_UDP_TO_VOFW0 ((SVIP_UDP_FROM) + (SVIP_CODEC_NUM) - 1) + +#define SVIP_PORT_INRANGE(nPort) \ + ((nPort) >= (SVIP_UDP_FROM) && (nPort) <= (SVIP_UDP_TO)) + +#define SVIP_PORT_INDEX(nPort) (nPort - SVIP_UDP_FROM) + +#define SVIP_NET_DEV_ETH0_IDX 0 +#define SVIP_NET_DEV_VETH0_IDX 1 +#define SVIP_NET_DEV_LO_IDX 2 + +#define SVIP_NET_DEV_ETH0_NAME "eth0" +#define SVIP_NET_DEV_ETH1_NAME "eth1" +#define SVIP_NET_DEV_VETH1_NAME "veth0" +#define SVIP_NET_DEV_LO_NAME "lo" + +#define SVIP_NAT_STATS_LOC2REM 0 +#define SVIP_NAT_STATS_REM2LOC 1 +#define SVIP_NAT_STATS_TYPES 2 + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) +#define SVIP_NAT_FOR_EACH_NETDEV(d) for_each_netdev(&init_net, dev) +#define SVIP_NAT_IP_HDR(ethhdr) ip_hdr(ethhdr) +#else +#define SVIP_NAT_FOR_EACH_NETDEV(d) for(d=dev_base; dev; dev = dev->next) +#define SVIP_NAT_IP_HDR(ethhdr) (ethhdr)->nh.iph +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +#define SVIP_NAT_SKB_MAC_HEADER(ethhdr) (ethhdr)->mac.ethernet +#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) +#define SVIP_NAT_SKB_MAC_HEADER(ethhdr) (ethhdr)->mac.raw +#else +#define SVIP_NAT_SKB_MAC_HEADER(ethhdr) skb_mac_header(ethhdr) +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) +#define VLAN_DEV_REAL_DEV(dev) vlan_dev_real_dev(dev) +#define VLAN_DEV_VLAN_ID(dev) vlan_dev_vlan_id(dev) +#else +#define VLAN_DEV_REAL_DEV(dev) (VLAN_DEV_INFO(dev)->real_dev) +#define VLAN_DEV_VLAN_ID(dev) (VLAN_DEV_INFO(dev)->vlan_id) +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +#if ! ((LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) && \ + (defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE))) +#define VLAN_8021Q_UNUSED +#endif + + +extern spinlock_t vlan_group_lock; +extern struct net_device *__vlan_find_dev_deep(struct net_device *real_dev, unsigned short VID); + +typedef struct SVIP_NAT_stats +{ + unsigned long inPackets; + unsigned long outPackets; + unsigned long outErrors; +} SVIP_NAT_stats_t; + +typedef struct SVIP_NAT_table_entry +{ + SVIP_NAT_IO_Rule_t natRule; + SVIP_NAT_stats_t natStats[SVIP_NAT_STATS_TYPES]; +} SVIP_NAT_table_entry_t; + +/* pointer to the SVIP NAT table */ +static SVIP_NAT_table_entry_t *pNatTable = NULL; + +struct net_device *net_devs[3]; +static u32 *paddr_eth0; +static u32 *paddr_eth0_0; +static u32 *paddr_veth0; +static u32 *pmask_veth0; + +static struct semaphore *sem_nat_tbl_access; +static int proc_read_in_progress = 0; + +static int nDeviceOpen = 0; + +/* saves the NAT table index between subsequent invocation */ +static int nProcReadIdx = 0; + +static long SVIP_NAT_device_ioctl(struct file *,unsigned int ,unsigned long); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) +static int SVIP_NAT_device_release (struct inode *,struct file *); +#else +static void SVIP_NAT_device_release (struct inode *,struct file *); +#endif +static int SVIP_NAT_device_open (struct inode *,struct file *); + +/* This structure holds the interface functions supported by + the SVIP NAT configuration device. */ +struct file_operations SVIP_NAT_Fops = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) +owner: THIS_MODULE, +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) */ + llseek: NULL, /* seek */ + read: NULL, + write: NULL, + readdir: NULL, /* readdir */ + poll: NULL, /* select */ + unlocked_ioctl: SVIP_NAT_device_ioctl, /* ioctl */ + mmap: NULL, /* mmap */ + open: SVIP_NAT_device_open, /* open, */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + flush: NULL, /* flush */ +#endif + release: SVIP_NAT_device_release /* close */ +}; + +/** Structure holding MISC module operations */ +static struct miscdevice SVIP_NAT_miscdev = +{ +minor: MINOR_NUM_SVIP_NAT, + name: SVIP_NAT_DEVICE_NAME, + fops: &SVIP_NAT_Fops +}; + +#ifdef CONFIG_SVIP_FW_PKT_SNIFFER +int nSVIP_NAT_Sniffer; +unsigned char pSVIP_NAT_SnifferMAC[ETH_ALEN]; +int nSVIP_NAT_SnifferMacSet; +#endif + +/******************************************************************************/ +/** + Function to read /proc/net/svip_nat/nat proc entry + + \arguments + page - pointer to page buffer + start - pointer to start address pointer + off - offset + count - maximum data length to read + eof - end of file flag + data - proc read data (provided by the function + pointed to by data) + + \return + length of read data + + \remarks: + Each call of this routine forces a copy_to_user of the data returned by + 'fn'. This routine will be called by the user until 'len = 0'. + ****************************************************************************/ +static int SVIP_NAT_ProcRead (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + unsigned long flags; + int (*fn)(char *buf, int size); + int len; + + /* If the NAT table index is negative, the reading has completed */ + if (nProcReadIdx < 0) + { + nProcReadIdx = 0; + *eof = 1; + proc_read_in_progress = 0; + up(sem_nat_tbl_access); + return 0; + } + + local_irq_save(flags); + if (!proc_read_in_progress) + { + proc_read_in_progress = 1; + local_irq_restore(flags); + /* we use this semaphore in order to ensure no other party(could be ioctl + FIO_SVIP_NAT_RULE_LIST), uses function SVIP_NAT_ProcReadNAT(), during + the time read of the proc file takes place */ + down(sem_nat_tbl_access); + } + else + { + local_irq_restore(flags); + } + + if (data != NULL) + { + fn = data; + len = fn (page, count); + /* In this setup each read of the proc entries returns the read data by + 'fn' to the user. The user keeps issuing read requests as long as the + returned value of 'len' is greater than zero. */ + *eof = 1; + *start = page; + } + else + { + len = 0; + } + + return len; +} + +#ifdef CONFIG_SVIP_FW_PKT_SNIFFER +/** + Function to read remaining proc entries + */ +static int SVIP_NAT_ProcReadGen (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int (*fn)(char *buf, int size); + int len = 0; + + MOD_INC_USE_COUNT; + + if (data == NULL) + { + MOD_DEC_USE_COUNT; + return 0; + } + + fn = data; + len = fn (page, count); + + if (len <= off + count) + { + *eof = 1; + } + *start = page + off; + len -= off; + if (len > count) + { + len = count; + } + if (len < 0) + { + len = 0; + } + + MOD_DEC_USE_COUNT; + + return len; +} +#endif + +/******************************************************************************/ +/** + Function for setting up /proc/net/svip_nat read data + + \arguments + buf - pointer to read buffer + count - size of read buffer + + \return + length of read data into buffer + + \remarks: + The global variable 'nProcReadIdx' is used to save the table index where + the reading of the NAT table stopped. Reading is stopped when the end of + the read buffer is approached. On the next itteration the reading continues + from the saved index. + *******************************************************************************/ +static int SVIP_NAT_ProcReadNAT(char *buf, int count) +{ + int i, j; + int len = 0; + SVIP_NAT_IO_Rule_t *pNatRule; + + if (nProcReadIdx == -1) + { + nProcReadIdx = 0; + return 0; + } + + if (nProcReadIdx == 0) + { + len = sprintf(buf+len, + "Remote host IP " /* 16 char */ + "Remote host MAC " /* 19 char */ + "Local host IP " /* 15 char */ + "Local host MAC " /* 19 char */ + "Local host UDP " /* 16 char */ + "Loc->Rem(in/out/err) " /* 22 char */ + "Rem->Loc(in/out/err)\n\r"); + } + + for (i = nProcReadIdx; i < SVIP_SYS_CODEC_NUM; i++) + { + int slen; + + pNatRule = &pNatTable[i].natRule; + + if (pNatRule->remIP != 0) + { + /* make sure not to overwrite the buffer */ + if (count < len+120) + break; + + /* remIP */ + slen = sprintf(buf+len, "%d.%d.%d.%d", + (int)((pNatRule->remIP >> 24) & 0xff), + (int)((pNatRule->remIP >> 16) & 0xff), + (int)((pNatRule->remIP >> 8) & 0xff), + (int)((pNatRule->remIP >> 0) & 0xff)); + len += slen; + for (j = 0; j < (16-slen); j++) + len += sprintf(buf+len, " "); + + /* remMAC */ + slen = 0; + for (j = 0; j < ETH_ALEN; j++) + { + slen += sprintf(buf+len+slen, "%02x%s", + pNatRule->remMAC[j], j < ETH_ALEN-1 ? ":" : " "); + } + len += slen; + for (j = 0; j < (19-slen); j++) + len += sprintf(buf+len, " "); + + /* locIP */ + slen = sprintf(buf+len, "%d.%d.%d.%d", + (int)((pNatRule->locIP >> 24) & 0xff), + (int)((pNatRule->locIP >> 16) & 0xff), + (int)((pNatRule->locIP >> 8) & 0xff), + (int)((pNatRule->locIP >> 0) & 0xff)); + len += slen; + for (j = 0; j < (15-slen); j++) + len += sprintf(buf+len, " "); + + /* locMAC */ + slen = 0; + for (j = 0; j < ETH_ALEN; j++) + { + slen += sprintf(buf+len+slen, "%02x%s", + pNatRule->locMAC[j], j < ETH_ALEN-1 ? ":" : " "); + } + len += slen; + for (j = 0; j < (19-slen); j++) + len += sprintf(buf+len, " "); + + /* locUDP */ + slen = sprintf(buf+len, "%d", pNatRule->locUDP); + len += slen; + for (j = 0; j < (16-slen); j++) + len += sprintf(buf+len, " "); + + /* NAT statistics, Local to Remote translation */ + slen = sprintf(buf+len, "(%ld/%ld/%ld)", + pNatTable[i].natStats[SVIP_NAT_STATS_LOC2REM].inPackets, + pNatTable[i].natStats[SVIP_NAT_STATS_LOC2REM].outPackets, + pNatTable[i].natStats[SVIP_NAT_STATS_LOC2REM].outErrors); + len += slen; + for (j = 0; j < (22-slen); j++) + len += sprintf(buf+len, " "); + + /* NAT statistics, Remote to Local translation */ + len += sprintf(buf+len, "(%ld/%ld/%ld)\n\r", + pNatTable[i].natStats[SVIP_NAT_STATS_REM2LOC].inPackets, + pNatTable[i].natStats[SVIP_NAT_STATS_REM2LOC].outPackets, + pNatTable[i].natStats[SVIP_NAT_STATS_REM2LOC].outErrors); + } + } + if (i == SVIP_SYS_CODEC_NUM) + nProcReadIdx = -1; /* reading completed */ + else + nProcReadIdx = i; /* reading still in process, buffer was full */ + + return len; +} + +#ifdef CONFIG_SVIP_FW_PKT_SNIFFER +/** + Converts MAC address from ascii to hex respesentaion + */ +static int SVIP_NAT_MacAsciiToHex(const char *pMacStr, unsigned char *pMacHex) +{ + int i=0, c=0, b=0, n=0; + + memset(pMacHex, 0, ETH_ALEN); + while (pMacStr[i] != '\0') + { + if (n >= 0) + { + unsigned char nToHex = 0; + + /* check for hex digit */ + if (pMacStr[i] >= '0' && pMacStr[i] <= '9') + nToHex = 0x30; + else if (pMacStr[i] >= 'a' && pMacStr[i] <= 'f') + nToHex = 0x57; + else if (pMacStr[i] >= 'A' && pMacStr[i] <= 'F') + nToHex = 0x37; + else + { + if (n != 0) + { + printk(KERN_ERR "SVIP NAT: invalid MAC address format[%s]\n", pMacStr); + return -1; + } + i++; + continue; + } + n^=1; + pMacHex[b] |= ((pMacStr[i] - nToHex)&0xf) << (4*n); + if (n == 0) + { + /* advance to next byte, check if complete */ + if (++b >= ETH_ALEN) + return 0; + /* byte completed, next we expect a colon... */ + c = 1; + /* and, do not check for hex digit */ + n = -1; + } + i++; + continue; + } + if (c == 1) + { + if (pMacStr[i] == ':') + { + /* next we expect hex digit, again */ + n = 0; + } + else + { + printk(KERN_ERR "SVIP NAT: invalid MAC address format[%s]\n", pMacStr); + return -1; + } + } + i++; + } + return 0; +} + +/** + Used to set the destination MAC address of a host where incoming + SVIP VoFW packets are to be addressed. In case the address is set + to 00:00:00:00:00:00 (the default case), the packets will written + out to eth0 with its original MAC addess. + + \remark +usage: 'echo "00:03:19:00:15:D1" > cat /proc/net/svip_nat/snifferMAC' +*/ +int SVIP_NAT_ProcWriteSnifferMAC (struct file *file, const char *buffer, + unsigned long count, void *data) +{ + /* at least strlen("xx:xx:xx:xx:xx:xx") characters, followed by '\0' */ + if (count >= 18) + { + int ret; + + ret = SVIP_NAT_MacAsciiToHex(buffer, pSVIP_NAT_SnifferMAC); + + if (ret != 0) + return 0; + + if (!(pSVIP_NAT_SnifferMAC[0]==0 && pSVIP_NAT_SnifferMAC[1]==0 && + pSVIP_NAT_SnifferMAC[2]==0 && pSVIP_NAT_SnifferMAC[3]==0 && + pSVIP_NAT_SnifferMAC[4]==0 && pSVIP_NAT_SnifferMAC[5]==0)) + { + nSVIP_NAT_SnifferMacSet = 1; + } + } + return count; +} + +/** + Used to read the destination MAC address of a sniffer host + */ +int SVIP_NAT_ProcReadSnifferMAC (char *buf, int count) +{ + int len = 0; + + len = snprintf(buf, count, "%02x:%02x:%02x:%02x:%02x:%02x\n", + pSVIP_NAT_SnifferMAC[0], pSVIP_NAT_SnifferMAC[1], + pSVIP_NAT_SnifferMAC[2], pSVIP_NAT_SnifferMAC[3], + pSVIP_NAT_SnifferMAC[4], pSVIP_NAT_SnifferMAC[5]); + + if (len > count) + { + printk(KERN_ERR "SVIP NAT: Only part of the text could be put into the buffer\n"); + return count; + } + + return len; +} + +/** + Used to switch VoFW message sniffer on/off + + \remark +usage: 'echo "1" > cat /proc/net/svip_nat/snifferOnOff' +*/ +int SVIP_NAT_ProcWriteSnifferOnOff (struct file *file, const char *buffer, + unsigned long count, void *data) +{ + /* at least one digit expected, followed by '\0' */ + if (count >= 2) + { + int ret, nSnifferOnOff; + + ret = sscanf(buffer, "%d", &nSnifferOnOff); + + if (ret != 1) + return count; + + if (nSnifferOnOff > 0) + nSnifferOnOff = 1; + + nSVIP_NAT_Sniffer = nSnifferOnOff; + } + return count; +} + +/** + Used to read the VoFW message sniffer configuration (on/off) + */ +int SVIP_NAT_ProcReadSnifferOnOff (char *buf, int count) +{ + int len = 0; + + len = snprintf(buf, count, "%d\n", nSVIP_NAT_Sniffer); + + if (len > count) + { + printk(KERN_ERR "SVIP NAT: Only part of the text could be put into the buffer\n"); + return count; + } + + return len; +} +#endif + +/******************************************************************************/ +/** + Creates proc read/write entries + + \return + 0 on success, -1 on error + */ +/******************************************************************************/ +static int SVIP_NAT_ProcInstall(void) +{ + struct proc_dir_entry *pProcParentDir, *pProcDir; + struct proc_dir_entry *pProcNode; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + pProcParentDir = proc_net; +#else + pProcParentDir = init_net.proc_net; +#endif + pProcDir = proc_mkdir(SVIP_NAT_DEVICE_NAME, pProcParentDir); + if (pProcDir == NULL) + { + printk(KERN_ERR "SVIP NAT: cannot create proc dir %s/%s\n\r", + pProcParentDir->name, SVIP_NAT_DEVICE_NAME); + return -1; + } + + pProcNode = create_proc_read_entry("nat", S_IFREG|S_IRUGO, pProcDir, + SVIP_NAT_ProcRead, (void *)SVIP_NAT_ProcReadNAT); + if (pProcNode == NULL) + { + printk(KERN_ERR "SVIP NAT: cannot create proc entry %s/%s", + pProcDir->name, "nat"); + return -1; + } + +#ifdef CONFIG_SVIP_FW_PKT_SNIFFER + nSVIP_NAT_Sniffer = 0; + /* creates proc entry for switching on/off sniffer to VoFW messages */ + pProcNode = create_proc_read_entry("snifferOnOff", S_IFREG|S_IRUGO|S_IWUGO, + pProcDir, SVIP_NAT_ProcReadGen, (void *)SVIP_NAT_ProcReadSnifferOnOff); + if (pProcNode == NULL) + { + printk(KERN_ERR "SVIP NAT: cannot create proc entry %s/%s\n\r", + pProcDir->name, "snifferOnOff"); + return -1; + } + pProcNode->write_proc = SVIP_NAT_ProcWriteSnifferOnOff; + + memset (pSVIP_NAT_SnifferMAC, 0, ETH_ALEN); + nSVIP_NAT_SnifferMacSet = 0; + /* creates proc entry for setting MAC address of sniffer host to VoFW messages */ + pProcNode = create_proc_read_entry("snifferMAC", S_IFREG|S_IRUGO|S_IWUGO, + pProcDir, SVIP_NAT_ProcReadGen, (void *)SVIP_NAT_ProcReadSnifferMAC); + if (pProcNode == NULL) + { + printk(KERN_ERR "SVIP NAT: cannot create proc entry %s/%s\n\r", + pProcDir->name, "snifferMAC"); + return -1; + } + pProcNode->write_proc = SVIP_NAT_ProcWriteSnifferMAC; +#endif + + return 0; +} + +/******************************************************************************/ +/** + No actions done here, simply a check is performed if an open has already + been performed. Currently only a single open is allowed as it is a sufficient + to have hat a single process configuring the SVIP NAT at one time. + + \arguments + inode - pointer to disk file data + file - pointer to device file data + + \return + 0 on success, else -1 + */ +/******************************************************************************/ +static int SVIP_NAT_device_open(struct inode *inode, struct file *file) +{ + unsigned long flags; + struct in_device *in_dev; + struct in_ifaddr *ifa; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + local_irq_save(flags); +#else + local_save_flags(flags); +#endif + + if (nDeviceOpen) + { + MOD_INC_USE_COUNT; + local_irq_restore(flags); + nDeviceOpen++; + return 0; + } + + /* find pointer to IP address of eth0 */ + if ((in_dev=in_dev_get(net_devs[SVIP_NET_DEV_ETH0_IDX])) != NULL) + { + for (ifa = in_dev->ifa_list; ifa != NULL; ifa = ifa->ifa_next) + { + if (!paddr_eth0 && ifa->ifa_address != 0) + { + paddr_eth0 = &ifa->ifa_address; + continue; + } + if (paddr_eth0 && ifa->ifa_address != 0) + { + paddr_eth0_0 = &ifa->ifa_address; + break; + } + } + in_dev_put(in_dev); + } + if (paddr_eth0 == NULL || paddr_eth0_0 == NULL) + { + local_irq_restore(flags); + return -ENODATA; + } + + /* find pointer to IP address of veth0 */ + if ((in_dev=in_dev_get(net_devs[SVIP_NET_DEV_VETH0_IDX])) != NULL) + { + for (ifa = in_dev->ifa_list; ifa != NULL; ifa = ifa->ifa_next) + { + if (ifa->ifa_address != 0) + { + paddr_veth0 = &ifa->ifa_address; + pmask_veth0 = &ifa->ifa_mask; + break; + } + } + in_dev_put(in_dev); + } + if (paddr_veth0 == NULL) + { + local_irq_restore(flags); + return -ENODATA; + } + + MOD_INC_USE_COUNT; + nDeviceOpen++; + local_irq_restore(flags); + + return 0; +} + + +/******************************************************************************/ +/** + This function is called when a process closes the SVIP NAT device file + + \arguments + inode - pointer to disk file data + file - pointer to device file data + + \return + 0 on success, else -1 + +*/ +/******************************************************************************/ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) +static int SVIP_NAT_device_release(struct inode *inode, + struct file *file) +#else +static void SVIP_NAT_device_release(struct inode *inode, + struct file *file) +#endif +{ + unsigned long flags; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + save_flags(flags); + cli(); +#else + local_save_flags(flags); +#endif + + /* The device can now be openned by the next caller */ + nDeviceOpen--; + + MOD_DEC_USE_COUNT; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + restore_flags(flags); +#else + local_irq_restore(flags); +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + return 0; +#endif +} + + +/******************************************************************************/ +/** + This function is called when a process closes the SVIP NAT device file + + \arguments + inode - pointer to disk file data + file - pointer to device file data + ioctl_num - ioctl number requested + ioctl_param - pointer to data related to the ioctl number + + \return + 0 on success, else -1 + +*/ +/******************************************************************************/ +long SVIP_NAT_device_ioctl (struct file *file, + unsigned int ioctl_num, unsigned long ioctl_param) +{ + int ret = 0; + SVIP_NAT_IO_Rule_t *pNatRule, *pNatRuleIn; + SVIP_UDP_PORT_t nPort; + int nNatIdx; + int bWrite = 0; + int bRead = 0; + unsigned char *pData = 0; + int nSize; + + if (_IOC_DIR(ioctl_num) & _IOC_WRITE) + bWrite = 1; + if (_IOC_DIR(ioctl_num) & _IOC_READ) + bRead = 1; + nSize = _IOC_SIZE(ioctl_num); + + if (nSize > sizeof(int)) + { + if (bRead || bWrite) + { + pData = kmalloc (nSize, GFP_KERNEL); + if (bWrite) + { + if (copy_from_user ((void *)pData, (void *)ioctl_param, nSize) != 0) + { + printk(KERN_ERR "SVIP NAT: ioctl %x: copy_from_user() failed!\n", ioctl_num); + ret = -1; + goto error; + } + } + } + } + + switch (ioctl_num) + { + case FIO_SVIP_NAT_RULE_ADD: + + pNatRuleIn = (SVIP_NAT_IO_Rule_t *)pData; + + /* check if destination UDP port is within range */ + nPort = ntohs(pNatRuleIn->locUDP); + + if (!SVIP_PORT_INRANGE(nPort)) + { + printk(KERN_ERR "SVIP NAT: Error, UDP port(%d) is out of range(%d..%d)\n", + nPort, SVIP_UDP_FROM, SVIP_UDP_TO); + ret = -1; + goto error; + } + nNatIdx = SVIP_PORT_INDEX(nPort); + + down(sem_nat_tbl_access); + pNatRule = &pNatTable[nNatIdx].natRule; + + /* add rule to the NAT table */ + pNatRule->remIP = pNatRuleIn->remIP; + memcpy((char *)pNatRule->remMAC, (char *)pNatRuleIn->remMAC, ETH_ALEN); + pNatRule->locIP = pNatRuleIn->locIP; + memcpy((char *)pNatRule->locMAC, (char *)pNatRuleIn->locMAC, ETH_ALEN); + pNatRule->locUDP = pNatRuleIn->locUDP; + + memset(pNatTable[nNatIdx].natStats, 0, + sizeof(SVIP_NAT_stats_t)*SVIP_NAT_STATS_TYPES); + up(sem_nat_tbl_access); + break; + + case FIO_SVIP_NAT_RULE_REMOVE: + + pNatRuleIn = (SVIP_NAT_IO_Rule_t *)pData; + + /* check if destination UDP port is within range */ + nPort = ntohs(pNatRuleIn->locUDP); + if (!SVIP_PORT_INRANGE(nPort)) + { + printk(KERN_ERR "SVIP NAT: Error, UDP port(%d) is out of range(%d..%d)\n", + nPort, SVIP_UDP_FROM, SVIP_UDP_TO); + ret = -1; + goto error; + } + nNatIdx = SVIP_PORT_INDEX(nPort); + down(sem_nat_tbl_access); + /* remove rule from the NAT table */ + memset(&pNatTable[nNatIdx], 0, sizeof(SVIP_NAT_table_entry_t)); + up(sem_nat_tbl_access); + break; + + case FIO_SVIP_NAT_RULE_LIST: + { + int len; + char buf[256]; + + down(sem_nat_tbl_access); + while (nProcReadIdx != -1) + { + len = SVIP_NAT_ProcReadNAT(buf, 256); + if (len > 0) + printk("%s", buf); + } + nProcReadIdx = 0; + up(sem_nat_tbl_access); + break; + } + + default: + printk(KERN_ERR "SVIP NAT: unsupported ioctl (%x) command for device %s\n", + ioctl_num, PATH_SVIP_NAT_DEVICE_NAME); + ret = -1; + goto error; + } + + if (nSize > sizeof(int)) + { + if (bRead) + { + if (copy_to_user ((void *)ioctl_param, (void *)pData, nSize) != 0) + { + printk(KERN_ERR "SVIP NAT: ioctl %x: copy_to_user() failed!\n", ioctl_num); + ret = -1; + goto error; + } + } + } + +error: + if (pData) + kfree(pData); + + return ret; +} + +#if 0 +void dump_msg(unsigned char *pData, unsigned int nLen) +{ + int i; + + for (i=0; i> 16) + sum = (sum & 0xffff)+((sum >> 16) & 0xffff); + + /* one's complement the result */ + sum = ~sum; + + return (u16)(sum & 0xffff); +} + + +/******************************************************************************/ +/** + Returns a pointer to an ipv4 address assigned to device dev. The ipv4 + instance checked is pointed to by ifa_start. The function is suited for + itterative calls. + + \arguments + dev - pointer to network interface + ifa_start - pointer to ipv4 instance to return ipv4 address assigned + to, NULL for the first one + ppifa_addr - output parameter + + \return + pointer to the next ipv4 instance, which can be null if ifa_start was + the last instance present + */ +/******************************************************************************/ +static struct in_ifaddr *get_ifaddr(struct net_device *dev, + struct in_ifaddr *ifa_start, unsigned int **ppifa_addr) +{ + struct in_device *in_dev; + struct in_ifaddr *ifa = NULL; + + if ((in_dev=in_dev_get(dev)) != NULL) + { + if (ifa_start == NULL) + ifa = in_dev->ifa_list; + else + ifa = ifa_start; + if (ifa) + { + *ppifa_addr = &ifa->ifa_address; + ifa = ifa->ifa_next; + } + in_dev_put(in_dev); + return ifa; + } + *ppifa_addr = NULL; + return NULL; +} + +/******************************************************************************/ +/** + This function performs IP NAT for received packets satisfying the + following requirements: + + - packet is destined to local IP host + - transport protocol type is UDP + - destination UDP port is within range + + \arguments + skb - pointer to the receiving socket buffer + + \return + returns 1 on performed SVIP NAT, else returns 0 + + \remarks + When function returns 0, it indicates the caller to pass the + packet up the IP stack to make further decision about it + */ +/******************************************************************************/ +int do_SVIP_NAT (struct sk_buff *skb) +{ + struct net_device *real_dev; + struct iphdr *iph; + struct udphdr *udph; + SVIP_NAT_IO_Rule_t *pNatRule; + int nNatIdx, in_eth0, nDir; +#ifndef VLAN_8021Q_UNUSED + int vlan; + unsigned short vid; +#endif /* ! VLAN_8021Q_UNUSED */ + SVIP_UDP_PORT_t nPort; + u32 orgSrcIp, orgDstIp, *pSrcIp, *pDstIp; + struct ethhdr *ethh; + + /* do not consider if SVIP NAT device not open. */ + if (!nDeviceOpen) + { + return 0; + } + + /* consider only UDP packets. */ + iph = SVIP_NAT_IP_HDR(skb); + if (iph->protocol != IPPROTO_UDP) + { + return 0; + } + + udph = (struct udphdr *)((u_int32_t *)iph + iph->ihl); + /* consider only packets which UDP port numbers reside within + the predefined SVIP NAT UDP port range. */ + if ((!SVIP_PORT_INRANGE(ntohs(udph->dest))) && + (!SVIP_PORT_INRANGE(ntohs(udph->source)))) + { + return 0; + } + +#ifndef VLAN_8021Q_UNUSED + /* check if packet delivered over VLAN. VLAN packets will be routed over + the VLAN interfaces of the respective real Ethernet interface, if one + exists(VIDs must match). Else, the packet will be send out as IEEE 802.3 + Ethernet frame */ + if (skb->dev->priv_flags & IFF_802_1Q_VLAN) + { + vlan = 1; + vid = VLAN_DEV_VLAN_ID(skb->dev); + real_dev = VLAN_DEV_REAL_DEV(skb->dev); + } + else + { + vlan = 0; + vid = 0; + real_dev = skb->dev; + } +#endif /* ! VLAN_8021Q_UNUSED */ + +#ifdef CONFIG_SVIP_FW_PKT_SNIFFER + /** Debugging feature which can be enabled by writing, + 'echo 1 > /proc/net/svip_nat/snifferOnOff'. + It copies all packets received on veth0 and, sends them out over eth0. + When a destination MAC address is specified through + /proc/net/svip_nat/snifferMAC, this MAC addess will substitute the + original MAC address of the packet. + It is recommended to specify a MAC address of some host where Wireshark + runs and sniffs for this traffic, else you may flood your LAN with + undeliverable traffic. + +NOTE: In case of VLAN traffic the VLAN header information is lost. */ + if (nSVIP_NAT_Sniffer) + { + if (real_dev == net_devs[SVIP_NET_DEV_VETH0_IDX]) + { + struct sk_buff *copied_skb; + + /* gain the Ethernet header from the skb */ + skb_push(skb, ETH_HLEN); + + copied_skb = skb_copy (skb, GFP_ATOMIC); + + if (nSVIP_NAT_SnifferMacSet == 1) + { + ethh = (struct ethhdr *)SVIP_NAT_SKB_MAC_HEADER(copied_skb); + memcpy((char *)ethh->h_dest, (char *)pSVIP_NAT_SnifferMAC, ETH_ALEN); + } + copied_skb->dev = net_devs[SVIP_NET_DEV_ETH0_IDX]; + dev_queue_xmit(copied_skb); + + /* skip the ETH header again */ + skb_pull(skb, ETH_HLEN); + } + } +#endif + + + /* check if packet arrived on eth0 */ + if (real_dev == net_devs[SVIP_NET_DEV_ETH0_IDX]) + { + /* check if destination IP address equals the primary assigned IP address + of interface eth0. This is the case of packets originating from a + remote peer that are to be delivered to a channel residing on THIS + voice linecard system. This is typical SVIP NAT case, therefore this + rule is placed on top. */ + if (iph->daddr == *paddr_eth0) + { + nPort = ntohs(udph->dest); + nDir = SVIP_NAT_STATS_REM2LOC; + } + /* check if destination IP address equals the secondary assigned IP address + of interface eth0. This is not a typical SVIP NAT case. It is basically + there, as someone might like for debugging purpose to use the LCC to route + Slave SVIP packets which are part of voice/fax streaming. */ + else if (iph->daddr == *paddr_eth0_0) + { + nPort = ntohs(udph->source); + nDir = SVIP_NAT_STATS_LOC2REM; + } +#ifndef VLAN_8021Q_UNUSED + /* when the packet did not hit the top two rules, here we check if the packet + has addressed any of the IP addresses assigned to the VLAN interface attached + to eth0. This is not recommended approach because of the CPU cost incurred. */ + else if (vlan) + { + unsigned int *pifa_addr; + struct in_ifaddr *ifa_start = NULL; + int i = 0; + + do + { + ifa_start = get_ifaddr(skb->dev, ifa_start, &pifa_addr); + if (!pifa_addr) + { + /* VLAN packet received on vlan interface attached to eth0, + however no IP address assigned to the interface. + The packet is ignored. */ + return 0; + } + if (iph->daddr == *pifa_addr) + { + /* packet destined to... */ + break; + } + if (!ifa_start) + { + return 0; + } + i++; + } while (ifa_start); + if (!i) + { + /* ...primary assigned IP address to the VLAN interface. */ + nPort = ntohs(udph->dest); + nDir = SVIP_NAT_STATS_REM2LOC; + } + else + { + /* ...secondary assigned IP address to the VLAN interface. */ + nPort = ntohs(udph->source); + nDir = SVIP_NAT_STATS_LOC2REM; + } + } +#endif /* ! VLAN_8021Q_UNUSED */ + else + { + return 0; + } + in_eth0 = 1; + } + /* check if packet arrived on veth0 */ + else if (real_dev == net_devs[SVIP_NET_DEV_VETH0_IDX]) + { + nPort = ntohs(udph->source); + nDir = SVIP_NAT_STATS_LOC2REM; + in_eth0 = 0; + } + else + { + /* packet arrived neither on eth0, nor veth0 */ + return 0; + } + + /* calculate the respective index of the NAT table */ + nNatIdx = SVIP_PORT_INDEX(nPort); + /* process the packet if a respective NAT rule exists */ + pNatRule = &pNatTable[nNatIdx].natRule; + + ethh = (struct ethhdr *)SVIP_NAT_SKB_MAC_HEADER(skb); + + /* copy packet's original source and destination IP addresses to use + later on to perform efficient checksum recalculation */ + orgSrcIp = iph->saddr; + orgDstIp = iph->daddr; + + if (in_eth0) + { + u8 *pDstMac; + + /* Process packet arrived on eth0 */ + + if (nDir == SVIP_NAT_STATS_REM2LOC && iph->saddr == pNatRule->remIP) + { + pDstIp = &pNatRule->locIP; + pDstMac = pNatRule->locMAC; + } + else if (nDir == SVIP_NAT_STATS_LOC2REM && iph->saddr == pNatRule->locIP) + { + pDstIp = &pNatRule->remIP; + pDstMac = pNatRule->remMAC; + } + else + { + /* Rule check failed. The packet is passed up the layers, + it will be dropped by UDP */ + return 0; + } + + if ((*pDstIp & *pmask_veth0) == (*paddr_veth0 & *pmask_veth0)) + { +#ifndef VLAN_8021Q_UNUSED + if (vlan) + { + struct net_device *vlan_dev; + + spin_lock_bh(&vlan_group_lock); + vlan_dev = __vlan_find_dev_deep(net_devs[SVIP_NET_DEV_VETH0_IDX], vid); + spin_unlock_bh(&vlan_group_lock); + if (vlan_dev) + { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + struct vlan_ethhdr *vethh; + + skb_push(skb, VLAN_ETH_HLEN); + /* reconstruct the VLAN header. +NOTE: priority information is lost */ + vethh = (struct vlan_ethhdr *)skb->data; + vethh->h_vlan_proto = htons(ETH_P_8021Q); + vethh->h_vlan_TCI = htons(vid); + vethh->h_vlan_encapsulated_proto = htons(ETH_P_IP); + ethh = (struct ethhdr *)vethh; +#else + skb_push(skb, ETH_HLEN); +#endif + skb->dev = vlan_dev; + } + else + { + skb->dev = net_devs[SVIP_NET_DEV_VETH0_IDX]; + skb_push(skb, ETH_HLEN); + } + } + else +#endif /* ! VLAN_8021Q_UNUSED */ + { + skb->dev = net_devs[SVIP_NET_DEV_VETH0_IDX]; + skb_push(skb, ETH_HLEN); + } + pSrcIp = paddr_veth0; + } + else + { +#ifndef VLAN_8021Q_UNUSED +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + if (vlan) + { + struct vlan_ethhdr *vethh; + + /* reconstruct the VLAN header. +NOTE: priority information is lost */ + skb_push(skb, VLAN_ETH_HLEN); + vethh = (struct vlan_ethhdr *)skb->data; + vethh->h_vlan_proto = htons(ETH_P_8021Q); + vethh->h_vlan_TCI = htons(vid); + vethh->h_vlan_encapsulated_proto = htons(ETH_P_IP); + ethh = (struct ethhdr *)vethh; + } + else +#endif +#endif /* ! VLAN_8021Q_UNUSED */ + { + skb_push(skb, ETH_HLEN); + } + /* source IP address equals the destination IP address + of the incoming packet */ + pSrcIp = &iph->daddr; + } + iph->saddr = *pSrcIp; + memcpy((char *)ethh->h_source, (char *)skb->dev->dev_addr, ETH_ALEN); + iph->daddr = *pDstIp; + memcpy((char *)ethh->h_dest, (char *)pDstMac, ETH_ALEN); + } + else + { + /* Process packet arrived on veth0 */ + + if (iph->saddr != pNatRule->locIP) + { + /* Rule check failed. The packet is passed up the layers, + it will be dropped by UDP */ + return 0; + } + + if (!((pNatRule->remIP & *pmask_veth0) == (*paddr_veth0 & *pmask_veth0))) + { +#ifndef VLAN_8021Q_UNUSED + if (vlan) + { + struct net_device *vlan_dev; + + spin_lock_bh(&vlan_group_lock); + vlan_dev = __vlan_find_dev_deep(net_devs[SVIP_NET_DEV_ETH0_IDX], vid); + spin_unlock_bh(&vlan_group_lock); + if (vlan_dev) + { + unsigned int *pifa_addr; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + struct vlan_ethhdr *vethh; + + skb_push(skb, VLAN_ETH_HLEN); + /* construct the VLAN header, note priority information is lost */ + vethh = (struct vlan_ethhdr *)skb->data; + vethh->h_vlan_proto = htons(ETH_P_8021Q); + vethh->h_vlan_TCI = htons(vid); + vethh->h_vlan_encapsulated_proto = htons(ETH_P_IP); + ethh = (struct ethhdr *)vethh; +#else + skb_push(skb, ETH_HLEN); +#endif + skb->dev = vlan_dev; + + get_ifaddr(skb->dev, NULL, &pifa_addr); + if (pifa_addr) + { + pSrcIp = pifa_addr; + } + else + { + pSrcIp = paddr_eth0; + } + } + else + { + skb->dev = net_devs[SVIP_NET_DEV_ETH0_IDX]; + pSrcIp = paddr_eth0; + skb_push(skb, ETH_HLEN); + } + } + else +#endif /* ! VLAN_8021Q_UNUSED */ + { + skb->dev = net_devs[SVIP_NET_DEV_ETH0_IDX]; + pSrcIp = paddr_eth0; + skb_push(skb, ETH_HLEN); + } + } + else + { + pSrcIp = paddr_veth0; +#ifndef VLAN_8021Q_UNUSED +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + if (vlan) + { + struct vlan_ethhdr *vethh; + + skb_push(skb, VLAN_ETH_HLEN); + /* reconstruct the VLAN header. +NOTE: priority information is lost. */ + vethh = (struct vlan_ethhdr *)skb->data; + vethh->h_vlan_proto = htons(ETH_P_8021Q); + vethh->h_vlan_TCI = htons(vid); + vethh->h_vlan_encapsulated_proto = htons(ETH_P_IP); + ethh = (struct ethhdr *)vethh; + } + else +#endif +#endif /* ! VLAN_8021Q_UNUSED */ + { + skb_push(skb, ETH_HLEN); + } + } + iph->saddr = *pSrcIp; + memcpy((char *)ethh->h_source, (char *)skb->dev->dev_addr, ETH_ALEN); + iph->daddr = pNatRule->remIP; + memcpy((char *)ethh->h_dest, (char *)pNatRule->remMAC, ETH_ALEN); + } + pNatTable[nNatIdx].natStats[nDir].inPackets++; + + iph->check = ip_udp_quick_csum(iph->check, (u16 *)&orgSrcIp, (u16 *)&iph->saddr, + (u16 *)&orgDstIp, (u16 *)&iph->daddr); + if (udph->check != 0) + { + udph->check = ip_udp_quick_csum(udph->check, (u16 *)&orgSrcIp, (u16 *)&iph->saddr, + (u16 *)&orgDstIp, (u16 *)&iph->daddr); + } + + /* write the packet out, directly to the network device */ + if (dev_queue_xmit(skb) < 0) + pNatTable[nNatIdx].natStats[nDir].outErrors++; + else + pNatTable[nNatIdx].natStats[nDir].outPackets++; + + return 1; +} + +/******************************************************************************/ +/** + Function executed upon unloading of the SVIP NAT module. It unregisters the + SVIP NAT configuration device and frees the memory used for the NAT table. + + \remarks: + Currently the SVIP NAT module is statically linked into the Linux kernel + therefore this routine cannot be executed. + *******************************************************************************/ +static int __init init(void) +{ + int ret = 0; + struct net_device *dev; + + if (misc_register(&SVIP_NAT_miscdev) != 0) + { + printk(KERN_ERR "%s: cannot register SVIP NAT device node.\n", + SVIP_NAT_miscdev.name); + return -EIO; + } + + /* allocation of memory for NAT table */ + pNatTable = (SVIP_NAT_table_entry_t *)kmalloc( + sizeof(SVIP_NAT_table_entry_t) * SVIP_SYS_CODEC_NUM, GFP_ATOMIC); + if (pNatTable == NULL) + { + printk (KERN_ERR "SVIP NAT: Error(%d), allocating memory for NAT table\n", ret); + return -1; + } + + /* clear the NAT table */ + memset((void *)pNatTable, 0, sizeof(SVIP_NAT_table_entry_t) * SVIP_SYS_CODEC_NUM); + + if ((sem_nat_tbl_access = kmalloc(sizeof(struct semaphore), GFP_KERNEL))) + { + sema_init(sem_nat_tbl_access, 1); + } + + SVIP_NAT_ProcInstall(); + + /* find pointers to 'struct net_device' of eth0 and veth0, respectevely */ + read_lock(&dev_base_lock); + SVIP_NAT_FOR_EACH_NETDEV(dev) + { + if (!strcmp(dev->name, SVIP_NET_DEV_ETH0_NAME)) + { + net_devs[SVIP_NET_DEV_ETH0_IDX] = dev; + } + if (!strcmp(dev->name, SVIP_NET_DEV_VETH1_NAME)) + { + net_devs[SVIP_NET_DEV_VETH0_IDX] = dev; + } + else if (!strcmp(dev->name, SVIP_NET_DEV_ETH1_NAME)) + { + net_devs[SVIP_NET_DEV_VETH0_IDX] = dev; + } + } + read_unlock(&dev_base_lock); + + if (net_devs[SVIP_NET_DEV_ETH0_IDX] == NULL || + net_devs[SVIP_NET_DEV_VETH0_IDX] == NULL) + { + printk (KERN_ERR "SVIP NAT: Error, unable to locate eth0 and veth0 interfaces\n"); + return -1; + } + + printk ("%s, (c) 2009, Lantiq Deutschland GmbH\n", &SVIP_NAT_INFO_STR[4]); + + return ret; +} + +/******************************************************************************/ +/** + Function executed upon unloading of the SVIP NAT module. It unregisters the + SVIP NAT configuration device and frees the memory used for the NAT table. + + \remarks: + Currently the SVIP NAT module is statically linked into the Linux kernel + therefore this routine cannot be executed. + *******************************************************************************/ +static void __exit fini(void) +{ + MOD_DEC_USE_COUNT; + + /* unregister SVIP NAT configuration device */ + misc_deregister(&SVIP_NAT_miscdev); + + /* release memory of SVIP NAT table */ + if (pNatTable != NULL) + { + kfree (pNatTable); + } +} + +module_init(init); +module_exit(fini); -- cgit v1.2.3