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"

转发自http://www.cnblogs.com/iceocean/articles/1594160.html

时间: 2024-10-05 05:20:01

sk_buff 结构分析的相关文章

【linux驱动分析】之dm9000驱动分析(三):sk_buff结构分析

[linux驱动分析]之dm9000驱动分析(一):dm9000原理及硬件分析 [linux驱动分析]之dm9000驱动分析(二):定义在板文件里的资源和设备以及几个宏 [linux驱动分析]之dm9000驱动分析(四):net_device结构体 [linux驱动分析]之dm9000驱动分析(五):另外几个重要的结构体 [linux驱动分析]之dm9000驱动分析(六):dm9000_init和dm9000_probe的实现 [linux驱动分析]之dm9000驱动分析(七):dm9000的卸

Linux Netfilter开发小结

前置知识: IP包: struct ip { #if BYTE_ORDER == LITTLE_ENDIAN unsigned char ip_hl:4, /* header length */ ip_v:4; /* version */ #endif unsigned char ip_tos; /* type of service */ short ip_len; /* total length */ unsigned short ip_id;/* identification */ shor

PNG文件结构分析 ---Png解析

PNG文件结构分析 ---Png解析 为了实现更高级的应用,我们必须充分挖掘PNG的潜力. PNG的文件结构 根据PNG文件的定义来说,其文件头位置总是由位固定的字节来描述的: 十进制数 137 80 78 71 13 10 26 10 十六进制数 89 50 4E 47 0D 0A 1A 0A 其中第一个字节0x89超出了ASCII字符的范围,这是为了避免某些软件将PNG文件当做文本文件来处理.文件中剩余的部分由3个以上的PNG的数据块(Chunk)按照特定的顺序组成,因此,一个标准的PNG文

【转载】nedmalloc结构分析

原文:nedmalloc结构分析 nedmalloc是一个跨平台的高性能多线程内存分配库,很多库都使用它,例如:OGRE.现在我们来看看nedmalloc的实现 (以WIN32部分为例)    位操作小技巧:i.获取最低位的出现位置的掩码:x&(-x)ii.判断值为2的幂:x & (x-1) == 0iii.获取从最低的值为1的位开始到左边MSB的掩码: x | (-x)iv.字节对齐:(x + 2^m) &( 2^m -1) nedmalloc设计的数据结构和使用方法有几个有趣的

sk_buff整理笔记(二、操作函数)

承接上一篇:sk_buff整理笔记(一.数据结构)这一篇要讲的是内核为sk_buff结构提供的一些操作函数. 第一.首先要讲的是sk_buff中的四大指针: 四大指针分别是:head.data.tail.end:这四个指针都是指向了数据区的,数据区中存放的是协议头和数据.head和end指针是每个数据包一样的,也是有这两个指针来确定数据区的大小的.最开始的时候head.data.tail三个指针都是指向一起的,当有协议数据加进来时才更改. 再来说下包的形成,首先是应用层的数据,然后到四层加上四层

chromium for android GPU进程结构分析

GPU进程的客户端(Browser进程,Render进程)都会创建一个 WebGraphicsContext3DCommandBufferImpl的实例,用于封装与GPU进程的通信. 所以我们从WebGraphicsContext3DCommandBufferImpl开始引出GPU进程的结构. 一.WebGraphicsContext3DCommandBufferImpl的创建. WebGraphicsContext3D暴露的接口基本与OpenGL ES 2.0 API对应. WebGraphi

struct sk_buff 结构

struct sk_buff可能是linux网络代码中最重要的数据结构,它表示接收或发送数据包的包头信息,并包含很多成员变量供网络代码中的各子系统使用.    这个结构被网络的不同层(MAC或者其他二层链路协议,三层的IP,四层的TCP或UDP等)使用,并且其中的成员变量在结构从一层向另一层传递时改变. L4向L3传递前会添加一个L4的头部,同样,L3向L2传递前,会添加一个L3的头部.添加头部比在不同层之间拷贝数据的效率更高.由于在缓冲区的头部 添加数据意味着要修改指向缓冲区的指针,这是个复杂

Android 4.0 ICS SystemUI浅析——StatusBar结构分析

Android 4.0 ICS SystemUI浅析——StatusBar结构分析 分类: Android2012-06-30 14:45 23687人阅读 评论(8) 收藏 举报 androidsignal代码分析iconseclipse平台 在上一篇文章<Android 4.0 ICS SystemUI浅析——SystemUI启动流程>中以及提到了SystemUI的组成,本文主要分析其中的StatusBar结构. 1.布局概览 首先,我们通过hierarchyviewer这个工具来查看一下

转载,crtmpserver文件夹结构分析

1. 顶层(crtmpserver/) 下的文件夹结构 3rdparty/             : 对lua, xml进行解析的源代码文件夹 sources/              : 项目的核心实现源代码文件夹 builders/              : 各种环境(VS2010, android, linux)下的项目生成 configs/               : 一些预定义的配置文件和示例启动脚本 docs/                  : 项目相关的文档 man