网络数据结构skb_buff原理

skb_buff基本原理

内核中sk_buff结构体在各层协议之间传输不是用拷贝sk_buff结构体,而是通过增加协议头和移动指针来操作的。如果是从L4传输到L2,则是通过往sk_buff结构体中增加该层协议头来操作;如果是从L4到L2,则是通过移动sk_buff结构体中的data指针来实现,不会删除各层协议头,这样方式极大的提高CPU工作效率。

sk_buff结构体是linux网络代码中最重要的数据结构,是整个网络传输载体。所以sk_buff结构体里面有很多关于其他功能的成员字段,比如:防火墙,子路由系统,多播等。这些字段并不是一定有的,只有在满足特点条件才有的。所以可以在需要时候再去关心这些成员字段,现在我们只来讲解主要的成员字段

skb_buff主要字段

为了好理解结构中的一些成员字段,先把后面要讲的内容提前说下。sk_buff结构体关联多个其他结构体,主要可以分为: 第一是数据区:由sk_buff中head和end指向的数据块,用来存储sk_buff结构的数据也即是存储数据包的内容和各层协议头。 第二是分片结构:用来表示IP分片的一个结构体,实则上是和sk_buff结构的数据区相连的,即是end指针的下一个字节开始就是分片结构。正因此,分片结构和sk_buff数据区内存分配及销毁时都是一起的。 第三个是分片结构指向的数据区,即是IP分片内容。

