ILD

udp6 local in path
作者:Yuan Jianpeng 邮箱:yuanjp89@163.com
发布时间:2022-8-12 站点:Inside Linux Development

最近在定位一个dhcpv6 server收到的dhcpv6 solicit报文源ip被修改的问题,学习了一下udp6的收包过程。


1 packet type 注册

ipv6收包的入口是 net/ipv6/ip6_input.c 

int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)


在 net/ipv6/af_inet6.c,static int __init inet6_init(void) 中,调用 ipv6_packet_init() 注册ipv6协议的packet type。

static struct packet_type ipv6_packet_type __read_mostly = {

        .type = cpu_to_be16(ETH_P_IPV6),

        .func = ipv6_rcv,

};


static int __init ipv6_packet_init(void)

{

        dev_add_pack(&ipv6_packet_type);

        return 0;

}


读取/proc/net/ptype可以,获得所有注册的packet type及其处理函数。

# cat /proc/net/ptype

Type Device      Function

0800          ip_rcv

0011          llc_rcv

8863          0x8c75c72c

8864          0x8c75ca04

0004          llc_rcv

0806          arp_rcv

86dd          ipv6_rcv


在 net/core/dev.c 的 __netif_receive_skb_core()函数中,数据包将递给packet type对应的处理函数处理

                deliver_ptype_list_skb(skb, &pt_prev, orig_dev, type,

                                       &ptype_base[ntohs(type) &

                                                   PTYPE_HASH_MASK]);


2 ipv6_rcv

做一些合法性检查,把不合法的包丢掉,然后计算传输层头的偏移:

hdr = ipv6_hdr(skb);


skb->transport_header = skb->network_header + sizeof(*hdr);


然后,执行PRE_ROUTING hook


        return NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING,

                       net, NULL, skb, dev, NULL,

                       ip6_rcv_finish);


pre routing hook执行完毕后,执行 ip6_rcv_finish(),也就定义在ip6_rcv()的前面。


3 ip6_rcv_finish

这个函数执行skb的路由表查找,查找结果决定包是发给本地,还是进行转发。


        if (!skb_valid_dst(skb))

                ip6_route_input(skb);


        return dst_input(skb);


在 net/ipv6/route.c 的 static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg) 中:


        if (addr_type & IPV6_ADDR_MULTICAST)

                rt->dst.input = ip6_mc_input;

        else if (cfg->fc_flags & RTF_LOCAL)

                rt->dst.input = ip6_input;

        else

                rt->dst.input = ip6_forward;


如果目的ip是本地ip,将调用ip6_input,如果目的ip是组播ip,将调用 ip6_mc_input。这两个函数也均定义在 net/ipv6/ip6_input.c 


4 ip6_input


直接就是执行 LOCAL IN


        return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_IN,

                       dev_net(skb->dev), NULL, skb, skb->dev, NULL,

                       ip6_input_finish);


执行完毕后,调用 ip6_input_finish。


5 ip6_input_finish

计算出传输层协议,然后执行其handler。

如下,nhoff是传输层协议在ipv6 header中的偏移。nexthdr就是传输层的协议号。


    const struct inet6_protocol *ipprot;


        nhoff = IP6CB(skb)->nhoff;

        nexthdr = skb_network_header(skb)[nhoff];


        raw = raw6_local_deliver(skb, nexthdr);

        ipprot = rcu_dereference(inet6_protos[nexthdr]);


        ipprot->handler(skb);


6 udpv6_rcv

ipv6 udp的代码位于:net/ipv6/udp.c


在int __init udpv6_init(void) 函数中,注册udp协议和处理函数:


    ret = inet6_add_protocol(&udpv6_protocol, IPPROTO_UDP);


static const struct inet6_protocol udpv6_protocol = {

        .handler        =       udpv6_rcv,

        .err_handler    =       udpv6_err,

        .flags          =       INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,

};


这里最终会找到socket,将skb queue到socket的buffer,等待应用层接收。


参考:

https://blogchiasekienthuc.wordpress.com/2016/06/23/path-of-a-packet-in-linux-kernel-stack-part-1/

https://blogchiasekienthuc.wordpress.com/2016/08/31/path-of-a-packet-in-linux-kernel-stack-part-2/



Copyright © linuxdev.cc 2017-2024. Some Rights Reserved.