Chinaunix首页 | 论坛 | 博客
  • 博客访问: 258227
  • 博文数量: 52
  • 博客积分: 406
  • 博客等级: 一等列兵
  • 技术积分: 549
  • 用 户 组: 普通用户
  • 注册时间: 2012-04-21 12:34
个人简介

......

文章分类

全部博文(52)

文章存档

2014年(1)

2013年(32)

2012年(19)

我的朋友

分类: LINUX

2013-12-01 17:20:23

就像题目所说的那样,接收流程简单分析,是对数据包接收流程的分析,不涉及route,还没那功力。从netif_receive_skb函数我们可以看到如果一个IP数据包到来,最终会送给注册的ip_rcv函数来处理,从ip_rcv函数说起吧:
1. if (skb->pkt_type == PACKET_OTHERHOST)
             goto drop;
    看数据包的MAC地址是不是本机的MAC地址,如果不是直接丢掉。这个 skb->pkt_type 是在调用netif_receive_skb函数之前赋值的,我们来看一下:
    skb->protocol = eth_type_trans(skb, netdev);
   

点击(此处)折叠或打开

  1. __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
  2. {
  3.     struct ethhdr *eth;
  4.     unsigned char *rawp;

  5.     skb->mac.raw = skb->data;
  6.     skb_pull(skb, ETH_HLEN);
  7.     eth = eth_hdr(skb);

  8.     if (is_multicast_ether_addr(eth->h_dest)) {
  9.         if (!compare_ether_addr(eth->h_dest, dev->broadcast))
  10.             skb->pkt_type = PACKET_BROADCAST;     //广播包
  11.         else
  12.             skb->pkt_type = PACKET_MULTICAST;  //组播包
  13.     }

  14.     /*
  15.      * This ALLMULTI check should be redundant by 1.4
  16.      * so don't forget to remove it.
  17.      *
  18.      * Seems, you forgot to remove it. All silly devices
  19.      * seems to set IFF_PROMISC.
  20.      */

  21.     else if (1 /*dev->flags&IFF_PROMISC */ ) {
  22.         if (unlikely(compare_ether_addr(eth->h_dest, dev->dev_addr)))  //在这里进行了MAC地址的比较
  23.             skb->pkt_type = PACKET_OTHERHOST;
  24.     }

  25.     if (ntohs(eth->h_proto) >= 1536)  //0x0800 IP
  26.         return eth->h_proto;

  27.     rawp = skb->data;

  28.     /*
  29.      * This is a magic hack to spot IPX packets. Older Novell breaks
  30.      * the protocol design and runs IPX over 802.3 without an 802.2 LLC
  31.      * layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
  32.      * won't work for fault tolerant netware but does for the rest.
  33.      */
  34.     if (*(unsigned short *)rawp == 0xFFFF)
  35.         return htons(ETH_P_802_3);

  36.     /*
  37.      * Real 802.2 LLC
  38.      */
  39.     return htons(ETH_P_802_2);
  40. }
     接下来就看数据包是否共享,共享的话要clone一份
    if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
           IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);
           goto out;
    }
    之后是对IP头的检查,最好调用ip_rcv_finish函数
    return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,
               ip_rcv_finish);    
    在这里使用了netfilter框架,在IP层有5个钩子点,从NF_IP_PRE_ROUTING 可以看出,这个是在路由之前的钩子点。
   

点击(此处)折叠或打开

  1. /* IP Hooks */
  2. /* After promisc drops, checksum checks. */
  3. #define NF_IP_PRE_ROUTING    0
  4. /* If the packet is destined for this box. */
  5. #define NF_IP_LOCAL_IN        1
  6. /* If the packet is destined for another interface. */
  7. #define NF_IP_FORWARD        2
  8. /* Packets coming from a local process. */
  9. #define NF_IP_LOCAL_OUT        3
  10. /* Packets about to hit the wire. */
  11. #define NF_IP_POST_ROUTING    4
  12. #define NF_IP_NUMHOOKS        5

2. ip_rcv_finish函数
    这个函数是涉及到了路由的地方,我们只要知道数据包经过路由后知道是否要转发,还是向上传递给L4 ,最终调用的是 skb->dst->input(skb);    这个input函数指针在路由时被挂上了钩子函数      
   ip_local_deliver或者是ip_forward,我们看ip_local_deliver这个函数

3. ip_local_deliver这个函数
    这个函数从代码量上看也很简单,看是否分片,是分片的话在这里重组,然后调用ip_local_deliver_finish函数,这个函数是在NF_IP_LOCAL_IN Hook点上挂的函数
   

点击(此处)折叠或打开

  1. int ip_local_deliver(struct sk_buff *skb)
  2. {
  3.     /*
  4.      *    Reassemble IP fragments.
  5.      */

  6.     if (skb->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) {
  7.         skb = ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER);
  8.         if (!skb)
  9.             return 0;
  10.     }

  11.     return NF_HOOK(PF_INET, NF_IP_LOCAL_IN, skb, skb->dev, NULL,
  12.          ip_local_deliver_finish);
  13. }

4. ip_local_deliver_finish 函数
    在这个函数中把数据包送到了L4,L3工作算是完成了 。这里说句体外话,当然也没出去。这个函数用inline去修饰有作用吗?
   

