linux ip头结构体,[linux]sk_buff结构体

简要说明

sk_buff结构可能是linux网络代码中最重要的数据结构,它表示接收或发送数据包的包头信息。它在中定义,并包含很多成员变量供网络代码中的各子系统使用。

这个结构被不同的网络层(MAC或者其他二层链路协议,三层的IP,四层的TCP或UDP等)使用,并且其中的成员变量在结构从一层向另一层传递时改变。L4向L3传递前会添加一个L4的头部,同样,L3向L2传递前,会添加一个L3的头部。添加头部比在不同层之间拷贝数据的效率更高。

由于在缓冲区的头部添加数据意味着要修改指向缓冲区的指针,这是个复杂的操作,所以内核提供了一个函数skb_reserve。

协议栈中的每一层在往下一层传递缓冲区前,第一件事就是调用skb_reserve在缓冲区的头部给协议头预留一定的空间。

skb_reserve同样被设备驱动使用来对齐接收到包的包头。如果缓冲区向上层协议传递,旧的协议层的头部信息就没什么用了。例如,L2的头部只有在网络驱动处理L2的协议时有用,L3是不会关心它的信息的。但是,内核并没有把L2的头部从缓冲区中删除,而是把有效荷载的指针指向L3的头部,这样做,可以节省CPU时间。

结构

内核把sk_buff组织成一个双向链表,这个链表还有另一个需求:每个sk_buff结构都必须能够很快找到链表头节点。为了满足这个需求,在第一个节点前面会插入另一个结构sk_buff_head,每个sk_buff结构都包含一个指向sk_buff_head的指针。

struct sk_buff_head {

struct sk_buff * next; /* These two members must be first. */

struct sk_buff * prev;

_ _u32 qlen;     //链表元素的个数

spinlock_t lock; //防止对链表的并发访问

};

成员变量

struct sock *sk这个指针在网络包由本机发出或者由本机进程接收时有效,因为插口相关的信息被L4(TCP或

UDP)或者用户空间程序使用。

如果sk_buff只在转发中使用(这意味着,源地址和目的地址都不是本机地址),这个指针是NULL。

unsigned int len

这是缓冲区中数据部分的长度。它包括主缓冲区中的数据长度(data指针指向它)和分片中的数据长度。它的值在缓冲区从一个层向另一个层传递时改变,因为往上层传递,旧的头部就没有用了,而往下层传递,需要添加本层的头部。len同样包含了协议头的长度。

unsigned int data_len

和len不同,data_len只计算分片中数据的长度。

atomic_t users

一个引用计数,用于计算有多少实体引用了这个sk_buff缓冲区。它的主要用途是防止释放sk_buff后,还有其他实体引用这个sk_buff。因此,每个引用这个缓冲区的实体都必须在适当的时候增加或减小这个变量。这个计数器只保护sk_buff结构本身,而缓冲区的数据部分由类似的计数器(dataref)来保护.

有时可以用atomic_inc和atomic_dec函数来直接增加或减小users,但是,通常还是使用函数skb_get和kfree_skb来操作这个变量。

unsigned int truesize

这是缓冲区的总长度,包括sk_buff结构和数据部分。如果申请一个len字节的缓冲区,alloc_skb函数会把它初始化成len+sizeof(sk_buff)

alloc_skb 和 dev_alloc_skb

alloc_skb用于分配缓冲区的函数。数据缓冲区和缓冲区的描述结构(sk_buff结构)是两种不同的实体,这就意味着,在分配一个缓冲区时,需要分配两块内存(一个是缓冲区,一个是缓冲区的描述结构

sk_buff)。

alloc_skb调用函数kmem_cache_alloc从缓存中获取一个sk_buff结构,并调用kmalloc分配缓冲区(如果有缓存的话,它同样从缓存中获取内存)。

dev_alloc_skb也是一个缓冲区分配函数,它主要被设备驱动使用,通常用在中断上下文中。这是一个alloc_skb函数的包装函数,它会在请求分配的大小上增加16字节的空间以优化缓冲区的读写效率,它的分配要求使用原子操作(GFP_ATOMIC),这是因为它是在中断处理函数中被调用的。

kfree_skb 和 dev_kfree_skb

这两个函数释放缓冲区,并把它返回给缓冲池(缓存)。kfree_skb可以直接调用,也可以通过包装函数dev_kfree_skb调用。后面这个函数一般被设备驱动使用,与之功能相反的函数是dev_alloc_skb。

