1.選擇路由
若要將數據包發至PC2,則Linux系統通過查詢路由表可知168.1.1.10(目的地址)的網關地址為192.168.1.1,此時Linux系統選擇網卡1發送數據包。
2.鄰居子系統(通過arp協議建立起鄰居的信息)
選擇網卡1發送數據時,首先將數據包發給鄰居(網關),再由鄰居轉發至後面,若要發送給鄰居,則必須知道鄰居的MAC地址,若不知道鄰居的MAC地址,則需要通過arp請求包獲取鄰居的MAC地址。
Linux網絡體系結構由以下五個部分組成 1)系統調用接口 2)協議無關接口 3)網絡協議 4)設備無關接口 5 設備驅動程序。下面分別簡述五個部分:
系統調用接口是用戶空間的應用程序正常訪問內核的唯一合法途徑(終端和陷入也可訪問內核)。如:
asmlingkage long sys_getpid(void)
{
return current->pid;
}
系統調用一般由sys開頭 ,前面的修飾符是asmlingkage,表示函數由堆棧獲得參數。
協議無關接口是由socket來實現的。它提供了一組通用函數來支持各種不同協議。
通過網絡棧進行的通信都需要對 socket 進行操作。Linux 中的 socket 結構是 struct sock ,這個結構是在 linux/include/net/sock.h 中定義的。這個巨大的結構中包含了特定 socket 所需要的所有狀態信息,其中包括 socket 所使用的特定協議和在 socket 上可以執行的一些操作。
網絡子系統可以通過一個定義了自己功能的特殊結構來了解可用協議。每個協議都維護了一個名為 proto 的結構(可以在 linux/include/net/sock.h 中找到)。這個結構定義了可以在從 socket 層到傳輸層中執行特定的 socket 操作
Linux支持多種網絡協議,可以在<linux/socket.h>中查到所支持的網絡協議:
#define AF_UNIX 1 /* Unix domain sockets */
#define AF_LOCAL 1 /* POSIX name for AF_UNIX */
#define AF_INET 2 /* Internet IP Protocol */
#define AF_AX25 3 /* Amateur Radio AX.25 */
#define AF_IPX 4 /* Novell IPX
… …
其中每一個所支持的協議對應net_family[]數組中的一項,net_family[]是結構體指針數組,其中的每一項都是一個結構體指針,指向一個net_proto_family 結構
struct net_proto_family {
int family;
int (*create) (struct socket * sock, int protocol);
short authentication;
short encryption;
short encrypt_net;
struct module *owner;
};這個結構體中注冊了關於協議的信息。
設備無關接口是由net_device實現的。任何設備和上層通信都是通過net_device設備無關接口。
它將協議與具有很多各種不同功能的硬件設備連接在一起。這一層提供了一組通用函數供底層網絡設備驅動程序使用,讓它們可以對高層協議棧進行操作。
首先,設備驅動程序可能會通過調用 register_netdevice 或 unregister_netdevice 在內核中進行注冊或注銷。調用者首先填寫 net_device 結構,然後傳遞這個結構進行注冊。內核調用它的 init 函數(如果定義了這種函數),然後執行一組健全性檢查,並創建一個 sysfs 條目,然後將新設備添加到設備列表中(內核中的活動設備鏈表)。在 linux/include/linux/netdevice.h 中可以找到這個 net_device 結構。這些函數都是在 linux/net/core/dev.c 中實現的。
要從協議層向設備中發送 sk_buff ,就需要使用 dev_queue_xmit 函數。這個函數可以對 sk_buff 進行排隊,從而由底層設備驅動程序進行最終傳輸(使用 sk_buff 中引用的 net_device 或 sk_buff->dev 所定義的網絡設備)。dev 結構中包含了一個名為 hard_start_xmit 的方法,其中保存有發起 sk_buff 傳輸所使用的驅動程序函數。
報文的接收通常是使用 netif_rx 執行的。當底層設備驅動程序接收一個報文(包含在所分配的 sk_buff 中)時,就會通過調用 netif_rx 將 sk_buff 上傳至網絡層。然後,這個函數通過 netif_rx_schedule 將 sk_buff 在上層協議隊列中進行排隊,供以後進行處理。可以在 linux/net/core/dev.c 中找到 dev_queue_xmit 和 netif_rx 函數。
網絡棧底部是負責管理物理網絡設備的設備驅動程序。例如,包串口使用的 SLIP 驅動程序以及以太網設備使用的以太網驅動程序都是這一層的設備。
在進行初始化時,設備驅動程序會分配一個 net_device 結構,然後使用必須的程序對其進行初始化。這些程序中有一個是 dev->hard_start_xmit ,它定義了上層應該如何對 sk_buff 排隊進行傳輸。這個程序的參數為 sk_buff 。這個函數的操作取決於底層硬件,但是通常 sk_buff 所描述的報文都會被移動到硬件環或隊列中。就像是設備無關層中所描述的一樣,對於 NAPI 兼容的網絡驅動程序來說,幀的接收使用了 netif_rx 和 netif_receive_skb 接口。NAPI 驅動程序會對底層硬件的能力進行一些限制。
分析內核代碼實現:
1.發送UDP數據包
在應用程序中
通過socket()函數建立一個socket,然後通過write()函數講數據寫入socket發送出去
在內核中:
a.在系統調用層和協議無關層中
首先通過socket_file_ops結構找出應用程序在內核中的入口,其函數為sock_aio_write,緊接著調用do_sock_write---__sock_sendmsg---__sock_sendmsg_nosec
b.在網絡協議層中
調用udp_sendmsg---ip_route_output_flow(選擇路由) udp_flush_pending_frames---ip_flush_pending_frames(ip協議入口)---ip_local_out---dst_output---ip_finish_output---ip_finish_output2---arp_generic_ops->neigh_resolve_output(建立鄰居信息)
c.在設備無關接口中
調用dev_queue_ximt---dev_hard_start_xmit
d.在驅動程序層中
調用ndo_start_xmit
2.接收IP數據包
在應用程序中
通過系統調用recvmsg函數獲取包的內容
在內核中:
網卡接收到數據包之後發生中斷,通過netif_rx(此處觸發一個軟中斷,當處理器空閒時再處理)函數將包遞給上層的netif_rx_action(除驅動層之外,上面四層的總入口),繼續調用netif_receive_skb(判斷由哪個協議處理該包)---deliver_skb,然後將數據包遞交給ip協議層處理(ip_rcv-ip協議棧層處理入口),緊接著有丟給udp協議層處理(udp_rcv-udp協議棧層處理入口),udp層處理完之後,遞交給sock->ops->recvmsg,即為recvmsg系統調用對應的函數。