Linux 网络子系统

时间:2023-11-25 20:35:44

今天记录一下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 = &eth_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)
{
//......
}