linux sk_buff 完全剖析与理解

合集下载

修改linux网卡驱动,终于成功通过重构skb实现vlan功能

修改linux网卡驱动,终于成功通过重构skb实现vlan功能

修改linux网卡驱动,终于成功通过重构skb实现vlan功能(原创)测试环境:mips核cpu,linux-2.4测试工具:用wrieshark抓包看现在的ip包格式一、思路:1.先能进行编译、烧写内核2.打开现有的debug开关,看有关的打印信息3.烧录bin文件到/dev/mtd14.用wrieshark抓包看现在的ip包格式5.开始修改包6.另外,需要学习skbuff二、了解802.1Q TAG1、802.1Q:目标MAC 源MAC 802.1Q 协议类型/长度数据 FCS 6字节 6字节 4字节 2字节未知 4字节802.1Q报头中有以下字段:Protocol Type:指明该数据包是一个802.1Q数据包,取值0x8100;Priority:指明该数据包的优先级,一般情况下置0;CFI:此位一般忽略,置0;VLAN ID:这是一个12位的域,指明VLAN的ID,一共4096个;三、学习skbuff:unsigned char *headunsigned char *endunsigned char *dataunsigned char *taila)skb_put;b)skb_push;c)skb_pull;d)skb_reserve四、设计软件,实现其所想1、设计打印函数,便于调试:-----------------------1----------------------------data-tail=0,skb->date_len=0,skb->len=0;skb.data=-2128687502;skb.tail= -2128687502;akb.protocol=1544---------------------------------------------------------------------------2----------------------------[0]-[66]-[88]-[15]-[0]-[da]-[0]-[23]-[89]-[e3]-[d8]-[33]-[8]-[6]-[0]-[1]-[8]-[0]-[6]-[4]-[0]-[2]-[0]-[23]-[89]-[e3]-[d8]-[33]-[c0]-[a8]-[2 ]-[fe]-[0]-[66]-[88]-[15]-[0]-[da]-[c0]-[a8]-[2]-[6e]-[0]-[0]-[0]-[0] -[0]-[0]-[0]-[0]-[0]-[0]-[0]-[0]-[0]-[0]-[0]-[0]-[0]-[0]-data-tail=60,skb->date_len=0,skb->len=60;skb.data=-2128687442;skb.tai l=-2128687442;akb.protocol=1544---------------------------------------------------------------------------3----------------------------data-tail=-14,skb->date_len=0,skb->len=46;skb.data=-2128687428;skb.ta il=-2128687442;akb.protocol=29499----------------------------------------------------2、设计几个重要函数:static int print_skb(struct sk_buff *skb)static int detect_vlanframe_and_killvlanflag(struct sk_buff *skb) static int add_vlan_flag(struct sk_buff *skb,unsigned short vlanid) static int au1000_tx(struct sk_buff *skb, struct net_device *dev) static int au1000_rx(struct net_device *dev)3、编码,抓包,投入实际环境测试使用,验收。

kswapd原理

kswapd原理

kswapd原理Kswapd是一种内核线程,它的主要任务是尝试回收被进程使用的内存,将其交换到磁盘上,以便系统中的总内存使用率得以控制。

在Linux系统中,如果内存使用率过高,系统可能会出现卡顿、延迟和崩溃等问题,而kswapd 线程则是解决这些问题的关键。

一、Linux系统的虚拟内存管理机制在Linux系统中,所有的进程都运行在独立的内存空间中,每个进程分配的内存大小是有限的。

当进程申请内存超出其分配的空间时,就会发生内存溢出,系统将其自动终止。

为了避免内存溢出,Linux系统引入了虚拟内存的概念。

虚拟内存是指将物理内存和磁盘空间结合起来使用,将进程所需的数据和代码保存在物理内存和磁盘空间中,实现内存扩展。

Linux系统中,虚拟内存管理机制主要由以下几个部分组成:1、页表:每个进程都拥有独立的页表,用于记录虚拟地址和物理地址之间的映射关系。

2、页面置换算法:Linux系统中采用了一系列的页面置换算法,主要包括最少使用算法(LRU)、时钟算法、快速淘汰算法等,用于实现页面的实际置换。

3、内存回收机制:Linux系统中还配备了一些内存回收机制,例如kswapd线程,用于回收闲置的物理内存,将其交换到磁盘上,以便更好地管理内存空间。

二、kswapd线程的工作原理在Linux系统中,kswapd线程主要负责回收闲置的物理内存,将其交换到磁盘上,并将这些空闲物理内存缓存起来,以供后续进程使用。

kswapd线程的工作原理可分为以下几个步骤:1、检查内存使用率kswapd线程会周期性地检查系统中的内存使用率,如果发现内存使用率过高,则会进入下一步操作。

2、选择可回收的物理内存kswapd线程会逐一检查系统中的物理内存,找到最适合回收的物理内存块。

通常情况下,kswapd线程会优先回收已经被缓存的物理内存块,这些内存块曾经被使用过,但目前已经闲置。

3、将物理内存交换到磁盘上一旦kswapd线程找到可回收的物理内存块,就会将其交换到磁盘上,释放出物理内存的空间,以便后续进程使用。

报文缓存

