Linux协议栈(4)——sk_buff及代码

简介: Linux协议栈(4)——sk_buff及代码   Linux内核网络中最终要的两个数据结构是sk_buff和net_device。本章介绍sk_buff结构体。 sk_buff结构可能是网络代码中最重要的数据结构。

Linux协议栈(4)——sk_buff及代码

  Linux内核网络中最终要的两个数据结构是sk_buff和net_device。本章介绍sk_buff结构体。

sk_buff结构可能是网络代码中最重要的数据结构。代表已接收或正要传输的数据报。定义在include/linux/skbuff.h头文件中。由变量堆(heap)组成。用于管理网络数据包。操作sk_buff的函数定义在net/core/skbuff.c中。

当网络包被内核接收处理时,底层协议的数据被传送高层,当数据传送时,过程反过来。sk_buff在网络实现层交换数据而不用拷贝来或去数据包,可以显著获得速度收益。

一个 skb 表示 Linux 网络栈中的一个 packetTCP 分段和 IP 分组生产的多个 skb 被一个 skb list 形式来保存。

当从上层往下层,或下层网上层传递时候,并不复制数据报,而是在缓冲区中操作增减报头而已,非常高效。

1.1.1.1  sk_buff定义

具体如下,差不多有两页左右:

struct sk_buff {

        union {

                struct {

                        /* These two members must be first. */

                        struct sk_buff          *next;//列表中下一个buffer

                        struct sk_buff          *prev;//列表中上一个buffer

 

                        union {

                                ktime_t         tstamp;//分组到达或离开的时间

                                u64             skb_mstamp;

                        };

                };

                struct rb_node  rbnode; /* used in netem & tcp stackRB树节点 */

        };

        struct sock             *sk;//指针,指向拥有此缓冲区套接字的sock数据结构。当缓冲区只是转发则不需要设置为NULL.

 

        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);//控制缓存,给每层使用,可以将私有变量放在此处。如果要跨越不同层,就需要调用skb_clone.

 

        unsigned long           _skb_refdst;//目标入口(with norefcount bit)

        void                    (*destructor)(struct sk_buff *skb);// Destruct function

#ifdef CONFIG_XFRM

        struct  sec_path        *sp;//安全路径,给xfrm使用

#endif

#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)

        unsigned long            _nfct;// Associated connection, if any (with nfctinfo bits)

#endif

#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)

        struct nf_bridge_info   *nf_bridge;// Saved data about a bridged frame - see br_netfilter.c

#endif

        unsigned int            len, //缓冲区中数据区块的大小。包括由head所指以及一些片段数据。当从一个分层移动到另一个分层时候回发生变化。协议报头也算在里面。

                                data_len;//只计算片段中的数据大小

        __u16                   mac_len,//mac报头的大小

                                hdr_len;//克隆的skb可写的头长度

 

        /* 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];

        __u8                    cloned:1,//头被复制(检测refcnt

                                nohdr:1,// Payload reference only, must not modify header

                                fclone:2,// skbuff clone status

                                peeked:1,// this packet has been seen already, so stats have been done for it, don't do them again

                                head_frag:1,

                                xmit_more:1,//有更多的skb在这个队列中

                                __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];

        __u8                    pkt_type:3;// 定义在include/uapi/linux/if_packet.h,共3位最多8个值。

        __u8                    pfmemalloc:1;

        __u8                    ignore_df:1;//允许本地分段

 

        __u8                    nf_trace:1;//netfilter packet trace flag

        __u8                    ip_summed:2;// Driver fed us an IP checksum

        __u8                    ooo_okay:1;// allow the mapping of a socket to a queue to be changed

        __u8                    l4_hash:1;// indicate hash is a canonical 4-tuple hash over transport port

        __u8                    sw_hash:1;// indicates hash was computed in software stack

        __u8                    wifi_acked_valid:1;// wifi_acked was set

 

        __u8                    wifi_acked:1;// whether frame was acked on wifi or not

 

        __u8                    no_fcs:1;// Request NIC to treat last 4 bytes as Ethernet FCS

        /* 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_not_inet:1;// use CRC32c to resolve CHECKSUM_PARTIAL

 

        __u8                    dst_pending_confirm:1;// need to confirm neighbour

#ifdef CONFIG_IPV6_NDISC_NODETYPE

        __u8                    ndisc_nodetype:2;// router type (from link layer)

#endif

        __u8                    ipvs_property:1;// skbuff is owned by ipvs

        __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;// do not classify packet. set by IFB device

        __u8                    tc_at_ingress:1;// used within tc_classify to distinguish in/egress

        __u8                    tc_redirected:1;// packet was redirected by a tc action

        __u8                    tc_from_ingress:1;// if tc_redirected, tc_at_ingress at time of redirect

#endif

 

#ifdef CONFIG_NET_SCHED

        __u16                   tc_index;       /* traffic control index */

