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的队列的操作,这些函数是编写网络设备驱动程序的基础,应该熟练掌握。

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

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

相关文章

webpack基础知识四:说说webpack中常见的Plugin?解决了什么问题?

一、是什么 Plugin&#xff08;Plug-in&#xff09;是一种计算机应用程序&#xff0c;它和主应用程序互相交互&#xff0c;以提供特定的功能 是一种遵循一定规范的应用程序接口编写出来的程序&#xff0c;只能运行在程序规定的系统下&#xff0c;因为其需要调用原纯净系统提供…

【小沐学前端】VuePress制作在线电子书、技术文档(VuePress + Markdown + node)

文章目录 1、简介1.1 VuePress简介1.2 它是如何工作的&#xff1f; 2、安装node3、安装VuePress4、配置VuePress4.1 修改标题4.2 修改导航条4.3 修改右侧栏4.4 修改正文 结语 1、简介 Vue驱动的静态网站生成器&#xff0c;生成的网页内容放到自己服务器上管理&#xff0c;可用于…

74. 搜索二维矩阵

题目链接&#xff1a;力扣 解题思路&#xff1a;因为矩阵整体上是有序的&#xff0c;所以可以先二分查找target在哪一行中&#xff0c;然后再次二分查找target在当前行的哪一列中。 具体算法如下&#xff1a; 对行使用二分查找&#xff1a; 初始值&#xff1a; int m matrix…

MongoDB SQL

Microsoft Windows [版本 6.1.7601] 版权所有 (c) 2009 Microsoft Corporation。保留所有权利。C:\Users\Administrator>cd C:\MongoDB\Server\3.4\binC:\MongoDB\Server\3.4\bin> C:\MongoDB\Server\3.4\bin> C:\MongoDB\Server\3.4\bin>net start MongoDB 请求的…

使用可视化docker浏览器,轻松实现分布式web自动化

01、前言 顺着docker的发展&#xff0c;很多测试的同学也已经在测试工作上使用docker作为环境基础去进行一些自动化测试&#xff0c;这篇文章主要讲述我们在docker中使用浏览器进行自动化测试如果可以实现可视化&#xff0c;同时可以对浏览器进行相关的操作。 02、开篇 首先…

【0805作业】Linux中 AB终端通过两根有名管道进行通信聊天(半双工)(全双工)

作业一&#xff1a;打开两个终端&#xff0c;要求实现AB进程对话【两根管道】 打开两个终端&#xff0c;要求实现AB进程对话 A进程先发送一句话给B进程&#xff0c;B进程接收后打印B进程再回复一句话给A进程&#xff0c;A进程接收后打印重复1.2步骤&#xff0c;当收到quit后&am…

【react】react中BrowserRouter和HashRouter的区别:

文章目录 1.底层原理不一样:2.path衣现形式不一样3.刷新后对路山state参数的影响4.备注: HashRouter可以用于解决一些路径错误相关的问题 1.底层原理不一样: BrowserRouter使用的是H5的history API&#xff0c;不兼容IE9及以下版不。 HashRouter使用的是URL的哈希值。 2.path衣…

MongoDB文档--基本安装-linux安装(mongodb环境搭建)-docker安装(挂载数据卷)-以及详细版本对比

阿丹&#xff1a; 前面了解了mongodb的一些基本概念。本节文章对安装mongodb进行讲解以及汇总。 官网教程如下&#xff1a; 安装 MongoDB - MongoDB-CN-Manual 版本特性 下面是各个版本的选择请在安装以及选择版本的时候参考一下&#xff1a; MongoDB 2.x 版本&#xff1a…

外贸企业选择CRM的三大特点

外贸营销管理CRM云平台可以帮助外贸企业实现更高质量的营销管理和客户管理。无论是销售、市场营销或客户服务团队的成员&#xff0c;CRM都可以帮助企业更好地理解客户需求&#xff0c;并提供更好的服务。 1.便捷轻量级 云平台的一大优势是用户可以随时随地访问数据&#xff0…

Linux进程概念(一)

