这几天在调试有关网卡驱动的东西,有很多地方不清楚。有关网卡驱动部分主要有两个很重要的结构体:struct net_device 和struct sk_buff。 驱动大部分都是围绕这两个东西进行操作的,包括加协议头尾,去头去尾等。为了搞清楚协议栈如何处理数据包,周末闲来无事就看看内核代码去了解下这部分东西,并做了简要记录:

/*
*sk_buff->h :传输层头 :udp头和tcp头
*sk_buff->nh :网络层头 :ip头
*sk_buff->mac :数据链路层头 :mac头
*
*
*sk_buff->head :指向数据缓冲区头部
*sk_buff->data :指向实际数据的头部
*sk_buff->tail :指向实际数据的尾部
*sk_buff->end :指向数据缓冲区尾部
*
*
*sk_buff控制区 :struct sk_buff所在的区域
*线性数据区 :数据缓冲区域 : sk_buff->head~sk_buff->end 之间的区域
*非线性数据区 :数据缓冲区域的补充区域 :skb_shared_info区域
*
*
*sk_buff->truesize:线性数据区+非线性数据区+sizeof(struct sk_buff)
*sk_buff->len :线性数据区+非线性数据区
*sk_buff->data_len:非线性数据区
*
*
*一个完整的网络帧(skb_buff)包括:
* 线性数据区 + 非线性数据区 + skb_buff控制区
*
* skb_clone() :只复制skb_buff控制区,其中新分配的skb_buff和原来的skb_buff共享线性数据区和非线性数据区
*
* pskb_copy() :复制skb_buff控制区 + 线性数据区,共享非线性数据区
*
* skb_copy() :复制skb_buff控制区 + 线性数据区 + 非线性数据区
*
*
*
*
*/

源码附上:

struct sk_buff { /*表示接收或发送数据包的包头信息,其成员变量在从一层向另一层传递时会发生修改*/
/* These two members must be first. */
struct sk_buff *next;
struct sk_buff *prev; struct sk_buff_head *list;
struct sock *sk;
struct timeval stamp;
struct net_device *dev;
struct net_device *input_dev;
struct net_device *real_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; /*
* This is the control buffer. It is free to use for every
* layer. Please put your private variables there. If you
* want to keep them across layers you have to do a skb_clone()
* first. This is owned by whoever has the skb queued ATM.
*/
char cb[40]; /*
*
*在sk_buff这个里面没有实际的数据,这里仅仅是控制信息,数据是通过后面的data指针指向其他内存块的!
*那个内存块中是线性数据和非线性数据!那么len 就是length(线性数据) + length(非线性数据),alloc分配的长度
*
*/
unsigned int len, /* len : 代表整个数据区域的长度!skb的组成是有sk_buff控制 + 线性数据 + 非线性数据(skb_shared_info) 组成!*/
data_len, /*data_len: 指的是length(非线性数据)*/
mac_len,
csum;
unsigned char local_df,
cloned:1,
nohdr:1, /*仅仅引用数据区域*/
pkt_type,
ip_summed;
__u32 priority;
unsigned short protocol,
security; void (*destructor)(struct sk_buff *skb); #ifdef CONFIG_NETFILTER
unsigned long nfmark; /*nfmark,用于钩子之间通信*/
__u32 nfcache;
__u32 nfctinfo;
struct nf_conntrack *nfct; #ifdef CONFIG_NETFILTER_DEBUG
unsigned int nf_debug;
#endif #ifdef CONFIG_BRIDGE_NETFILTER
struct nf_bridge_info *nf_bridge;
#endif
#endif /* CONFIG_NETFILTER */
#if defined(CONFIG_HIPPI)
union {
__u32 ifield;
} private;
#endif
#ifdef CONFIG_NET_SCHED
__u32 tc_index; /* traffic control index */
#ifdef CONFIG_NET_CLS_ACT
__u32 tc_verd; /* traffic control verdict */
__u32 tc_classid; /* traffic control classid */
#endif #endif /*
* 划重点!
*
* These elements must be at the end, see alloc_skb() for details.
*
*/
unsigned int truesize;
atomic_t users;
unsigned char *head, /*指向分配给的线性数据内存首地址*/
*data, /*指向保存数据内容的首地址*/
*tail, /*指向数据的结尾*/
*end; /*指向分配的内存块的结尾*/
};

