日期:2014-05-16 浏览次数:20730 次
1. 前言 在2.6.10内核后,netfilter的NAT部分作了一些较大的修改,而连接跟踪部分修改了一些但不大,本文大致描述一下NAT修改部分的情况。 以下内核代码版本为2.6.17.11。 2. 修改情况 2.1 多连接协议的跟踪和NAT 对于多连接协议,如FTP等,netfilter的处理进行了较大的修改,表现为: 1) 多连接跟踪改为一个nf_hook_ops点处理 2) 数据内容部分的NAT直接在跟踪函数中调用处理,不再由NAT部分处理 连接help操作在INPUT和POSTROUTING点上进行,优先级仅高于SEQ_ADJUST和confirm,数据包到这时地址端口部分已经经过NAT了。 /* net/ipv4/netfilter/ip_conntrack_standalne.c */ ...... { .hook = ip_conntrack_help, .owner = THIS_MODULE, .pf = PF_INET, .hooknum = NF_IP_POST_ROUTING, .priority = NF_IP_PRI_CONNTRACK_HELPER, }, { .hook = ip_conntrack_help, .owner = THIS_MODULE, .pf = PF_INET, .hooknum = NF_IP_LOCAL_IN, .priority = NF_IP_PRI_CONNTRACK_HELPER, }, ....... static unsigned int ip_conntrack_help(unsigned int hooknum, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { struct ip_conntrack *ct; enum ip_conntrack_info ctinfo; /* This is where we call the helper: as the packet goes out. */ ct = ip_conntrack_get(*pskb, &ctinfo); if (ct && ct->helper) { unsigned int ret; ret = ct->helper->help(pskb, ct, ctinfo); if (ret != NF_ACCEPT) return ret; } return NF_ACCEPT; } 多连接协议的跟踪辅助结构struct ip_conntrack_helper内容变化不大,但原来的NAT辅助结构struct ip_nat_helper已经取消了,数据内容的修改直接在跟踪函数中调用。 以FTP为例: /* net/ipv4/netfilter/ip_conntrack_ftp.c */ static int help(struct sk_buff **pskb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) { unsigned int dataoff, datalen; struct tcphdr _tcph, *th; char *fb_ptr; int ret; u32 seq, array[6] = { 0 }; int dir = CTINFO2DIR(ctinfo); unsigned int matchlen, matchoff; struct ip_ct_ftp_master *ct_ftp_info = &ct->help.ct_ftp_info; struct ip_conntrack_expect *exp; unsigned int i; int found = 0, ends_in_nl; /* Until there's been traffic both ways, don't look in packets. */ if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) { DEBUGP("ftp: Conntrackinfo = %u\n", ctinfo); return NF_ACCEPT; } // TCP头,会拷贝到缓冲区 th = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl*4, sizeof(_tcph), &_tcph); if (th == NULL) return NF_ACCEPT; dataoff = (*pskb)->nh.iph->ihl*4 + th->doff*4; /* No data? */ if (dataoff >= (*pskb)->len) { DEBUGP("ftp: pskblen = %u\n", (*pskb)->len); return NF_ACCEPT; } datalen = (*pskb)->len - dataoff; spin_lock_bh(&ip_ftp_lock); // 获取FTP内容数据部分,可能会拷贝到一个缓冲区进行,不直接在原数据区 // 上进行操作,这也许和2.6支持抢占式处理有关,如果被其他内核进程抢占 // 由对该skb进行操作就麻烦了 fb_ptr = skb_header_pointer(*pskb, dataoff, (*pskb)->len - dataoff, ftp_buffer); BUG_ON(fb_ptr == NULL); ends_in_nl = (fb_ptr[datalen - 1] == '\n'); seq = ntohl(th->seq) + datalen; /* Look up to see if we're just after a \n. */ // 检查序列号是否是在回车符后,防止phrack63中描述的序列号问题 if (!find_nl_seq(ntohl(th->seq), ct_ftp_info, dir)) { /* Now if this ends in \n, update ftp info. */ DEBUGP("ip_conntrack_ftp_help: wrong seq pos %s(%u) or %s(%u)\n", ct_ftp_info->seq_aft_nl[0][dir] old_seq_aft_nl_set ? "":"(UNSET) ", old_seq_aft_nl); ret = NF_ACCEPT; goto out_update_nl; } /* Initialize IP array to expected address (it's not mentioned in EPSV responses) */ // 期待的地址,如果进行了NAT,那得到的已经是NAT后的地址 array[0] = (ntohl(ct->tuplehash[dir].tuple.src.ip) >> 24) & 0xFF; array[1] = (ntohl(ct->tuplehash[dir].tuple.src.ip) >> 16) & 0xFF; array[2] = (ntohl(ct->tuplehash[dir].tuple.src.ip) >> 8) & 0xFF; array[3] = ntohl(ct->tuplehash[dir].tuple.src.ip) & 0xFF; // 模式查找 for (i = 0; i < ARRAY_SIZE(search); i++) { if (search[i].dir != dir) continue; found = find_pattern(fb_ptr, (*pskb)->len - dataoff, search[i].pattern, search[i].plen,