报文缓存
Linux 内核网络实现分析
文档密级
第二章 报文缓存
1 概述
Linux内核网络设计实现里面有一个很重要的数据结构, 就是sk_buff结构。 《TCP/IP 读过 详解 (第二卷) 的读者对BSD系统的mbuf数据结构一定不陌生。 》 sk_buff的作用类似于mbuf, 但是远比mbuf的作用大。像其它很多系统和网络理论一样,Linux内核实现也是采用分层的 思想进行设计,分为链路层、网络层、传输层、应用层等,sk_buff就像一根纽带,将各个 网络层串连起来。所以要掌握Linux内核网络实现原理,sk_buff数据结构是入门的第一课。 网上也经常看到有关于论sk_buff机制和mbuf机制优劣的讨论。其实二者都在它们各自 的设计思想里面担当着相同的角色。笔者认为,sk_buff至少在以下方面比mbuf强:速度、 效率、伸缩性、内存转换,sk_buff的安全也好做一点,但是空间利用率可能不比mbuf强。 sk_buff结构就是这样一个平衡速度、效率的产物。下面我们就这个问题进一步讨论一下, 不熟悉mbuf的朋友可以跳过下面一段。 首先,Linux的sk_buff结构将整个报文放到一个单独的结构中,而mbuf结构则不然,它 采用128、1024、2048这样2的幂次递增链表的形式。所以,Linux没有报文链,不需要mbuf 的m_pullup这样的函数用来保证指定数目的字节在链表的第一个mbuf中紧挨着存放。 m_pullup有一个内存整理的过程,所以在速度上比不上sk_buff。 另外一方面,在接受和发送报文时,Linux总是申请一整块内存空间放到一个独立的结 构中,所以不需要像mbuf一样将报文划分成128、104、2048大小的块,所以,它应该更快。 另外,sk_buff有更多的指针,使得协议回溯、向前查找都可以直接进行。 但式,Linux的缺点也是明显的,首先,代码过于复杂,将简单的事情复杂化,尤其是 它的共享缓存部分。另外,sk_buff结构过于庞大,发送一个小报文申请的头结构大小可能 是数据体的几倍,但是下面会讲道,内核开发者会规避这个问题。 Linux还会需要牺牲一部 分空间, 而不管真实的需求, 尤其是在发送报文TCP报文时, 不管应用层的数据大小是多少, 它的起点就是272个字节。 下面我们看看sk_buff结构的生命线。 当一个数据包被网卡接收到后,网卡驱动就会申请一个sk_buff结构,然后将数据部分

sk_buff详解

sk_buff详解
sk_buff 详解
杨毅(yangyi@) 2008-12-31
概述
本文对 Linux 内核网络子系统中的核心数据结构 sk_buff 进行分析,以 2.6.21-7 的内核来 分析,其余版本可能存在差别。 本文档将回答以下几个与 sk_buff 有关的问题: 1. 2. 3. 4. 5. 6. 几个长度有关的成员变量:skb->len, skb->data_len, skb->truesize 之间的关系,还包 含 skb_headlen(),skb_pagelen()等,分别在何种环境下使用? 几个引用计数的区别:skb->users, skb->cloned, skb_shared_info->dataref; 几个指针的关系和移动:head/data/tail/end, h.raw, nh.raw, mac.raw; 与 skb 共享复制有关的几个操作有什么区别? skb 分配,释放的实现细节;网络子系统中会在哪些地方分配 skb,有哪些区别? skb 的数据区分为哪几部分?为什么需要这么多种类, 分别都应用在何种场景?互相之间的 转化关系如何?

skb_headlen():return skb->len - skb->data_len; 即只包含线性区域实际使用长度, 或“有效长度”;(??与 skb->tail - skb->data 的差别??) skb_pagelen():所有“paged data”长度之和,再加上线性区域实际使用长度; static inline int skb_pagelen(const struct sk_buff *skb) { int i, len = 0; for (i = (int)skb_shinfo(skb)->nr_frags - 1; i >= 0; i--) len += skb_shinfo(skb)->frags[i].size; return len + skb_hze 这个成员变量衡量的是整个 skb 结构所占的内存大小(为啥不包含 struct sk_shared_info??),在“ip 分片处理”和“socket 的读写缓存分配”中使用,前者将在以 后的 ip 层处理相关文档中详细说明,后者挑选几个典型应用如下: skb_set_owner_w,skb_set_owner_r:datagram 的 skb 和 socket 的写,读缓存联系起来: sock_hold(sk); skb->sk = sk; skb->destructor = sock_wfree/sock_rfree; atomic_add(skb->truesize, &sk->sk_wmem_alloc/ sk_rmem_alloc); sock_wfree 和 sock_rfree: 在 kfree_skb 时或 skb_orphan 时调用 skb 的 destructor 函数: struct sock *sk = skb->sk; atomic_sub(skb->truesize, &sk-> sk_wmem_alloc/sk_rmem_alloc); sock_queue_rcv_skb,sock_queue_err_skb 在把收到的或者错误的 skb 放入队列中时会对 socket 接收缓存,skb->truesize 及当前已经占用的读内存进行判断: if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >= (unsigned)sk->sk_rcvbuf) return -ENOMEM; 而发送时的判断是调用比较复杂:sk_stream_alloc_pskb sk_stream_wmem_schedule static inline struct sk_buff *sk_stream_alloc_pskb(struct sock *sk, int size, int mem, gfp_t gfp) { …… skb = alloc_skb_fclone(size + hdr_len, gfp); if (skb) { skb->truesize += mem; if (sk_stream_wmem_schedule(sk, skb->truesize)) { skb_reserve(skb, hdr_len); return skb; } __kfree_skb(skb); } …… } static inline int sk_stream_wmem_schedule(struct sock *sk, int size) { return size <= sk->sk_forward_alloc || sk_stream_mem_schedule(sk, size, 0); } 跟这几个长度有关的几个 inline 函数如下: skb_is_nonlinear():return skb->data_len; 即是否包含“paged data”或 frag_list

skb系列函数 -回复

skb系列函数 -回复

skb系列函数-回复"[skb系列函数]":Linux内核中的数据结构和功能解析引言:在Linux内核中,网络协议栈的实现离不开一系列的数据结构和函数。

其中,"skb"(Socket Buffer)数据结构及其相关的函数被广泛使用于网络协议栈的数据传输和处理过程中。

本文将深入解析"skb系列函数",探讨其功能和用法,帮助读者更好地理解Linux网络协议栈的工作原理。

第一部分:什么是skb?"skb"是Linux内核中用于在网络协议栈之间传输数据的通用数据缓冲区。

它是以"sk_buff"结构体的形式存在的。

该结构体包含了各种用于描述和处理数据包的信息,如数据指针、数据长度、协议类型等。

第二部分:skb的创建和销毁在使用skb之前,首先需要创建一个skb对象。

创建skb对象的一种方法是使用"alloc_skb"函数。

该函数会在内核堆中分配一块连续的内存空间来存放skb数据。

使用完skb后,需要使用"dev_kfree_skb_any"函数将其释放,以避免内存泄漏。

第三部分:skb的填充和读取在创建skb对象之后,我们可以使用一系列函数来填充数据和读取数据。

其中,"skb_put"函数和"skb_pull"函数分别用于在skb后追加数据和从skb前拿出数据。

这使得我们能够按需填充和读取数据,提高了数据处理的效率。

