分享

netfilter中对多连接协议跟踪和NAT实现

 womking 2007-08-16
netfilter中对多连接协议跟踪和NAT实现
netfilter中对多连接协议跟踪和NAT实现
 
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.

1. 前言
 
对于多连接协议,netfilter需要对其进行特殊的跟踪和NAT以提供动态的对子连接的支持,详见“防火墙为什么要对多连接协议进行特殊处理”一文。netfilter对这些多连接协议的跟踪和NAT和匹配、目标或IP层协议那样进行模块化的跟踪和NAT处理。
 
以下内核代码说明都使用Linux-2.4.26版本的内核代码。
 
2. 协议连接跟踪
 
2.1 ip_conntrack_helper结构
 
netfilter中对每个要进行跟踪的多连接协议定义了以下的连接辅助结构,每个多连接协议的连接跟踪处理就是要填写这样一个结构:
 
include/linux/netfilter_ipv4/ip_conntrack_helper.h
struct ip_conntrack_helper

 struct list_head list;   /* Internal use. */
 const char *name;  /* name of the module */
 unsigned char flags;  /* Flags (see above) */
 struct module *me;  /* pointer to self */
 unsigned int max_expected; /* Maximum number of concurrent
      * expected connections */
 unsigned int timeout;  /* timeout for expecteds */
 /* Mask of things we will help (compared against server response) */
 struct ip_conntrack_tuple tuple;
 struct ip_conntrack_tuple mask;
 
 /* Function to call when data passes; return verdict, or -1 to
           invalidate. */
 int (*help)(const struct iphdr *, size_t len,
      struct ip_conntrack *ct,
      enum ip_conntrack_info conntrackinfo);
};
 
结构中包括以下参数:
 
struct list_head list:将该结构挂接到多连接协议跟踪链表helpers中, helpers链表在ip_conntrack_core.c文件中定义:
static LIST_HEAD(helpers);
注意是static的,只在该文件范围有效;
 
const char *name:协议名称,字符串常量
 
unsigned char flags:关于本连接跟踪模块的一些标志;
 
struct module *me:指向模块本身,统计模块是否被使用
 
unsigned int max_expected:子连接的数量,这只是表示主连接在每个时刻所拥有的的子连接的数量,而不是主连接的整个生存期内总共生成的子连接的数量,如FTP,不论传多少个文件,建立多少个子连接,每个时刻主连接最多只有一个子连接,一个子连接结束前按协议是不能再派生出第二个子连接的,所以该值为1;
 
unsigned int timeoout:超时,指在多少时间范围内子连接没有建立的话子连接跟踪失效
 
struct ip_conntrack_tuple tuple, mask:这两个参数用来描述子连接,判断一个新来的连接是否是主连接期待的子连接,之所以要有mask参数,是因为子连接的某些参数不能确定,如被动模式的FTP传输,只能得到子连接的目的端口而不能确定源端口,所以源端口部分要用mask来进行泛匹配;
 
结构中包括以下函数:
 
(*help):连接跟踪基本函数,解析主连接的通信内容,提取出关于子连接的信息,将子连接信息填充到一个struct ip_conntrack_expect结构中,然后将此结构通过调用函数ip_conntrack_expect_related()把子连接的信息添加到系统的期待子连接链表ip_conntrack_expect_list中。返回值是NF_ACCEPT或NF_DROP,或-1表示协议数据非法。该函数在ip_conntrack_in()函数中调用。

2.2 期待的连接的结构
 
期待的连接结构用来描述所期待的连接,但该连接目前是不存在的,是一个虚构的连接,只是netfilter根据主连接的信息解析出来的可能的子连接的信息,struct ip_conntrack_helper结构中的(*help)成员函数的目的就是建立一个struct ip_conntrack_expect结构:
 
