調用關係: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;
}