当前位置: 首页 > news >正文

sk_buff操作函数学习

一. 前言

        内核提供了大量实用的操作sk_buff的函数,在开发网络设备驱动程序和修改网络协议栈代码时需要用到。这些函数从功能上可以分为三类:创建,释放和复制socket buffer;操作sk_buff结构中的参数和指针;管理socket buffer的队列。本文主要介绍其中一些典型的函数。本文使用的内核版本是3.18.45。

二. 创建和释放socket buffer

1. alloc_skb

        alloc_skb是__alloc_skb函数的封装,是为socket buffer分配内存的主要函数。其中具体的实现不再分析了,主要介绍调用alloc_skb函数后,sk_buff各个参数和指针的情况。其中,skb->head,skb->data和skb->tail都指向分配内存的开始地址,skb->end指向分配内存的结束地址,分配的数据大小是4字节对齐的。如下图所示。

        注意:调用alloc_skb分配的skb,由于实际数据长度还不确定,其中skb->len = 0。

2. kree_skb

        kree_skb函数是__kfree_skb函数的封装,由于同一个socket buffer可能被多个地方使用,所以kfree_skb只有在skb->users = 1时,才会完全释放socket buffer,其他情况只会将skb->users减1。

三. 操作sk_buff参数和指针的函数

1. skb_reserve

skb_reserve的函数原型:

static inline void skb_reserve(struct sk_buff *skb, int len)
{skb->data += len;skb->tail += len;
}

        skb_reserve函数的使用是在alloc_skb函数之后,作用是为网络协议栈的二层,三层以及数据负载留出空间。如下图所示:

         上图中,留出的reserve len就是为协议栈的其他数据压入预留空间,从中也可以看出,skb->data到skb->tail之间保存的是真实负载数据的内容。

2. skb_put

skb_put的函数原型:

unsigned char *skb_put(struct sk_buff *skb, unsigned int len)
{unsigned char *tmp = skb_tail_pointer(skb);SKB_LINEAR_ASSERT(skb);skb->tail += len;skb->len  += len;if (unlikely(skb->tail > skb->end))skb_over_panic(skb, len, __builtin_return_address(0));return tmp;
}

        从函数实现可知,skb_put函数将skb->tail加了len,表示将skb的实际负载数据的长度加len,要实现这一点,alloc_skb分配的len减去skb_reserve的len应该要大于等于skb_put的len,这样才能保证skb_put函数运行没问题。示意图如下:

3. skb_push

skb_push的函数原型:

unsigned char *skb_push(struct sk_buff *skb, unsigned int len)
{skb->data -= len;skb->len  += len;if (unlikely(skb->data<skb->head))skb_under_panic(skb, len, __builtin_return_address(0));return skb->data;
}

        从函数实现可以看出,skb_push将data指针往低地址移动len长度,同时skb->len长度加len,然后程序将向该地址压入长度为len长度的数据。示意图如下:

4. skb_pull

skb_pull的函数原型:

static inline unsigned char *__skb_pull(struct sk_buff *skb, unsigned int len)
{skb->len -= len;BUG_ON(skb->len < skb->data_len);return skb->data += len;
}static inline unsigned char *skb_pull_inline(struct sk_buff *skb, unsigned int len)
{return unlikely(len > skb->len) ? NULL : __skb_pull(skb, len);
}unsigned char *skb_pull(struct sk_buff *skb, unsigned int len)
{return skb_pull_inline(skb, len);
}

        从函数实现可以看出,skb_pull是__skb_pull函数的封装,skb_pull_inline判断len要小于skb->len。skb_pull函数就是skb_push的反向操作函数,作用是将skb->data指针向高地址移动len,同时,skb->len减len,相当于从skb中删除了长度为len的数据。

5. skb_trim

skb_trim函数的原型:

static inline void skb_set_tail_pointer(struct sk_buff *skb, const int offset)
{skb->tail = skb->data + offset;
}static inline void __skb_trim(struct sk_buff *skb, unsigned int len)
{if (unlikely(skb_is_nonlinear(skb))) {WARN_ON(1);return;}skb->len = len;skb_set_tail_pointer(skb, len);
}void skb_trim(struct sk_buff *skb, unsigned int len)
{if (skb->len > len)__skb_trim(skb, len);
}

        从函数实现可以看出,skb_trim是__skb_trim的封装,作用是将skb的负载数据长度修剪为len,并修剪长度是通过修改skb->tail的指针来实现的。示意图如下:

