Chinaunix首页 | 论坛 | 博客
  • 博客访问: 434422
  • 博文数量: 99
  • 博客积分: 65
  • 博客等级: 民兵
  • 技术积分: 1012
  • 用 户 组: 普通用户
  • 注册时间: 2012-04-20 16:30
个人简介

linux kernel 工程师

文章分类

全部博文(99)

文章存档

2018年(5)

2017年(12)

2016年(27)

2015年(10)

2014年(43)

2012年(2)

我的朋友

分类: LINUX

2014-02-11 17:48:28

__netif_receive_skb用来实现驱动与协议栈的结合

static int __netif_receive_skb(struct sk_buff *skb)
{
 struct packet_type *ptype, *pt_prev;
 rx_handler_func_t *rx_handler;
 struct net_device *orig_dev;
 struct net_device *null_or_dev;
 bool deliver_exact = false;
 int ret = NET_RX_DROP;
 __be16 type;

 net_timestamp_check(!netdev_tstamp_prequeue, skb);

 trace_netif_receive_skb(skb);

 /* if we've gotten here through NAPI, check netpoll */
 if (netpoll_receive_skb(skb))
  return NET_RX_DROP;

 if (!skb->skb_iif)
  skb->skb_iif = skb->dev->ifindex;
 orig_dev = skb->dev;

 skb_reset_network_header(skb);
 skb_reset_transport_header(skb);
 skb_reset_mac_len(skb);

 pt_prev = NULL;

 rcu_read_lock();

another_round:

 __this_cpu_inc(softnet_data.processed);

 if (skb->protocol == cpu_to_be16(ETH_P_8021Q)) {
  skb = vlan_untag(skb);
  if (unlikely(!skb))
   goto out;
 }

#ifdef CONFIG_NET_CLS_ACT
 if (skb->tc_verd & TC_NCLS) {
  skb->tc_verd = CLR_TC_NCLS(skb->tc_verd);
  goto ncls;
 }
#endif

 //遍历ptype_all链表, sniffer是不是就是在这里干的?
 list_for_each_entry_rcu(ptype, &ptype_all, list) {
  if (!ptype->dev || ptype->dev == skb->dev) {
   if (pt_prev)
    ret = deliver_skb(skb, pt_prev, orig_dev);
   pt_prev = ptype;
  }
 }

#ifdef CONFIG_NET_CLS_ACT
 skb = handle_ing(skb, &pt_prev, &ret, orig_dev);
 if (!skb)
  goto out;
ncls:
#endif

 if (vlan_tx_tag_present(skb)) {
  if (pt_prev) {
   ret = deliver_skb(skb, pt_prev, orig_dev);
   pt_prev = NULL;
  }
  if (vlan_do_receive(&skb))
   goto another_round;
  else if (unlikely(!skb))
   goto out;
 }

 /* rx_handlers are functions called from inside __netif_receive_skb(), to do
 * special processing of the skb, prior to delivery to protocol handlers.
 *
 * Currently, a net_device can only have a single rx_handler registered. Trying
 * to register a second rx_handler will return -EBUSY.
 *
 * To register a rx_handler on a net_device, use netdev_rx_handler_register().
 * To unregister a rx_handler on a net_device, use
 * netdev_rx_handler_unregister(). */

 rx_handler = rcu_dereference(skb->dev->rx_handler);
 if (rx_handler) {
  if (pt_prev) {
   ret = deliver_skb(skb, pt_prev, orig_dev);
   pt_prev = NULL;
  }
  switch (rx_handler(&skb)) {
  case RX_HANDLER_CONSUMED:
   goto out;
  case RX_HANDLER_ANOTHER:
   goto another_round;
  case RX_HANDLER_EXACT:
   deliver_exact = true;
  case RX_HANDLER_PASS:
   break;
  default:
   BUG();
  }
 }

 if (vlan_tx_nonzero_tag_present(skb))
  skb->pkt_type = PACKET_OTHERHOST;

 /* deliver only exact match when indicated */
 null_or_dev = deliver_exact ? skb->dev : NULL;

 // 匹配skb->protocol 与ptype->type, ptype是协议处理的链表, dev_add_pack会把协议处理函数放到这里来, 比如ip_rcv,arp_rcv
 //注意这里的pt_prev用来指向前一个找到的协议处理函数,这么做的目的是为了节省一次kfree_skb
 //kfree_skb会判断skb->users, 如果users==1,就会执行释放操作,否则只是执行skb->users--操作
 //见下面代码分析
 type = skb->protocol;
 list_for_each_entry_rcu(ptype,
   &ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) {
  if (ptype->type == type &&
      (ptype->dev == null_or_dev || ptype->dev == skb->dev ||
       ptype->dev == orig_dev)) {
   if (pt_prev)
    ret = deliver_skb(skb, pt_prev, orig_dev);
   pt_prev = ptype;
  }
 }

 if (pt_prev) {
  ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
 } else {
  atomic_long_inc(&skb->dev->rx_dropped);
  kfree_skb(skb);
  /* Jamal, now you will not able to escape explaining
   * me how you were going to use this. :-)
   */
  ret = NET_RX_DROP;
 }

out:
 rcu_read_unlock();
 return ret;
}


假如代码如下,其实也是一样的
 type = skb->protocol;
 list_for_each_entry_rcu(ptype,
   &ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) {
  if (ptype->type == type &&
      (ptype->dev == null_or_dev || ptype->dev == skb->dev ||
       ptype->dev == orig_dev)) {
   ret = deliver_skb(skb, ptype, orig_dev);           //deliver_skb先增加skb->users,再调用ptype->func

  }
 }
 kfree_skb(skb); //最后再调用一次free

只不过对于只有一个匹配的ptype时,deliver_skb先增加skb->users,再调用ptype->func, 然后ptype->func里面会调用kfee_skb时减少一次users,出了ptype->func时,users ==1
最后再调用一次kfree_skb, 这样kfee_skb就会调用两次。但如果采用pt_prev这样的形式时,只在ptype->func调用kfee_skb一次。

 

 

 

 

 

 

 


 

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