分享

netfilter中IP协议跟踪和NAT实现

 womking 2007-08-16
netfilter中IP协议跟踪和NAT实现

本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: [email]yfydz_no1@hotmail.com[/email]
来源:[url]http://yfydz.[/url]

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可以新增模块,其他协议增加模块基本无意义。

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

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多