四. 管理socket buffer的队列

1. skb_queue_head_init

skb_queue_head_init函数的原型:

static inline void __skb_queue_head_init(struct sk_buff_head *list)
{list->prev = list->next = (struct sk_buff *)list;list->qlen = 0;
}static inline void skb_queue_head_init(struct sk_buff_head *list)
{spin_lock_init(&list->lock);__skb_queue_head_init(list);
}

        从函数实现可以看出,skb_queue_head_init是__skb_queue_head_init函数的封装,作用是初始化链表的指针以及链表的队列长度。

2. skb_dequeue

skb_dequeue的函数原型:

static inline struct sk_buff *__skb_dequeue(struct sk_buff_head *list)
{struct sk_buff *skb = skb_peek(list);if (skb)__skb_unlink(skb, list);return skb;
}struct sk_buff *skb_dequeue(struct sk_buff_head *list)
{unsigned long flags;struct sk_buff *result;spin_lock_irqsave(&list->lock, flags);result = __skb_dequeue(list);spin_unlock_irqrestore(&list->lock, flags);return result;
}

        函数的作用是从链表的头部取出一个skb,并且将skb从链表中删除。

3. skb_dequeue_tail

skb_dequeue_tail的函数原型:

static inline struct sk_buff *__skb_dequeue_tail(struct sk_buff_head *list)
{struct sk_buff *skb = skb_peek_tail(list);if (skb)__skb_unlink(skb, list);return skb;
}struct sk_buff *skb_dequeue_tail(struct sk_buff_head *list)
{unsigned long flags;struct sk_buff *result;spin_lock_irqsave(&list->lock, flags);result = __skb_dequeue_tail(list);spin_unlock_irqrestore(&list->lock, flags);return result;
}

        函数的作用是从链表的尾部取出一个skb,并且将skb从链表中删除。

4. skb_append

skb_append的函数原型:

static inline void __skb_insert(struct sk_buff *newsk,struct sk_buff *prev, struct sk_buff *next,struct sk_buff_head *list)
{newsk->next = next;newsk->prev = prev;next->prev  = prev->next = newsk;list->qlen++;
}static inline void __skb_queue_after(struct sk_buff_head *list,struct sk_buff *prev,struct sk_buff *newsk)
{__skb_insert(newsk, prev, prev->next, list);
}void skb_append(struct sk_buff *old, struct sk_buff *newsk, struct sk_buff_head *list)
{unsigned long flags;spin_lock_irqsave(&list->lock, flags);__skb_queue_after(list, old, newsk);spin_unlock_irqrestore(&list->lock, flags);
}

        函数的作用是在链表中的old节点后面插入newsk的skb,链表的队列长度也加1。

五. 实例

