日期:2014-05-16  浏览次数:20670 次

2.6.1*Linux内核中NAT处理的改变
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn

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,