点击(此处)折叠或打开

  1. static inline int ip_local_deliver_finish(struct sk_buff *skb)
  2. {
  3.     int ihl = skb->nh.iph->ihl*4;

  4.     __skb_pull(skb, ihl);   //用不到IP头了,剥掉

  5.     /* Point into the IP datagram, just past the header. */
  6.     skb->h.raw = skb->data;

  7.     rcu_read_lock();
  8.     {
  9.         /* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */
  10.         int protocol = skb->nh.iph->protocol;  //看数据包中L4 是UDP,还是TCP,还是Raw
  11.         int hash;
  12.         struct sock *raw_sk;
  13.         struct net_protocol *ipprot;

  14.     resubmit:
  15.         hash = protocol & (MAX_INET_PROTOS - 1);
  16.         raw_sk = sk_head(&raw_v4_htable[hash]);

  17.         /* If there maybe a raw socket we must check - if not we
  18.          * don't care less
  19.          */
  20.         if (raw_sk && !raw_v4_input(skb, skb->nh.iph, hash))  //这里是对Raw的处理
  21.             raw_sk = NULL;

  22.         if ((ipprot = rcu_dereference(inet_protos[hash])) != NULL) {  //从初始化的inet_protos取得相应的struct net_protocol 结构,在这个结构中挂了L4处理函数
  23.             int ret;

  24.             if (!ipprot->no_policy) {
  25.                 if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
  26.                     kfree_skb(skb);
  27.                     goto out;
  28.                 }
  29.                 nf_reset(skb);
  30.             }
  31.             ret = ipprot->handler(skb);  //调用L4处理函数来处理
  32.             if (ret < 0) {
  33.                 protocol = -ret;
  34.                 goto resubmit;
  35.             }
  36.             IP_INC_STATS_BH(IPSTATS_MIB_INDELIVERS);
  37.         } else {
  38.             if (!raw_sk) {
  39.                 if (xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
  40.                     IP_INC_STATS_BH(IPSTATS_MIB_INUNKNOWNPROTOS);
  41.                     icmp_send(skb, ICMP_DEST_UNREACH,
  42.                          ICMP_PROT_UNREACH, 0);
  43.                 }
  44.             } else
  45.                 IP_INC_STATS_BH(IPSTATS_MIB_INDELIVERS);
  46.             kfree_skb(skb);
  47.         }
  48.     }
  49.  out:
  50.     rcu_read_unlock();

  51.     return 0;
  52. }

从代码中我们看到了L3到L4的过程,所以我们要分析一下L4如何挂入的,也就是inet_protos数组的初始化过程,这个过程在inet_init函数中完成的

5. inet_protos数组的初始化以及L4处理函数的挂入
    #define MAX_INET_PROTOS    256        /* Must be a power of 2        */
    struct net_protocol *inet_protos[MAX_INET_PROTOS];
    /* Standard well-defined IP protocols.  */
enum {
  IPPROTO_IP = 0,        /* Dummy protocol for TCP        */
  IPPROTO_ICMP = 1,        /* Internet Control Message Protocol    */
  IPPROTO_IGMP = 2,        /* Internet Group Management Protocol    */
  IPPROTO_IPIP = 4,        /* IPIP tunnels (older KA9Q tunnels use 94) */
  IPPROTO_TCP = 6,        /* Transmission Control Protocol    */
  IPPROTO_EGP = 8,        /* Exterior Gateway Protocol        */
  IPPROTO_PUP = 12,        /* PUP protocol                */
  IPPROTO_UDP = 17,        /* User Datagram Protocol        */
  IPPROTO_IDP = 22,        /* XNS IDP protocol            */
  IPPROTO_DCCP = 33,        /* Datagram Congestion Control Protocol */
  IPPROTO_RSVP = 46,        /* RSVP protocol            */
  IPPROTO_GRE = 47,        /* Cisco GRE tunnels (rfc 1701,1702)    */

  IPPROTO_IPV6     = 41,        /* IPv6-in-IPv4 tunnelling        */

  IPPROTO_ESP = 50,            /* Encapsulation Security Payload protocol */
  IPPROTO_AH = 51,             /* Authentication Header protocol       */
  IPPROTO_BEETPH = 94,           /* IP option pseudo header for BEET */
  IPPROTO_PIM    = 103,        /* Protocol Independent Multicast    */

  IPPROTO_COMP   = 108,                /* Compression Header protocol */
  IPPROTO_SCTP   = 132,        /* Stream Control Transport Protocol    */
  IPPROTO_UDPLITE = 136,    /* UDP-Lite (RFC 3828)            */

  IPPROTO_RAW     = 255,        /* Raw IP packets            */
  IPPROTO_MAX
};

   我们看到L3到L4有这么多条路径可以走, 其实最长走的也就4个IPPROTO_ICMP  IPPROTO_UDP IPPROTO_TCP IPPROTO_IGMP, 在inet_init函数中也就挂入了这四个

  

点击(此处)折叠或打开

  1. /*
  2.      *    Add all the base protocols.
  3.      */

  4.     if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)
  5.         printk(KERN_CRIT "inet_init: Cannot add ICMP protocol\n");
  6.     if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)
  7.         printk(KERN_CRIT "inet_init: Cannot add UDP protocol\n");
  8.     if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)
  9.         printk(KERN_CRIT "inet_init: Cannot add TCP protocol\n");
  10. #ifdef CONFIG_IP_MULTICAST
  11.     if (inet_add_protocol(&igmp_protocol, IPPROTO_IGMP) < 0)
  12.         printk(KERN_CRIT "inet_init: Cannot add IGMP protocol\n");
  13. #endif

这个挂入过程和L2到L3的挂入过程一样,以协议类型为索引做hash,然后挂入。来看一下udp_protocol吧
static struct net_protocol udp_protocol = {
    .handler =    udp_rcv,
    .err_handler =    udp_err,
    .no_policy =    1,
};

我们看到了udp_rcv,进入L4了......
   
  
   
   

阅读(2475) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~