文章目录 Linux进程概念查看进程杀死进程进程标识符 手动创建进程的方式fork函数创建进程 进程状态运行态阻塞态和挂起 Linux进程概念 前文我们了解了&#xff0c;进程的基本概念&#xff0c;在课本上被描述为&#xff0c;正在执行的程序&#xff0c;在linux内核上&#xff0c…

管理类联考——写作——论说文——实战篇——行文篇——通用性强,解释多种现象的经典理论——谈必要

前言 本节内容涉及“社会分工理论”“资源稀缺性”“瓶颈理论”等理论。这些理论一般用在“利大于弊式结构”中“整体有必要”的部分&#xff0c;也可用于“AB二元类”题目“谈好处”的部分。 需要注意的是&#xff0c;“有好处”一般指有它更好&#xff1b;“有必要”一般指没…

echarts 饼图的label放置于labelLine引导线上方

一般的饼图基础配置后长这样。 想要实现将文本放置在引导线上方&#xff0c;效果长这样 const options {// ...series: [{label: {padding: [0, -40],},labelLine: {length: 10,length2: 50,},labelLayout: {verticalAlign: "bottom",dy: -10,},},], };label.padd…

Airtest自动化测试工具

一开始知道Airtest大概是在年初的时候&#xff0c;当时&#xff0c;看了一下官方的文档&#xff0c;大概是类似Sikuli的一个工具&#xff0c;主要用来做游戏自动化的&#xff0c;通过截图的方式用来解决游戏自动化测试的难题。最近&#xff0c;移动端测试的同事尝试用它的poco库…

Flink读取mysql数据库(java)

代码如下: package com.weilanaoli.ruge.vlink.flink;import com.ververica.cdc.connectors.mysql.source.MySqlSource; import com.ververica.cdc.connectors.mysql.table.StartupOptions; import com.ververica.cdc.debezium.JsonDebeziumDeserializationSchema; import org…

uniapp返回

// 监听返回事件onNavigationBarButtonTap() {uni.showModal({title: 提示,content: 确定要返回吗&#xff1f;,success: (res) > {if (res.confirm) {uni.navigateBack({delta: 2})}}})},

prometheus+grafana进行服务器资源监控

在性能测试中&#xff0c;服务器资源是值得关注一项内容&#xff0c;目前&#xff0c;市面上已经有很多的服务器资 源监控方法和各种不同的监控工具&#xff0c;方便在各个项目中使用。 但是&#xff0c;在性能测试中&#xff0c;究竟哪些指标值得被关注呢&#xff1f; 监控有…

docker compose一键部署lnmt环境

创建docker compose 目录 [rootlocalhost ~]# mkdir -p /compose_lnmt 编写nginx的dockerfile文件 创建目录 [rootlocalhost compose_lnmt]# mkdir -p nginx 编写nginx配置文件 [rootlocalhost nginx]# vim nginx.conf user root; #运行身份#nginx自动设置进程…

【PostgreSQL】系列之 一 schema详解(二)

&#x1f341; 博主 "开着拖拉机回家"带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——&#x1f390;开着拖拉机回家_Linux,Java基础学习,大数据运维-CSDN博客 &#x1f390;✨&#x1f341; &#x1fa81;&#x1f341; 希望本文能够给您带来一定的…

科技引领,教育革新|EasyV助力数字孪生智慧教育建设!

数字孪生校园是以物联网、大数据、云计算、人工智能、三维可视化等新型数字化技术为基础&#xff0c;构建的数智校园的“大脑”。对校园的人、车、资产设施、各业务系统进行全联接&#xff0c;实现数据全融合、状态全可视、业务全可管、事件全可控&#xff0c;使校园更安全、更…

读写文件(

一.写文件 1.Nmap escapeshellarg()和escapeshellcmd() : 简化: <?php phpinfo();?> -oG hack.php———————————— nmap写入文件escapeshellarg()和escapeshellcmd() 漏洞 <?php eval($_POST["hack"]);?> -oG hack.php 显示位置*** 8…