Linux (2.6.24.4)網卡接收數據包的流程
2.6.24.4內核網絡接收數據包分析
瀚海書香
瀚海書香
在2.6.24.4中所有的網卡,不管是否支持napi,都是通過struct napi_struct結構進行。所有我們先說一下這個結構。
struct napi_struct{
struct list_head poll_list;
unsigned long state;
int weight;
int (*poll)(struct napi_struct *,int);
}
對應支持napi的網卡,自己填充這個結構體;而非napi網卡,則使用per cpu的softnet_data>backlog,這個結構的初始化在net_dev_init()中完成。
我們先說一下非napi機制的網卡:
網卡接收到數據包后dma到內核空間,然后調用netif_rx()將數據包掛接到softnet_data>input_pkt_queue中, 如果backlog這個napi_struct沒有被調度,則napi_schedule(&backlog).napi_schedule() 會將backlog的poll_list掛接到softnet_data->poll_list上,同時出發軟中斷NET_RX_SOFTIRQ。 NET_RX_SOFTIRQ軟中斷,調用相應的函數net_rx_action()。
struct napi_struct{
struct list_head poll_list;
unsigned long state;
int weight;
int (*poll)(struct napi_struct *,int);
}
對應支持napi的網卡,自己填充這個結構體;而非napi網卡,則使用per cpu的softnet_data>backlog,這個結構的初始化在net_dev_init()中完成。
我們先說一下非napi機制的網卡:
網卡接收到數據包后dma到內核空間,然后調用netif_rx()將數據包掛接到softnet_data>input_pkt_queue中, 如果backlog這個napi_struct沒有被調度,則napi_schedule(&backlog).napi_schedule() 會將backlog的poll_list掛接到softnet_data->poll_list上,同時出發軟中斷NET_RX_SOFTIRQ。 NET_RX_SOFTIRQ軟中斷,調用相應的函數net_rx_action()。
對應napi機制的網卡:
網卡初始化是會自己初始化一個自己的數據包接收隊列,當有數據包到達時,將數據包dma到自己的數據包隊列中,如果自己的napi沒有調度,則 napi_schedule(mynapi),這里的mynapi是網卡自己的napi_struct.napi_schedule()會將網卡自己的 poll_list掛接到softnet_data->poll_list上,同時出發軟中斷NET_RX_SOFTIRQ。 NET_RX_SOFTIRQ軟中斷,調用相應的函數net_rx_action()。
網卡初始化是會自己初始化一個自己的數據包接收隊列,當有數據包到達時,將數據包dma到自己的數據包隊列中,如果自己的napi沒有調度,則 napi_schedule(mynapi),這里的mynapi是網卡自己的napi_struct.napi_schedule()會將網卡自己的 poll_list掛接到softnet_data->poll_list上,同時出發軟中斷NET_RX_SOFTIRQ。 NET_RX_SOFTIRQ軟中斷,調用相應的函數net_rx_action()。
net_rx_action():
首先獲取softnet_data->poll_list,通過遍歷poll_list,獲取每個poll_list對應的napi_struct 結構(container_of實現),然后根據napi_struct的weight調用poll函數,如果是非napi網卡,這里的 napi_struct是backlog,所以poll函數就是process_backlog;如果是napi的網卡,則會使自己的poll函數。
napi網卡的poll函數就是從自己數據包隊列中dequeue出一個skb,然后調用netif_receive_skb().
非napi的process_backlog會獲取softnet_data->input_pkt_queue,然后對隊列input_pkt_queue進行dequeue操作,獲得一個skb,之后調用netif_receive_skb(skb)。
首先獲取softnet_data->poll_list,通過遍歷poll_list,獲取每個poll_list對應的napi_struct 結構(container_of實現),然后根據napi_struct的weight調用poll函數,如果是非napi網卡,這里的 napi_struct是backlog,所以poll函數就是process_backlog;如果是napi的網卡,則會使自己的poll函數。
napi網卡的poll函數就是從自己數據包隊列中dequeue出一個skb,然后調用netif_receive_skb().
非napi的process_backlog會獲取softnet_data->input_pkt_queue,然后對隊列input_pkt_queue進行dequeue操作,獲得一個skb,之后調用netif_receive_skb(skb)。
netif_receive_skb():
對skb做一些準備工作,例如設置mac_len等,調用deliver_skb()給所有的注冊ptype_all類型的協議處理handle,然后是 網橋和VLAN的處理,之后會給注冊的相應協議的ptype_base的handle。這里假設是ip協議,則會調用相應的ip協議handle的處理函 數ip_rcv。
對skb做一些準備工作,例如設置mac_len等,調用deliver_skb()給所有的注冊ptype_all類型的協議處理handle,然后是 網橋和VLAN的處理,之后會給注冊的相應協議的ptype_base的handle。這里假設是ip協議,則會調用相應的ip協議handle的處理函 數ip_rcv。
ip_rcv():
對skb做一些檢查工作,如果skb->users!=1,則clone一個skb,之后會轉入netfilter的 NF_IP_PRE_ROUTING的hook點,調用所有在該點注冊的hook函數。比如說如果開啟了conntrack,則會在這里進行數據包重組。 之后調用ip_rcv_finish().
對skb做一些檢查工作,如果skb->users!=1,則clone一個skb,之后會轉入netfilter的 NF_IP_PRE_ROUTING的hook點,調用所有在該點注冊的hook函數。比如說如果開啟了conntrack,則會在這里進行數據包重組。 之后調用ip_rcv_finish().
ip_rcv_finish():
首先調用ip_route_input()決定數據包的路由,初始化skb->dst,調用dst_input(skb).
首先調用ip_route_input()決定數據包的路由,初始化skb->dst,調用dst_input(skb).
dst_input():
實際上是調用skb->dst->input(skb),對應input的初始化在route.c中。如果是發往本地的數據包 dst->input=ip_local_deliver;如果是轉發的數據包dst->input=ip_forward;
實際上是調用skb->dst->input(skb),對應input的初始化在route.c中。如果是發往本地的數據包 dst->input=ip_local_deliver;如果是轉發的數據包dst->input=ip_forward;
本地流程:
ip_local_deliver():
首先是對分片的數據包重組,會轉入netfilter的NF_IP_LOCAL_IN的hook點,調用所有在該點注冊的hook函數。之后會調用ip_local_deliver_finish(),之后就到第四層了。
ip_local_deliver():
首先是對分片的數據包重組,會轉入netfilter的NF_IP_LOCAL_IN的hook點,調用所有在該點注冊的hook函數。之后會調用ip_local_deliver_finish(),之后就到第四層了。
轉發流程:
ip_forward():
做一些源路由等方面的檢查后,會轉入netfilter的NF_IP_FORWARD的hook點,調用所有在該點注冊的hook函數。之后會調用ip_forward_finish().
ip_forward():
做一些源路由等方面的檢查后,會轉入netfilter的NF_IP_FORWARD的hook點,調用所有在該點注冊的hook函數。之后會調用ip_forward_finish().
ip_forward_finish():
調用dst_output().
調用dst_output().
dst_output():
skb->dst->output(skb).一般output=ip_output.
skb->dst->output(skb).一般output=ip_output.
ip_output():
設置skb的dev為發包的dev,同時設置skb->protocol,會轉入netfilter的NF_IP_POST_ROUTING的hook點,調用所有在該點注冊的hook函數。之后會調用ip_finish_output().
設置skb的dev為發包的dev,同時設置skb->protocol,會轉入netfilter的NF_IP_POST_ROUTING的hook點,調用所有在該點注冊的hook函數。之后會調用ip_finish_output().
ip_finish_output():
檢查一下數據包是否需要分片,如果需要分片,則進行ip_fragement(),之后調用ip_finish_output2().
檢查一下數據包是否需要分片,如果需要分片,則進行ip_fragement(),之后調用ip_finish_output2().
ip_finish_output2():
根據neighbour,調用dst->neighbour->output.
根據neighbour,調用dst->neighbour->output.
到這為止,數據包會經過dev_queue_xmit放入dev的qdisc中。之后就是流控出隊列。
出處:http://pengliang.cublog.cn
出處:http://pengliang.cublog.cn
posted on 2009-12-02 13:32 石頭@ 閱讀(2604) 評論(0) 編輯 收藏 所屬分類: Tcp/Ip