struct ip_conntrack_expect
{
 /* Internal linked list (global expectation list) */
 struct list_head list;
 /* reference count */
 atomic_t use;
 /* expectation list for this master */
 struct list_head expected_list;
 /* The conntrack of the master connection */
 struct ip_conntrack *expectant;
 /* The conntrack of the sibling connection, set after
  * expectation arrived */
 struct ip_conntrack *sibling;
 /* Tuple saved for conntrack */
 struct ip_conntrack_tuple ct_tuple;
 /* Timer function; deletes the expectation. */
 struct timer_list timeout;
 /* Data filled out by the conntrack helpers follow: */
 /* We expect this tuple, with the following mask */
 struct ip_conntrack_tuple tuple, mask;
 /* Function to call after setup and insertion */
 int (*expectfn)(struct ip_conntrack *new);
 /* At which sequence number did this expectation occur */
 u_int32_t seq;
 
 union ip_conntrack_expect_proto proto;
 union ip_conntrack_expect_help help;
};
 
结构中包括以下参数:
 
struct list_head list:将该结构挂接到期待连接链表
 
ip_conntrack_expect_list中;
 
atomic_t use:该期待连接结构的使用次数;
 
struct list_head expected_list:主连接的期待的子连接的链表;
 
struct ip_conntrack *expectant:期待连接对应的主连接;
 
struct ip_conntrack *sibling:期待连接对应的真实的子连接;
 
struct ip_conntrack_tuple ct_tuple:连接的tuple值
 
struct timer_list timeout:定时器
 
struct ip_conntrack_tuple tuple, mask:期待连接相关的tuple和mask;
 
u_int32_t seq:TCP协议时,主连接中描述子连接的数据起始处对应的序列号值;
 
union ip_conntrack_expect_proto proto:跟踪各个多连接IP层协议相关的数据;
 
union ip_conntrack_expect_help help:跟踪各个多连接应用层协议相关的数据;

结构中包括以下函数:
 
int (*expectfn)(struct ip_conntrack *new):期待连接相关函数,在ip_conntrack_core.c文件的init_conntrack()函数中调用:
...
 if (expected && expected->expectfn)
  expected->expectfn(conntrack);
...
 
2.3 多连接协议的连接跟踪过程
 
2.3.1 过程简述
 
新连接的数据包进入netfilter系统后先要建立对应连接,检查是否是期待的连接,如果与某期待连接符合,说明该连接是子连接,将连接和主连接建立相关的联系,并对连接设置相关标志,表明是RELATED的连接;否则,说明是主连接,则查找与这个连接对应的协议的连接辅助模块helper,如果找到,执行helper中的(*help)函数检查是否能提取出相关的子连接信息,生成期待的连接信息添加到期待连接链表中。

2.3.2 连接helper查找
 
在ip_conntrack_core.c文件的init_conntrack()函数中先查找期待连接是否存在,然后调用ip_ct_find_helper()函数来找到与该协议对应的helper函数,注意是用反向的tuple来查找的:
...
// 查找是否期待连接
 expected = LIST_FIND(&ip_conntrack_expect_list, expect_cmp,
        struct ip_conntrack_expect *, tuple);
...
// 查找连接对应的helper
 if (!expected)
  conntrack->helper = ip_ct_find_helper(&repl_tuple);
...
 
在查找helper之前先检查该连接是否存在对应的期待连接,如果是存在期待连接,说明是子连接,子连接就不再去找helper了,这就避免了phrack63的那篇文章中描述的防火墙穿透方法。但这种限制也带来一个问题,比如H.323协议,主连接是H.225协议,会派生出一个H.245的连接,由H.245连接再派生出一个连接进行数据传输,这种情况下H.245的helper就不能通过ip_ct_find_helper()函数来获取了,为解决这个问题,H.323跟踪NAT模块的作者使用了一个很巧妙的方法,就是通过期待连接结构的(*expectfn)函数来解决,在该函数中直接将H.245的helper直接赋值到该连接中。

2.3.3 (*help)函数执行
 
(*help)函数对主连接的每个合法数据包都会进行检查的,在ip_conntrack_core.c文件的ip_conntrack_in()函数中调用:
 