dev_kfree_skb仅是一个简单的宏,它什么都不做,只简单地调用kfree_skb。这些函数只有在skb->users为1地情况下才释放内存(没有人引用这个结构)。否则,它只是简单地减小 skb->users。

克隆操作

如果一个缓冲区需要被不同的用户独立地操作,而这些用户可能会修改sk_buff中某些变量的值(比如h和nh值),内核没有必要为每个用户复制一份完整的sk_buff以及相应的缓冲区。相反,为提高性能,内核克隆一个缓冲区。克隆过程只复制sk_buff结构,同时修改缓冲区的引用计数以避免共享的数据被提前释放。克隆缓冲区使用skb_clone函数。

一个使用包克隆的场景是:一个接收包的过程需要把这个包传递给多个接收者,例如包处理函数或者一个或多个网络模块。

被克隆的sk_buff不会放在任何链表中,同时也不会有到socket的引用。原始的和克隆的sk_buff中的

skb->cloned值都被置为1。克隆包的skb->users值被置为1,这样,在释放时,可以先释放sk_buff结构。同时,缓冲区的引用计数(dataref)增加1(因为有多个sk_buff结构指向它)。

skb_share_check用于检查引用计数skb->users,如果users变量表明skb是被共享的, 则克隆一个新的sk_buff。

如果一个缓冲区被克隆了,这个缓冲区的内容就不能被修改。这就意味着,访问数据的函数没有必要加锁。

因此,当一个函数不仅要修改sk_buff,而且要修改缓冲区内容时,就需要同时复制缓冲区。在这种情况下,程序员有两个选择。如果知道所修改的数据在skb->start和skb->end之间,可以使用pskb_copy来复制这部分数据。如果同时需要修改分片中的数据,必须使用skb_copy。

bb5e61c71e24d00ea6e9d00a9c487a6a.png

next:sk_buff链表中的下一个缓冲区。

prev:sk_buff链表中的前一个缓冲区。以上两个变量将sk_buff链接到一个双向链表中。

sk:本网络报文所属的sock结构,此值仅在本机发出的报文中有效,从网络收到的报文此值为空。

tstamp:报文收到的时间戳。

dev:收到此报文的网络设备。

transport_header:传输层头部。

network_header:网络层头部。

mac_header:链接层头部。

cb:用于控制缓冲区。每个层都可以使用此指针,将私有的数据放置于此。

len:有效数据长度。

data_len:数据长度。

mac_len:连接层头部长度,对于以太网,指MAC地址所用的长度,为6。

hdr_len:skb的可写头部长度。

csum:校验和(包含开始和偏移)。

csum_start:当开始计算校验和时从skb->head的偏移。

csum_offset:从csum_start开始的偏移。

local_df:允许本地分片。

pkt_type:包的类别。

priority:包队列的优先级。

truesize:报文缓冲区的大小。

head:报文缓冲区的头。

data:数据的头指针。

tail:数据的尾指针。

end:报文缓冲区的尾部

alloc_skb之后

100426094647.jpg

buffer从tcp层到链路层的过程中len,head,data,tail以及end的变化

