怎样做一个nat的nat静态转换网络工程的实验

在Linux上实现一个可用的stateless双向静态NAT模块_Linux教程_Linux公社-Linux系统门户网站
你好,游客
在Linux上实现一个可用的stateless双向静态NAT模块
来源:Linux社区&
作者:dog250
关于Linux上如何配置NAT的资料已经不少,可谓铺天盖地!本文与此无关。本文提供一种iptables之外的方式。
iptables?不!why?因为iptables配置的NAT是stateful的,它的实现依赖一个叫做conntrack的模块,什么是conntrack?Oh,NO!这可是我的专长,但我不想在本文中说它,认识我的人都知道,我扯这个话题我能扯上12个小时...都还扯不完。也许你不知道什么是stateful NAT,但是如果你是一个有心人,或者说是一个技术还算精湛的Linux网络管理员或者爱好者,你肯定在配置NAT的时候遇到过这样那样的问题,比如&在一个连接已经建立的时候再配置NAT为何不能及时生效&,&为什么iptables配置NAT之后数据只能从一个方向主动发往另一个方向&之类的。这就是state在作怪,你知道的,IP是无state的,但是NAT加入了第四层的逻辑之后就有了state,这就是stateful NAT,也就是iptables -t nat ..配置出来的NAT固有的性质,你改变不了。起码我在iptables最新的版本中看到的NAT还是stateful的。有的时候..
有的时候,你可能,你必须...
你必须配置一种stateless的NAT,双向的,静态的。这个问题,唉..
这个问题折腾了我半年,2013年的前9个月,一个让我欢喜让我忧的三个季度,我的精力几乎全部扑在了一件事上,从寒冬到40+摄氏度的高温,从早上6点半出发去上班到半夜10点多还呆在机房...要不是大前天收拾书架时发现了一张当时还没有报销掉的120元的例行加班打车票,我本来不想写这个模块了。120块不算什么,但借此机会回忆往事,顺便补上残缺的那一部分,算是给自己报销了,而且价值远大于120元。我得承认,那三个季度里并不是stateless NAT显得最为重要,我之所以在一年后的今天把它拿出来,是因为其它的问题都被我当时就overcome了,不管花多久,曾经有过72小时惊魂解决conntrack confirm问题,有过由于混乱急躁和陌生女人一起吃烤肉被老婆诈出真相...但是就这个stateless NAT始终没有解决,没有解决,这是why?
我做的是一个产品而不是个人试验,我所在的是一个公司的团队而不是干私活儿,所有使用的技术必须经过技术预研,确保可行性,更重要的是,要保证所有人处在一样的节奏,也许是并行,大的旋律却始终是个一,这是在玩卡农啊。我不能加入一些个人色彩,比如个人的突发奇想(日后我坦白,这一点我做的不好!),如果非要加入,那么必须有下面这么一个过程:
把领导,团队所有人拉进来开会,培训,确保任何研发人员和技术负责人都对使用的技术了如指掌不留死角。
但是哪有这个时间?!人性是脆弱的,不管人生多么坚强。任何人都可能突然哪天挂了,由于不可抗原因去别的城市或者国家了,和根本不熟识的同事由于接开水打了一架离职了...如果你做的东西也因此而离去了,对于一个公司而言,说明你根本没来过。......有点扯远了。
所以,即便当时我已经可以实现stateless NAT(当时我采用了路由target的方式,也许我之前的blog有写过),我也不能拿来用,我只能找机会,找一个闲暇的午后,一个没有紧急任务的下午,让所有与此相关的人都了解了这个技术,然后用与不用就是一个需求问题了,最好的办法除了代码还是代码,对于程序员而言,说1G个字符都是瞎掰,没有能跑起来的代码都是扯淡,就算代码狠烂也无所谓,正因为如此现实主义的性格,我喜欢这个职业,不发狠话,不长篇阔论,不打架,不煽动,只要代码能跑起来,仅此而已。
对了,stateless NAT在Linux 2.4已经有好的实现了,就是使用tc/policy routing来完成,但是2.6内核下已经很难做到了。作者基于解耦合的考虑将这个模块留给了真正想实现它的人,也许我算一个。我之所以做如此多别人看来没有意义的事,因为我想表达一个理念,那就是&开发运维&和&运维开发&的理念,这类人一定是将来最炙手可热的人。当我面对无数次Cisco认证工程师的责难后,我的第一个想法不是骂他们或打他们(没人家词汇丰富且[注意:此处不能用&或&,要用&且&]打不过人家就尴尬了,即便这些都不是问题,不还有法律的吗...),我的第一个想法就是,在Linux上能不能实现相同的功能,目的不是爆他们的菊花,而是让他觉得我可以爆他。幸运的是,我都做到了,每当此刻,我回到家里都会写几个模块然而测试,就像公司的技术预研一样。然后就呈现给朋友们,有兴趣的都可以去测试,这种事没钱赚,也得不到肯定,没用到git,也不是神马GPL开源,就是特殊的朋友圈分享,我十分讨厌分类,我十分喜欢随便。然后,然后,正如小小的话,我编码,写了一堆烂代码,Linux上基于Netfilter实现的一个双向,静态的,无状态的NAT,代码不复杂,只有几百行,但是....
但是,问题有二:
1.这个模块初级,但是可用,这是我自我肯定的一面;
2.这个模块有大量可疑改进的空间,我自我否定。
代码如下:
&*& &* 用法:& &* 对目标地址为1.2.1.2的数据包做目标地址转换,目标转为192.168.1.8& &* echo +1.2.1.2 192.168.1.8 dst &/proc/net/static_nat& &* 上述命令会同时添加一条反向的SNAT映射& &*& &* 请解释:& &* echo +192.168.184.250 192.168.184.154 src &/proc/net/static_nat& &*& &*/& & #include &linux/module.h&& #include &linux/skbuff.h&& #include &net/ip.h&& #include &net/netfilter/nf_conntrack.h&& & #define DIRMASK 0x11& #define BUCKETS 1024& & & #define NAT_OPT_DEL& & & &
0x01& #define NAT_OPT_FIND& & & & 0x04& & #define NAT_OPT_ACCT_BIT& & 0x02& & & enum nat_dir {& & & DIR_SNAT,& & & DIR_DNAT,& & & DIR_NUM& };& & /*& &* 记录统计信息& &*/& struct nat_account {& & & u32 nat_& & & u32 nat_& };& & struct static_nat_entry {& & & __be32 addr[DIR_NUM];& & & enum nat_& & & struct nat_account acct[DIR_NUM];& & & struct hlist_node node[DIR_NUM];& };& & static DEFINE_SPINLOCK(nat_lock);& & /* 保存SNAT映射 */& struct hlist_head *src_& & /* 保存DNAT映射 */& struct hlist_head *dst_& & /*& &* 用一个IP地址(对于PREROUTING是daddr,对于POSTROUTING是saddr)作为key来获取value。& &*/& static __be32 get_address_from_map(struct sk_buff *skb, unsigned int dir, __be32 addr_key, unsigned int opt)& {& & & __be32 ret = 0, cmp_key, ret_& & & u32& & & struct hlist_head *& & & struct hlist_node *iter, *& & & struct static_nat_entry *& & & & hash = jhash_1word(addr_key, 1);& & & hash = hash%BUCKETS;& & & & spin_lock(&nat_lock);& & & if (dir == DIR_DNAT) {& & & & & list = &dst_list[hash];& & & } else if (dir == DIR_SNAT) {& & & & & list = &src_list[hash];& & & } else {& & & & & spin_unlock(&nat_lock);& & & & && & & }& & & & hlist_for_each_safe(iter, tmp, list) {& & & & & ent = hlist_entry(iter, struct static_nat_entry, node[dir]);& & & & & /* 注意反转 */& & & & & cmp_key = (ent-&type == dir) ?& & & & & & & & & & & & & & & ent-&addr[0]:ent-&addr[1];& & & & & ret_value = (ent-&type == dir) ?& & & & & & & & & & & & & & & ent-&addr[1]:ent-&addr[0];& & & & & if (addr_key == cmp_key) {& & & & & & & ret = ret_& & & & & & & if (opt == NAT_OPT_DEL) {& & & & & & & & & if (dir == ent-&type) {& & & & & & & & & & & hlist_del(&ent-&node[0]);& & & & & & & & & & & hlist_del(&ent-&node[1]);& & & & & & & & & & & kfree(ent);& & & & & & & & & } else {& & & & & & & & & & & ret = 0;& & & & & & & & & }& & & & & & & }& & & & & & & if (opt & NAT_OPT_ACCT_BIT) {& & & & & & & & & ent-&acct[dir].nat_packets ++;& & & & & & & & & ent-&acct[dir].nat_bytes += skb == NULL?1:skb-&& & & & & & & }& & & & & & && & & & & }&
& & }& & & spin_unlock(&nat_lock);& out:& & && }& & /*& &* 更新第四层的校验码信息& &*/& static void nat4_update_l4(struct sk_buff *skb, __be32 oldip, __be32 newip)& {& & & struct iphdr *iph = ip_hdr(skb);& & & void *transport_hdr = (void *)iph + ip_hdrlen(skb);& & & struct tcphdr *& & & struct udphdr *& & && & & & switch (iph-&protocol) {& & & case IPPROTO_TCP:& & & & & tcph = transport_& & & & & inet_proto_csum_replace4(&tcph-&check, skb, oldip, newip, true);& & & & && & & case IPPROTO_UDP:& & & case IPPROTO_UDPLITE:& & & & & udph = transport_& & & & & cond = udph-&check != 0;& & & & & cond |= skb-&ip_summed == CHECKSUM_PARTIAL;& & & & & if (cond) {& & & & & & & inet_proto_csum_replace4(&udph-&check, skb, oldip, newip, true);& & & & & & & if (udph-&check == 0) {& & & & & & & & & udph-&check = CSUM_MANGLED_0;& & & & & & & }& & & & & }& & & & && & & }& }& & /*& &* 在POSTROUTING上执行源地址转换:& &* 1.正向源地址转换;& &* 2.目标地址转换的逆向源地址转换& &*/& static unsigned int ipv4_nat_out(unsigned int hooknum,& & & & & & & & &
struct sk_buff *skb,& & & & & & & & &
const struct net_device *in,& & & & & & & & &
const struct net_device *out,& & & & & & & & &
int (*okfn)(struct sk_buff *))& {& & & unsigned int ret = NF_ACCEPT;& & & __be32 to_trans = 0;& & & & struct iphdr *hdr = ip_hdr(skb);& & & & & & to_trans = get_address_from_map(skb, DIR_SNAT, hdr-&saddr, NAT_OPT_FIND|NAT_OPT_ACCT_BIT);& & & if (!to_trans) {& & & & && & & }& & & & if (hdr-&saddr == to_trans) {& & & & && & & }& & & & /* 执行SNAT */& & & & & csum_replace4(&hdr-&check, hdr-&saddr, to_trans);& & & nat4_update_l4(skb, hdr-&saddr, to_trans);& & & hdr-&saddr = to_& out:& & && }& & /*& &* 在PREROUTING上执行目标地址转换:& &* 1.正向目标地址转换;& &* 2.源地址转换的逆向目标地址转换& &*/& static unsigned int ipv4_nat_in(unsigned int hooknum,& & & & & & & & & & & & struct sk_buff *skb,& & & & & & & & & & & & const struct net_device *in,& & & & & & & & & & & & const struct net_device *out,& & & & & & & & & & & & int (*okfn)(struct sk_buff *))& {& & & unsigned int ret = NF_ACCEPT;& & & __be32 to_trans = 0;& & & & struct iphdr *hdr = ip_hdr(skb);& & & & if (skb-&nfct && skb-&nfct != &nf_conntrack_untracked.ct_general) {& & & & && & & }& & & & & & to_trans = get_address_from_map(skb, DIR_DNAT, hdr-&daddr, NAT_OPT_FIND|NAT_OPT_ACCT_BIT);& & & if (!to_trans) {& & & & && & & }& & & & if (hdr-&daddr == to_trans) {& & & & && & & }& & & & & & /* 执行DNAT */& & & csum_replace4(&hdr-&check, hdr-&daddr, to_trans);& & & nat4_update_l4(skb, hdr-&daddr, to_trans);& & & hdr-&daddr = to_& & & & & & /*& & &
*& 设置一个notrack 防止其被track以及nat.& & &
*& 这是绝对合适的,因为既然是static的stateless NAT& & &
*& 我们就不希望它被状态左右& & &
**/& & & & /*& & &
* 其实,并不是主要避开基于conntrack的NAT就可以了,因为& & &
* conntrack本身就不容你对两个方向的tuple进行随意修改& & &
*/& & & if (!skb-&nfct) {& & & & & skb-&nfct = &nf_conntrack_untracked.ct_& & & & & skb-&nfctinfo = IP_CT_NEW;& & & & & nf_conntrack_get(skb-&nfct);& & & }& & out:& & && }& & static struct nf_hook_ops ipv4_nat_ops[] __read_mostly = {& & & {& & & & & .hook& & &
= ipv4_nat_in,& & & & & .owner& & & = THIS_MODULE,& & & & & .pf& &
= NFPROTO_IPV4,& & & & & .hooknum& & = NF_INET_PRE_ROUTING,& & & & & .priority&
= NF_IP_PRI_CONNTRACK-1,& & & },& & & {& & & & & .hook& & &
= ipv4_nat_out,& & & & & .owner& & & = THIS_MODULE,& & & & & .pf& &
= NFPROTO_IPV4,& & & & & .hooknum& & = NF_INET_POST_ROUTING,& & & & & .priority&
= NF_IP_PRI_CONNTRACK+1,& & & },& };& & static char *parse_addr(const char *input, __be32 *from, __be32 *to)& {& & & char *p1, *p2;& & & size_t length = strlen(input);& & & & & & if (!(p1 = memchr(input, ' ', length))) {& & & & & return NULL;& & & }& & & & if (!(p2 = memchr(p1 + 1, ' ', length - (p1 + 1 - input)))) {& & & & & return NULL;& & & }& & & & if (!(in4_pton(input, p1 - input, (u8 *)from, ' ', NULL))& & & & & & & || !(in4_pton(p1 + 1, p2 - p1 - 1, (u8 *)to, ' ', NULL))) {& & & & & return NULL;& & & }& & & & return ++p2;& }& & static ssize_t static_nat_config_write(struct file *file, const char *buffer, size_t count, loff_t *unused)& {& & & int ret = 0;& & & size_t length =& & & __be32 from,& & & u32 normal,& & & char *buf = NULL;& & & char *p;& & & struct static_nat_entry *& & & & if (length) {& & & & & char *pp = (char *)(buffer + (length - 1));&
& & & & for (; (*pp & (char)32) || (*pp & (char)126); pp--) {& & & & & & & if (length &= 0) {& & & & & & & & & ret = -EINVAL;& & & & & & & & && & & & & & & }& & & & & & & length--;& & & & & }& & & } else {& & & & && & & }& & & & buf = kzalloc((length + 1), GFP_ATOMIC);& & & if (!buf) {& & & & & ret = -ENOMEM;& & & & && & & }& & & memcpy(buf, buffer, length);& & & if (!(p = parse_addr(buf + 1, &from, &to))) {& & & & & ret = -EINVAL;& & & & && & & }& & & & if ('+' == *buf) {& & & & & ent = (struct static_nat_entry *)kzalloc(sizeof(struct static_nat_entry), GFP_KERNEL);& & & & & if (!ent) {& & & & & & & ret = -EFAULT;& & & & & & && & & & & }& & & & & & /* 计算原始项的hash桶位置 */& & & & & normal = jhash_1word(from, 1);& & & & & normal = normal%BUCKETS;& & & & & & /* 计算反转位置的hash桶位置 */& & & & & reverse = jhash_1word(to, 1);& & & & & reverse = reverse%BUCKETS;& & & & & & /*& & & & &
*& 设置key/value对& & & & &
*& 注意,反转类型的hnode其key/value也要反转& & & & &
*/& & & & & ent-&addr[0] =& & & & & ent-&addr[1] =& & & & & & /* 初始化链表节点 */& & & & & INIT_HLIST_NODE(&ent-&node[DIR_SNAT]);& & & & & INIT_HLIST_NODE(&ent-&node[DIR_DNAT]);& & & & & & if (strstr(p, "src")) { /* 添加SNAT项,自动生成DNAT项 */& & & & & & & /* 首先判断是否已经存在了 */& & & & & & & if (get_address_from_map(NULL, DIR_SNAT, from, NAT_OPT_FIND) ||& & & & & & & & & & & get_address_from_map(NULL, DIR_SNAT, to, NAT_OPT_FIND)) {& & & & & & & & & ret = -EEXIST;& & & & & & & & & kfree(ent);& & & & & & & & && & & & & & & }& & & & & & & & /* 这是这个entry的type,用来区分生成的两条配置项 */& & & & & & & ent-&type = DIR_SNAT;& & & & & & & & /* 落实到链表 */& & & & & & & spin_lock(&nat_lock);& & & & & & & hlist_add_head(&ent-&node[DIR_SNAT], &src_list[normal]);& & & & & & & hlist_add_head(&ent-&node[DIR_DNAT], &dst_list[reverse]);& & & & & & & spin_unlock(&nat_lock);& & & & & } else if(strstr(p, "dst")) { /* 添加DNAT项,自动生成SNAT项 */& & & & & & & /* 首先判断是否已经存在了 */& & & & & & & if (get_address_from_map(NULL, DIR_DNAT, from, NAT_OPT_FIND) ||& & & & & & & & & & & get_address_from_map(NULL, DIR_DNAT, to, NAT_OPT_FIND)){& & & & & & & & & ret = -EEXIST;& & & & & & & & & kfree(ent);& & & & & & & & && & & & & & & }& & & & & & & & /* 这是这个entry的type,用来区分生成的两条配置项 */& & & & & & & ent-&type = DIR_DNAT;& & & & & & & & /* 落实到链表 */& & & & & & & spin_lock(&nat_lock);& & & & & & & hlist_add_head(&ent-&node[DIR_DNAT], &dst_list[normal]);& & & & & & & hlist_add_head(&ent-&node[DIR_SNAT], &src_list[reverse]);& & & & & & & spin_unlock(&nat_lock);& & & & & } else {& & & & & & & ret = -EFAULT;& & & & & & & kfree(ent);& & & & & & && & & & & }& & & & } else if ('-' ==*buf) {& & & & & u32 r1;& & & & & & if (strstr(p, "src")) {& & & & & & & r1 = get_address_from_map(NULL, DIR_SNAT, from, NAT_OPT_DEL);& & & & & & & if (r1 == 0) {& & & & & & & & & ret = -ENOENT;& & & & & & & & && & & & & & & }& & & & & } else if(strstr(p, "dst")) {& & & & & & & r1 = get_address_from_map(NULL, DIR_DNAT, from, NAT_OPT_DEL);& & & & & & & if (r1 == 0) {& & & & & & & & & ret = -ENOENT;& & & & & & & & && & & & & & & }& & & & & } else {& & & & & }& & & & } else {& & & & & ret = -EINVAL;& & & & && & & }& & & & & & ret =& out:& & & kfree(buf);& & && }& & static ssize_t static_nat_config_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)& {& & & int len = 0;& & & static int done = 0;& & && & & char from[15], to[15];& & & char *kbuf_to_avoid_user_space_memory_page_fault = NULL;& & /* 每一行的最大长度 */& #define MAX_LINE_CHARS& 128& & & & if (done) {& & & & & done = 0;& & & & && & & }& & & & & & /*& & &
* 分配一块内核内存,为了避免直接操作用户内存而引发页面调度,& & &
* 页面调度会导致睡眠切换,而我们操作的内容处在自旋锁的保护& & &
* 下,所以不能切换!& & &
*/& & & & /*& & &
* 问题:& & &
* 我这里仅仅分配count大小的内存,是因为这个版本不支持多次读,& & &
* 只能一次读完。也许我应该学学seq read的方法。& & &
*/& & & kbuf_to_avoid_user_space_memory_page_fault = kzalloc(count, GFP_KERNEL);& & & if (!kbuf_to_avoid_user_space_memory_page_fault) {& & & & & len = -ENOMEM;& & & & & done = 1;& & & & && & & }& &
& & & spin_lock(&nat_lock);& & & len += sprintf(kbuf_to_avoid_user_space_memory_page_fault + len, "Source trans table:\n");& & & if (len + MAX_LINE_CHARS & count) {& & & & & goto copy_& & & }& & & for (i = 0; i & BUCKETS; i++) {& & & & & struct hlist_node *iter, *& & & & & struct static_nat_entry *& & & & & hlist_for_each_safe(iter, tmp, &src_list[i]) {& & & & & & & ent = hlist_entry(iter, struct static_nat_entry, node[DIR_SNAT]);& & & & & & & sprintf(from, "%pI4", (ent-&type == DIR_SNAT)? &ent-&addr[0]:&ent-&addr[1]);& & & & & & & sprintf(to, "%pI4", (ent-&type == DIR_SNAT)? &ent-&addr[1]:&ent-&addr[0]);& & & & & & & len += sprintf(kbuf_to_avoid_user_space_memory_page_fault + len, "From:%-15s To:%-15s& & [%s]& [Bytes:%u& Packet:%u]\n",&
& & & & & & & & & & & & from,&
& & & & & & & & & & & & to,&
& & & & & & & & & & & & (ent-&type == DIR_SNAT)?"STATIC":"AUTO",& & & & & & & & & & & & & ent-&acct[DIR_SNAT].nat_bytes,& & & & & & & & & & & & & ent-&acct[DIR_SNAT].nat_packets);& & & & & & & & if (len + MAX_LINE_CHARS & count) {& & & & & & & & & goto copy_& & & & & & & }& & & & & }&
& & }& & & len += sprintf(kbuf_to_avoid_user_space_memory_page_fault + len, "\nDestination trans table:\n");& & & if (len + MAX_LINE_CHARS & count) {& & & & & goto copy_& & & }& & & for (i = 0; i & BUCKETS; i++) {& & & & & struct hlist_node *iter, *& & & & & struct static_nat_entry *& & & & & hlist_for_each_safe(iter, tmp, &dst_list[i]) {& & & & & & & ent = hlist_entry(iter, struct static_nat_entry, node[DIR_DNAT]);& & & & & & & sprintf(from, "%pI4", (ent-&type == DIR_DNAT)? &ent-&addr[0]:&ent-&addr[1]);& & & & & & & sprintf(to, "%pI4", (ent-&type == DIR_DNAT)? &ent-&addr[1]:&ent-&addr[0]);& & & & & & & len += sprintf(kbuf_to_avoid_user_space_memory_page_fault + len, "From:%-15s To:%-15s& & [%s]& [Bytes:%u& Packet:%u]\n",&
& & & & & & & & & & & & from,&
& & & & & & & & & & & & to,&
& & & & & & & & & & & & (ent-&type == DIR_DNAT)?"STATIC":"AUTO",& & & & & & & & & & & & & ent-&acct[DIR_DNAT].nat_bytes,& & & & & & & & & & & & & ent-&acct[DIR_DNAT].nat_packets);& & & & & & & & & if (len + MAX_LINE_CHARS & count) {& & & & & & & & & goto copy_& & & & & & & }& & & & & }&
& & }& copy_now:& & & spin_unlock(&nat_lock);& & & done = 1;& & & /* 这里已经解除自旋锁 */& & & if (copy_to_user(buf, kbuf_to_avoid_user_space_memory_page_fault, len))& {& & & & & len = EFAULT;& & & & && & & }& & & & out:& & & if (kbuf_to_avoid_user_space_memory_page_fault) {& & & & & kfree(kbuf_to_avoid_user_space_memory_page_fault);& & & }& & && }& & static const struct file_operations static_nat_file_ops = {& & & .owner& & & = THIS_MODULE,& & & .read& & &
= static_nat_config_read,& & & .write& & & = static_nat_config_write,& };& & static int __init nf_static_nat_init(void)& {& & & int ret = 0;& & && & & & src_list = kzalloc(sizeof(struct hlist_head) * BUCKETS, GFP_KERNEL);& & & if (!src_list) {& & & & & ret = -ENOMEM;& & & & && & & }& & & dst_list = kzalloc(sizeof(struct hlist_head) * BUCKETS, GFP_KERNEL);& & & if (!dst_list) {& & & & & ret = -ENOMEM;& & & & && & & }& & & & ret = nf_register_hooks(ipv4_nat_ops, ARRAY_SIZE(ipv4_nat_ops));& & & if (ret & 0) {& & & & & printk("nf_nat_ipv4: can't register hooks.\n");& & & & && & & }& & & & if (!proc_create("static_nat", 0644, init_net.proc_net, &static_nat_file_ops)) {& & & & & ret = -ENOMEM;& & & & && & & }& & & & for (i = 0; i & BUCKETS; i++) {& & & & & INIT_HLIST_HEAD(&src_list[i]);& & & & & INIT_HLIST_HEAD(&dst_list[i]);& & & }& & && out:& & & if (src_list) {& & & & & kfree(src_list);& & & }&
& & if (dst_list) {& & & & & kfree(dst_list);& & & }&
& & && }& & static void __exit nf_static_nat_fini(void)& {& & && & & & remove_proc_entry("static_nat", init_net.proc_net);& & & nf_unregister_hooks(ipv4_nat_ops, ARRAY_SIZE(ipv4_nat_ops));& & & & spin_lock(&nat_lock);& & & for (i = 0; i & BUCKETS; i++) {& & & & & struct hlist_node *iter, *& & & & & struct static_nat_entry *& & & & & hlist_for_each_safe(iter, tmp, &src_list[i]) {& & & & & & & ent = hlist_entry(iter, struct static_nat_entry, node[0]);& & & & & & & hlist_del(&ent-&node[DIR_SNAT]);& & & & & & & hlist_del(&ent-&node[DIR_DNAT]);& & & & & & & kfree(ent);& & & & & }&
& & }& & & spin_unlock(&nat_lock);& & & if (src_list) {& & & & & kfree(src_list);& & & }&
& & if (dst_list) {& & & & & kfree(dst_list);& & & }&
}& & module_init(nf_static_nat_init);& module_exit(nf_static_nat_fini);& & MODULE_DESCRIPTION("STATIC two-way NAT");& MODULE_AUTHOR("");& MODULE_LICENSE("GPL");&
Makefile:
obj-m += nf_rawnat.o& & all:&
& & make -C /lib/modules/`uname -r`/build SUBDIRS=`pwd` modules& & clean:& & & rm -rf *.ko *.o .tmp_versions .*.mod.o .*.o.cmd *.mod.c .*.ko.cmd Module.symvers modules.order&
我不看好应用相关的私活儿并不代表我不喜欢,并不代表我不会。工作毕竟已经很累了,干嘛还要继续累啊。工作之余,要搞点别的,别的是什么呢?stateless NAT...这是很多人都不涉足的,我感叹,我悲哀,微斯人,吾谁与归?...
本文永久更新链接地址:
相关资讯 & & &
& (05/31/:38)
图片资讯 & & &
   同意评论声明
   发表
尊重网上道德,遵守中华人民共和国的各项有关法律法规
承担一切因您的行为而直接或间接导致的民事或刑事法律责任
本站管理人员有权保留或删除其管辖留言中的任意内容
本站有权在网站内转载或引用您的评论
参与本评论即表明您已经阅读并接受上述条款}

我要回帖

更多关于 nat静态转换 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信