第二部分 连接跟踪

来源:互联网 发布:linux bg命令 编辑:程序博客网 时间:2024/04/25 18:44


并没有任何特定的先决条件。从2.6.23到2.6.25,API保持了极其的相识。在2.6.26(-rc1)中有一点点类型变化。Xtables-addons并没有提供包括连接跟踪在内的可移植的API,因为并没有模块的合入需要这个,但是编译系统还是非常方便的使用。

6 nf_conn结构体

       有些时候有必要去获取连接的参数。函数nf_ct_get可以找到数据包对应的连接,如果存在,那么将会返回这个连接并且返回连接状态。为了使用nf_ct_get函数,你需要去包含<net/netfilter/nf_conntrack.h>,同时为了使用enum ip_conntrack_info,你也需要包含<linux/netfilter/nf_conntrack_common.h>。这里将这个头文件分布在linux/目录下,是为了将其导出到用户空间,这些静态值,例如IP_CT_NEW在用户空间也是非常有用的,当然nf_ct_get只能在内核中可以使用。

#include <linux/netfilter/nf_conntrack_common.h>

enum ip_conntrack_infoctinfo;

struct nf_conn *ct;

ct = nf_ct_get(skb,&ctinfo);

存在着众多的连接状态,在Xtables的conntrack的匹配中,你可以匹配他们,或者通过xt_LOGMARK打印出连接的信息,这是一个target的扩展。注意,连接跟踪子系统将在raw表后面,mangle表之前处理。

if (ct == NULL)

pr_info("Thisis --ctstate INVALID\n");

当连接跟踪子系统将这个连接声明为INVALID,那么ct就有可能是NULL,例如在一个已经存在的连接上面发送了一个TCP SYN。

else if (ct ==&nf_conntrack_untracked)

pr_info("Thisone is not tracked\n");

在raw表中,NOTRACK的目标将会使数据包从连接跟踪中豁免;这个特性对TARPIT目标非常有用。它也通常被用于任何你想要丢弃的数据包,但是一般来说,人们并不会抱怨因为它通常发生在重复的规则中。如果在filter表中仅仅是丢弃数据包,那将意味着对应的连接跟踪表象将会一直保存到其超时,在一般情况下,这种处理方式可以很好的工作。默认的超时时间依赖于具体的协议和实现,你丢弃的连接如果是NEW,那么一般为30秒到2分钟。

else if (ctinfo % IP_CT_IS_REPLY == IP_CT_NEW)

pr_info("Thisis the first packet in a connection\n");

else if (ctinfo % IP_CT_IS_REPLY == IP_CT_RELATED)

pr_info("WelcomeMr. Bond, we have been expecting you\n");

else if (ctinfo % IP_CT_IS_REPLY == IP_CT_ESTABLISHED)

pr_info("Youcan figure out this one!\n");

如果ct不是NULL,并且不是无效的连接跟踪表项,例如没有跟踪的包,那么这个连接就是有效的,并且它的状态可以在ctinfo中找到。enumip_conntrack_ctinfo混合了连接的状态和数据包的方向。

       IP_CT_NEW,新连接

       IP_CT_RELATED,同样也是新连接,不过是一个期待连接。

       IP_CT_ESTABLISHED,连接已经建立,当前数据包在原始方向。

       IP_CT_ESTABLISHED +IP_CT_IS_REPLY,连接已经建立,数据包在回复方向。

       IP_CT_RELATED + IP_CT_IS_REPLY,期待连接开始,同时数据包在回复方向。非常惊奇的是第一个包可以在回复方向(注意:回复方向的连接不是原始的那个)。这个实际是用于ICMP回复。

       IP_CT_NEW + IP_CT_IS_REPLY没有使用,并且是不合法的。

通过使用ctinfo %IP_CT_IS_REPLY(在这种情况下类似于ctinfo & ~IP_CT_IS_REPLY),提取连接跟踪的状态。数据包的方向也可以通过ctinfo /IP_CT_IS_REPLY来获取,但是一个更方便的宏CTINFO2DIR(ctinfo)可以使用,定义在<linux/netfilter/nf_conntrack_tuple_common.h>。

 

连接跟踪器

连接跟踪器是连接跟踪架构中非常重要的一个部分,并且关系到了状态防火墙的实现。它的主要工作是将一个IP数据包找到对应的连接信息,并保证数据包和其参数的正确性。例如TCP数据流,检测窗口大小和TCP状态转换是否正确。

