sk_buff结构体成员变量说明

一. 前言

        Socket Buffer的数据包在穿越内核空间的TCP/IP协议栈过程中,数据内容不会被修改,只是数据包缓冲区中的协议头信息发生变化。大量操作都是围绕sk_buff结构体来进行的。

        sk_buff结构的成员大致分为3类:结构管理域,常规数据域和网络功能配置相关域。

二. sk_buff数据结构体解析

1. 结构管理域

*next和prev:

        sk_buff会被链入到一个双链表中,next指向链表的下一个成员,prev指向链表的前一个成员。链表的头是一个sk_buff_head的结构体,如下所示:

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

	__u32		qlen;
	spinlock_t	lock;
};

        其中qlen表示链表中sk_buff结构实例成员的个数,lock用于对双链表操作的保护的锁,防止并发访问链表。示意图如下

         内核管理Socket Buffer的优点:某个Socket Buffer的状态变化了,需要将Socket Buffer在各队列之间移动时,无需复制整个缓冲区,只需要修改prev和next指针,就可以将Socket Buffer的缓冲区从一个队列放入到另一个队列管理。例如struct usbnet结构体如下:

struct usbnet {
    ......
    struct sk_buff_head     rxq;
    struct sk_buff_head     txq;
    struct sk_buff_head     done;
    struct sk_buff_head     rxq_pause;
    ......
}

struct sock *sk:

        指向拥有该Socket Buffer的套接字数据结构的指针。当数据是由本机的应用产生,将要对外发送时,或从网络来的数据包的目的地址是本机的应用程序时,这个数据域需要被设置。

        套接字本质是端口号加IP,用来唯一识别系统中的网络应用程序。sk_buff->sk数据域表示网络数据包最终应该送给到哪个应用程序。如果是需要从本机Forward的数据包,sk应该被设置为空。

unsigned int len:

        表示数据包的实际的长度,也就是sk_buff->data指向的数据的实际长度。sk_buff->len在数据包通过协议栈的各层时其值也会发生变化因为各层的协议头信息在不断加入或从Socket Buffer中去掉,因为sk_buff->len包含了协议头的长度。

        sk_buff->len包括两个部分:主缓冲区的数据长度和各个分片数据的长度

unsigned int data_len:

        data_len只是计算了被分了片的数据块长度。所以data_len应该要等于或小于len。

__u16 mac_len:

        数据链路层协议头的长度。

__u16 hdr_len:

        hdr_len是针对克隆数据包时使用的,它表示克隆的数据包的头长度。

static struct sk_buff *__skb_clone(struct sk_buff *n, struct sk_buff *skb)
{
    ......
	n->hdr_len = skb->nohdr ? skb_headroom(skb) : skb->hdr_len;
    ......
}

atomic_t users:

        引用计数,所有使用该sk_buff缓冲区的进程计数。这个参数的作用是防止sk_buff还在使用就被释放了。任何进程要使用sk_buff时,应该对sk_buff->users加1,使用完后应该对sk_buff->users减1。通常加和减操作分别使用skb_get和free_skb函数,这样做更安全。

unsigned int truesize:

        记录整个Socket buffer的大小,即sk_buff数据结构的长度和数据包的长度和。它是由alloc_skb函数将其初始化为len+sizeof(sk_buff)。

sk_buff_data_t tail;

sk_buff_data_t end;

unsigned char *head,*data:

        Socket Buffer的数据缓冲区的内容包括:TCP/IP协议栈各层的协议头信息;负载数据。这是最终在网络上传送的内容。以上的几个域代表数据包缓冲区中各个信息的边界。

        head和end指向整个数据包缓冲区的起始和结束地址,data和tail指向实际数据的起始和结束地址,各层的协议处理函数可以在data和head之间的空隙处填写头信息,在tail和end之间放新数据。

 void (*destructor)(...):

        destructor函数指针可以在sk_buff数据结构初始化时指向Socket Buffer的析构函数。在释放Socket Buffer时,完成具体的清除工作。当sk_buff不属于任何套接字时,析构函数不需要初始化。

2. 常规数据域

ktime_t tstamp:

        描述数据包到达内核的时间。由接收数据包处理函数netif_rx调用net_timestamp(skb)来对该数据域赋值。

struct net_device *dev:

        dev是指向代表网络设备数据结构的指针。它表示该数据包是通过哪个网络设备接收或传送的。当网络设备从网络上收到一个数据包时,设备驱动程序将该域更新为一个net_device类型的指针,指向接收该数据包的网络设备。

static int fe_poll_rx(struct napi_struct *napi, int budget,
		struct fe_priv *priv, u32 rx_intr)
{
    ......    
	skb->protocol = eth_type_trans(skb, netdev);
    ......
}

__be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
{
    ......
	skb->dev = dev;
    ......
}

