Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1489000
  • 博文数量: 408
  • 博客积分: 10036
  • 博客等级: 上将
  • 技术积分: 4440
  • 用 户 组: 普通用户
  • 注册时间: 2006-04-06 13:57
文章分类

全部博文(408)

文章存档

2011年(1)

2010年(2)

2009年(1)

2008年(3)

2007年(7)

2006年(394)

我的朋友

分类: LINUX

2006-04-12 16:23:08

 
Web me09.cublog.cn
搜索更多
netfilter中IP协议跟踪和NAT实现
 
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn:
来源:http://yfydz.cublog.cn

1. 前言
 
和匹配和目标一样,netfilter提供了模块化的IP层协议的跟踪和NAT处理,除了内核自带的模块外,用户可以根据模块格式自己编写其他 IP协议的跟踪和NAT处理。注意:本文针对的跟踪和NAT模块是针对IP上层的协议,如TCP、UDP、ICMP等,而TCP、UDP上层的协议如 FTP、TFTP等的跟踪和NAT使用其他方式处理,将在以后的文章中介绍。
 
2. tuple
 
在具体介绍IP协议跟踪前,需要说明一个结构ip_conntrack_tuple,这是netfilter用来描述跟踪或NAT各IP协议时需要跟踪或修改的各协议的信息,这些信息和连接的一一对应的,对于所有IP协议,协议类型、源地址、目的地址这三个参数是识别连接所必须的,具体到各个协议,就要提取出各协议的唯一特征数据,如TCP、UDP的源端口、目的端口,ICMP的ID、TYPE、CODE等值,这些值就是tuple结构要处理的数据。各协议相关数据是以联合形式定义在tuple结构中的,netfilter缺省支持TCP、UDP和ICMP协议,如果还要支持其他IP协议,如 GRE、ESP、AH、SCTP等,需要在联合中添加相应的协议参数值。
 
include/linux/netfilter_ipv4/ip_conntrack_tuple.h
/* The protocol-specific manipulable parts of the tuple: always in
   network order! */
union ip_conntrack_manip_proto
{
 /* Add other protocols here. */
 u_int16_t all;
 struct {
  u_int16_t port;
 } tcp;
 struct {
  u_int16_t port;
 } udp;
 struct {
  u_int16_t id;
 } icmp;
};
/* The manipulable part of the tuple. */
struct ip_conntrack_manip
{
 u_int32_t ip;
 union ip_conntrack_manip_proto u;
};

/* This contains the information to distinguish a connection. */
struct ip_conntrack_tuple
{
 struct ip_conntrack_manip src;
 /* These are the parts of the tuple which are fixed. */
 struct {
  u_int32_t ip;
  union {
   /* Add other protocols here. */
   u_int16_t all;
   struct {
    u_int16_t port;
   } tcp;
   struct {
    u_int16_t port;
   } udp;
   struct {
    u_int8_t type, code;
   } icmp;
  } u;
  /* The protocol. */
  u_int16_t protonum;
 } dst;
};

3. 协议连接跟踪
 
netfilter中对每个要进行跟踪的IP协议定义了以下结构,每个IP协议的连接跟踪处理就是要填写这样一个结构:
 
include/linux/netfilter_ipv4/ip_conntrack_protocol.h
struct ip_conntrack_protocol
{
 /* Next pointer. */
 struct list_head list;
 /* Protocol number. */
 u_int8_t proto;
 /* Protocol name */
 const char *name;
 /* Try to fill in the third arg; return true if possible. */
 int (*pkt_to_tuple)(const void *datah, size_t datalen,
       struct ip_conntrack_tuple *tuple);
 /* Invert the per-proto part of the tuple: ie. turn xmit into reply.
  * Some packets can't be inverted: return 0 in that case.
  */
 int (*invert_tuple)(struct ip_conntrack_tuple *inverse,
       const struct ip_conntrack_tuple *orig);
 /* Print out the per-protocol part of the tuple. */
 unsigned int (*print_tuple)(char *buffer,
        const struct ip_conntrack_tuple *);
 /* Print out the private part of the conntrack. */
 unsigned int (*print_conntrack)(char *buffer,
     const struct ip_conntrack *);
 /* Returns verdict for packet, or -1 for invalid. */
 int (*packet)(struct ip_conntrack *conntrack,
        struct iphdr *iph, size_t len,
        enum ip_conntrack_info ctinfo);
 /* Called when a new connection for this protocol found;
  * returns TRUE if it's OK.  If so, packet() called next. */
 int (*new)(struct ip_conntrack *conntrack, struct iphdr *iph,
     size_t len);
 /* Called when a conntrack entry is destroyed */
 void (*destroy)(struct ip_conntrack *conntrack);
 /* Has to decide if a expectation matches one packet or not */
 int (*exp_matches_pkt)(struct ip_conntrack_expect *exp,
          struct sk_buff **pskb);
 /* Module (if any) which this is connected to. */
 struct module *me;
};
 