#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/skbuff.h>
#include <linux/udp.h>
#include <linux/ip.h>#define UDP_SRC_PORT	3015
#define UDP_DST_PORT	3016char data[32] = "chaos is a ladder!";
char src_ip[] = "192.168.0.56";
char dst_ip[] = "192.168.0.105";
char src_mac[] = {0x00, 0x0C, 0x43, 0x28, 0x80, 0xF5};
char dst_mac[] = {0x3C, 0x7C, 0x3F, 0xD7, 0x94, 0xAF};struct sk_buff *skb;
struct udphdr *uhdr;
struct iphdr *ihdr;
struct ethhdr *ehdr;
struct net_device * dev = NULL;void assemble_udp_header(struct udphdr *udp_hdr)
{udp_hdr->source = htons(UDP_SRC_PORT);udp_hdr->dest = htons(UDP_DST_PORT);udp_hdr->len = htons(sizeof(data) + sizeof(struct udphdr));udp_hdr->check = ~csum_tcpudp_magic(ihdr->saddr, ihdr->daddr, skb->len, IPPROTO_UDP, 0);
}void assemble_ip_header(struct iphdr *ip_hdr)
{ip_hdr->version = IPVERSION;ip_hdr->ihl = sizeof(struct iphdr)/4;ip_hdr->tos =  0;ip_hdr->tot_len = htons(sizeof(data) + sizeof(struct udphdr) + sizeof(struct iphdr));ip_hdr->id = htons(42351);ip_hdr->frag_off = 0;ip_hdr->ttl = 64;ip_hdr->protocol = IPPROTO_UDP;ip_hdr->check = 0;ip_hdr->saddr = in_aton(src_ip);ip_hdr->daddr = in_aton(dst_ip);
}void assemble_eth_header(struct ethhdr *eth_hdr)
{memcpy(eth_hdr->h_dest, dst_mac, 6);memcpy(eth_hdr->h_source, src_mac, 6) ;eth_hdr->h_proto = htons(ETH_P_IP);
}int __init skb_package_init(void)
{printk("skb init\n");unsigned char *p;skb = alloc_skb(MAX_SKB_SIZE, GFP_ATOMIC);dev = dev_get_by_name(&init_net, "br0");skb->dev = dev;skb_reserve(skb, 2 + sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(data));p = skb_push(skb, sizeof(data));memcpy(p, (void*)data, sizeof(data));p = skb_push(skb, sizeof(struct udphdr));uhdr = (struct udphdr *)p;skb_reset_transport_header(skb);p = skb_push(skb, sizeof(struct iphdr));ihdr = (struct iphdr *)p;skb_reset_network_header(skb);p = skb_push(skb, sizeof(struct ethhdr));ehdr = (struct ethhdr *)p;skb_reset_mac_header(skb);	assemble_ip_header(ihdr);assemble_udp_header(uhdr);assemble_eth_header(ehdr);dev_queue_xmit(skb);	return 0;
}void __exit skb_package_exit(void)
{kfree_skb(skb);printk("skb exit\n");
}module_init(skb_package_init);
module_exit(skb_package_exit);
MODULE_LICENSE("GPL");

        代码的功能是组一个UDP包并且通过网口br0发送出去。其中涉及到了skb的分配,预留数据包的空间,将协议头和实际负载数据放入到skb->data中,最后调用dev_queue_xmit将包发送出去的过程。

六. 总结

        本文总结了socket buffer的一些常用的操作函数,并介绍了它们的作用,其中包括skb分配,以及skb的指针的操作和skb的队列的操作,这些函数是编写网络设备驱动程序的基础,应该熟练掌握。

相关文章:

sk_buff操作函数学习

一. 前言 内核提供了大量实用的操作sk_buff的函数&#xff0c;在开发网络设备驱动程序和修改网络协议栈代码时需要用到。这些函数从功能上可以分为三类&#xff1a;创建&#xff0c;释放和复制socket buffer&#xff1b;操作sk_buff结构中的参数和指针&#xff1b;管理socket b…...

conda create时候出现JSONDecoderError解决方法

起因是我的conda出现了JSONDecoderError&#xff0c;这个我搜了一下是因为某些配置文件错误&#xff0c;所以让我update conda&#xff0c;于是我先用了下面的指令&#xff1a; conda update conda 但是在执行过程中依然会出现 JSONDecoderError的问题&#xff0c;后来参考了这…...

Electron 工具进程utilityProcess 使用中遇到的坑点汇集

简介 这是基于 node.js 中的子进程的概念推出来的&#xff0c;可参考链接&#xff1a;utilityProcess | Electron 官网有一句话非常重要&#xff0c;它提供一个相当于 Node.js 的 child_process.fork API&#xff0c;但使用 Chromium 的 Services API 代替来执行子进程。这句话…...

JdbcTemplate

目录 1、简介 2、开发步骤 2.1、导入坐标 2.2、创建表和类 2.3、创建JdbcTemplate对象 2.4、执行数据库操作 3、解耦 4、增删改查 ⭐作者介绍&#xff1a;大二本科网络工程专业在读&#xff0c;持续学习Java&#xff0c;努力输出优质文章 ⭐作者主页&#xff1a;逐梦苍穹…...

PROFINET转TCP/IP网关profinet网线接头接法