最简单的情况,仅仅是将源地址和目标地址拷贝到structnf_conntrack_tuple,structnf_conntrack_tuple将会和其他一起连接形成一个已知连接表。连接跟踪分割成两个类别,layer-3和layer-4模块,以实现最大的模块化。当然也存在layer-5的连接跟踪器,称为连接辅助,因为这些函数并不是为当前连接工作的,而是为了以后的新连接。

 

7 layer-3连接跟踪器

7.1 目标

当然,问题来了,这章将会充满着什么古怪的想法,这些答案将会如何被分解?

荟萃一个在内核中并不存在的layer-3连接跟踪器将会使一个非常困难的工作。不仅仅因为IPv4和IPv6是最具统治的协议,更因为内核现在并没有在其他协议中有netfilter的钩子,当然除了特殊的以外。

这就导致了一些非常有趣的任务。Jan有一个为IPX写的连接跟踪器,但是振兴老的DOS游戏最后被证明是一个长期的认为,因为现代操作系统的技术问题。当我们发现在ARP的输入输出路径上并没有netfilter的钩子,诉诸arp的连接跟踪也并不是那么有收获,所以这个想法也就报废了,因为我们不想读者去修改内核,编译并安装等等流程。

 

7.2 结构体定义

Layer-3跟踪器结构体定义在<net/netfilter/nf_conntrack_l3proto.h>。它包含了数据包到tuple的关联,tuple翻转函数和去获取4层协议号的函数。

struct nf_conntrack_l3proto {

const char *name;

uint16_t l3proto;

bool (*pkt_to_tuple)(const struct sk_buff *skb, unsigned int nhoff,

struct nf_conntrack_tuple *tuple);

bool (*invert_tuple)(struct nf_conntrack_tuple *inverse,

const struct nf_conntrack_tuple *orig);

int (*print_tuple)(struct seq_file *,

const struct nf_conntrack_tuple *);

int (*get_l4proto)(const struct sk_buff *skb, unsigned int nhoff,

unsigned int *dataoff, uint8_t *protonum);

struct module *me;

};

当缺少实际的四层部分时,三层连接跟踪器是基本没什么实际用途。函数pkt_to_tuple和invert_tuple将会被调用,但是仍然没有创建任何的连接跟踪表项,你通过conntrack –L命令或者是/proc/net/nf_conntrack里面也就看不到相应的表项。只有在四层连接跟踪器注册才能有连接跟踪和事件产生。

       函数get_l4proto应该检查数据包和返回4层协议号,它也有可能返回-NF_ACCEPT,那么意味着这个连接没有被正常跟踪。

 

7.3 通用的L4跟踪

       虽然四层跟踪仅仅只针对最通用的协议,但是很多的协议仍然可以被跟踪,因为可以使用通用的跟踪器。AH和ESP就是这个类别的两个例子。

       通用跟踪器将所有的数据包的四层协议映射到一个连接中,这也是逻辑上可以做到的。这个连接跟踪表将会看起来像:

       # conntrack -L |grep "^unknown"

unknown 50 537 src=192.168.0.137dst=192.168.16.34 packets=12 bytes=1456

src=192.168.16.34 dst=192.168.0.137packets=12 bytes=2704 mark=0 use=1

       我们希望读者可以原谅这个章节太短。同时我们确认写三层的跟踪器和四层的非常相似,我们将在下一章节将四层跟踪。

 

8 四层连接跟踪器

8.1 结构体定义

       四层跟踪器的定义包含在<net/netfilter/nf_conntrack_l4proto.h>,这些回调函数的顺序从error到destroy排序,描述了数据包流程的整个顺序。

struct nf_conntrack_l4proto {

const char *name;

uint16_t l3proto;

uint8_t l4proto;

int (*error)(struct sk_buff *, unsigned int dataoff,

enum ip_conntrack_info *ctinfo,unsigned int pf,

unsigned int hooknum);

bool (*pkt_to_tuple)(const struct sk_buff *skb,

unsigned int dataoff,

struct nf_conntrack_tuple *tuple);

bool (*invert_tuple)(struct nf_conntrack_tuple *inverse,

const struct nf_conntrack_tuple *original);

int (*packet)(struct nf_conn *ct, const struct sk_buff *skb,

unsigned int dataoff, enum ip_conntrack_info ctinfo,

unsigned int pf, unsigned int hooknum);

bool (*new)(struct nf_conn *ct, const struct sk_buff *skb,

unsigned int dataoff);

void (*destroy)(struct nf_conn *ct);

int (*print_conntrack)(struct seq_file *s,

const struct nf_conn *ct);

int (*print_tuple)(struct seq_file *s,

const struct nf_conntrack_tuple *tuple);

struct module *me;

};