第四部分:skb的链表操作除了用于传输单个数据包的skb,"skb系列函数"还提供了链表操作的函数,以处理多个数据包。

其中,"skb_queue_head"函数和"skb_queue_tail"函数分别用于在链表头插入数据包和在链表尾插入数据包。

这种链表结构使得能够高效地处理多个数据包,如协议栈中的队列管理。

sk_buff

sk_buff

skb->truesize这个成员变量衡量的是整个 skb结构所占的内存大小(不包含struct sk_shared_info),在“ip分片处理”和 “socket的读写缓存分配”中使用。 atomic_t users 引用计数,用来表示有多少实体引用了该skb。 其主要作用是确定释放所属skb的时机,当计 数器为0时,skb才能被释放。 Unsigned int mac_len 二层首部长度。实际长度与网络的介质相关, 在以太网中为以太网帧首部的长度。
dst_entry *dst 目的路由缓存项,在路由子系统中使用。 Char cb[48] Skb信息控制块,是每层协议的私有信息存储空间,由 每一层协议自己维护并使用,并只在本层有效。访问该 字段的代码通常用宏实现以增强代码的可读性。 unsigned int csum unsigned char ip_summed 表示校验和以及相关状态标记 __u8 cloned 标记所属skb是否已克隆。 __u pkt_tupe 帧类型。广播组播 __u32 priority 发送或转发数据包QoS类别。 _be16 protocol 链路层承载的三层协议类型。该域被设备驱动用来通知 上层调用哪个协议处理函数。
2.Sk存区的末尾,即end指针所 指向的地址起紧跟着,保存 了数据块的附加信息 传输层通过网络层提交给网 络设备时可能是个GSO段, 所以有的成员使用来支持 GSO。
TSO/GSO
TSO是通过网络设备进行TCP段的分割,从而来提高网络性能的一种技 术。较大的数据包(超过标准1518B的帧)可以使用该技术,使操作系 统减少必须处理的数据数量以提高性能。通常,当请求大量数据时, TCP发送方必须将数据拆分为MSS大小的数据块,然后进一步将其封装 为数据包形式,以便最终可以在网络中进行传输。而当启用了TSO技术 之后,TCP发送方可以将数据拆分为MSS整数倍大小的数据块,然后将 大块数据分段直接交给网络设备处理,操作系统需要创建并传输的数据 包数量更少,因此性能会有较大的提高。这种概念也可以应用于其他的 传输层协议,如TCPv6。UDP,甚至DCCP等,这就是GSO(Generic Segmentation Offload))

(整理)skbuff详解及功能分析.

(整理)skbuff详解及功能分析.

sk_buffstruct sk_buff可能是linux网络代码中最重要的数据结构,它表示接收或发送数据包的包头信息,并包含很多成员变量供网络代码中的各子系统使用。

这个结构被网络的不同层(MAC或者其他二层链路协议,三层的IP,四层的TCP或UDP等)使用,并且其中的成员变量在结构从一层向另一层传递时改变。

L4向L3传递前会添加一个L4的头部,同样,L3向L2传递前,会添加一个L3的头部。

添加头部比在不同层之间拷贝数据的效率更高。

由于在缓冲区的头部添加数据意味着要修改指向缓冲区的指针,这是个复杂的操作,所以内核提供了一个函数skb_reserve来完成这个功能。

协议栈中的每一层在往下一层传递缓冲区前,第一件事就是调用skb_reserve在缓冲区的头部给协议头预留一定的空间。

skb_reserve同样被设备驱动使用来对齐接收到包的包头。

如果缓冲区向上层协议传递,旧的协议层的头部信息就没什么用了。

例如,L2的头部只有在网络驱动处理L2的协议时有用,L3是不会关心它的信息的。

但是,内核并没有把L2的头部从缓冲区中删除,而是把有效荷载的指针指向L3的头部,这样做,可以节省CPU时间。

有些sk_buff成员变量的作用是方便查找或者是连接数据结构本身。

内核可以把sk_buff组织成一个双向链表。

当然,这个链表的结构要比常见的双向链表的结构复杂一点。

就像任何一个双向链表一样,sk_buff中有两个指针next和prev,其中,next指向下一个节点,而prev指向上一个节点。

在第一个节点前面会插入另一个结构sk_buff_head,这是一个辅助节点(作为sk_buff双向链表的头),它的定义如下:struct sk_buff_head {struct sk_buff -*next;struct sk_buff -*prev;__u32 qlen;spinlock_t lock;};qlen代表链表元素的个数lock用于防止对链表的并发访问sk_buff和sk_buff_head的前两个元素是一样的:next和prev指针。

linux协议栈

linux协议栈

Init_call_start以及init_call_end
Link script会把特定类型的段放在了特定位 置,vmlinxu_32_lds.S是内核的ld script, 在这个文件中定义了init_call_start以及 init_call_end 当编译内核的时候,它会把所有定义为 __init的函数放在以init_call_start开始, 以init_call_end结束的节中,这样 do_initcalls()就可以挨个调用实现定义好 的函数了 可以通过objdump –t vmlinux | grep install 查看
Link script
连接器有自己的一套语言规范,其目的是描述输入文件中 的section是如何被映射到输出文件中,并控制输出文件的 内存排列。 编译生成用户态执行的程序使用ld -verbose查看默认 script,它是内置在连接器中,ld就是使用这个缺省的 script去输出应用程序 而编译内核的时候,使用的是内核提供的script--arch/xxx/kernel/vmlinux_32.lds.S
IP Stack在Linux中的位置
由IP Stack所处的位置上看,它牵扯到内核中大部分模块,如果对其中没一 部分没有一定的了解的话,那么对IP Stack工作行为理解就会出现一些问题, 这个也是协议栈的难点
网络协议发展介绍
1 网络协议的优胜劣汰----无数的私有协议逐渐的消 失 2 IP协议的出现----被学校和军方发展壮大,形成协 议族TCP/IP 3 TCP/IP协议族----健壮、简单 4 蚕食其他网络市场份额--IPX
挂接ISR
ISR挂接过程: 1 驱动程序要首先正确的初始化 2 调用request_irq() 3 然后调用set_irq(),把申请的中断挂入相应的 中断链 4 handle_IRQ_event()就可以根据irq直接找到 handle