...
 if (ret != NF_DROP && ct->helper) {
  ret = ct->helper->help((*pskb)->nh.iph, (*pskb)->len,
           ct, ctinfo);
  if (ret == -1) {
// 协议数据格式错误时help函数返回-1,清空该包对应的nfct,则该包在状态检测时将被视为非法包
   /* Invalid */
   nf_conntrack_put((*pskb)->nfct);
   (*pskb)->nfct = NULL;
   return NF_ACCEPT;
  }
 }
...

3. 多连接协议NAT
 
3.1 ip_nat_helper结构
 
netfilter中对每个要进行NAT的多连接协议定义了以下结构,每个多连接协议的连接跟踪处理就是要填写这样一个结构:
 
struct ip_nat_helper
{
 struct list_head list;  /* Internal use */
 const char *name;  /* name of the module */
 unsigned char flags;  /* Flags (see above) */
 struct module *me;  /* pointer to self */
 
 /* Mask of things we will help: vs. tuple from server */
 struct ip_conntrack_tuple tuple;
 struct ip_conntrack_tuple mask;
 
 /* Helper function: returns verdict */
 unsigned int (*help)(struct ip_conntrack *ct,
        struct ip_conntrack_expect *exp,
        struct ip_nat_info *info,
        enum ip_conntrack_info ctinfo,
        unsigned int hooknum,
        struct sk_buff **pskb);
 /* Returns verdict and sets up NAT for this connection */
 unsigned int (*expect)(struct sk_buff **pskb,
          unsigned int hooknum,
          struct ip_conntrack *ct,
          struct ip_nat_info *info);
};

结构中包括以下参数:
 
struct list_head list:将该结构挂接到多连接协议NAT链表helpers中,helpers链表在ip_nat_core.c文件中定义:
LIST_HEAD(helpers);
注意该定义不是static的,而是全局有效的,连接跟踪的helpers则是static的,只在该文件中起作用,而且屏蔽了ip_nat_core.c中的helpers,所以在此两个文件外的helpers是指nat的helpers;
 
const char *name:协议名称,字符串常量
 
unsigned char flags:关于本连接跟踪模块的一些标志;
 
struct module *me:指向模块本身,统计模块是否被使用
 
struct ip_conntrack_tuple tuple, mask:这两个参数用来描述进行了NAT后的子连接,同样因为子连接的某些参数不能确定,要用mask来进行泛匹配;
 
结构中包括以下函数:
 
(*help):多协议连接NAT操作基本函数,由于作NAT后要修改IP地址以及端口,因此原来的主连接中描述子连接的信息必须进行修改,(*help)函数的功能就要要找到一个空闲的tuple对应新的子连接,修改期待的子连接,然后修改主连接的通信内容,修改关于IP地址和端口部分的描述信息为空闲tuple的信息,由于修改了应用层数据,数据的校验和必须重新计算,而且如果数据长度发生变化,会引起TCP序列号的变化,在连接的协议相关数据中会记录这些变化,对后续的所有数据都要进行相应的调整;该函数在do_bindings()函数中调用;
 
(*expect):该函数建立子连接对应的NAT相关信息,主连接的NAT相关信息是通过iptables的NAT规则建立的;该函数在ip_nat_statndalone.c的call_expect()函数中调用
 
在NAT的基本函数ip_nat_fn()中,先调用call_expect(),最后调用的do_bindings(),所以(*expect)函数先调用,(*help)函数后调用。
 
3.2 多连接协议的NAT过程
 
2.3.1 过程简述
 
不论是SNAT还是DNAT,其对应的netfilter的nf_hook_ops节点都要执行ip_nat_fn(),此时与数据包对应的连接已经建立,对于连接的后续包,只是把连接的NAT信息绑定到该包(do_binding()函数);如果是新连接,如果是子连接,则调用协议相关nat_helper的(*expect)函数建立子连接的相关信息;否不论是SNAT还是DNAT都会调用ip_nat_setup_info()函数建立与该连接对应的NAT信息,所以在NAT规则中是不需要加状态是NEW的匹配的,最后将连接的NAT信息绑定到数据包。
 