它也有数据包到tuple和关联的tuple翻转,同时还包含了“packet”、“new”和“destroy”。

 

8.2 目标

       在这一章节我们将会关注一下ESP连接跟踪模块。它当然可以工作,并且没有什么实际的域是需要处理,因为通用的连接跟踪已经处理了通用的功能。如果你需要跟踪一些特殊的SPI数据流,那么这个模块适合你。

       ESP是一个加密的,所以除非是两端,否则没有任何途径可以查看它里面的数据。即使如此,对于管道模型和其他任何类型的(非加密)管道和封装,我们并不经常希望检测里面的内容,而是跟踪管道连接本身。

      

8.3 模块初始化

       结构体成员name是用于提供给用户空间的短字符串,尽量保持简单,并且不能有空格。成员l3proto和l4proto分别标示了3层和4层对应的辅助函数。

static struct nf_conntrack_l4protoesp_ctrack_reg __read_mostly = {

.name ="esp",

.l3proto =NFPROTO_IPV6,

.l4proto =IPPROTO_ESP,

       剩下的结构体组成成员是函数指针:

       esp_ctrack_pkt2tuple 将数据包中的信息映射到tuple中。

       esp_ctrack_new 当创建一个新的连接跟踪表项时就会调用。

       esp_ctrack_packet 数据包处理函数,例如更新内部跟踪状态。TCP使用这个函数进行状态转换。

       esp_ctrack_invtuple 翻转tuple

       esp_ctrack_prct 打印连接跟踪表项

       esp_ctrack_prtuple 打印连接tuple

      

              .pkt_to_tuple = esp_ctrack_pkt2tuple

.new =esp_ctrack_new

.packet =esp_ctrack_packet,

.invert_tuple =esp_ctrack_invtuple,

.print_conntrack =esp_ctrack_prct,

.print_tuple =esp_ctrack_prtuple,

.me = THIS_MODULE,

};

 

8.4 tuple结构体(五元组)

union nf_conntrack_man_proto包含了“协议可变”部分,(在NAT情况下,这部分保持的是源地址,在外网IP和内网IP间切换)。

注释:我认为更不会让人迷糊的介绍是查看structnf_conntrack_tuple,这个结构体包含有src和dst,其中dst是固定的,因为例如你访问baidu.com,dst就是百度,但是src因为存在NAT,那么就有可能是内网IP和公网IP。

 

8.5 数据包到连接跟踪

       当一个数据包进入连接跟踪子系统,它将被传递到对应的4层连接跟踪模块,进入pkt_to_tuple钩子。这个函数是将数据包映射到连接五元组中。后者是在netfilter中唯一确定一个连接。

 

8.6 五元组反转

       当一个数据包达到,它会和已经存在的tuple进行对比,看是否能找到另一个方向的对应连接。

       它们并不关注是从哪个网口进来的,因为策略路由情况下,数据包可能会走不同的路径。

       static bool tcp_invert_tuple(struct nf_conntrack_tuple *tuple,

const struct nf_conntrack_tuple *orig)

{

inverse->src.u.all= original->dst.u.all;

inverse->dst.u.all= original->src.u.all;

}

注释:例如有一个出去的数据包,那么通过反转,在reply方向就赋予了dst和src,当然这个src可能还是内网地址。这样等到回包时,就可以很快的找到对应的连接。

 

8.7 输出表项

8.8 总结

 

9 连接跟踪辅助函数

       注释:理解这个应该先应该知道ALG(应用层网关),例如FTP等,在应用层数据中涉及到内网IP,同时存在NAT,那么就需要将内网IP换成外网IP,这些就是辅助函数做的事情。

struct nf_conntrack_helper{

const char *name;

unsigned int max_expected;

unsigned int timeout;

/* Tuple of connection to analyze */

struct nf_conntrack_tuple tuple;

/* Our helpful function */

int (*help)(struct sk_buff *skb, unsigned int protoff,

struct nf_conn *ct, enum ip_conntrack_info ctinfo);

void (*destroy)(struct nf_conn *ct);

struct module *me;

};

原创粉丝点击