#endif

 

        union {

                __wsum          csum;//检验码,必须包括开始/偏移

                struct {

                        __u16   csum_start;//Offset from skb->head where checksumming should start

                        __u16   csum_offset;//Offset from csum_start where checksum should be stored

                };

        };

        __u32                   priority;//表示正被传输或转发的封包QoS. 如果在本地产生,套接字层会定义优先级值。如果转发,会根据IP报头的ToS设置此字段的值。

        int                     skb_iif;//输入设备的接口索引号

        __u32                   hash;//包的哈希值

        __be16                  vlan_proto;// vlan encapsulation protocol

        __u16                   vlan_tci;// vlan tag control information

#if defined(CONFIG_NET_RX_BUSY_POLL) || defined(CONFIG_XPS)

        union {

                unsigned int    napi_id;// id of the NAPI struct this skb came from

                unsigned int    sender_cpu;

        };

#endif

#ifdef CONFIG_NETWORK_SECMARK

        __u32           secmark;// security marking

#endif

 

        union {

                __u32           mark;// Generic packet mark

                __u32           reserved_tailroom;

        };

 

        union {

                __be16          inner_protocol;

                __u8            inner_ipproto;

        };

 

        __u16                   inner_transport_header;

        __u16                   inner_network_header;

        __u16                   inner_mac_header;

 

        __be16                  protocol;//下一个较高层的协议,例如IP,IPv6,ARP.每种协议都有自己的函数处理例程用来处理输入的封包。驱动程序用这个字段通知上层该使用哪个处理例程。

        __u16                   transport_header;//传输层头

        __u16                   network_header;//网络层头

        __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;//End 指针

        unsigned char           *head,//缓冲区头

                                *data;//数据头指针

        unsigned int            truesize;//此缓存区的大小,包括sk_buff本身结构大小。

        refcount_t              users;//引用计数,使用缓冲区实例的数目

};

 

1.1.1.1  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;

};

 

1.1.1.2  sk_buff关系

sk_buff在一个双链表中,没有使用内核标准的标准链表。

sk_buff之间的关系如下图:

d3c392a90455b7d484d691ca744a6aa70ca7f82e

1.1.1.3  sk_buff函数

skb链表的管理函数

主要位于include/linux/skbuff.h和skbuff.c文件中。

alloc_skb

alloc_skb函数分配套接字缓冲区。分配两块内存,一块是数据缓存区,另一块是SKB描述符。

dev_alloc_skb

  dev_alloc_skb也是缓存区分配函数,通常用在被设备驱动用在的中断上下文中。是alloc_skb的封装函数。

kfree_skb

kfree_skb释放数据包占用的套接字缓冲区。返回给高速缓存。

dev_kfree_skb

dev_kfree_skb是kfree_skb的封装函数。

skb_reserver函数

  当缓冲区往下传经每个分层时,调用skb_reserver函数为该协议的报头预留空间。

  实现在数据缓存区头部预留一定的空间。被用来在数据缓存区中插入协议首部或者某个边界上对齐。更新数据缓存区的两个指针,分别指向负载起始和结尾的data和tail指针。

skb_push

  skb_push在数据缓存区的前头加入一块数据。也是移动data和tail指针。

skb_put

  skb_put修改指向数据区末尾的指针tail, 使之往下移len字节。使数据区向下扩大len字节,并更新数据区长度len。

skb_pull

  skb_pull通过将data指针往下移动,在数据区首部忽略len字节长度的数据,用于接收到的数据包在各层间由下往上传递时,上层忽略下层的首部。

skb_clone

  没有必要复制一份完整的SKB描述及其相应的数据缓存区,而会为了提高性能,只作克隆操作。复制SKB描述符,同时增加数据缓冲区的引用计数即可。

pskb_copy

  当函数不仅要修改SKB描述符,而且还要修改数据缓存区中的数据时,需要同时复制数据缓存区。需要使用pskb_copy函数来复制这部分数据。

skb_copy

如果需要同时修改聚合分散I/O存储区中的数据,使用skb_copy。

skb链表管理函数

skb_queue_head_init函数用来初始化sk_buff_head结构。