char cb[48]:

        控制缓冲区(Control Buffer),是各层协议在处理数据包时存放私有信息或变量的地方。各层协议可以自由使用该控制缓冲区。控制缓冲区大小是48字节,如果在传输层,UDP协议用控制缓冲区来存放它的udp_skb_cb数据结构。

struct udp_skb_cb {
	union {
		struct inet_skb_parm	h4;
#if IS_ENABLED(CONFIG_IPV6)
		struct inet6_skb_parm	h6;
#endif
	} header;
	__u16		cscov;
	__u8		partial_cov;
};

        内核定义了宏来访问控制缓冲区,一下代码是UDP访问其私有数据的宏。

#define UDP_SKB_CB(__skb)	((struct udp_skb_cb *)((__skb)->cb))

       以下是在初始化过程中对UDP数据包做校验和时,填写控制缓冲区代码:

static inline int udp4_csum_init(struct sk_buff *skb, struct udphdr *uh,
				 int proto)
{
    ......
	UDP_SKB_CB(skb)->partial_cov = 0;
	UDP_SKB_CB(skb)->cscov = skb->len;
    ......
}

        r如果需要让控制缓冲区的信息跨协议层传送,必须克隆sk_buff。

__wsum csum:

        csum用于存放发送数据的校验和。发送数据包时,我们将数据从用户地址空间复制到内核地址空间,同时以相应的算法计算数据包的校验和存放在该数据域。

csum_start/csum_end:

        存放接收数据的校验和。csum_start以skb->head为起始地址的偏移量,指出校验和从数据什么位置开始计算。csun_offset以csum_start为起始地址的偏移量,指出校验和存放的地址。

__u8 ip_summed:

          描述网络设备是否可用硬件对IP数据进行校验编码和解码。ip_summed是两位描述网络设备硬件对校验和的支持,它是设备驱动程序反馈的信息。ip_summed的值可以如下:

CHECKSUM_NONE:网络设备不具备计算校验和的功能。

CHECKSUM_UNNECESSARY:不需要对数据包计算校验和,这个值一般用于lookback设备

CHECKSUM_COMPLETE:网络硬件具有计算校验和的功能。

CHECKSUM_PARITAL:针对输出数据包,要求设备对有hard_start_xmit函数发送的数据包做校验和,教养和的范围从csum_start指明的地址起始到数据包结束处,校验和存放在csum_start+csum_offset的地址。

__u32 priority:

        priority数据域是用来实现质量服务(Quality of service)QoS功能特性的。QoS描述了数据包传送的优先级别。例如网络视频数据、语音数据需要QoS以保证视频、语音的流畅。

__be16 protocol:

        接收数据包的网络层协议(如IP协议)。它标志了网络数据包应传给TCP/IP协议栈网络层的哪个协议处理函数。protocol域协议的完整定义在include/linux/if_ether.h头文件中。该域是由网络适配器的驱动程序调用相关函数来填写的。

__u16 vlan_tci:

        虚拟局域网的标记控制信息(Vlan Tag Control Information)。

sk_buff_data_t transport_header,

sk_buff_data_t network_header,

sk_buff_data_t mac_header:

        以上3个域是sk_buff结构中描述Linux内核网络协议栈中各层协议头在网络数据包的位置信息。他们含义如下,transport_header表示传输层协议头在网络数据包中的地址;network_header编号网络协议头在网络数据包中的地址;mac_header表示数据链路层协议头在网络数据包中的地址。

        在64位系统中,这3个协议值表示相应的协议头在网络数据包中的地址是以skb->head为起始的偏移量,在32位系统中,sk_buff_data_t的类型就为指针,存放各层协议头在网络数据包中的起始地址。 如下:

// include/linux/skbuff.h
#if BITS_PER_LONG > 32
#define NET_SKBUFF_DATA_USES_OFFSET 1
#endif

#ifdef NET_SKBUFF_DATA_USES_OFFSET
typedef unsigned int sk_buff_data_t;
#else
typedef unsigned char *sk_buff_data_t;
#endif

3. 连接跟踪

unsigned long _nfct:

        如果是32位的系统,_nfct是4个字节。_nfct用来标识该skb的连接跟踪状态(enum ip_conntrack_info)和记录一个struct nf_conn的地址。这个可以由nf_ct_get函数可以看出:

#define NFCT_INFOMASK	7UL
#define NFCT_PTRMASK	~(NFCT_INFOMASK)

static inline struct nf_conn *
nf_ct_get(const struct sk_buff *skb, enum ip_conntrack_info *ctinfo)
{
	*ctinfo = skb->_nfct & NFCT_INFOMASK;

	return (struct nf_conn *)(skb->_nfct & NFCT_PTRMASK);
}

        由上代码可知,_nfct的低三位保存是连接状态,可能的状态如下:

include/uapi/linux/netfilter/nf_conntrack_common.h:

enum ip_conntrack_info {
	/* Part of an established connection (either direction). */
	IP_CT_ESTABLISHED,