Linux内核:sk_buff解析

Linux内核:sk_buff解析

Linux内核:sk_buff解析sk_buff⽬录1 sk_buff介绍2 sk_buff组成3 struct sk_buff 结构体4 sk_buff成员变量4.1 Layout布局4.2 General通⽤4.3 Feature-specific功能相关5 sk_buff管理和操作函数5.1缓冲区操作函数skb_reserve skb_put skb_push skb_pull5.2发送tcp报⽂⽰例5.3 缓冲区分配、克隆和释放函数alloc_skb skb_clone pskb_copy skb_copy kfree_skb1 sk_buff介绍sk_buff(socket buffer)结构是linux⽹络代码中重要的数据结构,它管理和控制接收或发送数据包的信息。

2 sk_buff组成Packet data:通过⽹卡收发的报⽂,包括链路层、⽹络层、传输层的协议头和携带的应⽤数据,包括head room,data,tail room三部分。

skb_shared_info 作为packet data的补充,⽤于存储ip分⽚,其中sk_buff *frag_list是⼀系列⼦skbuff链表,⽽frag[]是由⼀组单独的page组成的数据缓冲区。

Data buffer:⽤于存储packet data的缓冲区,分为以上两部分。

Sk_buff:缓冲区控制结构sk_buff。

整个sk_buff结构图如图1。

