图解Linux网络包接收过程( 七 )

在__netif_receive_skb_core中,我看着原来经常使用的tcpdump的抓包点,很是激动,看来读一遍源代码时间真的没白浪费 。接着__netif_receive_skb_core取出protocol,它会从数据包中取出协议信息,然后遍历注册在这个协议上的回调函数列表 。ptype_base 是一个 hash table,在协议注册小节我们提到过 。ip_rcv 函数地址就是存在这个 hash table中的 。
//file: net/core/dev.cstatic inline int deliver_skb(struct sk_buff *skb,                  struct packet_type *pt_prev,                  struct net_device *orig_dev){    ......    return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);}pt_prev->func这一行就调用到了协议层注册的处理函数了 。对于ip包来讲,就会进入到ip_rcv(如果是arp包的话,会进入到arp_rcv) 。
3.4 IP协议层处理我们再来大致看一下linux在ip协议层都做了什么,包又是怎么样进一步被送到udp或tcp协议处理函数中的 。
//file: net/ipv4/ip_input.cint ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev){    ......    return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, dev, NULL,               ip_rcv_finish);}这里NF_HOOK是一个钩子函数,当执行完注册的钩子后就会执行到最后一个参数指向的函数ip_rcv_finish 。
static int ip_rcv_finish(struct sk_buff *skb){    ......    if (!skb_dst(skb)) {        int err = ip_route_input_noref(skb, iph->daddr, iph->saddr,                           iph->tos, skb->dev);        ...    }    ......    return dst_input(skb);}跟踪ip_route_input_noref 后看到它又调用了 ip_route_input_mc 。在ip_route_input_mc中,函数ip_local_deliver被赋值给了dst.input, 如下:
//file: net/ipv4/route.cstatic int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr,u8 tos, struct net_device *dev, int our){    if (our) {        rth->dst.input= ip_local_deliver;        rth->rt_flags |= RTCF_LOCAL;    }}所以回到ip_rcv_finish中的return dst_input(skb); 。
/* Input packet from network to transport.  */static inline int dst_input(struct sk_buff *skb){    return skb_dst(skb)->input(skb);}skb_dst(skb)->input调用的input方法就是路由子系统赋的ip_local_deliver 。
//file: net/ipv4/ip_input.cint ip_local_deliver(struct sk_buff *skb){    /*     *  Reassemble IP fragments.     */    if (ip_is_fragment(ip_hdr(skb))) {        if (ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER))            return 0;    }    return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN, skb, skb->dev, NULL,               ip_local_deliver_finish);}static int ip_local_deliver_finish(struct sk_buff *skb){    ......    int protocol = ip_hdr(skb)->protocol;    const struct net_protocol *ipprot;    ipprot = rcu_dereference(inet_protos[protocol]);    if (ipprot != NULL) {        ret = ipprot->handler(skb);    }}如协议注册小节看到inet_protos中保存着tcp_rcv()和udp_rcv()的函数地址 。这里将会根据包中的协议类型选择进行分发,在这里skb包将会进一步被派送到更上层的协议中,udp和tcp 。
3.5 UDP协议层处理在协议注册小节的时候我们说过,udp协议的处理函数是udp_rcv 。
//file: net/ipv4/udp.cint udp_rcv(struct sk_buff *skb){    return __udp4_lib_rcv(skb, &udp_table, IPPROTO_UDP);}int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,           int proto){    sk = __udp4_lib_lookup_skb(skb, uh->source, uh->dest, udptable);    if (sk != NULL) {        int ret = udp_queue_rcv_skb(sk, skb    }    icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);}__udp4_lib_lookup_skb是根据skb来寻找对应的socket,当找到以后将数据包放到socket的缓存队列里 。如果没有找到,则发送一个目标不可达的icmp包 。
//file: net/ipv4/udp.cint udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb){      ......    if (sk_rcvqueues_full(sk, skb, sk->sk_rcvbuf))        goto drop;    rc = 0;    ipv4_pktinfo_prepare(skb);    bh_lock_sock(sk);    if (!sock_owned_by_user(sk))        rc = __udp_queue_rcv_skb(sk, skb);    else if (sk_add_backlog(sk, skb, sk->sk_rcvbuf)) {        bh_unlock_sock(sk);        goto drop;    }    bh_unlock_sock(sk);    return rc;}


推荐阅读