sk_buff结构分析
前言:
以下是根据《深入理解Linux网络技术内幕》对sk_buff的相关总结,由于是刚刚看这本书(太厚了),不免在前期出现错误,随着对此书的深入我会在修改前面的错误,也希望各位牛人给予指点。帮助我成长。
sk_buff分析:
sk_buff是Linux网络代码中最重要的结构体之一。它是Linux在其协议栈里传送的结构体,也就是所谓的“包”,在他里面包含了各层协议的头部,比如ethernet, ip ,tcp ,udp等等。也有相关的操作等。熟悉他是进一步了解Linux网络协议栈的基础。
此结构定义在<include/linux/skbuff.h>头文件中,结构体布局大致可分为以下四部分:
l 布局(layout)
l 通用(general)
l 功能专用(feature-specific)
l 管理函数(management functions)
网络选项以及内核结构
我们可以看到在此结构体里有很多预处理,他是在需要指定相应功能时才起作用,我们在这里先对通用的作出分析。
布局字段:
sk_buff是一个复杂的双向链表,在他结构中有next和prev指针,分别指向链表的下一个节点和前一个节点。并且为了某些需求(不知道是哪些目前)需要很快定位到链表头部,所以还有一个指向链表头部的指针list(我在2.6.25内核没有发现这个指针)。
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; //加锁,防止对表的并发访问
};
struct sock *sk
这个指针指向一个套接字sock数据结构。当数据在本地产生或者本地进程接受时,需要这个指针;里面的数据会有tcp/udp和用户态程序使用。如果是转发此指针为NULL
unsigned int len
缓冲区中数据块大小。长度包括:主要缓冲区(head所指)的数据以及一些片断(fragment)的数据。当包在协议栈向上或向下走时,其大小会变,因为有头部的丢弃和添加。
unsigned int data_len
片段中数据大小
unsigned int mac_len
mac包头大小
atomic_t users
引用计数,使用这个sk_buff的使用者的数目,可能有多个函数要使用同一个sk_buff所以防止提前释放掉,设置此计数
unsigned int truesize
此缓冲区总大小,包括sk_buff。sk_buff只不过是个指针的集合,他所指的才是真正的数据区,所以是两部分。(见下图)
sk_buff_data_t tail;
sk_buff_data_t end;
unsigned char *head, *data;
这些指针很重要,他们指向的是真正的数据区,他们的边界。head和end指向的是数据区的开端和尾端(注意和data,tail区别)如下图,data和tail指向的是实际数据的开头和结尾。
因为数据区在协议栈走的时候要一层层添加或去掉一些数据(比如报头)所以申请一块大的足够的内存,然后在往里放东西。真实的实际数据可能用不了这么多,所以用data,tail指向真实的,head,tail指向边界。刚开始没填充数据时前三个指针指向的是一个地方。
void (*destructor) (…….)
此函数指针被初始化一个函数,当此缓冲区删除时,完成某些工作。
通用字段
struct timeval stamp(2.6.25没有,估计是ktime_t tstamp)
时间戳,表示何时被接受或有时表示包预定的传输时间
struct net_device *dev
描述一个网络设备,我会以后分析他。
sk_buff_data_t transport_header; //L4
sk_buff_data_t network_header; //L3
sk_buff_data_t mac_header; //L2
这些指针分别指向报文头部,和2.4版本比较有了变化,不再是联合体,使用更加方便了,Linux给出了很方便的函数直接定位到各层的头部。下图是2.4版本的,只是说明一下。
struct dst_entry dst
路由子系统使用。目前不知道怎么回事呢。据说比较复杂。
char cb[40]
缓冲控制区,用来存储私有信息的空间。比如tcp用这个空间存储一个结构体tcp_skb_cb ,可以用宏TCP_SKB_CB(__skb)定位到他,然后使用里面的变量。
ip_summed:2
__wsum csum;
校验和
unsigned char pkt_type
根据L2层帧的目的地址进行类型划分。
unsigned char cloned
表示该结构是另一个sk_buff克隆的。
__u32 priority;
QoS等级
__be16 protocol;
从L2层设备驱动看使用在下一个较高层的协议。
功能专用字段
Linux是模块化的,你编译时可以带上特定功能,比如netfilter等,相应的字段才会生效。应该是那些预定义控制的。
管理函数
下面这个图是:(a*)skb_put; (b*) skb_push; (c*) skb_pull (d*) skb_reserve的使用,主要是对skb_buf所指向的数据区的指针移动。(数据预留以及对齐)
下图是用skb_reserve函数,把一个14字节的ethernet帧拷贝到缓冲区。skb_reserve(skb, 2), 2表示16字节对齐。14+2=16
下图是穿过协议栈从tcp层向下到链路层的过程
分配内存:
alloc_skb 分配缓冲区和一个sk_buff结构
dev_alloc_skb 设备驱动程序使用的缓冲区分配函数
释放内存:
kfree_skb 只有skb->users计数器为1时才释放
dev_kfree_skb
缓冲区克隆函数 skb_clone
列表管理函数:
skb_queue_head_init
队列初始化
skb_queue_head , skb_queue_tail
把一个缓冲区添加到队列头或尾
skb_dequeue, skb_dequeue_tail
从头或尾去掉
skb_queue_purge
把队列变空
skb_queue_walk
循环队列每个元素
内核也新增了几个函数,来提供获取这些偏移的接口: #ifdef NET_SKBUFF_DATA_USES_OFFSET 如果使用了offset来表示偏移的话,就是说是一个相对偏移的情况: static inline unsigned char *skb_transport_header(const struct sk_buff *skb) {return skb->head + skb->transport_header; } static inline void skb_reset_transport_header(struct sk_buff *skb) {skb->transport_header = skb->data - skb->head; } static inline void skb_set_transport_header(struct sk_buff *skb,const int offset) {skb_reset_transport_header(skb);skb->transport_header += offset; } static inline unsigned char *skb_network_header(const struct sk_buff *skb) {return skb->head + skb->network_header; } static inline void skb_reset_network_header(struct sk_buff *skb) {skb->network_header = skb->data - skb->head; } static inline void skb_set_network_header(struct sk_buff *skb, const int offset) {skb_reset_network_header(skb);skb->network_header += offset; } static inline unsigned char *skb_mac_header(const struct sk_buff *skb) {return skb->head + skb->mac_header; } static inline int skb_mac_header_was_set(const struct sk_buff *skb) {return skb->mac_header != ~0U; } static inline void skb_reset_mac_header(struct sk_buff *skb) {skb->mac_header = skb->data - skb->head; } static inline void skb_set_mac_header(struct sk_buff *skb, const int offset) {skb_reset_mac_header(skb);skb->mac_header += offset; } #else /* NET_SKBUFF_DATA_USES_OFFSET */ 不使用相对偏移的情况 static inline unsigned char *skb_transport_header(const struct sk_buff *skb) {return skb->transport_header; } static inline void skb_reset_transport_header(struct sk_buff *skb) {skb->transport_header = skb->data; } static inline void skb_set_transport_header(struct sk_buff *skb,const int offset) {skb->transport_header = skb->data + offset; } static inline unsigned char *skb_network_header(const struct sk_buff *skb) {return skb->network_header; } static inline void skb_reset_network_header(struct sk_buff *skb) {skb->network_header = skb->data; } static inline void skb_set_network_header(struct sk_buff *skb, const int offset) {skb->network_header = skb->data + offset; } static inline unsigned char *skb_mac_header(const struct sk_buff *skb) {return skb->mac_header; } static inline int skb_mac_header_was_set(const struct sk_buff *skb) {return skb->mac_header != NULL; } static inline void skb_reset_mac_header(struct sk_buff *skb) {skb->mac_header = skb->data; } static inline void skb_set_mac_header(struct sk_buff *skb, const int offset) {skb->mac_header = skb->data + offset; } #endif /* NET_SKBUFF_DATA_USES_OFFSET */1、TCP层获取相关偏移的函数 static inline struct tcphdr *tcp_hdr(const struct sk_buff *skb) {return (struct tcphdr *)skb_transport_header(skb); } 这个函数用来获得sk_buff结构中TCP头的指针 static inline unsigned int tcp_hdrlen(const struct sk_buff *skb) {return tcp_hdr(skb)->doff * 4; } 这个函数用来获得TCP头的长度 static inline unsigned int tcp_optlen(const struct sk_buff *skb) {return (tcp_hdr(skb)->doff - 5) * 4; } 获取tcp option的长度 2、IP相关的函数 static inline struct iphdr *ip_hdr(const struct sk_buff *skb) {return (struct iphdr *)skb_network_header(skb); } 该函数获得ip头 static inline struct iphdr *ipip_hdr(const struct sk_buff *skb) {return (struct iphdr *)skb_transport_header(skb); } 该函数获得ipip头,实际上偏移已经跑到了传输层的开始 3、MAC相关函数 static inline struct ebt_802_3_hdr *ebt_802_3_hdr(const struct sk_buff *skb) {return (struct ebt_802_3_hdr *)skb_mac_header(skb); } 获取802.3MAC头指针。static inline struct ethhdr *eth_hdr(const struct sk_buff *skb) {return (struct ethhdr *)skb_mac_header(skb); } 获取以太网MAC头指针。以太网头指针结构体: struct ethhdr {unsigned char h_dest[ETH_ALEN]; /* destination eth addr */unsigned char h_source[ETH_ALEN]; /* source ether addr */__be16 h_proto; /* packet type ID field */ } __attribute__((packed));内核中网络地址转化为字符串形式的IP地址的宏定义: #define NIPQUAD(addr) \ ((unsigned char *)&addr)[0], \ ((unsigned char *)&addr)[1], \ ((unsigned char *)&addr)[2], \ ((unsigned char *)&addr)[3] #define NIPQUAD_FMT "%u.%u.%u.%u"
sk_buff结构分析相关推荐
- 【linux驱动分析】之dm9000驱动分析(三):sk_buff结构分析
[linux驱动分析]之dm9000驱动分析(一):dm9000原理及硬件分析 [linux驱动分析]之dm9000驱动分析(二):定义在板文件里的资源和设备以及几个宏 [linux驱动分析]之dm9 ...
- 【linux驱动分析】之dm9000驱动分析
[linux驱动分析]之dm9000驱动分析(一):dm9000原理及硬件分析 [linux驱动分析]之dm9000驱动分析(一):dm9000原理及硬件分析 [linux驱动分析]之dm9000驱动 ...
- LINUX网络驱动开发
1 Linux网络设备驱动程序概述 Linux网络驱动程序遵循通用的接口.设计时采用的是面向对象的方法. 一个设备就是一个对象(net_device 结构),它内部有自己的数据和方法. 一个网络设备最 ...
- Network | sk_buff
sk_buff结构可能是linux网络代码中最重要的数据结构,它表示接收或发送数据包的包头信息.它在中定义,并包含很多成员变量供网络代码中的各子系统使用. 这个结构被不同的网络层(MAC或者其 ...
- PHPCMS V9数据库表结构分析
PHPCMS V9可以轻松承载百万级的访问数据,最大的功臣就是PHPCMS良好的数据库结构,在数据库的设计方面,一定是下足了功夫. 一般网站的信息量离这个级别相差甚远,但是了解学习一下PHPCMS的数 ...
- 第二讲:Android系统构架分析和应用程序目录结构分析
2019独角兽企业重金招聘Python工程师标准>>> 本讲内容: Android系统构架简介 Android应用程序结构分析 点这里下载:Android学习指南第二讲源代码 一.A ...
- Spring Boot常见企业开发场景应用、自动配置原理结构分析
读者应具备: Spring SpringMVC服务器端开发基础 Maven基础 本篇主要介绍Spring Boot在企业开发中常见场景的使用.以及Spring Boot的基本原理结构. 以下为本篇设计 ...
- Linux内核--网络协议栈深入分析(一)--与sk_buff有关的几个重要的数据结构
本文分析基于Linux Kernel 3.2.1 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7971463 更多请查看专栏htt ...
- VS2013 解决方案文件结构分析
VS2013 解决方案文件结构分析 参考文章: (1)VS2013 解决方案文件结构分析 (2)https://www.cnblogs.com/haogj/p/4248030.html 备忘一下.
最新文章
- hdu2006 求奇数的乘积【C++】
- Android非空格式验证框架Validation的使用---第三方库学习笔记(四)
- html写个用户协议,五分钟学会HTML5的WebSocket协议
- Linux实时查看进程命令top笔记
- Java面向对象---重写(Override)与重载(Overload)
- mysql linux32_Linux 配置 mysql 5.7.32 实操记录
- http://ju.outofmemory.cn/entry/307891---------TICK
- 鸿蒙系统可以安装teams吗,鸿蒙致命弱点被曝光!不能装这个软件,80%用户将望而却步!...
- 十六进制高低位转换,返回十进制数
- 算法设计与分析——概述
- 类似微信的即时通讯服务器,除了微信,还有这些常用即时通讯APP
- java随机数生成字母_java生成随机数字和字母组合
- 微信公众上传头像和分享
- 库克谈人工智能:增长飞快 兼具颠覆性和创造性
- SpringBoot解决跨域请求的OPTIONS问题
- 分别编写计算球的表面积和体积的函数area和volume。在主函数中输入球的半径,分别调用函数area和volume计算并输出球的表面积和体积。
- Pandas 实用技能,数据筛选 query 函数详细介绍
- 26个顶尖战略咨询公司常用分析模型详解!
- [概率论]图像里的“白噪声”——电视机搜不到台时雪花斑点的形成原因 (不信谣,不传谣,与宇宙微波背景辐射没有任何关系)
- C语言设计窗帘自动拉伸,基于st8952的自动窗帘设计.doc