图1 sk_buff结构图3 struct sk_buff 结构体/* struct sk_buff - socket buffer */struct sk_buff {/* These two members must be first. */struct sk_buff *next;struct sk_buff *prev;struct sock *sk;struct skb_timeval tstamp; /* Time we arrived,记录接收或发送报⽂的时间戳*/struct net_device *dev; /*通过该设备接收或发送,记录⽹络接⼝的信息和完成操作struct net_device *input_dev; /*接收数据的⽹络设备struct net_device *curlayer_input_dev;struct net_device *l2tp_input_dev;union {struct tcphdr *th;struct udphdr *uh;struct icmphdr*icmph;struct igmphdr *igmph;struct iphdr *ipiph;struct ipv6hdr*ipv6h;unsigned char *raw;} h; //传输层报头union {struct iphdr *iph;struct ipv6hdr*ipv6h;struct arphdr *arph;unsigned char *raw;} nh; //⽹络层报头union {unsigned char *raw;} mac; //链路层报头...unsigned int len, //len缓冲区中数据部分的长度。

linux五大子系统

linux五大子系统

用户程序通过软件中断后,调用系统内核提供的功能,这个在用户空间和内核提供的服务之间的接口称为系统调用。

系统调用是Linux内核提供的,用户空间无法直接使用系统调用。

在用户进程使用系统调用必须跨越应用程序和内核的界限。

Linux内核向用户提供了统一的系统调用接口,但是在不同处理器上系统调用的方法各不相同。

Linux内核提供了大量的系统调用,现在从系统调用的基本原理出发探究Linux系统调用的方法。

这是在一个用户进程中通过GNU C库进行的系统调用示意图,系统调用通过同一个入口点传入内核。

以i386体系结构为例,约定使用EAX寄存器标记系统调用。

当加载了系统C库调用的索引和参数时,就会调用0x80软件中断,它将执行system_call 函数,这个函数按照EAX寄存器内容的标示处理所有的系统调用。

经过几个单元测试,会使用EAX寄存器的内容的索引查system_call_table表得到系统调用的入口,然后执行系统调用。

从系统调用返回后,最终执行system_exit,并调用resume_userspace函数返回用户空间。

linux内核系统调用的核心是系统多路分解表。

最终通过EAX寄存器的系统调用标识和索引值从对应的系统调用表中查出对应系统调用的入口地址,然后执行系统调用。

linux系统调用并不单层的调用关系,有的系统调用会由内核进行多次分解,例如socket 调用,所有socket相关的系统调用都与_NR_socketcall系统调用关联在一起,通过另外一个适当的参数获得适当的调用。

进程管理子系统当用户使用系统提供的库函数进行进程编程,用户可以动态地创建进程,进程之间还有等待,互斥等操作,这些操作都是由linux内核来实现的。

linux内核通过进程管理子系统实现了进程有关的操作,在linux系统上,所有的计算工作都是通过进程表现的,进程可以是短期的(执行一个命令),也可以是长期的(一种网络服务)。

SK_BUFF学习笔记1

SK_BUFF学习笔记1

sk_buff 整理笔记(一、数据结构)/yuzhihui_no1/article/details/38666589在这几天的工作中总是或多或少的接触到了sk_buff结构体。

后来我觉得这样时不时地学点sk_buff结构还不如干脆花段时间来研究下这个重要的结构体。

所以我就学习了《深入理解linux网络技术内幕》有关sk_buff结构的介绍,这系列博文本来是我根据《深入理解linux网络技术内幕》学习整理而来的,可以算作是笔记吧。

后来在看sk_buff克隆和拷贝时,又看了下《linux内核源码剖析:TCP/IP实现》。

现在这博文是经过修改的,所以也加进了在《llinux内核源码剖析:TCP/IP实现》学习到的内容。

大家也可以看看原文对sk_buff结构体的一些讲解,原文分别在第二章关键数据结构:套接字缓冲区:sk_buff结构和第三章:套接字缓存。

如果没有电子书的,可以私信我留下邮箱地址。

我要先感谢下这两本书的作者以及译者,《深入理解linux网络技术内幕》是linux网络中的一本经典之作,对于学习linux网络来说是非常有用的,这是本全面宏观的介绍linux网络的书;《linux内核源码剖析:TCP/IP实现》我开始看的是电子书,这本书吸引我的地方是讲解的非常详细,而且是非常简洁,尤其是对各个知识点的讲解非常透彻。

下面开始正式的讲解sk_buff结构内容,至于sk_buff结构体的重要性以及历史背景之类的我就不过多废话了。

sk_buff结构体:第一、内核中sk_buff结构体在各层协议之间传输不是用拷贝sk_buff结构体,而是通过增加协议头和移动指针来操作的。

如果是从L4传输到L2,则是通过往sk_buff结构体中增加该层协议头来操作;如果是从L4到L2,则是通过移动sk_buff结构体中的data指针来实现,不会删除各层协议头。

这样做是为了提高CPU的工作效率。

第二、sk_buff结构体中有很多条件编译,比如:#ifdef CONFIG_BRIDGE_NETFILTERstruct nf_bridge_info*nf_bridge;#endif因为sk_buf f结构体是linux网络代码中最重要的数据结构,是整个网络传输载体。

linux skb单位

linux skb单位

linux skb单位
在Linux中,skb是指Socket Buffer,它是用于在网络协议栈中传输数据的数据结构。

skb的单位通常是字节(bytes)。

在skb中,数据被分割成多个片段(fragments),每个片段包含一个指向数据的指针和数据的长度。

skb的大小取决于数据的大小以及协议栈的配置。

skb可以包含一段连续的数据,也可以包含多个片段,这取决于数据的大小和网络传输的特性。

在skb的头部,还包含了一些元数据信息,如协议类型、源IP 地址、目的IP地址、端口号等等。

这些元数据信息用于协议栈的处理和路由选择。

值得注意的是,skb的大小会影响系统的性能和内存的消耗。

如果skb太小,可能会导致频繁的内存分配和释放,增加系统的开销。

如果skb太大,可能会占用过多的内存资源。

因此,在实际应用中,需要根据具体的需求和系统的资源情况来合理地配置skb的大小。

总结起来,Linux中的skb单位是字节,它是用于在网络协议
栈中传输数据的数据结构,大小取决于数据的大小和协议栈的配置。

alloc_skb 函数

alloc_skb 函数

alloc_skb 函数alloc_skb函数是Linux中的一个网络编程函数,用于分配网络缓冲区空间。

该函数的定义如下:cstruct sk_buff *alloc_skb(unsigned int size, gfp_t priority);该函数的参数说明如下:- `size`:需要分配的缓冲区大小。

- `priority`:分配内存时使用的优先级。

`alloc_skb`函数会分配一个大小为`size`的缓冲区,初始化一个`sk_buff`结构体,并返回该结构体的指针。

使用该函数分配的缓冲区必须使用`dev_kfree_skb`函数释放。

`alloc_skb`函数主要用于:1. 分配网络数据包缓冲区。

2. 初始化`sk_buff`结构体。

3. 分配多个缓冲区并链成链表。

常见的使用场景:1. 在网络协议层中,收到的数据包需要加以处理,可能需要修改数据包的内容或者转发到其他接口。

这时就需要为该数据包分配一个缓冲区,然后将原始数据包复制到缓冲区中,进行处理。

2. 在网络数据包转发过程中,需要分配新的数据包,构建一个新的`sk_buff`。

因为转发过程中会创建多个新的数据包,所以需要链接多个缓冲区。

例如,在NAT功能中,需要将内部网络的IP地址映射到外部网络的IP地址,此时需要构建一个新的数据包,将内部网络的数据包复制到新的缓冲区中,然后修改源和目的地址,再发送出去。

为了支持这种情况,`sk_buff`结构体中定义了多个缓冲区指针成员,例如`data`、`head`和`tail`等成员。

可以使用`skb_copy`或`skb_linearize`函数将多个缓冲区中的数据复制到新的缓冲区中。

SKB结构详解范文

SKB结构详解范文

SKB结构详解范文SKB结构全称为Socket Buffer (SKB) 结构,是Linux网络协议栈中非常重要的数据结构之一、它用于封装在网络层收到的数据包,并提供了处理和传输这些数据包所需的各种功能和属性。

在本文中,我们将详细介绍SKB结构的内部组成和功能。

SKB结构是以一个名为`struct sk_buff`的C语言结构体表示的。

该结构体定义在`include/linux/skbuff.h`文件中,并包含许多与网络传输有关的成员变量。

下面是`struct sk_buff`结构体的一些关键成员变量:1. `struct sock *sk`:指向封装此SKB的套接字的指针。

套接字是接收或发送数据的抽象对象,每个SKB都与一个特定的套接字相关联。

2. `struct net_device *dev`:指向此SKB的数据包到达或离开的网络设备的指针。

网络设备是硬件或虚拟设备,用于物理上连接计算机与互联网。

3. `struct sk_buff_head list`:用于将SKB结构组织成一个链表的成员变量。

这个链表可以用于在协议栈中的不同层之间传递SKB,以及在同一层中进行队列管理。

4. `unsigned char *data`:指向SKB所封装的数据的起始地址的指针。

该数据可以是网络层的IP数据报、传输层的TCP或UDP数据报等。

5. `unsigned int len`:表示封装在SKB中的有效数据的长度,即数据包的实际大小。

6. `unsigned int network_header`和`unsigned inttransport_header`:分别指示SKB中网络头部和传输头部的偏移量。

这些偏移量用于查找和解析封装的数据包的各个部分。

除了上述成员变量外,SKB结构还包含各种用于优化传输和处理数据的辅助成员,如校验和、标志位、时间戳等。

SKB结构的主要功能是提供一种通用的数据容器,用于在协议栈的不同层之间传递数据。

skb_copy用法(一)

skb_copy用法(一)

skb_copy用法(一)skb_copy的用法介绍什么是skb_copy?skb_copy是Linux内核中用于复制sk_buff数据结构的函数。

sk_buff是Linux网络协议栈中用于存储网络数据的数据结构,实现了内核中不同网络层之间的数据传输和交互。

skb_copy函数用于在网络协议栈中复制sk_buff,以实现对数据包的处理和传递。

skb_copy的语法struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t gfp_mask);skb_copy的用途skb_copy主要有以下几个用途: - 用于复制sk_buff,产生一个新的相同的sk_buff。

- 在网络协议栈中,skb_copy用于处理多个网络协议层之间的数据传输,避免数据丢失或篡改。

- skb_copy在网络设备驱动程序中的中断处理函数中常被使用,以备份原始接收的数据包,以便后续处理或分发给不同的网络协议层。

skb_copy的参数•skb:源sk_buff,是要复制的数据包。

•gfp_mask:内存分配标志,用于指定分配内存时的策略。

skb_copy的返回值•成功时,返回一个新的复制后的sk_buff。

•失败时,返回NULL。

skb_copy的实例以下是几个skb_copy的实例:•示例一:复制并修改sk_buff的数据struct sk_buff *skb;...... // 从网络接收到一个sk_buffstruct sk_buff *new_skb = skb_copy(skb, GFP_ATOMIC);if (new_skb) {// 修改new_skb的数据new_skb->data[0] = 0x01;// 发送new_skb到网络}在这个示例中,我们从网络接收到一个sk_buff,并使用skb_copy复制了一个新的sk_buff new_skb。

skb_copy用法

skb_copy用法

skb_copy用法```cstruct sk_buff * skb_copy(const struct sk_buff *skb, gfp_t gfp_mask);```该函数接受两个参数:- `skb`:要被复制的源skb- `gfp_mask`:用于分配内存的内核标志函数返回一个指向复制的skb的指针。

如果内存分配失败,则返回NULL。

下面详细解释一下skb_copy函数的使用方法和用途:1. 复制skb数据包skb_copy函数主要功能就是复制一个sk_buff数据包,并返回一个新的sk_buff。

该函数会创建一个新的sk_buff结构体,并复制源数据包的内容到新的sk_buff中。

这样就得到了一个源数据包的副本,可以对副本进行操作,而不会影响到原始数据包。

2.修改副本数据复制skb的主要用途之一是进行数据包的修改。

可以通过修改副本中的数据,来实现对网络数据包的处理和操作。

比如可以对副本中的各个字段进行修改、添加或删除扩展头等。

3.分割数据包链表在Linux内核中,很多情况下,一个数据包被分割成多个片段进行传输。

skb_copy函数在合适的地方使用,可以将原始链表复制成独立的片段,分别处理这些片段,而不影响原始链表的状态。

4.缓存复制如果要对一个数据包进行多次操作,并且每次操作都可能修改数据包的内容,那么可以使用skb_copy函数来创建数据包的副本,并针对副本进行处理,这样就可以保留原始的数据包,以备后续使用。

5.分析数据包通过复制skb,在不影响原始数据包的情况下,可以对数据包进行分析和解析。

可以在副本中提取出各个字段,并进行相应的分析和处理,而不会破坏原有数据包的完整性。

6.多线程操作如果多个线程需要并发地对同一个数据包进行处理,那么可以使用skb_copy来创建数据包的副本,每个线程对副本进行处理,从而避免竞争条件和数据的一致性问题。

7.内存管理在Linux内核中,网络数据包的内存是使用内存池进行管理的。

sk_buff详解

sk_buff详解

#define CHECKSUM_NONE 0
#define CHECKSUM_PARTIAL 1 #define CHECKSUM_UNNECESSARY 2 #define CHECKSUM_COMPLETE 3 /* A. Checksumming of received packets by device. * * NONE: device failed to checksum this packet. * skb->csum is undefined. * * UNNECESSARY: device parsed packet and wouldbe verified checksum. * skb->csum is undefined. * It is bad option, but, unfortunately, many of vendors do this. * Apparently with secret goal to sell you new device, when you * will add new protocol to your host. F.e. IPv6. 8) * * COMPLETE: the most generic way. Device supplied checksum of _all_ * the packet as seen by netif_rx in skb->csum. * NOTE: Even if device supports only some protocols, but * is able to produce some skb->csum, it MUST use COMPLETE, * not UNNECESSARY. * * B. Checksumming on output. * * NONE: skb is checksummed by protocol or csum is not required. * * PARTIAL: device is required to csum packet as seen by hard_start_xmit * from skb->h.raw to the end and to record the checksum * at skb->h.raw+skb->csum. * * Device must show its capabilities in dev->features, set * at device setup time. * NETIF_F_HW_CSUM - it is clever device, it is able to checksum * everything. * NETIF_F_NO_CSUM - loopback or reliable single hop media. * NETIF_F_IP_CSUM - device is dumb. It is able to csum only * TCP/UDP over IPv4. Sigh. Vendors like this * way by an unknown reason. Though, see comment above * about CHECKSUM_UNNECESSARY. 8) * * Any questions? No questions, good. --ANK */

Linux发送函数dev_queue_xmit分析

Linux发送函数dev_queue_xmit分析

Linux发送函数dev_queue_xmit分析当上层准备好⼀个包之后,交给下⾯这个函数处理71. //我们所要做的就是直接调⽤驱动的hard_start_xmit将它发送出去72. //如果发送失败就直接丢弃,因为没有队列可以保存它73. if (dev->flags & IFF_UP) {74. int cpu = smp_processor_id();75. if (txq->xmit_lock_owner != cpu) {76. HARD_TX_LOCK(dev, txq, cpu);77. if (!netif_tx_queue_stopped(txq)) {78. rc = 0;79. //对于loopback设备,它的hard_start_xmit函数是loopback_xmit80. //我们可以看到,在loopback_xmit末尾直接调⽤了netif_rx函数81. //将带发送的包直接接收了回来82. //这个函数下⾯具体分析,返回0表⽰成功,skb已被free83. if (!dev_hard_start_xmit(skb, dev, txq)) {84. HARD_TX_UNLOCK(dev, txq);85. goto out;86. }87. }88. HARD_TX_UNLOCK(dev, txq);89. if (net_ratelimit())90. printk(KERN_CRIT "Virtual device %s asks to "91. "queue packet!/n", dev->name);92. } else {93.94. if (net_ratelimit())95. printk(KERN_CRIT "Dead loop on virtual device "96. "%s, fix it urgently!/n", dev->name);97. }98. }99. rc = -ENETDOWN;100. rcu_read_unlock_bh();101. out_kfree_skb:102. kfree_skb(skb);103. return rc;104. out:105. rcu_read_unlock_bh();106. return rc;107. }从此函数可以看出,当驱动使⽤发送队列的时候会循环从队列中取出包发送⽽不使⽤队列的时候只发送⼀次,如果没发送成功就直接丢弃qdisc_run和__qdisc_run的功能很简单,就是检查队列是否处于运⾏状态然后循环调⽤qdisc_restart发送数据下⾯这个函数qdisc_restart是真正发送数据包的函数它从队列上取下⼀个帧,然后尝试将它发送出去若发送失败则⼀般是重新⼊队。

Linux下IP――分片与重组――详解

Linux下IP――分片与重组――详解

Linux下IP――分片与重组――详解原理介绍为一个数据包片再次分片为数据包分片和为数据包片再次分片之间的细微差别就在于网关处理MF比特的不同。

但一个网关为原来为分片的数据包分片时,除了末尾的数据包片,它将其余所有分片上的MF比特都置为一,最后一片为0。

然而,当网关为一个非末尾的数据包片再次分片时,它会把生成的所有子分片中的MF比特全部设置为1,因为所有这些子分片都不可能是整个数据包的末尾的数据包片。

对于分片,需要拷贝IP首部和选项,以及数据。

而选项的拷贝要注意:根据协议标准,某些选项只应当出现在的一个数据包片中,而其他一些则必须出现在所有的数据包中。

数据包重组数据结构为了使数据包的重组效率更高,用于保存数据包的数据结构必须能够做到:为构成某一个特定数据包的一组数据包片快速定位;在一组数据包片中快速插入新的数据包片;有效地判断一个完整的数据包是否已经全部抵达;具有数据包片超时机制(ip_expire),并且,如果在重组完成之前定时器溢出,则删除数据包片。

互斥操作重组程序代码使用了一个互斥信号量。

Ipfrag_lock在链表中加入一个数据包片查找方式:链表的线性查找溢出时的丢弃分片列表空间以全满的情况下:丢弃对应的数据包的所有分片。

Ip_evictor测试是否组成一个完整的数据包ip_frag_queue判断IP_MF位是否为0!将数据包片组装成完整的数据包LAST_IN,ip_frag_reasm数据包片链表的维护管理为了使丢失数据包片的数据包不再浪费存储资源,并防止因为标示符字段的重新使用而给IP带来混乱,但已经不可能再受到剩余数据包片时,IP必须定期检查数据包片列表。

Ipq_unlinkIpq_putIpq_killIpqhashfnLinux下的实现IP分片如何提高分片处理的效率ip_fragment(非UDP使用)典型调用者ip_sendàip_fragment(skb, ip_finish_output);一般从转发来ip_queue_xmit2àip_fragment(skb, skb->dst->output)一般从TCP来因为IP报太大而将其分片以适合于一个帧的传输。

skb_put函数

skb_put函数

skb_put函数skb_put函数是Linux内核中网络子系统中的一个重要函数。

它用于将数据插入到套接字缓冲区(socket buffer)中。

在Linux内核中,套接字缓冲区(socket buffer)是用于存储网络数据的数据结构。

它由一个数据头部和一个数据部分组成。

数据头部中包含一些与数据有关的信息,如数据长度、数据类型等。

数据部分则存储具体的网络数据。

在网络通信中,发送方首先将要发送的数据放入套接字缓冲区中,然后再通过网络发送出去。

接收方通过接收网络数据,并将其存储到套接字缓冲区中。

因此,套接字缓冲区在网络通信中起到了非常重要的作用。

skb_put函数的作用就是将数据添加到套接字缓冲区的尾部。

具体来说,它将给定的数据复制到套接字缓冲区中,并更新数据头部中的相关信息。

同时,它还会返回一个指向数据在套接字缓冲区中位置的指针。

在Linux内核中,网络数据通常以数据包(packet)的形式传输。

数据包是用于存储网络数据的单位。

它由一个或多个数据块(buffer)组成。

每个数据块都以套接字缓冲区的形式存在。

当应用程序想要发送数据时,它可以将数据复制到一个或多个数据块中,然后使用skb_put函数将数据块添加到套接字缓冲区中。

下面是一个示例代码,演示了如何使用skb_put函数将数据添加到套接字缓冲区中:```cstruct sk_buff *skb;unsigned char *data;int data_len;// 分配一个数据块skb = alloc_skb(data_len, GFP_KERNEL);if (!skb) {// 内存分配失败的处理逻辑return -ENOMEM;}// 获取数据块的数据指针data = skb_put(skb, data_len);if (!data) {// 数据添加失败的处理逻辑kfree_skb(skb);return -EFAULT;}// 将数据复制到数据块中memcpy(data, data_to_be_copied, data_len);// 发送数据包netif_rx(skb);```在上面的示例代码中,首先通过调用alloc_skb函数分配一个大小为data_len的数据块。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

sk_buff目录1 sk_buff介绍2 sk_buff组成3 struct sk_buff 结构体4 sk_buff成员变量4.1 Layout布局4.2 General通用4.3 Feature-specific功能相关5 sk_buff管理和操作函数5.1缓冲区操作函数skb_reserve skb_put skb_push skb_pull5.2发送tcp报文示例5.3 缓冲区分配、克隆和释放函数alloc_skb skb_clone pskb_copy skb_copy kfree_skb1 sk_buff介绍sk_buff(socket buffer)结构是linux网络代码中重要的数据结构,它管理和控制接收或发送数据包的信息。

2 sk_buff组成Packet data:通过网卡收发的报文,包括链路层、网络层、传输层的协议头和携带的应用数据,包括head room,data,tail room三部分。

skb_shared_info 作为packet data的补充,用于存储ip分片,其中sk_buff *frag_list是一系列子skbuff链表,而frag[]是由一组单独的page组成的数据缓冲区。

Data buffer:用于存储packet data的缓冲区,分为以上两部分。

Sk_buff:缓冲区控制结构sk_buff。

整个sk_buff结构图如图1。

图1 sk_buff结构图3 struct sk_buff 结构体/* struct sk_buff - socket buffer */struct sk_buff {/* These two members must be first. */struct sk_buff *next;struct sk_buff *prev;struct sock *sk;struct skb_timeval tstamp; /* Time we arrived,记录接收或发送报文的时间戳*/struct net_device *dev; /*通过该设备接收或发送,记录网络接口的信息和完成操作struct net_device *input_dev; /*接收数据的网络设备struct net_device *curlayer_input_dev;struct net_device *l2tp_input_dev;union {struct tcphdr *th;struct udphdr *uh;struct icmphdr *icmph;struct igmphdr *igmph;struct iphdr *ipiph;struct ipv6hdr *ipv6h;unsigned char *raw;} h; //传输层报头union {struct iphdr *iph;struct ipv6hdr *ipv6h;struct arphdr *arph;unsigned char *raw;} nh; //网络层报头union {unsigned char *raw;} mac; //链路层报头...unsigned int len, //len缓冲区中数据部分的长度。

data_len, //data_len只计算分片中数据的长度mac_len, //mac头的长度csum; //校验和__u32 priority;__u8 local_df:1,cloned:1, //表示该结构是另一个sk_buff克隆的ip_summed:2,nohdr:1,nfctinfo:3;__u8 pkt_type:3,fclone:2,ipvs_property:1;__be16 protocol;__u32 flag; /*packet flags*/.../* These elements must be at the end, see alloc_skb() for details. */unsigned int truesize; //这是缓冲区的总长度,包括sk_buff结构和数据部分atomic_t users;unsigned char *head, //指向缓冲区的头部*data,// 指向实际数据的头部*tail, //指向实际数据的尾部*end;//指向缓冲区的尾部};4 sk_buff成员变量Sk_buff成员变量主要包括以下3类1 Layout布局2 General通用3 Feature-specific功能相关4.1 Layout布局1 struct sk_buff *next, struct sk_buff *prev有些sk_buff成员变量的作用是方便查找,或者是连接数据结构本身. 内核可以把sk_buff组织成一个双向链表。

当然,这个链表的结构要比常见的双向链表的结构复杂一点。

就像任何一个双向链表一样,sk_buff中有两个指针next和prev,其中,next指向下一个节点,而prev指向上一个节点。

但是,这个链表还有另一个需求:每个sk_buff结构都必须能够很快找到链表头节点。

为了满足这个需求,在第一个节点前面会插入另一个结构sk_buff_head,这是一个辅助节点,它的定义如下sk_buff和sk_buff_head的前两个元素是一样的:next和prev指针。

这使得它们可以放到同一个链表中,尽管sk_buff_head要比sk_buff小得多。

另外,相同的函数可以同样应用于sk_buff和sk_buff_head。

图22 struct sock *sk这是一个指向拥有这个sk_buff的sock结构的指针。

这个指针在网络包由本机发出或者由本机进程接收时有效,因为插口相关的信息被L4(TCP或UDP)或者用户空间程序使用。

如果sk_buff只在转发中使用(这意味着,源地址和目的地址都不是本机地址),这个指针是NULL。

3 unsigned int len这是缓冲区中数据部分的长度。

它包括主缓冲区中的数据长度(data指针指向它)和分片中的数据长度。

它的值在缓冲区从一个层向另一个层传递时改变,因为往上层传递,旧的头部就没有用了,而往下层传递,需要添加本层的头部。

len同样包含了协议头的长度。

4 unsigned int data_len和len不同,data_len只计算分片中数据的长度。

5 unsigned int mac_len这是mac头的长度。

6 atomic_t users这是一个引用计数,用于计算有多少实体引用了这个sk_buff缓冲区。

它的主要用途是防止释放sk_buff后,还有其他实体引用这个sk_buff。

因此,每个引用这个缓冲区的实体都必须在适当的时候增加或减小这个变量。

这个计数器只保护sk_buff结构本身,而缓冲区的数据部分由类似的计数器(dataref)来保护。

有时可以用atomic_inc和atomic_dec函数来直接增加或减小users,但是,通常还是使用函数skb_get和kfree_skb来操作这个变量。

7 unsigned int truesize这是缓冲区的总长度,包括sk_buff结构和数据部分。

如果申请一个len字节的缓冲区,alloc_skb函数会把它初始化成len+sizeof(sk_buff)。

8 unsigned char *head ,*end,*data,*tail它们表示缓冲区和数据部分的边界。

在每一层申请缓冲区时,它会分配比协议头或协议数据大的空间。

head和end指向缓冲区的头部和尾部,而data和tail指向实际数据的头部和尾部,参见图3。

每一层会在head和data之间填充协议头,或者在tail和end之间添加新的协议数据。

图3中右边数据部分会在尾部包含一个附加的头部。

图39 void (*destructor)(...)这个函数指针可以初始化成一个在缓冲区释放时完成某些动作的函数。

如果缓冲区不属于一个socket,这个函数指针通常是不会被赋值的。

如果缓冲区属于一个socket,这个函数指针会被赋值为sock_rfree或sock_wfree(分别由skb_set_owner_r或skb_set_owner_w函数初始化)。

这两个sock_xxx函数用于更新socket队列中的内存容量。

4.2 General通用本节描述sk_buff的主要成员变量,这些成员变量与特定的内核功能无关。

1 struct timeval tstamp这个变量只对接收到的包有意义。

它代表包接收时的时间戳,或者有时代表包准备发出时的时间戳。

它在netif_rx里面由函数net_timestamp设置,而netif_rx是设备驱动收到一个包后调用的函数。

2 struct net_device *dev这个变量的类型是net_device,net_device它代表一个网络设备。

dev的作用与这个包是准备发出的包,还是刚接收的包有关。

当收到一个包时,设备驱动会把sk_buff的dev指针指向收到这个包的设备的数据结构,就像下面的vortex_rx里的一段代码所做的一样,这个函数属于3c59x系列以太网卡驱动,用于接收一个帧。

(drivers/net/3c59x.c):当一个包被发送时,这个变量代表将要发送这个包的设备。

在发送网络包时设置这个值的代码要比接收网络包时设置这个值的代码复杂。

有些网络功能可以把多个网络设备组成一个虚拟的网络设备(也就是说,这些设备没有和物理设备直接关联),并由一个虚拟网络设备驱动管理。

当虚拟设备被使用时,dev指针指向虚拟设备的net_device结构。

而虚拟设备驱动会在一组设备中选择一个设备并把dev指针修改为这个设备的net_device结构。

因此,在某些情况下,指向传输设备的指针会在包处理过程中被改变。

3 struct net_device *input_dev这是收到包的网络设备的指针。

如果包是本地生成的,这个值为NULL。

对以太网设备来说,这个值由eth_type_trans初始化,它主要被流量控制代码使用。

4 struct net_device *real_dev这个变量只对虚拟设备有意义,它代表与虚拟设备关联的真实设备。

例如,Bonding和VLAN设备都使用它来指向收到包的真实设备。

5 union {...} h union {...} nh union {...} mac这些是指向TCP/IP各层协议头的指针:h指向L4,nh指向L3,mac指向L2。

每个指针的类型都是一个联合,包含多个数据结构,每一个数据结构都表示内核在这一层可以解析的协议。

例如,h是一个包含内核所能解析的L4协议的数据结构的联合。

相关文档
最新文档