今天記錄一下Linux網絡子系統相關的東西。
因為感覺對這一塊還是有一個很大的空白,這件事情太可怕了。
摘抄多份博客進行總結一下Linux網絡子系統的相關東西。
一. Linux網絡子系統體系結構
Linux 網絡體系結構由如下圖抽象的形容一下
1 . 用戶空間:-----> 應用層
2 . 內核空間:-----> 系統調用接口: 主要指socket 系統調用
-----> 協議無關接口: 實現一組基於socket的通用函數訪問各種不同的協議
-----> 網絡協議: udp, tcp 協議
-----> 設備無關接口: 將協議與各種網絡設備驅動連接在一起。
-----> 設備驅動: 網絡體系結構的最底層部分是負責管理物理網絡設備的設備驅動層
3 . 硬件: 網絡物理設備。
從上往下,形成Linux網絡子系統。
二. 重要的頭文件以及數據結構介紹
重要的頭文件:include/linux/netdevice.h
這里面有個網絡子系統的核心數據結構:net_device
net_device
這里面包含了網絡設備的各種屬性,主要有如下重要屬性:
struct net_device
{
char name[IFNAMSIZ];
// 這是設備的名稱,包含一個%d格式串,設備注冊時,
// 用一個數替換它形成一個新的名稱,分配的編號從零開始
//...
unsigned long state;
//記錄了設備的狀態,是否激活是否是有效是否沒有載波等等
unsigned int irq;
/* device IRQ number 中斷號 */
unsigned int mtu;
/* interface MTU value 最大傳輸單元,默認1500 */
unsigned char *dev_addr;
/* hw address, (before bcast
because most packets are
unicast) */
//......還有很多相關的屬性。
//關於網絡設備的操作結構體
const struct net_device_ops *netdev_ops;
const struct ethtool_ops *ethtool_ops;
}
#define alloc_netdev(sizeof_priv, name, setup) \
alloc_netdev_mqs(sizeof_priv, name, setup, 1, 1)\
/* 分配一個net_device結構體可以用上面這個宏函數進行分配
其實最終調用是調用了alloc_netdev_mqs 函數*/
/* 其中,參數sizeof_priv 表示網卡設備的私有數據大小,
name指定網卡設備名稱,setup指定網卡設備初始化回調函數 */
//對於不同的網卡設備,kernel提供了更直接的分配函數,
//例如以太網卡,可用用下面的宏函數進行分配:
//include/linux/etherdevice.h
#define alloc_etherdev(sizeof_priv) alloc_etherdev_mq(sizeof_priv, 1)
#define alloc_etherdev_mq(sizeof_priv, count) alloc_etherdev_mqs(sizeof_priv, count, count)
//其實最終調用是alloc_etherdev_mqs
//....
//其實這個函數只是做了簡單的封裝 net/ethernet/eth.c
struct net_device *alloc_etherdev_mqs(int sizeof_priv, unsigned int txqs,
unsigned int rxqs)
{
return alloc_netdev_mqs(sizeof_priv, "eth%d", ether_setup, txqs, rxqs);
}
EXPORT_SYMBOL(alloc_etherdev_mqs);
//最終調用還是alloc_netdev_mqs 只不過名字是eth開頭,%d會在后面解析出來。
//.......
//ether_setup 函數,以太網初始化函數 net/ethernet/eth.c
/**
* ether_setup - setup Ethernet network device
* @dev: network device
* Fill in the fields of the device structure with Ethernet-generic values.
*/
void ether_setup(struct net_device *dev)
{
dev->header_ops = ð_header_ops;
dev->type = ARPHRD_ETHER;
dev->hard_header_len = ETH_HLEN;
dev->mtu = ETH_DATA_LEN;
dev->addr_len = ETH_ALEN;
dev->tx_queue_len = 1000; /* Ethernet wants good queues */
dev->flags = IFF_BROADCAST|IFF_MULTICAST;
dev->priv_flags |= IFF_TX_SKB_SHARING;
memset(dev->broadcast, 0xFF, ETH_ALEN);
}
EXPORT_SYMBOL(ether_setup);
struct net_device_ops
和字符設備有一定的相似之處,網絡設備也有相關的操作函數。
他包含在net_device 結構體中的struct net_device_ops。
他的原型如下:
struct net_device_ops {
int (*ndo_init)(struct net_device *dev);
/* 該函數將在設備注冊時,
調用(即調用register_netdev()時)來做一些基本的初始化工作。 */
void (*ndo_uninit)(struct net_device *dev);
int (*ndo_open)(struct net_device *dev);
/* 該函數在網絡設備被激活變為up狀態時被調用 */
int (*ndo_stop)(struct net_device *dev);
/* 變為down狀態時被調用 */
netdev_tx_t (*ndo_start_xmit) (struct sk_buff *skb,
struct net_device *dev);
/* 當上層協議需要發送數據時,調用該函數
需要返回NETDEV_TX_OK 或NET_TX_BUSY來確認結果 */
u16 (*ndo_select_queue)(struct net_device *dev,
struct sk_buff *skb);
/* 呼叫然后決定哪個隊列當設備支持多重傳輸隊列的時候 */
void (*ndo_change_rx_flags)(struct net_device *dev,
int flags);
/* 調用這個函數允許設備接收器更改配置當運行多路傳輸的時候 */
void (*ndo_set_rx_mode)(struct net_device *dev);
/* 當調用這個函數改變地址過濾
如果驅動沒有設置地址過濾,那個這個標志要設置為IFF_UNICAST_FLT */
int (*ndo_set_mac_address)(struct net_device *dev,
void *addr);
/* 調用設置MAC地址 */
int (*ndo_validate_addr)(struct net_device *dev);
/* 測試多媒體訪問地址是否有效 */
int (*ndo_do_ioctl)(struct net_device *dev,
struct ifreq *ifr, int cmd);
/* 當用戶請求一個ioctl,如果ioctl沒有相對應的接口函數,則返回not supported error code */
int (*ndo_set_config)(struct net_device *dev,
struct ifmap *map);
/* 用它設置網絡設備總線接口參數
保留這個接口的原因是因為新的設備要用PCI為了低水平的管理*/
int (*ndo_change_mtu)(struct net_device *dev,
int new_mtu);
/* 使用這個函數該表最大傳輸單元,
如果沒有定義,那么任何請求改變MTU的請求都會報錯 */
int (*ndo_neigh_setup)(struct net_device *dev,
struct neigh_parms *);
void (*ndo_tx_timeout) (struct net_device *dev);
/* 當傳送器沒有任何操作超時調用 */
struct rtnl_link_stats64* (*ndo_get_stats64)(struct net_device *dev,
struct rtnl_link_stats64 *storage);
struct net_device_stats* (*ndo_get_stats)(struct net_device *dev);
/* 上面兩個函數必須定義其中一個,這是用戶想獲取幫助的所調用的 */
void (*ndo_vlan_rx_add_vid)(struct net_device *dev,
unsigned short vid);
/* 當設備支持VLAN過濾的時候,這個函數在一個VLAN ID 被注冊時候調用 */
void (*ndo_vlan_rx_kill_vid)(struct net_device *dev,
unsigned short vid);
/* 當設備支持VLAN過濾的時候,這個函數在一個VLAN ID 被注銷時候調用 */
#ifdef CONFIG_NET_POLL_CONTROLLER
void (*ndo_poll_controller)(struct net_device *dev);
int (*ndo_netpoll_setup)(struct net_device *dev,
struct netpoll_info *info);
void (*ndo_netpoll_cleanup)(struct net_device *dev);
#endif
/* SR-IOV management functions */
int (*ndo_set_vf_mac)(struct net_device *dev,
int queue, u8 *mac);
int (*ndo_set_vf_vlan)(struct net_device *dev,
int queue, u16 vlan, u8 qos);
int (*ndo_set_vf_tx_rate)(struct net_device *dev,
int vf, int rate);
int (*ndo_set_vf_spoofchk)(struct net_device *dev,
int vf, bool setting);
int (*ndo_get_vf_config)(struct net_device *dev,
int vf,
struct ifla_vf_info *ivf);
int (*ndo_set_vf_port)(struct net_device *dev,
int vf,
struct nlattr *port[]);
int (*ndo_get_vf_port)(struct net_device *dev,
int vf, struct sk_buff *skb);
int (*ndo_setup_tc)(struct net_device *dev, u8 tc);
/* 調用這個函數設置tc 數字關於net設備的傳輸類別
這個允許網絡設備安全的管理隊列 */
#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)
int (*ndo_fcoe_enable)(struct net_device *dev);
/* 當FCoE協議棧使用LLD,所以下面的設備需要配置或者初始化關於FCoE傳輸的加速。
這時就調用這個函數 */
int (*ndo_fcoe_disable)(struct net_device *dev);
/* 與上面那個函數剛好相反 */
/* .........*/
int (*ndo_fcoe_ddp_setup)(struct net_device *dev,
u16 xid,
struct scatterlist *sgl , unsigned int sgc);
/* 當FCoE初始化想初始一個IO因為有個可能的候選人為直接的數據放置,成功在這個IO執行PPD這個函數返回1, 失敗返回0 */
int (*ndo_fcoe_ddp_done)(struct net_device *dev,
u16 xid);
/* 當FCoE目標已經完成,這時候要釋放這個資源以便下次請求這個資源 */
int (*ndo_fcoe_ddp_target)(struct net_device *dev,
u16 xid,
struct scatterlist *sgl,
unsigned int sgc);
#endif
#if defined(CONFIG_LIBFCOE) || defined(CONFIG_LIBFCOE_MODULE)
#define NETDEV_FCOE_WWNN 0
#define NETDEV_FCOE_WWPN 1
int (*ndo_fcoe_get_wwn)(struct net_device *dev,
u64 *wwn, int type);
#endif
#ifdef CONFIG_RFS_ACCEL
int (*ndo_rx_flow_steer)(struct net_device *dev,
const struct sk_buff *skb,
u16 rxq_index,
u32 flow_id);
/* 設置硬件過濾為RFS */
#endif
int (*ndo_add_slave)(struct net_device *dev,
struct net_device *slave_dev);
/* 添加一個網絡設備的從設備 */
int (*ndo_del_slave)(struct net_device *dev,
struct net_device *slave_dev);
/* 刪除 */
u32 (*ndo_fix_features)(struct net_device *dev,
u32 features);
/* 調整請求特征標志通過設備專用的 約束,返回標志結果,不能改變設備的狀態*/
int (*ndo_set_features)(struct net_device *dev,
u32 features);
/* 升級設備新的配置 */
};
sk_buff
sk_buff 在include/linux/skbuff.h 里面被聲明
/**
* struct sk_buff - socket buffer
* @next: Next buffer in list
* @prev: Previous buffer in list
* @tstamp: Time we arrived
* @sk: Socket we are owned by
* @dev: Device we arrived on/are leaving by
* @cb: Control buffer. Free for use by every layer. Put private vars here
* @_skb_refdst: destination entry (with norefcount bit)
* @sp: the security path, used for xfrm
* @len: Length of actual data
* @data_len: Data length
* @mac_len: Length of link layer header
* @hdr_len: writable header length of cloned skb
* @csum: Checksum (must include start/offset pair)
* @csum_start: Offset from skb->head where checksumming should start
* @csum_offset: Offset from csum_start where checksum should be stored
* @priority: Packet queueing priority
* @local_df: allow local fragmentation
* @cloned: Head may be cloned (check refcnt to be sure)
* @ip_summed: Driver fed us an IP checksum
* @nohdr: Payload reference only, must not modify header
* @nfctinfo: Relationship of this skb to the connection
* @pkt_type: Packet class
* @fclone: skbuff clone status
* @ipvs_property: skbuff is owned by ipvs
* @peeked: this packet has been seen already, so stats have been
* done for it, don't do them again
* @nf_trace: netfilter packet trace flag
* @protocol: Packet protocol from driver
* @destructor: Destruct function
* @nfct: Associated connection, if any
* @nfct_reasm: netfilter conntrack re-assembly pointer
* @nf_bridge: Saved data about a bridged frame - see br_netfilter.c
* @skb_iif: ifindex of device we arrived on
* @tc_index: Traffic control index
* @tc_verd: traffic control verdict
* @rxhash: the packet hash computed on receive
* @queue_mapping: Queue mapping for multiqueue devices
* @ndisc_nodetype: router type (from link layer)
* @ooo_okay: allow the mapping of a socket to a queue to be changed
* @ndisc_nodetype: router type (from link layer)
* @ooo_okay: allow the mapping of a socket to a queue to be changed
* @l4_rxhash: indicate rxhash is a canonical 4-tuple hash over transport
* ports.
* @dma_cookie: a cookie to one of several possible DMA operations
* done by skb DMA functions
* @secmark: security marking
* @mark: Generic packet mark
* @dropcount: total number of sk_receive_queue overflows
* @vlan_tci: vlan tag control information
* @transport_header: Transport layer header
* @network_header: Network layer header
* @mac_header: Link layer header
* @tail: Tail pointer
* @end: End pointer
* @head: Head of buffer
* @data: Data head pointer
* @truesize: Buffer size
* @users: User count - see {datagram,tcp}.c
*/
//這個結構體里面所有的成員如上注釋
//其中重要的有如下幾個着重解釋一下:
struct sk_buff
{
/* These two members must be first. */
struct sk_buff *next; /* 指向下一個的sk_buff */
struct sk_buff *prev; /* 指向前一個的sk_buff */
struct sock *sk; /* 該sk_buff 擁有的套接字 */
struct net_device *dev; /* 處理該包的設備 */
__be16 protocol; /* 該包所屬的協議類型 */
unsigned int len,
data_len; /* 有效數據的長度以及數據總長度 */
sk_buff_data_t transport_header; /* 指向傳輸層包頭 */
sk_buff_data_t network_header; /* 指向網絡層包頭 */
sk_buff_data_t mac_header; /* 指向鏈路層包頭 */
/* These elements must be at the end, see alloc_skb() for details. */
sk_buff_data_t tail; /* 有效數據的結束 */
sk_buff_data_t end; /* 分配空間的結束 */
unsigned char *head, /* 分配空間的開始 */
*data; /* 有效數據的開始 */
}
//sk_buff中定義了4個指向數據包緩沖區不同位置的指針head、data、tail、end、
//.......
//sk_buff 的基本操作函數
static inline struct sk_buff *__dev_alloc_skb(unsigned int length, <-------------|
gfp_t gfp_mask) |
{ |
struct sk_buff *skb = alloc_skb(length + NET_SKB_PAD, gfp_mask); --- |
if (likely(skb)) | |
skb_reserve(skb, NET_SKB_PAD); | |
return skb; | |
} | |
| |
extern struct sk_buff *dev_alloc_skb(unsigned int length); | |
/* | |
struct sk_buff *dev_alloc_skb(unsigned int length) //最開始位置 | |
{ | |
* There is more code here than it seems: | |
* __dev_alloc_skb is an inline | |
*/ | |
return __dev_alloc_skb(length, GFP_ATOMIC); -------------------------------- |
} |
EXPORT_SYMBOL(dev_alloc_skb); |
*/ | |
|
extern struct sk_buff *__netdev_alloc_skb(struct net_device *dev, |
unsigned int length, gfp_t gfp_mask); |
|
|
|
extern struct sk_buff *__alloc_skb(unsigned int size, |
gfp_t priority, int fclone, int node); |
static inline struct sk_buff *alloc_skb(unsigned int size, <—————
gfp_t priority)
{
return __alloc_skb(size, priority, 0, NUMA_NO_NODE); -----
} |
//最終調用__alloc_skb進行申請一個sk_buff結構體 |
//他的函數原型在net/core/skbuff.c |
/** |
* __alloc_skb - allocate a network buffer <____________|
* @size: size to allocate 長度
* @gfp_mask: allocation mask 掩碼
* @fclone: allocate from fclone cache instead of head cache
* and allocate a cloned (child) skb
* @node: numa node to allocate memory on
*
* Allocate a new &sk_buff. The returned buffer has no headroom and a
* tail room of size bytes. The object has a reference count of one.
* The return is the buffer. On a failure the return is %NULL.
*
* Buffers may only be allocated from interrupts using a @gfp_mask of
* %GFP_ATOMIC.
*/
struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,
int fclone, int node)
{
//......
}