	/* Like NEW, but related to an existing connection, or ICMP error
	   (in either direction). */
	IP_CT_RELATED,

	/* Started a new connection to track (only
           IP_CT_DIR_ORIGINAL); may be a retransmission. */
	IP_CT_NEW,

	/* >= this indicates reply direction */
	IP_CT_IS_REPLY,

	IP_CT_ESTABLISHED_REPLY = IP_CT_ESTABLISHED + IP_CT_IS_REPLY,
	IP_CT_RELATED_REPLY = IP_CT_RELATED + IP_CT_IS_REPLY,
	/* No NEW in reply direction. */

	/* Number of distinct IP_CT types. */
	IP_CT_NUMBER,

	/* only for userspace compatibility */
#ifndef __KERNEL__
	IP_CT_NEW_REPLY = IP_CT_NUMBER,
#else
	IP_CT_UNTRACKED = 7,
#endif
};

        由函数返回的结果进行了强转,_nfct的其余的位存放的一个struct nf_conn结构体的地址。

struct nf_conn {
	/* Usage count in here is 1 for hash table, 1 per skb,
	 * plus 1 for any connection(s) we are `master' for
	 *
	 * Hint, SKB address this struct and refcnt via skb->_nfct and
	 * helpers nf_conntrack_get() and nf_conntrack_put().
	 * Helper nf_ct_put() equals nf_conntrack_put() by dec refcnt,
	 * beware nf_ct_get() is different and don't inc refcnt.
	 */
	struct nf_conntrack ct_general;

	spinlock_t	lock;
	u16		cpu;

#ifdef CONFIG_NF_CONNTRACK_ZONES
	struct nf_conntrack_zone zone;
#endif
	/* XXX should I move this to the tail ? - Y.K */
	/* These are my tuples; original and reply */
	struct nf_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX];

	/* Have we seen traffic both ways yet? (bitset) */
	unsigned long status;

	/* jiffies32 when this ct is considered dead */
	u32 timeout;

	possible_net_t ct_net;

#if IS_ENABLED(CONFIG_NF_NAT)
	struct hlist_node	nat_bysource;
#endif
	/* all members below initialized via memset */
	struct { } __nfct_init_offset;

	/* If we were expected by an expectation, this will be it */
	struct nf_conn *master;

#if defined(CONFIG_NF_CONNTRACK_MARK)
	u_int32_t mark;
#endif

#ifdef CONFIG_NF_CONNTRACK_SECMARK
	u_int32_t secmark;
#endif

	/* Extensions */
	struct nf_ct_ext *ext;

	/* Storage reserved for other modules, must be the last member */
	union nf_conntrack_proto proto;
};

三. 总结

        本文主要学习了struct sk_buff结构的一些字段的含义和用途,为后续的网络协议栈的学习打好基础。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
sk_buffLinux网络协议栈中一个非常重要的数据结构,它用于管理网络数据包。sk_buff结构体的成员包括: 1. struct sk_buff *next:下一个sk_buff结构体的指针,用于形成链表。 2. struct sk_buff *prev:上一个sk_buff结构体的指针,用于形成链表。 3. struct sock *sk:指向关联的套接字的指针。 4. struct sk_buff_head *list:指向该sk_buff结构体所在的链表的指针。 5. unsigned char *head:数据包的头指针。 6. unsigned char *data:数据包的数据指针。 7. unsigned char *tail:数据包的尾指针。 8. unsigned char *end:数据包的结束指针。 9. struct net_device *dev:指向发送或接收该数据包的网络设备的指针。 10. unsigned int len:数据包的长度。 11. unsigned int data_len:数据包实际包含的数据长度。 12. unsigned int truesize:该sk_buff结构体占用的内存空间大小。 13. unsigned int protocol:数据包的协议类型。 14. __u16 vlan_proto:VLAN标签协议类型。 15. __u16 vlan_tci:VLAN标签的TCI值。 16. unsigned char vlan_present:表示该数据包是否带有VLAN标签。 17. unsigned char mac_header:MAC头部在skb->data中的偏移量。 18. unsigned char transport_header:传输层头部在skb->data中的偏移量。 19. unsigned char network_header:网络层头部在skb->data中的偏移量。 20. unsigned char inner_transport_header:内层传输层头部在skb->data中的偏移量。 21. unsigned char inner_network_header:内层网络层头部在skb->data中的偏移量。 22. unsigned char transport_header_was:表示skb->transport_header字段是否已被修改。 23. unsigned char network_header_was:表示skb->network_header字段是否已被修改。 24. unsigned char mac_header_was:表示skb->mac_header字段是否已被修改。 25. unsigned char cb[48]:可用于存储协议栈的私有数据。 26. sk_buff_data_t skb_mstamp:数据包的时间戳。 27. sk_buff_data_t skb_iif:数据包的输入接口索引。 28. unsigned int priority:数据包的优先级。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值