Chinaunix首页 | 论坛 | 博客
  • 博客访问: 401026
  • 博文数量: 124
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 872
  • 用 户 组: 普通用户
  • 注册时间: 2018-03-29 14:38
个人简介

默默的一块石头

文章分类

全部博文(124)

文章存档

2022年(26)

2021年(10)

2020年(28)

2019年(60)

我的朋友

分类: LINUX

2019-12-10 22:09:55

原文地址:netif_receive_skb函数分析 作者:liubangbo

这个函数可谓大名鼎鼎, 网络驱动工程师,把包送给这个函数就算完成了自己的工作了。这个函数是L2和L3的接口函数。一句话概述这个函数功能的话,应该是根据skb->protocol 向注册的三层函数做分发。
分析这个函数之前先看看struct packet_type 这个结构体,ptype_all和ptype_base这两个链表 :

点击(此处)折叠或打开

  1. struct packet_type {
  2.     __be16            type;    /* This is really htons(ether_type). 根据这个类型域做哈希*/  
  3.     struct net_device    *dev;    /* NULL is wildcarded here     */
  4.     int            (*func) (struct sk_buff *,
  5.                      struct net_device *,
  6.                      struct packet_type *,
  7.                      struct net_device *);          //在这里注册三层回调函数,例如ip_rcv, arp_rcv等
  8.     struct sk_buff        *(*gso_segment)(struct sk_buff *skb,
  9.                         int features);
  10.     int            (*gso_send_check)(struct sk_buff *skb);
  11.     void            *af_packet_priv;
  12.     struct list_head    list;        //看到这个字段,我们就知道要形成一个链表
  13. };


  1. static struct list_head ptype_base[16];    /* 16 way hashed list */
    static struct list_head ptype_all;        /* Taps */


       INIT_LIST_HEAD(&ptype_all);   //在net_dev_init函数中进行了初始化
       for (i = 0; i < 16; i++)
            INIT_LIST_HEAD(&ptype_base[i]);


        
       /*
        *    IP protocol layer initialiser
        */

       static struct packet_type ip_packet_type = {
              .type = __constant_htons(ETH_P_IP),
              .func = ip_rcv,
              .gso_send_check = inet_gso_send_check,
              .gso_segment = inet_gso_segment,
       };

       void dev_add_pack(struct packet_type *pt)
       {
            int hash;

            spin_lock_bh(&ptype_lock);
            if (pt->type == htons(ETH_P_ALL)) {       //抓包软件定义的类型为ETH_P_ALL
                netdev_nit++;
                list_add_rcu(&pt->list, &ptype_all);  //抓包软件就挂在这里
            } else {
               hash = ntohs(pt->type) & 15;    //这个哈希很简单啊
               list_add_rcu(&pt->list, &ptype_base[hash]);  //根据协议类型进行哈希挂在ptype_base数组中,
            }
            spin_unlock_bh(&ptype_lock);
       }
       dev_add_pack(&ip_packet_type);  //在inet_init初始化函数中我们ip协议packet_type结构根据ETH_P_IP类型挂在了ptype_base数组中。在netif_receive_skb函数中我们根据
                                       //收到数据包的类型为索引来遍历ptype_base数组,找到挂接的ip协议类型,然后就能把数据包交给注册的L3回调函数ip_rcv来处理了
                                       //我们可以推测像其他协议appTalk等也会有相应的操作,如果收到的数据包的类型是appTalk,那么会交个appTalk注册的回调函数

从上面代码的分析可以看到,我们只需设计一个struct packet_type类型的结构体,注册我们的回调函数my_rcv,然后在inet_init函数中调用dev_add_pack(&ip_packet_type); 就可以得到一个完整的数据包。
虽然我们没有看wireshark,tcpdump这些抓包函数具体代码,但是一定就是这么干的。其实我们从netif_receive_skb函数中可以看到ptype_all这个链表就为我网络嗅探器准备的。


点击(此处)折叠或打开

  1. int netif_receive_skb(struct sk_buff *skb)
  2. {
  3.     struct packet_type *ptype, *pt_prev;
  4.     struct net_device *orig_dev;
  5.     int ret = NET_RX_DROP;
  6.     __be16 type;

  7.     /* if we've gotten here through NAPI, check netpoll */
  8.     if (skb->dev->poll && netpoll_rx(skb))  //如果打开了netpoll,像netconsole,数据包就直接被勾走了
  9.         return NET_RX_DROP;

  10.     if (!skb->tstamp.off_sec)
  11.         net_timestamp(skb);

  12.     if (!skb->iif)
  13.         skb->iif = skb->dev->ifindex;

  14.     orig_dev = skb_bond(skb);   //这个应该和多网卡绑定有关

  15.     if (!orig_dev)
  16.         return NET_RX_DROP;

  17.     __get_cpu_var(netdev_rx_stat).total++;

  18.     skb->h.raw = skb->nh.raw = skb->data;
  19.     skb->mac_len = skb->nh.raw - skb->mac.raw;

  20.     pt_prev = NULL;

  21.     rcu_read_lock();

  22. #ifdef CONFIG_NET_CLS_ACT
  23.     if (skb->tc_verd & TC_NCLS) {
  24.         skb->tc_verd = CLR_TC_NCLS(skb->tc_verd);
  25.         goto ncls;
  26.     }
  27. #endif


  28.     list_for_each_entry_rcu(ptype, &ptype_all, list) {     //遍历第一个链表, 如果抓包工具注册了,就拷贝一份给抓包工具
  29.         if (!ptype->dev || ptype->dev == skb->dev) {
  30.             if (pt_prev)
  31.                 ret = deliver_skb(skb, pt_prev, orig_dev);
  32.             pt_prev = ptype;
  33.         }
  34.     }


  35. #ifdef CONFIG_NET_CLS_ACT
  36.     if (pt_prev) {
  37.         ret = deliver_skb(skb, pt_prev, orig_dev);
  38.         pt_prev = NULL; /* noone else should process this after*/
  39.     } else {
  40.         skb->tc_verd = SET_TC_OK2MUNGE(skb->tc_verd);
  41.     }

  42.     ret = ing_filter(skb);

  43.     if (ret == TC_ACT_SHOT || (ret == TC_ACT_STOLEN)) {
  44.         kfree_skb(skb);
  45.         goto out;
  46.     }

  47.     skb->tc_verd = 0;
  48. ncls:
  49. #endif

  50.     if (handle_bridge(&skb, &pt_prev, &ret, orig_dev))   //如果打开了桥, 就进入桥处理函数进行处理
  51.         goto out;

  52.     type = skb->protocol; //ip 0x0800 arp 0x0806 loopback 0x0060
  53.     list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type)&15], list) {   //遍历第二个链表,这是个数组,索引是skb->protocol
  54.         if (ptype->type == type &&
  55.          (!ptype->dev || ptype->dev == skb->dev)) {
  56.             if (pt_prev)                                          
  57.                 ret = deliver_skb(skb, pt_prev, orig_dev);
  58.             pt_prev = ptype;
  59.         }
  60.     }

  61.     if (pt_prev) {
  62.         ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);   //调用根据注册的函数进行处理
  63.     } else {
  64.         kfree_skb(skb);
  65.         /* Jamal, now you will not able to escape explaining
  66.          * me how you were going to use this. :-)
  67.          */
  68.         ret = NET_RX_DROP;
  69.     }

  70. out:
  71.     rcu_read_unlock();
  72.     return ret;
  73. }


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