100426100100.jpg

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
struct sk_buffLinux 内核网络子系统中的一个非常重要的数据结构,它代表了内核中网络协议栈中的一个网络数据包。在 Linux 内核中,网络数据包都是封装在 sk_buff 中进行传输和处理的,因此可以说 sk_buffLinux 网络子系统中最核心的数据结构之一。 下面是 struct sk_buff 结构的详细说明: ```c struct sk_buff { struct sk_buff *next; /* 下一个 sk_buff */ struct sk_buff *prev; /* 上一个 sk_buff */ ktime_t tstamp; /* 时间戳 */ struct sock *sk; /* socket */ struct net_device *dev; /* 网络设备 */ unsigned long _skb_dst; /* 目标地址 */ unsigned long _skb_src; /* 源地址 */ struct skb_shared_info *shinfo; /* 共享数据 */ atomic_t users; /* 引用计数 */ unsigned int len, data_len; /* 总长度和数据长度 */ __u16 protocol; /* 协议类型 */ __u16 vlan_proto; /* VLAN 协议 */ __u16 vlan_tci; /* VLAN 标记 */ union { __be16 ip4_frag_id; /* IPv4 报文分片标识 */ __u8 hdr_len; /* 首部长度 */ __u16 mac_len; /* MAC 长度 */ }; __u16 queue_mapping; /* 网络队列映射 */ __u16 tc_index; /* 网络流量控制 */ __u16 pkt_type; /* 数据包类型 */ __u32 priority; /* 优先级 */ __u32 skb_mstamp; /* 时间戳 */ u32 secmark; /* 安全标记 */ unsigned int mark; /* skb 标记 */ unsigned int nf_trace; /* 网络跟踪 */ __u32 hash; /* 哈希值 */ __u16 nfctinfo; /* nf_conntrack 信息 */ __u8 queue_bypass; /* 是否绕队列 */ __u8 protocol_was_802_3; /* 协议是否是 802.3 */ __u8 encapsulation; /* 封装类型 */ __u8 transport_header_was; /* 传输层首部是否有效 */ union { __wsum csum; /* 校验和 */ struct { __u16 csum_start; /* 校验和起始位置 */ __u16 csum_offset; /* 校验和偏移量 */ }; }; union { void *dst; /* 目标地址 */ struct { __be32 saddr; /* 源 IP 地址 */ __be32 daddr; /* 目标 IP 地址 */ } ip4; struct { const void *hdr; /* MAC 指针 */ const void *payload; /* 数据负载指针 */ } mac; struct { unsigned char *tail; /* 尾部指针 */ unsigned char *end; /* 结束指针 */ }; }; }; ``` 下面是各个字段的详细说明: - next 和 prev 字段:这两个字段分别指向下一个和上一个 sk_buff,用于将 sk_buff 组织成链表。这样可以方便地进行遍历和管理多个 sk_buff。 - tstamp 字段:这个字段表示 sk_buff 的时间戳,记录了 sk_buff 的创建时间。 - sk 字段:这个字段指向一个 socket,表示这个 sk_buff 相关联的 socket。 - dev 字段:这个字段指向一个网络设备,表示这个 sk_buff 是从哪个网络设备接收到的,或者将要发送到哪个网络设备。 - \_skb_dst 和 \_skb_src 字段:这两个字段是目标地址和源地址的指针,分别指向目标地址和源地址的内存空间。 - shinfo 字段:这个字段指向一个 skb_shared_info 结构,用于共享数据。 - users 字段:这个字段是一个引用计数器,用于记录当前有多少个指针指向这个 sk_buff。 - len 和 data_len 字段:这两个字段分别表示 sk_buff 的总长度和数据长度。 - protocol 字段:这个字段表示 sk_buff 中数据的协议类型,例如 ETH_P_IP 表示 IPv4 协议,ETH_P_ARP 表示 ARP 协议等。 - vlan_proto 和 vlan_tci 字段:这两个字段用于处理 VLAN 标记。 - ip4_frag_id 字段:这个字段用于处理 IPv4 报文分片标识。 - queue_mapping 字段:这个字段表示网络队列映射。 - tc_index 字段:这个字段表示网络流量控制。 - pkt_type 字段:这个字段表示数据包的类型,例如数据包是从网络设备接收而来的、或者是要发送到网络设备的等。 - priority 字段:这个字段表示 sk_buff 的优先级。 - skb_mstamp 字段:这个字段表示 sk_buff 的时间戳,记录了 sk_buff 的最后修改时间。 - secmark 字段:这个字段用于安全标记。 - mark 字段:这个字段用于 skb 标记。 - nf_trace 字段:这个字段用于网络跟踪。 - hash 字段:这个字段用于哈希值。 - nfctinfo 字段:这个字段用于 nf_conntrack 信息。 - queue_bypass 字段:这个字段表示是否绕过队列。 - protocol_was_802_3 字段:这个字段表示协议是否是 802.3。 - encapsulation 字段:这个字段表示封装类型。 - transport_header_was 字段:这个字段表示传输层首部是否有效。 - csum 字段:这个字段用于校验和。 - csum_start 和 csum_offset 字段:这两个字段分别表示校验和的起始位置和偏移量。 - dst 字段:这个字段指向目标地址。 - ip4.saddr 和 ip4.daddr 字段:这两个字段分别表示 IPv4 报文的源 IP 地址和目标 IP 地址。 - mac.hdr 和 mac.payload 字段:这两个字段分别指向 MAC 和数据负载的指针。 - tail 和 end 字段:这两个字段分别指向 sk_buff 数据的尾部和结束位置。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值