调用关系:netif_receive_skb-->netif_receive_skb-->netif_receive_skb_internal(->__netif_receive_skb)-->__netif_receive_skb_core
1.netif_receive_skb_internal的实现
static int netif_receive_skb_internal(struct sk_buff *skb)
{
int ret;
net_timestamp_check(netdev_tstamp_prequeue, skb);//记录收包时间
if (skb_defer_rx_timestamp(skb))
return NET_RX_SUCCESS;
rcu_read_lock();
/*RPS逻辑处理,现在内核中使用了RPS机制, 将报文分散到各个cpu的接收队列中进行负载均衡处理*/
#ifdef CONFIG_RPS
if (static_key_false(&rps_needed)) {
struct rps_dev_flow voidflow, *rflow = &voidflow;
int cpu = get_rps_cpu(skb->dev, skb, &rflow);//从一个给定skb的rx队列的rps map中返回一个目的cpu
if (cpu >= 0) {
ret = enqueue_to_backlog(skb, cpu, &rflow->last_qtail);//将此skb放到获取的cpu的接收队列上
rcu_read_unlock();
return ret;
}
}
#endif
ret = __netif_receive_skb(skb);//最终调用__netif_receive_skb_core
rcu_read_unlock();
return ret;
}
2.__netif_receive_skb_core的实现
了解此函数,首先需要知道ptype_base和ptype_all变量,二个结变量的定义如下:"net/core/dev.c"
struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly; //PTYPE_HASH_SIZE路的hash链表
struct list_head ptype_all __read_mostly; /* Taps 双向链表 */
这二个都是list_head变量,list_head 链表上挂了很多packet_type数据结构,此结构体是对应于具体协议的实例,packet_type数据结构如下:
struct packet_type {
__be16 type; /* type指定了协议的标识符,处理程序func会使用该标识符 ,保存了三层协议类型,ETH_P_IP、ETH_P_ARP等等 */
struct net_device *dev; /* NULL指针表示该处理程序对系统中所有网络设备都有效 */
/* func是该结构的主要成员。它是一个指向网络层函数的指针,如果分组的类型适当,将其传递给该函数。其中可能的处理程序就是ip_rcv */
int (*func) (struct sk_buff *,
struct net_device *,
struct packet_type *,
struct net_device *);
bool (*id_match)(struct packet_type *ptype,
struct sock *sk);
void *af_packet_priv;
struct list_head list;
RH_KABI_RESERVE(1)
RH_KABI_RESERVE(2)
RH_KABI_RESERVE(3)
RH_KABI_RESERVE(4)
};
注册packet_type的api主要在相应的协议初始化时,通过dev_add_pack函数实现,把packet_type结构挂在对应协议的的list_head上面,移除则采用dev_remove_pack。
dev_add_pack函数除了将packet_type结构挂在对应协议的的list_head上面,还有个重要的功能就是通过函数ptype_head获取对应协议的list_head,实现如下:
static inline struct list_head *ptype_head(const struct packet_type *pt)
{
if (pt->type == htons(ETH_P_ALL)) //type为ETH_P_ALL时,则挂在ptype_all上面
return &ptype_all;
else
return &ptype_base[ntohs(pt->type) & PTYPE_HASH_MASK]; //否则,挂在ptype_base[type&15]上面,即对应协议的list_head上面。
}
好了,上面的可以一带而过了,下面主要看看__netif_receive_skb_core的实现,其实就是对上面注册的结构体packet_type的处理。
此函数主要以下四个功能:
1)处理 ptype_all 上所有的 packet_type->func() ,典型场景就是抓包
2) 处理vlan报文
3)rx_handler函数处理,例如网桥
4)处理ptype_base上所有的 packet_type->func() ,数据包传递给上层协议层处理,例如ip_rcv函数;
static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc) // 将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);//记录收包时间,netdev_tstamp_prequeue为0,表示可能有包延迟
trace_netif_receive_skb(skb);
orig_dev = skb->dev;//记录收包设备
skb_reset_network_header(skb);//重置network header,此时skb指向IP头(没有vlan的情况下)
if (!skb_transport_header_was_set(skb))
skb_reset_transport_header(skb);
skb_reset_mac_len(skb);
// 留下一个节点,最后一次向上层传递时,不需要再inc引用,回调中会free
这样相当于少调用了一次free
pt_prev = NULL;
another_round:
skb->skb_iif = skb->dev->ifindex;//设置接收设备索引号
__this_cpu_inc(softnet_data.processed);//处理包数统计
if (skb->protocol == cpu_to_be16(ETH_P_8021Q) ||
skb->protocol == cpu_to_be16(ETH_P_8021AD)) {//vxlan报文处理,剥除vxlan头
skb = skb_vlan_untag(skb);//剥除vxlan头
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
if (pfmemalloc)此类报文不允许ptype_all处理,即tcpdump也抓不到
goto skip_taps;
//先处理 ptype_all 上所有的 packet_type->func()
//所有包都会调func,对性能影响严重!所有有的钩子是随模块加载挂上的。
list_for_each_entry_rcu(ptype, &ptype_all, list) {//遍历ptye_all链表
if (!ptype->dev || ptype->dev == skb->dev) {//上面的paket_type.type 为 ETH_P_ALL,典型场景就是tcpdump抓包所使用的协议
if (pt_prev)//pt_prev提高效率
ret = deliver_skb(skb, pt_prev, orig_dev);//此函数最终调用paket_type.func()
pt_prev = ptype;
}
}
skip_taps:
#ifdef CONFIG_NET_CLS_ACT
if (static_key_false(&ingress_needed)) {
skb = handle_ing(skb, &pt_prev, &ret, orig_dev);
if (!skb)
goto out;
}
skb->tc_verd = 0;
ncls:
#endif
if (pfmemalloc && !skb_pfmemalloc_protocol(skb))//不支持使用pfmemalloc
goto drop;
if (skb_vlan_tag_present(skb)) {// 如果是vlan包
if (pt_prev) {/* 处理pt_prev */
ret = deliver_skb(skb, pt_prev, orig_dev);
pt_prev = NULL;
}
if (vlan_do_receive(&skb))/* 根据实际的vlan设备调整信息,再走一遍 */
goto another_round;
else if (unlikely(!skb))
goto out;
}
/*如果一个dev被添加到一个bridge(做为bridge的一个接口),这个接口设备的rx_handler将被设置为br_handle_frame函数,这是在br_add_if函数中设置的,而br_add_if (net/bridge/br_if.c)是在向网桥设备上添加接口时设置的。进入br_handle_frame也就进入了bridge的逻辑代码。*/
rx_handler = rcu_dereference(skb->dev->rx_handler);/* 如果有注册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:/* 已处理,无需进一步处理 */
ret = NET_RX_SUCCESS;
goto out;
case RX_HANDLER_ANOTHER:/* 修改了skb->dev,在处理一次 */
goto another_round;
case RX_HANDLER_EXACT:/* 精确传递到ptype->dev == skb->dev */
deliver_exact = true;
case RX_HANDLER_PASS:
break;
default:
BUG();
}
}
if (unlikely(skb_vlan_tag_present(skb))) {/* 还有vlan标记,说明找不到vlanid对应的设备 */
if (skb_vlan_tag_get_id(skb))/* 存在vlanid,则判定是到其他设备的包 */
skb->pkt_type = PACKET_OTHERHOST;
/* Note: we might in the future use prio bits
* and set skb->priority like in vlan_do_receive()
* For the time being, just ignore Priority Code Point
*/
skb->vlan_tci = 0;
}
/* deliver only exact match when indicated */
null_or_dev = deliver_exact ? skb->dev : NULL;//指定精确传递的话,就精确传递,否则向未指定设备的指定协议全局发送一份
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) {
if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC)))
goto drop;
else
//使用pt_prev这里就不需要deliver_skb来inc应用数了, func执行内部会free,减少了一次skb_free
ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);/* 传递到上层*/
} else {
drop:
if (!deliver_exact)
atomic_long_inc(&skb->dev->rx_dropped);//网卡丢包计数
else
atomic_long_inc(&skb->dev->rx_nohandler);
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:
return ret;
}