/**
* skb_copy - create private copy of an sk_buff 如果要修改数据,使用该函数。不仅复制sk_buff控制区,也复制数据区。是一个完整的备份
* @skb: buffer to copy
* @gfp_mask: allocation priority
*
* Make a copy of both an &sk_buff and its data. This is used when the
* caller wishes to modify the data and needs a private copy of the
* data to alter. Returns %NULL on failure or the pointer to the buffer
* on success. The returned buffer has a reference count of 1.
*
* As by-product this function converts non-linear &sk_buff to linear
* one, so that &sk_buff becomes completely private and caller is allowed
* to modify all the data of returned buffer. This means that this
* function is not recommended for use in circumstances when only
* header is going to be modified. Use pskb_copy() instead.
*/ struct sk_buff *skb_copy(const struct sk_buff *skb, int gfp_mask)
{
int headerlen = skb->data - skb->head;
/*
* Allocate the copy buffer
*/
struct sk_buff *n = alloc_skb(skb->end - skb->head + skb->data_len, /*分配的空间大小为:sizeof(线性数据区 + 非线性数据区)*/
gfp_mask);
if (!n)
return NULL; /* Set the data pointer */
skb_reserve(n, headerlen); /*skb_reserve 分配headerlen大小的headroom 空间*/
/* Set the tail pointer and length */
skb_put(n, skb->len);
n->csum = skb->csum;
n->ip_summed = skb->ip_summed; if (skb_copy_bits(skb, -headerlen, n->head, headerlen + skb->len))
BUG(); copy_skb_header(n, skb);
return n;
} ​
/**
* pskb_copy - create copy of an sk_buff with private head.
* @skb: buffer to copy
* @gfp_mask: allocation priority
*
* Make a copy of both an &sk_buff and part of its data, located
* in header. Fragmented data remain shared. This is used when
* the caller wishes to modify only header of &sk_buff and needs
* private copy of the header to alter. Returns %NULL on failure
* or the pointer to the buffer on success.
* The returned buffer has a reference count of 1.
*/ struct sk_buff *pskb_copy(struct sk_buff *skb, int gfp_mask)/*复制sk_buff控制区和线性数据区,非线性数据区依然共享*/
{
/*
* Allocate the copy buffer
*/
struct sk_buff *n = alloc_skb(skb->end - skb->head, gfp_mask); if (!n)
goto out; /* Set the data pointer */
skb_reserve(n, skb->data - skb->head);
/* Set the tail pointer and length */
skb_put(n, skb_headlen(skb));
/* Copy the bytes */
memcpy(n->data, skb->data, n->len);
n->csum = skb->csum;
n->ip_summed = skb->ip_summed; n->data_len = skb->data_len;
n->len = skb->len; if (skb_shinfo(skb)->nr_frags) {
int i; for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i];
get_page(skb_shinfo(n)->frags[i].page);
}
skb_shinfo(n)->nr_frags = i;
} if (skb_shinfo(skb)->frag_list) {
skb_shinfo(n)->frag_list = skb_shinfo(skb)->frag_list;
skb_clone_fraglist(n);
} copy_skb_header(n, skb);
out:
return n;
}
/**
* skb_clone - duplicate an sk_buff :只复制一个和skb_buff,该skb_buff的指针的值与原来的skb值相同
* @skb: buffer to clone
* @gfp_mask: allocation priority
*
* Duplicate an &sk_buff. The new one is not owned by a socket. Both
* copies share the same packet data but not structure. The new
* buffer has a reference count of 1. If the allocation fails the
* function returns %NULL otherwise the new buffer is returned.
*
* If this function is called from an interrupt gfp_mask() must be
* %GFP_ATOMIC.
*/ struct sk_buff *skb_clone(struct sk_buff *skb, int gfp_mask)
{
struct sk_buff *n = kmem_cache_alloc(skbuff_head_cache, gfp_mask); if (!n)
return NULL; #define C(x) n->x = skb->x /*只复制控制区,因此定义一个宏函数方便复制操作*/ n->next = n->prev = NULL;
n->list = NULL;
n->sk = NULL;
C(stamp);
C(dev);
C(real_dev);
C(h);
C(nh);
C(mac);
C(dst);
dst_clone(skb->dst);
C(sp);
#ifdef CONFIG_INET
secpath_get(skb->sp);
#endif
memcpy(n->cb, skb->cb, sizeof(skb->cb));
C(len);
C(data_len);
C(csum);
C(local_df);
n->cloned = 1;
n->nohdr = 0;
C(pkt_type);
C(ip_summed);
C(priority);
C(protocol);
C(security);
n->destructor = NULL;
#ifdef CONFIG_NETFILTER
C(nfmark);
C(nfcache);
C(nfct);
nf_conntrack_get(skb->nfct);
C(nfctinfo);
#ifdef CONFIG_NETFILTER_DEBUG
C(nf_debug);
#endif
#ifdef CONFIG_BRIDGE_NETFILTER
C(nf_bridge);
nf_bridge_get(skb->nf_bridge);
#endif
#endif /*CONFIG_NETFILTER*/
#if defined(CONFIG_HIPPI)
C(private);
#endif
#ifdef CONFIG_NET_SCHED
C(tc_index);
#ifdef CONFIG_NET_CLS_ACT
n->tc_verd = SET_TC_VERD(skb->tc_verd,0);
n->tc_verd = CLR_TC_OK2MUNGE(skb->tc_verd);
n->tc_verd = CLR_TC_MUNGED(skb->tc_verd);
C(input_dev);
C(tc_classid);
#endif #endif
C(truesize);
atomic_set(&n->users, 1);
C(head);
C(data);
C(tail);
C(end); atomic_inc(&(skb_shinfo(skb)->dataref));
skb->cloned = 1; return n;
}