skb_queue_head和skb_queue_tail将SKB加入到队列的头首部和尾部。

skb_dequeue和skb_dequeue_tail从队列的首部和尾部取一下SKB。

skb_queue_purge清空一个SKB链表

skb_queue_walk宏,定义一个for语句,来顺序遍历SKB链表中的每一个元素。

skb添加或删除尾部数据

skb_add_data

  将用户空间的数据添加到SKB的数据缓存区的尾部。

skb_trim

根据指定长度删除SKB的数据缓存区尾部的数据。如果长度大于当前长度,则不作处理。前提是待操作的SKB数据必须是线性存储的。

pskb_trim

skb_trim函数的功能超集,不仅可以处理线性数据的SKB,还可以处理非线性的SKB。

skb_split

可以根据指定长度拆分SKB。原SKB中的数据长度为指定的长度,剩下的数据保存到拆分得到的SKB中。

pskb_expand_head

根据指定长度重新扩展headroom和tailroom空间。

skb_shared_info

  用于管理套接字缓冲区的数据包分片信息。在数据缓存区的末尾,即end指针所指向的地址起紧跟着有一个skb_shared_info结构。保存了数据块的附加信息。

            sk_buff结构中并没有指向skb_shared_info结构的指针,可以用skb_info宏来访问skb_shared_info结构。


目录
相关文章
|
1月前
|
安全 Linux 网络安全
Linux使用HTTP隧道代理代码示例模版
Linux使用HTTP隧道代理代码示例模版
16 0
|
2月前
|
自然语言处理 数据挖掘 Linux
ModelScope问题之拷贝到内网linux系统运行代码报错如何解决
本合集将提供ModelScope安装步骤、配置要求和环境准备,以便用户顺利启动ModelScope进行模型开发和测试。
59 0
|
28天前
|
存储 Shell Linux
【Shell 命令集合 磁盘管理 】⭐⭐ Linux 显示当前shell会话中的目录栈 dirs命令使用教程
【Shell 命令集合 磁盘管理 】⭐⭐ Linux 显示当前shell会话中的目录栈 dirs命令使用教程
27 0
|
24天前
|
Linux 编译器 程序员
【Linux 调试秘籍】深入探索 C++:运行时获取堆栈信息和源代码行数的终极指南
【Linux 调试秘籍】深入探索 C++:运行时获取堆栈信息和源代码行数的终极指南
65 0
|
28天前
|
Shell Linux C语言
【Shell 命令集合 磁盘管理 】Linux 显示当前shell会话中的目录栈 dirs命令使用教程
【Shell 命令集合 磁盘管理 】Linux 显示当前shell会话中的目录栈 dirs命令使用教程
35 1
|
2月前
|
传感器 网络协议 物联网
在Linux中搭建Mosquitto MQTT协议消息服务端并结合内网穿透工具实现公网访问
Mosquitto是一个开源的消息代理,它实现了MQTT协议版本3.1和3.1.1。它可以在不同的平台上运行,包括Windows、Linux、macOS等。mosquitto可以用于物联网、传感器、移动应用程序等场景,提供了一种轻量级的、可靠的、基于发布/订阅模式的消息传递机制。
|
1月前
|
安全 Linux 开发者
⭐⭐⭐⭐⭐Linux C/C++ 进程崩溃诊断以及有效数据收集:解锁代码问题快速定位与修复的方法
⭐⭐⭐⭐⭐Linux C/C++ 进程崩溃诊断以及有效数据收集:解锁代码问题快速定位与修复的方法
77 1
|
11天前
|
网络协议 Linux SDN
虚拟网络设备与Linux网络协议栈
在现代计算环境中,虚拟网络设备在实现灵活的网络配置和隔离方面发挥了至关重要的作用🔧,特别是在容器化和虚拟化技术广泛应用的今天🌐。而Linux网络协议栈则是操作系统处理网络通信的核心💻,它支持广泛的协议和网络服务🌍,确保数据正确地在网络中传输。本文将深入分析虚拟网络设备与Linux网络协议栈的关联,揭示它们如何共同工作以支持复杂的网络需求。
|
1月前
|
消息中间件 并行计算 网络协议
探秘高效Linux C/C++项目架构:让进程、线程和通信方式助力你的代码飞跃
探秘高效Linux C/C++项目架构:让进程、线程和通信方式助力你的代码飞跃
33 0
|
1月前
|
Linux 网络安全
Linux NFS协议详解
Linux NFS协议详解
29 5