在NAT信息的绑定过程中,会检查当前的数据包是否属于期待的要进行NAT修改的包,具体是用exp_for_packet()函数进行检查,该函数中调用相应传输层协议的(*exp_matches_pkt)函数,该函数只在TCP的协议中定义,但UDP没有,没有定义此函数时exp_for_packet()始终返回要求检查;如果要修改,将调用相应nat_helper的(*help)函数来修改数据,修改期待的子连接。

2.3.2 nat helper查找
 
在ip_nat_core.c文件的ip_nat_setup_info()函数中
...
 /* If there‘s a helper, assign it; based on new tuple. */
 if (!conntrack->master)
  info->helper = LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *,
      &reply);
...
static inline int
helper_cmp(const struct ip_nat_helper *helper,
    const struct ip_conntrack_tuple *tuple)
{
 return ip_ct_tuple_mask_cmp(tuple, &helper->tuple, &helper->mask);
}
 
3.3.3 (*expect)函数执行
 
在ip_nat_standalone.c文件的ip_nat_fn()函数中调用call_expect()函数,在call_expect()函数中调用(*expect)函数:
 
static inline int call_expect(struct ip_conntrack *master,
         struct sk_buff **pskb,
         unsigned int hooknum,
         struct ip_conntrack *ct,
         struct ip_nat_info *info)
{
 return master->nat.info.helper->expect(pskb, hooknum, ct, info);
}
 
static unsigned int
ip_nat_fn(unsigned int hooknum,
   struct sk_buff **pskb,
   const struct net_device *in,
   const struct net_device *out,
   int (*okfn)(struct sk_buff *))
{
...
 case IP_CT_NEW:
...
   if (ct->master
       && master_ct(ct)->nat.info.helper
       && master_ct(ct)->nat.info.helper->expect) {
    ret = call_expect(master_ct(ct), pskb,
        hooknum, ct, info);
   } else {
#ifdef CONFIG_IP_NF_NAT_LOCAL
    /* LOCAL_IN hook doesn‘t have a chain!  */
    if (hooknum == NF_IP_LOCAL_IN)
     ret = alloc_null_binding(ct, info,
         hooknum);
    else
#endif
    ret = ip_nat_rule_find(pskb, hooknum, in, out,
             ct, info);
   }
...
 
3.3.3 (*help)函数执行
 
在ip_nat_core.c文件的do_binding()函数中:
 
...
// 判断是否是期待的修改点
   if (exp_for_packet(exp, pskb)) {
    /* FIXME: May be true multiple times in the
     * case of UDP!! */
// 因为UDP没有序列号,没办法指示修改点,不象TCP可以用序列号表示,所以所有UDP包
// 都会执行help
    DEBUGP("calling nat helper (exp=%p) for packet\n", exp);
// 修改数据参数
    ret = helper->help(ct, exp, info, ctinfo,
         hooknum, pskb);
    if (ret != NF_ACCEPT) {
     READ_UNLOCK(&ip_conntrack_lock);
     return ret;
    }
    helper_called = 1;
   }
  }
  /* Helper might want to manip the packet even when there is no
   * matching expectation for this packet */
  if (!helper_called && helper->flags & IP_NAT_HELPER_F_ALWAYS) {
   DEBUGP("calling nat helper for packet without expectation\n");
   ret = helper->help(ct, NULL, info, ctinfo,
        hooknum, pskb);
   if (ret != NF_ACCEPT) {
    READ_UNLOCK(&ip_conntrack_lock);
    return ret;
   }
  }
...
 
有两处地方调用了(*help)函数,第一处(*help)执行了后面的(*help)就不会执行,但即使前面的(*help)没有执行,由于很多helper的flags都设置为0,所以后面那次(*help)基本都不会执行。
 
4. 结论
 
netfilter对多连接协议跟踪和NAT处理很好地实现了模块化,只要按固定的格式编写跟踪和NAT程序就能支持新的多连接协议,现在netfilter已经可以支持很多多连接协议,如FTP、TFTP、IRC、TALK、H.323、SIP、MMS、QUAKE3等。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多