sk_buff结构--转载
阅读原文时间:2023年07月12日阅读:2

套接字缓存之sk_buff结构

https://www.cnblogs.com/wanpengcoder/p/7529486.html 来此此处

sk_buff结构用来描述已接收或者待发送的数据报文信息;skb在不同网络协议层之间传递,可被用于不同网络协议,如二层的以太网协议,三层的ip协议,四层的tcp或者udp协议,其中某些成员变量会在该结构从一层向另一层传递时发生改变,从上层向下层传递需要添加首部,从下层向上层传递需要移除首部;

多个skb通过sk_buff_head表头部结构的next和prev指针连接成双向链表;头部还包含了链表中skb节点的总数量;

/* skb头结构 */
struct sk_buff_head {
/* These two members must be first. */
/* 通过下面两个指针成员将skb连接成双向链表 */
struct sk_buff *next; /* 指向后一个skb */
struct sk_buff *prev; /* 指向前一个skb */

\_\_u32        qlen; /\* 链表中元素个数 \*/  
spinlock\_t    lock; /\* 自旋锁 \*/  

};

2/

/* skb结构 */
struct sk_buff {
union {
struct {
/* These two members must be first. */
struct sk_buff *next;
struct sk_buff *prev;

        /\* 报文到达或者离开的时间戳 \*/  
        union {  
            ktime\_t        tstamp;  
            struct skb\_mstamp skb\_mstamp;  
        };  
    };  
    /\* 红黑树的节点,用在netem和tcp协议栈 \*/  
    struct rb\_node    rbnode; /\* used in netem & tcp stack \*/  
};

/\*  
    指向缓冲区的套接字sock数据结构。当数据在本地产生或者正由本地进程接收时,  
    该数据以及套接字相关信息会被L4(tcp或者udp)以及用户应用程序使用  
    当缓冲区只是被转发时(本地机器不是来源也不是目的地),该指针为NULL  
\*/  
struct sock        \*sk;

union {  
    /\* 报文到达或者离开时的网络设备 \*/  
    struct net\_device    \*dev;  
    /\* Some protocols might use this space to store information,  
     \* while device pointer would be NULL.  
     \* UDP receive path is one user.  
     \*/  
    unsigned long        dev\_scratch;  
};  
/\*  
 \* This is the control buffer. It is free to use for every  
 \* layer. Please put your private variables there. If you  
 \* want to keep them across layers you have to do a skb\_clone()  
 \* first. This is owned by whoever has the skb queued ATM.  
 \*/  
/\*  
    控制缓冲区,用于存储私有信息,每层协议自己维护并使用,  
    并且只在本层有有效  
\*/  
char            cb\[48\] \_\_aligned(8);

/\* 路由缓存,输入或者输出报文都要查询到目的路由缓存项,才能确定流向 \*/  
unsigned long        \_skb\_refdst;

/\*  
    当缓冲区被删除时,可以完成某些清理工作  
    当缓冲区不属于一个套接字时,该函数通常不被初始化  
    属于一个套接字时,通常设置为sock\_rfree或sock\_wfree  
    sock\_xxx函数用于更新套接字队列中所持有的内存  
\*/  
void            (\*destructor)(struct sk\_buff \*skb);  

#ifdef CONFIG_XFRM
/* ipsec用于跟踪传输信息 */
struct sec_path *sp;
#endif
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
/* 连接跟踪 */
unsigned long _nfct;
#endif
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
/* 桥接帧的相关信息 */
struct nf_bridge_info *nf_bridge;
#endif
/*
缓冲区的数据区块大小,该长度包括主缓冲区(head指针指向)的数据
以及一些片段(fragment)的数据,当缓冲区从一个网络分层移动到下一个
网络分层时,该值会发生变化,因为在协议栈中向上层移动时报头会被丢弃
向下层移动时报头会添加,len也会把协议报头算在内,与"数据预留和对齐"操作
*/
unsigned int len,
/* 片段(fragment)中的数据大小 */
data_len;
/* mac报头大小 */
__u16 mac_len,
/* 克隆skb时可写报文头部长度 */
hdr_len;

/\* Following fields are \_not\_ copied in \_\_copy\_skb\_header()  
 \* Note that queue\_mapping is here mostly to fill a hole.  
 \*/  
kmemcheck\_bitfield\_begin(flags1);  
/\* 多设备的队列映射 \*/  
\_\_u16            queue\_mapping;

/* if you move cloned around you also must adapt those constants */
#ifdef __BIG_ENDIAN_BITFIELD
#define CLONED_MASK (1 << 7)
#else
#define CLONED_MASK 1
#endif
#define CLONED_OFFSET() offsetof(struct sk_buff, __cloned_offset)

\_\_u8            \_\_cloned\_offset\[0\];  
/\* 表示该skb是另外一个skb的克隆 \*/  
\_\_u8            cloned:1,  
            /\*  
                payload是否被单独引用,不存在协议首部,如果被引用,则不能修改协议首部,  
                也不能通过skb->data来访问协议首部  
            \*/  
            nohdr:1,  
            /\*  
                当前克隆状态  
                SKB\_FCLONE\_UNAVAILABLE-skb未被克隆  
                SKB\_FCLONE\_ORIG-在skbuff\_fclone\_cache分配的父skb,可以被克隆  
                SKB\_FCLONE\_CLONE-在skbuff\_fclone\_cache分配的子skb,从父skb克隆得到  
            \*/  
            fclone:2,  
            peeked:1,  
            /\* 通过page\_fragment\_alloc分配内存 \*/  
            head\_frag:1,  
            xmit\_more:1,  
            \_\_unused:1; /\* one bit hole \*/  
kmemcheck\_bitfield\_end(flags1);

/\* fields enclosed in headers\_start/headers\_end are copied  
 \* using a single memcpy() in \_\_copy\_skb\_header()  
 \*/  
/\* private: \*/  
\_\_u32            headers\_start\[0\];  
/\* public: \*/

/* if you move pkt_type around you also must adapt those constants */
#ifdef __BIG_ENDIAN_BITFIELD
#define PKT_TYPE_MAX (7 << 5)
#else
#define PKT_TYPE_MAX 7
#endif
#define PKT_TYPE_OFFSET() offsetof(struct sk_buff, __pkt_type_offset)

\_\_u8            \_\_pkt\_type\_offset\[0\];  
/\*  
    此字段根据l2的目的地址进行划分  
    PACKET\_HOST-mac地址与接收设备mac地址相等,说明是发给该主机的  
    PACKET\_BROADCAST-mac地址是接收设备的广播地址  
    PACKET\_MULTICAST-mac地址接收改设备注册的多播地址之一  
    PACKET\_OTHERHOST-mac地址不属于接收设备的地址,启用转发则转发,否则丢弃  
    PACKET\_OUTGOING-数据包将被发出,用到这个标记的功能包括decnet,或者为每个  
    网络tab都复制一份发出包的函数  
    PACKET\_LOOPBACK-数据包发往回环设备,有此标识,处理回环设备时,  
    可以跳过一些真实设备所需的操作  
    PACKET\_USER-发送到用户空间,netlink使用  
    PACKET\_KERNEL-发送到内核空间,netlink使用  
    PACKET\_FASTROUTE-未使用  
\*/  
\_\_u8            pkt\_type:3;  
/\* PFMEMALLOC内存分配标记 \*/  
\_\_u8            pfmemalloc:1;  
\_\_u8            ignore\_df:1;

\_\_u8            nf\_trace:1;  
/\*  
    CHECKSUM\_NONE-硬件不支持,完全由软件执行校验和  
    CHECKSUM\_PARTIAL-由硬件来执行校验和  
    CHECKSUM\_UNNECESSARY-没必要执行校验和  
    CHECKSUM\_COMPLETE-已完成执行校验和  
\*/  
\_\_u8            ip\_summed:2;  
\_\_u8            ooo\_okay:1;  
\_\_u8            l4\_hash:1;  
\_\_u8            sw\_hash:1;  
\_\_u8            wifi\_acked\_valid:1;  
\_\_u8            wifi\_acked:1;

\_\_u8            no\_fcs:1;  
/\* Indicates the inner headers are valid in the skbuff. \*/  
\_\_u8            encapsulation:1;  
\_\_u8            encap\_hdr\_csum:1;  
\_\_u8            csum\_valid:1;  
\_\_u8            csum\_complete\_sw:1;  
\_\_u8            csum\_level:2;  
\_\_u8            csum\_bad:1;

\_\_u8            dst\_pending\_confirm:1;  

#ifdef CONFIG_IPV6_NDISC_NODETYPE
__u8 ndisc_nodetype:2;
#endif
__u8 ipvs_property:1;
__u8 inner_protocol_type:1;
__u8 remcsum_offload:1;
#ifdef CONFIG_NET_SWITCHDEV
__u8 offload_fwd_mark:1;
#endif
#ifdef CONFIG_NET_CLS_ACT
__u8 tc_skip_classify:1;
__u8 tc_at_ingress:1;
__u8 tc_redirected:1;
__u8 tc_from_ingress:1;
#endif

#ifdef CONFIG_NET_SCHED
__u16 tc_index; /* traffic control index */
#endif

union {  
    /\* 校验和,必须包含csum\_start和csum\_offset \*/  
    \_\_wsum        csum;  
    struct {  
        /\* 校验开始位置,相对于header \*/  
        \_\_u16    csum\_start;  
        /\* 校验和存储位置,相对于csum\_start \*/  
        \_\_u16    csum\_offset;  
    };  
};  
/\*  
    正在被传输的数据包QoS等级  
    数据包由本地产生,套接字会定义优先级的值  
    数据包在被转发,则在调用ip\_forward函数时,会根据  
    ip头本身的ToS字段定义该值  
\*/  
\_\_u32            priority;  
/\* 数据包接收时的网络设备索引号 \*/  
int            skb\_iif;  
/\* 数据包的hash值 \*/  
\_\_u32            hash;  
/\* vlan封装协议 \*/  
\_\_be16            vlan\_proto;  
/\* vlan标签控制信息 \*/  
\_\_u16            vlan\_tci;  

#if defined(CONFIG_NET_RX_BUSY_POLL) || defined(CONFIG_XPS)
union {
unsigned int napi_id;
unsigned int sender_cpu;
};
#endif
#ifdef CONFIG_NETWORK_SECMARK
__u32 secmark;
#endif

union {  
    \_\_u32        mark;  
    \_\_u32        reserved\_tailroom;  
};

/\* 封装的协议 \*/  
union {  
    \_\_be16        inner\_protocol;  
    \_\_u8        inner\_ipproto;  
};  
/\* 封装的传输层头部相对于head的偏移 \*/  
\_\_u16            inner\_transport\_header;  
/\* 封装的网络层头部相对于head的偏移 \*/  
\_\_u16            inner\_network\_header;  
/\* 封装的链路层头部相对于head的偏移 \*/  
\_\_u16            inner\_mac\_header;

/\*  
    l3层协议值  
    如ETH\_P\_IP-ipv4报文  
    ETH\_P\_ARP-arp报文等  
\*/  
\_\_be16            protocol;  
/\* 传输层头部相对于head的偏移 \*/  
\_\_u16            transport\_header;  
/\* 网络层头部相对于head的偏移 \*/  
\_\_u16            network\_header;  
/\* 链路层头部相对于head的偏移 \*/  
\_\_u16            mac\_header;

/\* private: \*/  
\_\_u32            headers\_end\[0\];  
/\* public: \*/

/\* 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;  
/\*  
    缓冲区的总大小,包括skb本身和实际数据len大小,alloc\_skb函数将  
    该字段设置为len+sizeof(sk\_buff)  
    每当len值更新,该值也要对应更新  
\*/  
unsigned int        truesize;

/\*  
    引用计数,在使用该skb缓冲区的实例个数,当引用计数为0时,skb才能被释放  
    skb\_get()获取操作中会增加引用计数,kfree\_skb释放过程中检查引用计数,  
    引用计数为0时,才真正释放skb  
    该计数器只计算sk\_buff结构引用计数,缓冲区包含的实际数据由  
    skb\_shared\_info->dataref字段记录  
\*/  
atomic\_t        users;  

};