大家好&#xff0c;今天要和大家分享一款自主研发的通讯网关&#xff0c;捷米JM-PN-TCPIP。这款网关可是集多种功能于一身&#xff0c;PROFINET从站功能&#xff0c;让它在通讯领域独领风骚。想知道这款网关如何实现PROFINET和TCP/IP网络的连接吗&#xff1f;一起来看看吧&…...

【FPGA IP系列】FIFO的通俗理解

FPGA厂商提供了丰富的IP核&#xff0c;基础性IP核都是可以直接免费调用的&#xff0c;比如FIFO、RAM等等。 本文主要介绍FIFO的一些基础知识&#xff0c;帮助大家能够理解FIFO的基础概念。 一、FIFO介绍 FIFO全称是First In First Out&#xff0c;即先进先出。 FIFO是一个数…...

Kylin v10基于cephadm工具离线部署ceph分布式存储

1. 环境&#xff1a; ceph&#xff1a;octopus OS&#xff1a;Kylin-Server-V10_U1-Release-Build02-20210824-GFB-x86_64、CentOS Linux release 7.9.2009 2. ceph和cephadm 2.1 ceph简介 Ceph可用于向云平台提供对象存储、块设备服务和文件系统。所有Ceph存储集群部署都从…...

框架的前置学习-反射

运行java代码要经历的三个阶段 反射&#xff0c;程序框架设计的灵魂&#xff0c;将类的各个组成部分封装成其他对象&#xff0c;这就是反射机制。 框架&#xff1a;半成品的软件&#xff0c;在框架的基础上进行开发&#xff0c;可以简化编码 反射的好处&#xff1a; 可以在…...

【使用bat脚本实现批量创建文件夹、批量复制文件至对应文件夹中】

使用bat脚本实现批量创建文件夹、批量复制文件至对应文件夹中 常用cmd命令 场景一&#xff1a;在指定位置批量创建文件夹 在桌面创建一个txt文件编写创建目录代码 //在桌面"五保户结算单"的文件夹下创建名称为"1张三"的文件夹 md E:\桌面\五保户结算单\…...

面向视频会议场景的 H.266/VVC 码率控制算法研究

文章目录 面向视频会议场景的 H.266/VVC 码率控制算法研究个人总结摘要为什么要码率控制码率控制的关键会议类视频码率控制研究背景视频会议系统研究现状目前基于 R-λ模型的码率控制算法的问题文章主要两大优化算法优化算法1&#xff1a;基于视频内容相关特征值的码率控制算法…...

【网络基础实战之路】设计网络划分的实战详解

系列文章传送门&#xff1a; 【网络基础实战之路】设计网络划分的实战详解 【网络基础实战之路】一文弄懂TCP的三次握手与四次断开 【网络基础实战之路】基于MGRE多点协议的实战详解 【网络基础实战之路】基于OSPF协议建立两个MGRE网络的实验详解 PS&#xff1a;本要求基于…...

MacBook触控板窗口管理 Swish for Mac

Swish for Mac是一款用于通过手势来控制mac应用窗口的软件&#xff0c;你可以通过这款软件在触控板上进行手势控制&#xff0c;你可以在使用前预设好不同手势的功能&#xff0c;然后就能直接通过这些手势让窗口按照你想要的方式进行变动了 Swish 支持 Haptick Feedback 震动反…...

VS开发Qt程序,无法打印QDebug调试信息,VS进行Qt开发时Qt Designer无法使用“转到槽”选项

VS开发Qt程序&#xff0c;无法打印QDebug调试信息&#xff0c;VS进行Qt开发时Qt Designer无法使用“转到槽”选项 VS开发Qt程序&#xff0c;无法打印QDebug调试信息VS进行Qt开发时Qt Designer无法使用“转到槽”选项 VS开发Qt程序&#xff0c;无法打印QDebug调试信息 解决方案…...

MySQL操作命令详解:增删改查

文章目录 一、CRUD1.1 数据库操作1.2 表操作1.2.1 五大约束1.2.2 创建表1.2.3 修改表1.2.3 删除表1.2.4 表数据的增删改查1.2.5 去重方式 二、高级查询2.1 基础查询2.2 条件查询2.3 范围查询2.4 判空查询2.5 模糊查询2.6 分页查询2.7 查询后排序2.8 聚合查询2.9 分组查询2.10 联…...

MySQL字段类型与存储空间的关系