struct sk_buff {

/* These two members must be first. */

struct sk_buff  *next;  //  因为sk_buff结构体是双链表,所以有前驱后继。这是个指向后面的sk_buff结构体指针

struct sk_buff  *prev;  //  这是指向前一个sk_buff结构体指针

//老版本(2.6以前)应该还有个字段: sk_buff_head *list  //即每个sk_buff结构都有个指针指向头节点

struct sock *sk;  // 指向拥有此缓冲的套接字sock结构体,即:宿主传输控制模块

ktime_t tstamp;  // 时间戳,表示这个skb的接收到的时间,一般是在包从驱动中往二层发送的接口函数中设置

struct net_device *dev;  // 表示一个网络设备,当skb为输出/输入时,dev表示要输出/输入到的设备

unsigned long  skb_dst;  // 主要用于路由子系统,保存路由有关的东西

char cb[48];  // 保存每层的控制信息,每一层的私有信息

unsigned int len,  // 表示数据区的长度(tail - data)与分片结构体数据区的长度之和。其实这个len中数据区长度是个有效长度,因为不删除协议头,所以只计算有效协议头和包内容。如:当在L3时,不会计算L2的协议头长度。

data_len;  // 只表示分片结构体数据区的长度,所以len = (tail - data) + data_len;

__u16 mac_len,  // mac报头的长度

hdr_len;  // 用于clone时,表示clone的skb的头长度

// 接下来是校验相关域,这里就不详细讲了。

__u32 priority;  // 优先级,主要用于QOS

kmemcheck_bitfield_begin(flags1);

__u8 local_df:1,  // 是否可以本地切片的标志

cloned:1,  // 为1表示该结构被克隆,或者自己是个克隆的结构体;同理被克隆时,自身skb和克隆skb的cloned都要置1

ip_summed:2,

nohdr:1,  // nohdr标识payload是否被单独引用,不存在协议首部。                                                                                                      // 如果被引用,则决不能再修改协议首部,也不能通过skb->data来访问协议首部。</span></span>

nfctinfo:3;

__u8 pkt_type:3,  // 标记帧的类型

fclone:2,   // 这个成员字段是克隆时使用,表示克隆状态

ipvs_property:1,

peeked:1,

nf_trace:1;

__be16 protocol:16;  // 这是包的协议类型,标识是IP包还是ARP包或者其他数据包。

kmemcheck_bitfield_end(flags1);

void (*destructor)(struct sk_buff *skb);  // 这是析构函数,后期在skb内存销毁时会用到

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

struct nf_conntrack *nfct;

struct sk_buff *nfct_reasm;

#endif

#ifdef CONFIG_BRIDGE_NETFILTER

struct nf_bridge_info *nf_bridge;

#endif

int iif;  // 接受设备的index

#ifdef CONFIG_NET_SCHED

__u16 tc_index; /* traffic control index */

#ifdef CONFIG_NET_CLS_ACT

__u16 tc_verd; /* traffic control verdict */

#endif

#endif

kmemcheck_bitfield_begin(flags2);

__u16 queue_mapping:16;

#ifdef CONFIG_IPV6_NDISC_NODETYPE

__u8 ndisc_nodetype:2;

#endif

kmemcheck_bitfield_end(flags2);

/* 0/14 bit hole */

#ifdef CONFIG_NET_DMA

dma_cookie_t dma_cookie;

#endif

#ifdef CONFIG_NETWORK_SECMARK

__u32 secmark;

#endif

__u32 mark;

__u16 vlan_tci;

sk_buff_data_t transport_header;      // 指向四层帧头结构体指针

sk_buff_data_t network_header;        // 指向三层IP头结构体指针

sk_buff_data_t mac_header;        // 指向二层mac头的头

/* 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;   // 指向数据区中实际数据开始的位置

unsigned int truesize;   // 表示总长度,包括sk_buff自身长度和数据区以及分片结构体的数据区长度

atomic_t users;           // skb被克隆引用的次数,在内存申请和克隆时会用到

};   //end sk_buff

char cb[48];这个字段是skb信息控制块,也就是存储每层的一些协议信息,当数据包在哪一层时,存储的就是哪一层协议信息。这个字段由数据包所在层使用和维护,如果要访问本层协议信息,可以通过用一些宏来操作这个成员字段。如:#define TCP_SKB_CB(__skb) ((struct tcp_skb_cb *)&((__skb)->cb[0]))

_u8 fclone:2;这是个克隆状态标志,到sk_buff结构内存申请时会使用到。这里提前讲下:若fclone = SKB_FCLONE_UNAVAILABLE,则表明SKB未被克隆;若fclone = SKB_FCLONE_ORIG,则表明是从skbuff_fclone_cache缓存池(这个缓存池上分配内存时,每次都分配一对skb内存)中分配的父skb,可以被克隆;若fclone = SKB_FCLONE_CLONE,则表明是在skbuff_fclone_cache分配的子SKB,从父SKB克隆得到的;

atomic_t users;这是个引用计数,表明了有多少实体引用了这个skb。其作用就是在销毁skb结构体时,先查看下users是否为零,若不为零,则调用函数递减下引用计数users即可;当某一次销毁时,users为零才真正释放内存空间。有两个操作函数:atomic_inc()引用计数增加1;atomic_dec()引用计数减去1;

sk_buff->len:表示当前缓冲区中数据块的大小的总长度。它包括主缓冲中(即是sk_buff结构中指针data指向)的数据区的实际长度(data-tail)和分片中的数据长度。这个长度在数据包在各层间传输时会改变,因为分片数据长度不变,从L2到L4时,则len要减去帧头大小和网络头大小;从L4到L2则相反,要加上帧头和网络头大小。所以:len = (data - tail) + data_len;

sk_buff->truesize:这是缓冲区的总长度,包括sk_buff结构和数据部分。如果申请一个len字节的缓冲区,alloc_skb函数会把它初始化成len+sizeof(sk_buff)。当skb->len变化时,这个变量也会变化。所以:truesize = len + sizeof(sk_buff) = (data - tail) + data_len + sizeof(sk_buff);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/168430.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Kafka(五)消费者回调 +定时重试 + 理解Rebalance

文章目录 消费者回调如何抽象callBack消息&#xff1f;为什么要设置serverId?如何消费callBack消息&#xff1f; 定时重试消息失败表的设计重试逻辑设计 理解Rabalance通过日志来理解rebalance 参考资料结语示例源码仓库 消费者回调 有些邮件发送成功之后&#xff0c;需要执行…

【Linux】fork()

文章目录 一、fork()是什么&#xff1f;二、fork()干了什么&#xff1f;三、fork()怎么用&#xff1f; 一、fork()是什么&#xff1f; fork()函数其实是在Linux系统中用于创建一个新的进程。让我们看看Linux中是怎么描述的&#xff1f;运行man fork。 RETURN VALUE On success…

php站点伪静态配置(Apache+Linux)

404报错&#xff1a; 404 Not Found nginx/1.15.11 问题解决&#xff1a; 1、Linux location / { if (!-e $request_filename) { rewrite ^(.*)$ /index.php?s/$1 last; } } 2、Apache <IfModule mod_rewrite.c> RewriteEngine on RewriteBase / RewriteCond %{REQU…

英特尔和 ARM 将合作开发移动芯片技术,如何看待双方合作?

英特尔和 ARM 将合作开发移动芯片技术&#xff0c;如何看待双方合作&#xff1f; 最近市场传出Arm要自产芯片&#xff0c;供智能手机与笔电等使用后&#xff0c;外媒指Arm自产芯片将由英特尔晶圆代工部门打造&#xff0c;变成英特尔晶圆代工客户。将采用英特尔18A工艺&#xff…

利用Nginx与php处理方式不同绕过Nginx_host实现SQL注入

目录 首先需要搭建环境 nginxphpmysql环境&#xff1a; 搭建网站 FILTER_VALIDATE_EMAIL 绕过 方法1&#xff1a;冒号号分割host字段 方法2&#xff1a;冒号号分割host字段 方法3&#xff1a;SNI扩展绕过 首先需要搭建环境 nginxphpmysql环境&#xff1a; php安装包&a…

深入了解Spring Cloud中的分布式事务解决方案

引言 介绍分布式系统中事务管理的重要性&#xff0c;以及在云计算环境下分布式事务所面临的挑战。 传统事务和分布式事务 解释本地事务与分布式事务的区别&#xff0c;以及为什么在分布式环境中需要特殊的事务管理机制。 分布式事务的挑战 探讨在分布式系统中实现事务一致性所…

vite和webpack的区别和练习

Vite和Webpack都是现代化的前端构建工具&#xff0c;但它们之间存在一些区别&#xff1a; 构建性能&#xff1a;Vite使用ES Modules提高了构建性能&#xff0c;可以在构建时只构建需要的部分&#xff0c;而Webpack则需要在构建时处理整个应用程序。 开发体验&#xff1a;Vite具…

vue一个页面左边是el-table表格 当点击每条数据时可以在右边界面编辑表格参数,右边保存更新左边表格数据

实现思路&#xff1a; 1.点击当前行通过row拿到当前行数据。 2.将当前行数据传给子组件。 3.子组件监听父组件传过来的数据并映射在界面。 4.点击保存将修改的值传给父组件更新表格。 5.父组件收到修改过后的值&#xff0c;可以通过字段判断比如id&#xff0c;通过 findIn…

VR Interaction Framework2.0使用

1 按键 &#xff0c;比如按压下手柄的B键 if (InputBridge.Instance.BButtonDown){print("kkkkkkbbbbb456");} 2抓取某个物体&#xff0c;那么就在要抓取的那个物体上加一些组件&#xff0c;特别是Grabble Unity Events

cocos2dx DrawNode

cocos2dx 两种绘图方式 DrawPrimitivesDrawNode DrawPrimitives 3.x 已经弃用 绘制的图形可以是实心的&#xff0c;也可以是空心的。 DrawNode 在一个单独的批处理中绘制了所以元素&#xff0c;因此它绘制点、线段、多边形都要比“drawing primitives”快。 绘制的图形都…

【基础知识】AB软件RSLinx如何实现OPC通讯组态

哈喽&#xff0c;大家好&#xff0c;我是雷工。 在上一节了解了什么是RSLinx&#xff1f;以及RSLinx Lite、RSLinx Classice、RSLinx Professional、RSLinx Gateway几个版本的特点。 本节了解AB的RSLinx如何实现OPC组态。 一、创建RSLinx通讯&#xff1a; 1.1、【Communicati…

excel自己记录

1、清除换行符号 2、添加特殊符号&并清除换行符号 7日&15日&30日&60日 3、判断单元格最后一个字符是不是数字&#xff0c;不是就删掉 IF(ISNUMBER(--RIGHT(B2,1)),B2,SUBSTITUTE(B2,RIGHT(B2,1),"")) ISNUMBER(--RIGHT(B2,1))判断最右边的一个数是否…

Wireshark的捕获过滤器

Wireshark的过滤器&#xff0c;顾名思义&#xff0c;作用是对数据包进行过滤处理。具体过滤器包括捕获过滤器和显示过滤器。本文对捕获过滤器进行分析。 捕获过滤器&#xff1a;当进行数据包捕获时&#xff0c;只有那些满足给定的包含/排除表达式的数据包会被捕获。 捕获过滤器…

一起学docker系列之九docker运行mysql 碰到的各种坑及解决方法

目录 前言1 Docker 运行mysql命令2 坑一&#xff1a;无法读取/etc/mysql/conf.d目录的问题3 坑二&#xff1a;/tmp/ibnr0mis 文件无法创建/写入的问题4 坑三&#xff1a;Navicat 连接错误&#xff08;1045-access denied&#xff09;5 坑四&#xff1a;MySQL 登录失败问题结语 …

ros2文件package.xml与cmakelists.txt比较

每次在ros2里面添加文件以后&#xff0c;都要修改packages.xml,与cmakelists.txt文件。

Vue服务端渲染——同构渲染

Vue.js 可以用于构建客户端应用程序&#xff0c;组件的代码在浏览器中运行&#xff0c;并输出 DOM 元素。同时&#xff0c;Vue.js 还可以在 Node.js 环境中运行&#xff0c;它可以将同样的组件渲染为字符串并发送给浏览器。这实际上描述了 Vue.js 的两种渲染方式&#xff0c;即…

爬虫项目实战:利用基于selenium框架的爬虫模板爬取豆瓣电影Top250

&#x1f44b; Hi, I’m 货又星&#x1f440; I’m interested in …&#x1f331; I’m currently learning …&#x1f49e; I’m looking to collaborate on …&#x1f4eb; How to reach me … README 目录&#xff08;持续更新中&#xff09; 各种错误处理、爬虫实战及模…

Lua实现面向对象三大特性

面向对象是基于table实现的 封装 :(冒号) 自动将调用该函数的对象作为第一个参数传入 --Object就是第一参数 function Object:new() self&#xff1a;代表默认传入的第一个参数 _index&#xff1a;当自己的变量中找不到时&#xff0c;会默认找原表中_index指向的内容 Obj…

AD9361快速开发指南

AD9361是ADI&#xff08;Analog Devices&#xff09;公司推出的一款全集成的RF收发器芯片&#xff0c;广泛应用于无线通信系统&#xff0c;包括基于FPGA和ARM处理器的数码电视&#xff0c;卫星通信&#xff0c;雷达通信&#xff0c;军事通信和工业控制等领域。AD9361提供了广泛…

Qt问题 QString 和 void* 相互转化

QString转为void*格式 //将路径QString转为void*格式QByteArray byteArray qstrFilePath.toUtf8();char* charArray byteArray.data();void* voidPath static_cast<void*>(charArray);void*转为QString格式 char* charPath static_cast<char*>(voidPath); QS…