Linux 网卡驱动sk_buff内核源码随笔的更多相关文章

  1. Linux内核驱动学习(二)添加自定义菜单到内核源码menuconfig

    文章目录 目标 drivers/Kconfig demo下的Kconfig 和 Makefile Kconfig Makefile demo_gpio.c 目标 Kernel:Linux 4.4 我编 ...

  2. Linux内核源码分析方法

    一.内核源码之我见 Linux内核代码的庞大令不少人“望而生畏”,也正因为如此,使得人们对Linux的了解仅处于泛泛的层次.如果想透析Linux,深入操作系统的本质,阅读内核源码是最有效的途径.我们都 ...

  3. Linux基础系列—Linux内核源码目录结构

    /** ****************************************************************************** * @author    暴走的小 ...

  4. Linux内核分析(一)---linux体系简介|内核源码简介|内核配置编译安装

    原文:Linux内核分析(一)---linux体系简介|内核源码简介|内核配置编译安装 Linux内核分析(一) 从本篇博文开始我将对linux内核进行学习和分析,整个过程必将十分艰辛,但我会坚持到底 ...

  5. Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7)【转】

    原文地址:Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://bl ...

  6. Linux内核(3) - 分析内核源码如何入手(下)

    下面的分析,米卢教练说了,内容不重要,重要的是态度.就像韩局长对待日记的态度那样,严谨而细致. 只要你使用这样的态度开始分析内核,那么无论你选择内核的哪个部分作为切入点,比如USB,比如进程管理,在花 ...

  7. Linux内核(2) - 分析内核源码如何入手(上)

    透过现象看本质,兽兽们无非就是一些人体艺术展示.同样往本质里看过去,学习内核,就是学习内核的源代码,任何内核有关的书籍都是基于内核,而又不高于内核的. 既然要学习内核源码,就要经常对内核代码进行分析, ...

  8. 【转】Linux内核源码分析方法

    一.内核源码之我见 Linux内核代码的庞大令不少人“望而生畏”,也正因为如此,使得人们对Linux的了解仅处于泛泛的层次.如果想透析Linux,深入操作系统的本质,阅读内核源码是最有效的途径.我们都 ...

  9. Linux内核源码目录说明

    Linux内核源代码位于/usr/src/linux目录下,其结构分布如图1.3所示,每一个目录或子目录可以看作一个模块,其目录之间的连线表示“子目录或子模块”的关系.下面是对每一个目录的简单描述. ...

随机推荐

  1. NODEJS对象

    1.全局对象 Node.js: global 交互模式下var声明的变量都属于全局下的变量,可以使用global访问,例如global.a 脚本模式下var声明的变量不属于全局下的变量.不能使用glo ...

  2. Python: 解析crontab正则,增加+操作

    以下是使用Python解析crontab时间格式的一个类, 同时minute和hour支持了 + 的操作. 记录一下备忘. 其中的line参数是字符串分拆后的格式, 包含了 "week&qu ...

  3. DC-8 靶机渗透测试

    DC-8 渗透测试 冲冲冲 ,好好学习 . 核心:cms上传添加存在漏洞组件,利用该组件getshell 操作机:kali 172.66.66.129 靶机:DC-4 172.66.66.137 网络 ...

  4. 什么是软件的CLI安装

    Websoft9 在进行开源软件的集成与自动化安装研究过程中发现有些软件有CLI安装模式,例如Gitlab CLI版本.Ghost CLI.PHP CLI等,CLI安装是什么意思? CLI(Comma ...

  5. 【PTA|Python】浙大版《Python 程序设计》题目集:第二章

    前言 Hello!小伙伴! 非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出-   自我介绍 ଘ(੭ˊᵕˋ)੭ 昵称:海轰 标签:程序猿|C++选手|学生 简介:因C语言结识编程,随后转入计 ...

  6. 什么是RSA

    一.RSA引入: RSA是什么,嗯,这是一个好问题,有没有兴趣啊 二.RSA的解释: RSA是一种加密方式,它是现代密码学的代表(什么是现代密码学,这个吗,我感觉就是我们所使用的密码的加密的方式之一可 ...

  7. vue的项目初始化

    1.创建文件 blog 2.下载安装node mongoose 3.(1)vue创建后端项目文件 vue create admin (2)vue创建前端项目文件 vue create web (3)新 ...

  8. 双倍NB!字节跳动资深研发花7天肝出的这份286页“Flutter技术进阶”

    前言 截至目前,字节跳动有很多业务落地了 Flutter 技术方案,包括今日头条.西瓜视频.皮皮虾等 20 多个业务在使用 Flutter 开发,有纯 Flutter 工程,也有 Flutter 与 ...

  9. springboot上传文件路径存放

    @Beanpublic EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer() { return (Config ...

  10. Linux从头学07:中断那么重要,它的本质到底是什么?

    作 者:道哥,10+年的嵌入式开发老兵. 公众号:[IOT物联网小镇],专注于:C/C++.Linux操作系统.应用程序设计.物联网.单片机和嵌入式开发等领域. 公众号回复[书籍],获取 Linux. ...