目录
本专栏文章将有70篇左右,欢迎+关注,查看后续文章。
12.5 网络命名空间
一个网卡可能只在某个特定命名空间可见。
struct net:
表示一个网络命名空间,用于隔离网络资源。
资源包括:网络设备、路由表、ARP等。
struct net {
refcount_t count; // 引用计数。
struct list_head list; // 连接 所有 struct net。
struct user_namespace *user_ns; //所属的user命名空间。
struct list_head dev_base_head;
// 连接该命名空间中所有网络设备。
struct hlist_head *dev_name_head;
// 以name计算hash值,为数组dev_name_head索引,连接所有网络设备。
struct hlist_head *dev_index_head;
// 以 index 计算hash值,为数组dev_index_head索引,连接所有网络设备。
struct proc_dir_entry *proc_root; // 指向 /proc目录。
struct proc_dir_entry *proc_net; // 指向 /proc/net 目录。
struct proc_dir_entry *proc_net_stat; // 指向 /proc/net/stat 目录。
struct net_device *loopback_dev; // 环回设备。
struct netns_core core;
struct netns_mib mib; // tcp,udp,ip,icmp等统计信息。
struct netns_packet packet;
struct netns_unix unx;
struct netns_ipv4 ipv4; //包含IPv4信息,如FIB表,iptable表。
struct netns_ipv6 ipv6;
struct sock *nfnl; //与路由子系统通信的socket。
} __randomize_layout;
一个硬件设备对应多个逻辑接口。
init_net:默认命名空间。
struct net init_net = {
.count = REFCOUNT_INIT(1),
.dev_base_head = LIST_HEAD_INIT(init_net.dev_base_head),
};
12.6 套接字缓冲区
套接字缓冲区:socket buffer。
即 struct sk_buff 结构体。
代表一个数据包的缓冲区。
作用:通过指针偏移,实现网络各层传递中的数据修改。而不用赋值数据。
12.6.1 使用 sk_buff 管理数据
struct sk_buff {
struct sk_buff *next,*prev;
// 链接多个 sk_buff,便于遍历和管理多个数据包。
struct sock *sk; //拥有此数据包的sock,转发时为NULL。
struct net_device *dev; // 收发包的网络设备。
struct net_device *input_dev; // 收到包的原始设备,用于流量控制。
__u8 ip_summed:2 // CHECKSUM_PARTIAL则卸载到硬件checksum
__u8 encapsulation:1; // 该skb是否是隧道报文。
__be16 protocol;
__u16 transport_header; // 传输层头。
__u16 network_header; // 网络层头。
__u16 mac_header; // MAC头。
sk_buff_data_t tail; // 有效数据的结尾。
sk_buff_data_t end; // 申请数据的结尾。
unsigned char *head, // 申请数据的开始。
unsigned char *data; // 有效数据的开始。
ktime_t tstamp: // 数据包收发时间戳。
unsigned int len, // 该sk_buff 分配的总长度。
unsigned int data_len; // 仅数据负载。即data到end的长度。
}
部分成员与报文关系:
发送报文时,head,data,tail,end成员的操作流程:
1. malloc 分配 sk_buff 结构体,初始化head,data,tail,end。
2. skb_reserve,预留头部空间。
3. skb_put,存储报文数据。
4. skb_push,封装协议头。
收包:
4 -> 3:调用skb_pull,删除协议头,解封装协议头。
skb_trim:
从数据区尾部移走 len 数据,skb->tail = skb->tail - len(tail 指针向上移动)。
产生新分组时,TCP 分配大于实际需要长度的内存,便于底层协议增加头部(如上述左图)
sk_buff 相关API:
alloc_skb 分配新的 sk_buff。
skb_copy 复制 sk_buff 及其数据。
skb_clone 只复制sk_buff,但数据共享。
skb_tailroom 返回末端空闲长度。
skb_headroom 返回头部空闲长度。
skb_realloc_headroom 增加head空间。
skb_transport_header(struct sk_buff *skb)
获取传输层首部地址。
skb_reset_transport_header(struct sk_buff *skb)
传输层首部重置为数据部分起始。
skb_set_transport_header(struct sk_buff *skb, int offset)
数据部分中偏移指定量设为传输层首部起始。
struct tcphdr *tcp_hdr(const struct sk_buff *skb)
{
return (struct tcphdr *) skb_transport_header(skb);
// 即 skb->head + skb->transport_header;
}
struct iphdr *ip_hdr(const struct sk_buff *skb)
{
return (struct iphdr *) skb->head + skb->network_header;
}
12.6.2 管理套接字缓冲区数据
struct sk_buff {
ktime_t tstamp; //分组到达的时间戳。
struct net_device *dev;
//处理该分组的设备,处理过程中可能会改变 如:转发。
int skb_iif; //接收分组的接口索引号。
struct sock *sk; //处理该分组的socket。
struct sk_buff *next;
struct sk_buff *prev;
...
}
struc sk_buff_head { // sk_buff 的等待队列。
struct sk_buff *next;
struct sk_buff *prev;
__u32 qlen; // 队列长度。
spinlock_t lock;
};