连接跟踪与nat转换

很长时间没上来,一时兴起只来剪草,却正巧赶上开博一周年,天意如此,我且顺了天,附上点近期所学东西。

连接跟踪对nat实现的辅助

一个新建连接的数据包从物理层传递到网络层后,连接跟踪会对其进行跟踪,记录下其连接状态和重要信息,从指定的内置的处理函数的优先级enum nf_ip_hook_priorities{}中可以看出连接跟踪函数的优先级是在nat处理之前的,是辅助完成nat转换的,但其不对数据包进行任何实质性处理,所以修改数据包信息的工作还是由nat完成。

连接跟踪就是获取数据报的数据结构sk_buff中ip头和协议头的信息(源ip、端口,目的ip、端口、传输协议和网络协议),存放于连接跟踪特定的多元组tuple中,tuple的成员dir还对数据包的方向进行了记录,用来区分是进来的包还是发出的包。内核中对本地接受和发送的数据包的信息分别用两个多元组original tuple和reply tuple分开存储,这个就相当理论所说的napt映射表。original tuple多元组的信息全从sk_buff中获取,回包的时候只需对tuple中的源ip和目的ip,源端口和目的端口的值使用函数nf_ct_invert_tuple进行交换处理,赋值给reply tuple这个多元组中的成员,连接跟踪的任务便完成了,根据前面的原理描述,我们知道网络地址转换主要是对sk_buff中ip地址和端口号动手脚来实现,这些工作还是由nat模块完成。

 

连接跟踪是nat转换的基础,下面针对在nat转换中起关键作用的几个函数进行分析。以接收tcp包为例。

1. 函数nf_ct_get_tuple从sk_buff中获取源ip、端口,目的ip、端口、传输协议和网络协议信息,之后赋值给多元组tuple。

bool  nf_ct_get_tuple(const struct sk_buff *skb,unsigned int nhoff,unsigned int dataoff,u_int16_t l3num,u_int8_t protonum,struct nf_conntrack_tuple *tuple, const struct nf_conntrack_l3proto *l3proto,const struct nf_conntrack_l4proto *l4proto)

{

         memset(tuple, 0, sizeof(*tuple));

         /*协议类型赋给tuple成员*/

tuple->src.l3num = l3num;

         if (l3proto->pkt_to_tuple(skb, nhoff, tuple) == 0)

               return false;

         /*网络协议赋值*/

        tuple->dst.protonum = protonum;

         /*获取传输方向*/

        tuple->dst.dir = IP_CT_DIR_ORIGINAL;

         /*获取l4层信息*/

        return l4proto->pkt_to_tuple(skb, dataoff, tuple);

}

EXPORT_SYMBOL_GPL(nf_ct_get_tuple);

获取sk_buff网络层信息的具体操作如下:

struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4 __read_mostly = {

        .l3proto           = PF_INET,

        .name             = "ipv4",

        .pkt_to_tuple       = ipv4_pkt_to_tuple,

        .invert_tuple       = ipv4_invert_tuple,

        .print_tuple        = ipv4_print_tuple,

        .get_l4proto       = ipv4_get_l4proto,

            …                 …

};

ipv4_pkt_to_tuple所做的工作是从ip首部自动的源地址开始,读取两个32字节的变量,分别赋给tuple中的源地址和目的地址。

获取sk_buff传输层信息的具体操作如下:

struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp4 __read_mostly =

{

        .l3proto                = PF_INET,

        .l4proto                = IPPROTO_TCP,

        .name                 = "tcp",

        .pkt_to_tuple            = tcp_pkt_to_tuple,

        .invert_tuple            = tcp_invert_tuple,

        .print_tuple             = tcp_print_tuple,

        .print_conntrack         = tcp_print_conntrack,

        .packet                 = tcp_packet,

        .new                   = tcp_new,

        .error                  = tcp_error,

                 …                 …

};

EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_tcp4);

 

函数tcp_pkt_to_tuple从传输头首地址开始,读取两个8字节的变量,分别赋给tuple中的源端口和目的端口。

static bool tcp_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff,

                              struct nf_conntrack_tuple *tuple)

{

        const struct tcphdr *hp;

        struct tcphdr _hdr;

        hp = skb_header_pointer(skb, dataoff, 8, &_hdr);

        if (hp == NULL)

               return false;

        tuple->src.u.tcp.port = hp->source;

        tuple->dst.u.tcp.port = hp->dest;

        return true;

 }

 

2. 最后一个重要的函数莫过于实现交换处理的函数nf_ct_invert_tuple(),由original tuple经过层层处理从而得到reply tuple,以达到最终目的。

nf_ct_invert_tuple(struct nf_conntrack_tuple *inverse,

                                  const struct nf_conntrack_tuple *orig,

                                  const struct nf_conntrack_l3proto *l3proto,

                                  const struct nf_conntrack_l4proto *l4proto)

 {      memset(inverse, 0, sizeof(*inverse));

        inverse->src.l3num = orig->src.l3num;

        if (l3proto->invert_tuple(inverse, orig) == 0)

                 return false;

        inverse->dst.dir = !orig->dst.dir;

        inverse->dst.protonum = orig->dst.protonum;

        return l4proto->invert_tuple(inverse, orig);

}

 EXPORT_SYMBOL_GPL(nf_ct_invert_tuple);

从代码中可以清楚地看到同样是l3和l4层分开交换处理,与获取sk_buff信息的处理步骤基本一致,此处不再赘述。仅附l4处理代码。

static bool tcp_invert_tuple(struct nf_conntrack_tuple *tuple,

                            const struct nf_conntrack_tuple *orig)

{

        tuple->src.u.tcp.port = orig->dst.u.tcp.port;

        tuple->dst.u.tcp.port = orig->src.u.tcp.port;

        return true;

}

 

你可能感兴趣的:(linux,入门,C,语言,分析报告,struct,tcp,网络协议,数据结构,工作,网络)