Linux中ipv6代码阅读(2)

二、在网络层的处理

这部分是说明数据报文在网络层的处理。上面一部分已经说明了在链路层的处理。在链路层的处理,基本都是在驱动中已经实现了的。接着链路层的处理,对于ipv6协议,处理过程在ipv6_recv()中。

1. ipv6_rcv()中,会做一些必要的检查和更新MIB的一些信息,接着处理hopbyhop报头。然后进入NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL, ip6_rcv_finish); 对于NF_HOOK的作用解释。 如果没有配置netfilter,可以简单认为NF_HOOK就等于直接调用ip6_rcv_finish (skb)

Ipv6_rcv()会处理hopbyhop报头,在ipv6_parse_hopopts()函数中处理。注意ip6_parse_tlv()的处理过程,它本身只处理PAD0PAD1type,就是rfc2460里面最早定义的两个选项,其它选项都是通过tlvprochopopt_lst中定义的回调函数来处理的。这样就能够根据将来协议的发展,灵活的添加新的hopbyhop类型,而不需要修改这个函数本身。

 

2.对于除hopbyhop以外的扩展报头的处理,是通过路由表来进行的。在ip6_rcv_finish()里面,会调用ip6_route_input(skb),这个函数返回的是路由表中对应的fib6_node,这个节点的input函数,就会根据不同的目的地调用不同的函数来处理。具体说来:

1If the destination address matches FE80::<EUI64>

skb->dst->input=ip6_input

 skb->dst->output=ip6_output

2Else if the destination address’s first 10 bits matches FE80::

skb->dst->input=ip6_forward

skb->dst->output=ip6_output

3Else if the destination address’s first 8 bits matches FF00::

skb->dst->input=ip6_mc_input

skb->dst->output=ip6_output

4Else ( no match)

skb->dst->input= ip6_pkt_discard

skb->dst->output=ip6_pkt_discard

对于ip6_route_input(skb)函数的分析,我们后面讲路由查找的时候再叙述,这里先跳过去。

3.说明一下到本机的路由表项的初始化过程。到本机的路由表的初始化是在给网卡分配ipv6地址的时候初始化的,代码在addrconf.c中。比如当用户在给网卡手动赋ipv6地址的时候,会通过netlink接口,传递到内核以后,就由rtnetlink_rcv_msg()来处理。Rtnetlink_recv_msg()会根据family的值,在rtnetlink_links[family]表中进行查找,找到对应协议簇的处理表。对于ipv6而言是PF_INET6协议簇,调用的是inet6_rtnetlink_table[]。在inet6_rtnetlink_table[]表中,对应添加网卡ipv6地址的处理函数是inet6_rtm_newaddr()函数,因此整个处理过程是inet6_rtm_newaddr()àipv6_add_addr() à addrconf_dst_alloc() à rt->u.dst.input = ip6_input(),从而转入ip6_input()函数的处理。

转发路由表项的初始化和到本机的路由表的初始化过程类似。从netlink再到ip6_route_add(),添加ip6_forward()的处理函数。

4. 接上面第2点,在ip6_route_input(skb)函数调用中,返回的是路由表中对应的fib6_node结构,它会调用 skb->dst->input()函数。如果数据报文是到本机,这个函数就是ip6_input()函数。扩展报头的处理就在ip6_input()函数中。然后调用ip6_input_finish() à ipprot->handler(&skb) 然后调用在inet6_protos[]里面注册了的ipv6各个扩展报头的处理函数。

5.  ipv6协议簇中的函数调用流程如下图所示。


 

Linux中ipv6代码阅读(2)_第1张图片 

 

6. tcpv6_init()函数中,通过inet6_add_protocol()inet6_protos[]注册了处理函数tcp_v6_rcv(),这样,协议就会交给tcp_v6_rcv()处理了。这样就交给了传输层的协议栈来处理了。ICMPv6UPDv6协议的处理类似。

这里注意各个处理函数的返回值,像icmpv6_rcv()返回0,表示这个数据报不再处理了,已经处理完了。而ipv6_destopt_rcv()则返回-1/1-1表示出错了,就不再处理;1表示当前的报头已经处理完了,要接着处理这个数据报的下一个报头。这样,就把报文传送到了传输层了。对于传输层,我们选择一个简单的updv6协议,它注册的处理函数是udpv6_rcv(),这部分会在传输层的处理中论述。

你可能感兴趣的:(Linux中ipv6代码阅读(2))