结构中包括以下参数:
 
struct list_head list:这是将该结构挂接到协议跟踪链表中的
u_int8_t proto:协议号,在IP头中的协议号是8位,1为ICMP,2为IGMP,6为TCP,17为UDP等等
const char *name:协议名称,字符串常量
struct module *me:指向模块本身,统计模块是否被使用
 
结构中包括以下函数:
 
(*pkt_to_tuple):将数据包中的信息提取到tuple结构中,如对于TCP/UDP,提取其端口值,在net/ipv4/netfilter/ip_conntrack_core.c的get_tuple()函数中调用;
 
(*invert_tuple):将tuple中数据进行倒置,用来匹配处理连接的返回包,如对于TCP/UDP,要将端口值倒置,在net/ipv4/netfilter/ip_conntrack_core.c的invert_tuple()函数中调用;
 
(*print_tuple):打印协议相关的tuple值,在查看/proc/net/ip_conntrack文件时调用,net/ipv4/netfilter/ip_conntrack_standalone.c;
 
(*print_conntrack):打印协议相关的值,在查看/proc/net/ip_conntrack文件时调用,net/ipv4/netfilter/ip_conntrack_standalone.c;
 
(*packet):判断数据包是否合法,并调整相应连接的信息,也就是实现各协议的状态检测,对于UDP等本身是无连接的协议的判断比较简单,netfilter建立一个虚拟连接,每个新发包都是合法包,只等待回应包到后连接都结束;但对于TCP之类的有状态协议必须检查数据是否符合协议的状态转换过程,这是靠一个状态转换数组实现的,在我以前的文章“什么是状态检测”中对这个数组进行了描述。在 net/ipv4/netfilter/ip_conntrack_core.c的ip_conntrack_in()函数中调用;
 
(*new):判断是否是该协议的新连接,如对于TCP,必须用SYN包表示连接开始,而对于UDP和ICMP则始终是新连接,在net/ipv4/netfilter/ip_conntrack_core.c的init_conntrack()函数中调用;
 
(*destroy):在系统删除连接时释放该协议的特定数据,不过目前都没有使用,在net/ipv4/netfilter/ip_conntrack_core.c的destroy_conntrack()函数中调用;
 
(*exp_matches_pkt):判断该数据包是否是期待的新包还是以前的重发包,只是在NAT处理时使用,针对的是有序列号控制的协议,如TCP,而无序列号控制的协议无此函数处理,在net/ipv4/netfilter/ip_nat_core.c的exp_for_packet ()函数中调用;
最后,这些协议跟踪结构在net/ipv4/netfilter/ip_conntrack_core.c的
 
ip_conntrack_init()函数中挂接到协议跟踪链表中:
 list_append(&protocol_list, &ip_conntrack_protocol_tcp);
 list_append(&protocol_list, &ip_conntrack_protocol_udp);
 list_append(&protocol_list, &ip_conntrack_protocol_icmp);
 
要编写自己的IP协议跟踪模块,先要分析这些协议头中哪些信息可以用来唯一识别连接,作NAT时要修改哪些信息,把这些信息添加到 ip_conntrack_tuple结构的联合中;然后填写该协议的ip_conntrack_protocol结构,实现结构中的内部函数;最后在 ip_conntrack_init()函数中将此结构挂接到协议跟踪链表中。
 
4. 协议NAT
 
netfilter中对每个要进行NAT的IP协议定义了以下结构,每个IP协议的NAT处理就是要填写这样一个结构:
 
include/linux/netfilter_ipv4/ip_nat_protocol.h
struct ip_nat_protocol
{
 struct list_head list;
 /* Protocol name */
 const char *name;
 /* Protocol number. */
 unsigned int protonum;
 /* Do a packet translation according to the ip_nat_proto_manip
  * and manip type. */
 void (*manip_pkt)(struct iphdr *iph, size_t len,
     const struct ip_conntrack_manip *manip,
     enum ip_nat_manip_type maniptype);
 /* Is the manipable part of the tuple between min and max incl? */
 int (*in_range)(const struct ip_conntrack_tuple *tuple,
   enum ip_nat_manip_type maniptype,
   const union ip_conntrack_manip_proto *min,
   const union ip_conntrack_manip_proto *max);
 /* Alter the per-proto part of the tuple (depending on
    maniptype), to give a unique tuple in the given range if
    possible; return false if not.  Per-protocol part of tuple
    is initialized to the incoming packet. */
 int (*unique_tuple)(struct ip_conntrack_tuple *tuple,
       const struct ip_nat_range *range,
       enum ip_nat_manip_type maniptype,
       const struct ip_conntrack *conntrack);
 unsigned int (*print)(char *buffer,
         const struct ip_conntrack_tuple *match,
         const struct ip_conntrack_tuple *mask);
 unsigned int (*print_range)(char *buffer,
        const struct ip_nat_range *range);
};