在 MySQL 中&#xff0c;对于整数类型&#xff08;如 INT&#xff09;、字符类型&#xff08;如 VARCHAR&#xff09;、浮点数类型&#xff08;如 DOUBLE&#xff09;等&#xff0c;参数&#xff08;括号中的数字或长度&#xff09;通常用于限制数据的范围或精度&#xff0c;但…...

红船元宇宙 上海布袋除尘器后一家太平洋百货月底停业

上海布袋除尘器后一家太平洋百货即将停业。 7月31日&#xff0c;上海太平洋百货微信公号发布公告称&#xff0c;由于与合资方的合作期限今年届满&#xff0c;上海太平洋百货徐汇店将于2023年8月31日营业结束后正式谢幕&#xff0c;终止经营&#xff0c;并于即日起开展大型主题感…...

vue 图片回显标签

第一种 <el-form-item label"打款银行回单"><image-preview :src"form.bankreceiptUrl" :width"120" :height"120"/></el-form-item>// 值为 https://t11.baidu.com/it/app106&fJPEG&fm30&fmtauto&…...

《向量数据库指南》——使用SQuAD 数据集演示Faiss 功能

使用 SQuAD 数据集进行演示 现在,我们可以通过示例演示了解 Faiss 功能。本次示例中,将使用斯坦福的问答数据集(SQuAD)。SQuAD 是一个常用的自然语言处理(NLP)数据集,该数据集基于用户在百科中提出的问题,每个问题的答案都来自于对应阅读段落的一段文本,共计 500 多…...

java多线程并发面试题总结(史上最全40道)

1、多线程有什么用&#xff1f; 一个可能在很多人看来很扯淡的一个问题&#xff1a;我会用多线程就好了&#xff0c;还管它有什么用&#xff1f;在我看来&#xff0c;这个回答更扯淡。所谓"知其然知其所以然"&#xff0c;"会用"只是"知其然"&am…...

IDEA强大的VisualGC插件

前言 开发阶段实时监测&#xff0c;自己的JVM信息&#xff0c;实时可视化 Hotspot JVM 垃圾回收监控工具, 支持查看本地和远程JVM进程, 支持G1 and ZGC算法。 插件安装 在线安装 IntelliJ IDEA 可通过在线安装的方式&#xff0c;安装插件 JDK VisualGC&#xff0c;安装步骤: …...

生成xcframework

打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式&#xff0c;可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

java_网络服务相关_gateway_nacos_feign区别联系

1. spring-cloud-starter-gateway 作用&#xff1a;作为微服务架构的网关&#xff0c;统一入口&#xff0c;处理所有外部请求。 核心能力&#xff1a; 路由转发&#xff08;基于路径、服务名等&#xff09;过滤器&#xff08;鉴权、限流、日志、Header 处理&#xff09;支持负…...

三维GIS开发cesium智慧地铁教程(5)Cesium相机控制

一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点&#xff1a; 路径验证&#xff1a;确保相对路径.…...

React Native在HarmonyOS 5.0阅读类应用开发中的实践

一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强&#xff0c;React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 &#xff08;1&#xff09;使用React Native…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

ArcGIS Pro制作水平横向图例+多级标注

今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作&#xff1a;ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等&#xff08;ArcGIS出图图例8大技巧&#xff09;&#xff0c;那这次我们看看ArcGIS Pro如何更加快捷的操作。…...

JS设计模式(4):观察者模式

JS设计模式(4):观察者模式 一、引入 在开发中&#xff0c;我们经常会遇到这样的场景&#xff1a;一个对象的状态变化需要自动通知其他对象&#xff0c;比如&#xff1a; 电商平台中&#xff0c;商品库存变化时需要通知所有订阅该商品的用户&#xff1b;新闻网站中&#xff0…...

IP如何挑?2025年海外专线IP如何购买?

你花了时间和预算买了IP&#xff0c;结果IP质量不佳&#xff0c;项目效率低下不说&#xff0c;还可能带来莫名的网络问题&#xff0c;是不是太闹心了&#xff1f;尤其是在面对海外专线IP时&#xff0c;到底怎么才能买到适合自己的呢&#xff1f;所以&#xff0c;挑IP绝对是个技…...