skb解析

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了skb解析相关的知识,希望对你有一定的参考价值。

skb(Struct sk_buffer)是TCP/IP堆栈中用于收发包的缓冲区域。它在接收数据的时候会进行2次拷贝,以提升性能:数据包进入网卡驱动后拷贝一次,从内核空间递交给用户空间的应用时再拷贝一次。网络中所有数据包的封装及解封都是通过这个结构进行的。

struct sk_buff {
struct sk_buff                       *next;
struct sk_buff                       *prev;
struct sock                            *sk;
struct skb_timeval               tstamp;
struct net_device           *dev;
struct net_device           *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;

struct  dst_entry                   *dst;
struct       sec_path                *sp;
char                                         cb[40];

unsigned int                           len,
data_len,
mac_len,
csum;
__u32                                      priority;

__u8                                         local_df:1,
cloned:1,
ip_summed:2,
nohdr:1,
nfctinfo:3;
__u8                                         pkt_type:3,
fclone:2;
__be16                                    protocol;
void                                          (*destructor)(struct sk_buff *skb);

/* These elements must be at the end, see alloc_skb() for details.  */
unsigned int                           truesize;
atomic_t                                 users;
unsigned char                       *head,
*data,
*tail,
*end;
};

一、成员变量解析

tstamp 可通过调用net_enable_timestamp()用于记录数据包send/recv时间。但是这个计算 会消耗一些时间。

*dev、*input_dev 这两个值都是用来区分不同的设备,input_dev通常用来指向接收数据包优先级最高的网络输入设备。

cb[40] skb的控制块, 用来保存重传状态或数据包的序列号等信息。

len 表示skb中数据缓冲的总长度

priority 定义数据包的优先级, 传输控制模块根据这个值来实现对数据包的分类,以决定调度策略

cloned 当对一个已存在的skb进行复制后,新skb会共享已有的skb数据区,这时这两个skb的clone值都会被设置成1

truesize 就是skb所消耗的内存(skb本身+databuffer)

rcvbuf 接收数据包的总大小

sndbuf 发送数据包的总大小

[*head, *data, *tail, *end]

这四个指正用来实现对databuffer的管理。head和end用于指向buffer, data和tail用来指向实际的数据。这四个指正将buffer划分为三个区

  • packet data:用来保存真正的数据

  • head room:packetdata上面的空闲空间,用于给协议头分配内存

  • tail room: packetdata下面的空闲空间, 用于给数据分配内存

技术分享图片技术分享图片

划分了三个区,这样当数据包需要增加协议头的时候就只需要从上层空间拿一块内存,当需要增加数据的时候就只需要从下层空间拿一块内存。这样skb就只需要申请一次内存,之后的处理只需通过指正就可完成。


二、关于skb的一些操作

  • 创建一个skb结构

static inline struct sk_buff *alloc_skb( unsigned int size, gfp_t priority)
{
return  __alloc_skb(size, priority, 0, -1) //fclone位,1表对当前skb克隆, 0表不克隆
}

size : 为将要分配的缓存区的大小;

priority : 取值为GFP_ATOMIC, GFP_KERNEL等(指对内存抢占的优先级);

技术分享图片技术分享图片

  • 释放skb结构

void kfree_skb(struct sk_buff *skb);这里会进行一个判断,如果skb->users > 1,则只进行atomic_dec(&skb->user)操作,然后返回。如果skb->users = 1,则才会将struct sk_buff结构体所占的内存还给系统。

这里应该是判断是否存在共享,没咋搞懂。。。


三、内核接收数据包方式

  • 中断方式

2.4之前都是用中断方式接收的。数据包到达网卡后产生一个中断,中断服务程序将接收的数据拷贝到skb,然后将skb挂入软中断队列上,同时将设备poll_list加入软中断的poll_list列表并标记,然后等待软中断处理。发生软中断的时候将skb从软中断队列中取出,发送给网络上层(netif_receive_skb())。

  • 轮询方式

2.6中增加了轮询机制。当中断来临时,它首先直接将设备poll_list挂入软中断的poll_list,打上软中断标记,先不对数据包进行接收,把接收数据包的任务给软中断了。发生软中断时直接把网卡数据poll给skb, 然后交给上层(netif_receive_skb())。

netif_receive_skb()得到skb包之后就处理该数据包, 检查正确性,打印时间戳


以上是关于skb解析的主要内容,如果未能解决你的问题,请参考以下文章

关于skb_header_pointer函数

Linux 网络协议栈开发代码分析篇之数据收发 —— netif_receive_skb()函数

片段(Java) | 机试题+算法思路+考点+代码解析 2023

Linux内核:从skb获取udp头,udp_hdr()获取到是错误的udp头(转)

Linux内核:从skb获取udp头,udp_hdr()获取到是错误的udp头(转)

skb管理函数之skb_clonepskb_copyskb_copy