结构中包括以下参数:
 
struct list_head list:这是将该结构挂接到协议跟踪链表中的
const char *name:协议名称,字符串常量
unsigned int protonum:协议号,在IP头中的协议号是8位,在此用unsigned int有点浪费
 
结构中包括以下函数:
(*manip_pkt):修改协议相关数据,根据NAT规则来确定是修改源部分还是目的部分,在net/ipv4/netfilter/ip_nat_core.c的manip_pkt()函数中调用;
 
(*in_range):判断数据包是否是要进行NAT修改,在net/ipv4/netfilter/ip_nat_core.c的in_range()、get_unique_tuple()等函数中调用;
 
(*unique_tuple):构造一个新tuple处理将原tuple在进行NAT后对应的连接参数,如TCP源NAT时,除了源地址必须要修改外,一般还要修改源端口,这个连接的后续包的源端口就都改这个端口值,而修改后的这个端口值必须是唯一的,和这个连接绑定,其他连接就不能再使用这个端口,如果找不到合适的tuple值,NAT将失败,也就是说,对于多对一的NAT转换,理论上最多只能处理65535个TCP连接,超过此数的新的 TCP连接就无法进行NAT了,对于TCP、UDP,(*unique_tuple)就是检测查找一个新的未用端口生成一个新的tuple结构对应该连接,对应ICMP,则是找一个未用的ID值,该函数在net/ipv4/netfilter/ip_nat_core.c的 get_unique_tuple()函数中调用;
 
(*print):打印struct ip_conntrack_tuple中的协议相关信息;
 
(*print_range):打印struct ip_nat_range结构中要进行NAT修改的那部分协议信息;
 
最后,这些协议跟踪结构在net/ipv4/netfilter/ip_nat_core.c的ip_nat_init()函数中挂接到协议NAT链表中:
 list_append(&protos, &ip_nat_protocol_tcp);
 list_append(&protos, &ip_nat_protocol_udp);
 list_append(&protos, &ip_nat_protocol_icmp);
对新IP协议的NAT模块的添加和跟踪模块的添加类似。

5. 其他IP协议的跟踪和NAT
 
下面讨论其他IP协议如果要进行跟踪和NAT要处理哪些协议相关数据:
 
SCTP:RFC2960,协议号132,和TCP非常类似,用源端口和目的端口来识别;
                 SCTP Common Header Format
 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     Source Port Number        |     Destination Port Number   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                      Verification Tag                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           Checksum                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

IGMP:RFC3376,协议号2,IGMP头内信息太少,没有特殊数据供识别
    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |      Type     | Max Resp Time |           Checksum            |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                         Group Address                         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

GRE:RFC1701,RFC2784,协议号47,使用KEY来作为修改数据,ver和protocol作为识别用的固定数据
 
 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|C|R|K|S|s|Recur|  Flags  | Ver |         Protocol Type         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      Checksum (optional)      |       Offset (optional)       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                         Key (optional)                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Sequence Number (optional)                 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                         Routing (optional)                    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|C|       Reserved0       | Ver |         Protocol Type         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      Checksum (optional)      |       Reserved1 (Optional)    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 
ESP:RFC4303,协议号50,只能用SPI来识别,SPI是SA的一部分,不过是不能修改的,因为SPI是在IKE协商过程中确定的,两边都已经预先知道,一旦修改了就匹配不到SA了
 
 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               Security Parameters Index (SPI)                 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |                      Sequence Number                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
|                    Payload Data* (variable)                   | 
|                                                               |
|                                                               |
+               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               |     Padding (0-255 bytes)                     |
+-+-+-+-+-+-+-+-+               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
|                               |  Pad Length   | Next Header   | 
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         Integrity Check Value-ICV   (variable)                |
~                                                               ~
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 
AH:RFC4304,协议号51,AH协议无法进行NAT的,否则认证就会失败,跟踪也只能靠SPI

6. 结论
 
netfilter的IP协议跟踪和NAT处理很好地实现了模块化,但除了netfilter自带的模块外,可处理其他IP协议也已经不多了,只有GRE和SCTP可以新增模块,其他协议增加模块基本无意义。
阅读(927) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~