#include <linux/module.h>
#include <linux/init.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter.h>
#include <linux/ip.h>
static unsigned int skb_buff_ops(unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *)
)
{
struct iphdr *iph = ip_hdr(skb);
printk("\n=========================================\n");
printk("nsec=%u\n", (unsigned int)skb->tstamp.tv64);
printk("skb_buf=%u\n", (unsigned int)skb);
printk("tail=%u\n", (unsigned int)skb->tail);
printk("head=%u\n", (unsigned int)skb->head);
printk("end=%u\n", (unsigned int)skb->end);
printk("data=%u\n", (unsigned int)skb->data);
printk("len=%u\n", ntohs(iph->tot_len) + 14);
printk("skb->len=%d\n", skb->len);
printk("skb->data_len=%d\n", skb->data_len);
printk("===========================================");
return NF_ACCEPT;
}
static struct nf_hook_ops skb_ops ={
.hook = skb_buff_ops,
.pf = PF_INET,
.hooknum = NF_INET_FORWARD,
.priority = NF_IP_PRI_FIRST+2,
};
static int __init init_eg(void)
{
if(!nf_register_hook(&skb_ops))
{
return 0;
}
return 0;
}
static void __exit exit_eg(void)
{
nf_unregister_hook(&skb_ops);
}
MODULE_LICENSE("GPL");
module_init(init_eg);
module_exit(exit_eg);
输出结果:
由于netfilter处于网络层(L3),所以skb->data指向网络层,以上的所有分析都是基于网络层的。
struct sk_buff *next; //指向下一个sk_buff的指针
struct sk_buff *prev; //指向上一个sk_buff的指针
skb->sk
这是一个指向拥有这个sk_buff的sock结构的指针。这个指针在网络包由本机发出或者由本机进程接收时有效,因为插口相关的信息被L4(TCP或 UDP)或者用户空间程序使用。如果sk_buff只在转发中使用(这意味着,源地址和目的地址都不是本机地址),这个指针是NULL
sk_buff->len
表示当前协议数据包的长度。它包括主缓冲区中的数据长度(data指针指向它)和分片中的数据长度。
skb->data_len
data_len只计算分片中数据的长度,由于没有分片所以截图中的值为0
skb->mac_len
这是mac头的长度
skb->users
这是一个引用计数,用于计算有多少实体引用了这个sk_buff缓冲区。它的主要用途是防止释放sk_buff后,还有其他实体引用这个sk_buff。 因此,每个引用这个缓冲区的实体都必须在适当的时候增加或减小这个变量。这个计数器只保护sk_buff结构本身,而缓冲区的数据部分由类似的计数器 (dataref)来保护.有时可以用atomic_inc和atomic_dec函数来直接增加或减小users,但是,通常还是使用函数 skb_get和kfree_skb来操作这个变量。
skb->truesize
这是缓冲区的总长度,包括sk_buff结构和数据部分。如果申请一个len字节的缓冲区,alloc_skb函数会把它初始化成len+sizeof(sk_buff)
void (*destructor)(struct sk_buff *skb)
这个函数指针可以初始化成一个在缓冲区释放时完成某些动作的函数。如果缓冲区不属于一个socket,这个函数指针通常是不会被赋值的。如果缓冲区属于一 个socket,这个函数指针会被赋值为sock_rfree或sock_wfree(分别由skb_set_owner_r或 skb_set_owner_w函数初始化)。这两个sock_xxx函数用于更新socket的队列中的内存容量。
skb->tstamp
这个变量只对接收到的包有意义。它代表包接收时的时间戳,或者有时代表包准备发出时的时间戳。它在netif_rx里面由函数net_timestamp设置,而netif_rx是设备驱动收到一个包后调用的函数。
skb->dev
这个变量的类型是net_device,net_device它代表一个网络设备。dev的作用与这个包是准备发出的包还是刚接收的包有关。当收到一个包时,设备驱动会把sk_buff的dev指针指向收到这个包的网络设备;当一个包被发送时,这个变量代表将要发送这个包的设备。在发送网络包时设置这个值的代码要比接收网络包时设置这个值的代码复杂。有些网络功能可以把多个网 络设备组成一个虚拟的网络设备(也就是说,这些设备没有和物理设备直接关联),并由一个虚拟网络设备驱动管理。当虚拟设备被使用时,dev指针指向虚拟设 备的net_device结构。而虚拟设备驱动会在一组设备中选择一个设备并把dev指针修改为这个设备的net_device结构。因此,在某些情况 下,指向传输设备的指针会在包处理过程中被改变。
skb->input_dev
这是收到包的网络设备的指针。如果包是本地生成的,这个值为NULL。对以太网设备来说,这个值由eth_type_trans初始化,它主要被流量控制代码使用。
skb->transport_header
skb->network_header
skb->mac_header
这些是指向TCP/IP各层协议头的指针:transport_header指向L4(传输层),network_header指向L3(网络层),mac_header指向L2(数据链路层)。每个指针的类型都是一个联合, 包含多个数据结构,每一个数据结构都表示内核在这一层可以解析的协议。例如,transport_header是一个包含内核所能解析的L4协议的数据结构的联合。每一个联合都有一个 raw变量用于初始化,后续的访问都是通过协议相关的变量进行的。
当接收一个包时,处理n层协议头的函数从其下层(n-1层)收到一个缓冲区,它的skb->data指向n层协议的头。处理n层协议的函数把本层的 指针(例如,L3对应的是skb->network_header指针)初始化为skb->data,因为这个指针(data指针)的值会在处理下一层协议时改变 (skb->data将被初始化成缓冲区里的其他地址)。在处理n层协议的函数结束时,在把包传递给n+1层的处理函数前,它会把skb->data指针指向n层协议头的末尾,这正好是n+1层协议的协议头。
当网卡驱动程序收到一个UDP数据报后,它创建一个结构体struct sk_buff,确保sk_buff->data成员指向的空间足够存放收到的数据(对于数据报分片的情况,因为比较复杂,我们暂时忽略,我们假设 一次收到的是一个完整的UDP数据报)。把收到的数据全部拷贝到sk_buff->data指向的空间,然后,把skb->mac.raw指 向data,此时,数据报的开始位置是一个以太网头,所以skb->mac.raw指向链路层的以太网头。然后通过调用skb_pull剥掉以太网 头,所谓剥掉以太网头,只是把data加上sizeof(struct ethhdr),同时len减去这个值,这样,在逻辑上,skb已经不包含以太网头了,但通过skb->mac.raw还能找到它。这就是我们通常 所说的,IP数据报被收到后,在链路层被剥去以太网头。
sk_buff->cloned
一个布尔标记,当被设置时,表示这个结构是另一个sk_buff的克隆
sk_buff->pkt_type
这个变量表示帧的类型,分类是由L2的目的地址来决定的。这个值在网卡驱动程序中由函数eth_type_trans通过判断目的以太网地址来确定。如果 目的地址是FF:FF:FF:FF:FF:FF,则为广播地址,pkt_type = PACKET_BROADCAST;如果最高位为1,则为组播地址,pkt_type = PACKET_MULTICAST;如果目的mac地址跟本机mac地址不相等,则不是发给本机的数据报,pkt_type = PACKET_OTHERHOST;否则就是缺省值PACKET_HOST。
/* Packet types */
#define PACKET_HOST 0 /* To us */
#define PACKET_BROADCAST 1 /* To all */
#define PACKET_MULTICAST 2 /* To group */
#define PACKET_OTHERHOST 3 /* To someone else */
#define PACKET_OUTGOING 4 /* Outgoing of any type */
sk_buff->protocol
这个变量是高层协议从二层设备的角度所看到的协议。典型的协议包括IP,IPV6和ARP。完整的列表在 include/linux/if_ether.h中。由于每个协议都有自己的协议处理函数来处理接收到的包,因此,这个域被设备驱动用于通知上层调用哪 个协议处理函数。每个网络驱动都调用netif_rx来通知上层网络协议的协议处理函数,因此protocol变量必须在这些协议处理函数调用之前初始 化。
----------------------------------------
linux内核是模块化的,你可以选择包含或者删除某些功能。因此,sk_buff结构里面的一些成员变量只有在内核选择支持某些功能时才有效,比如防火墙(netfilter)或者qos:
__u32 nfctinfo
...
#ifdef CONFIG_NETFILTER
struct nf_conntrack *nfct;
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
struct sk_buff *nfct_reasm;
#endif
#ifdef CONFIG_BRIDGE_NETFILTER
struct nf_bridge_info *nf_bridge;
#endif
__u32 nfmark;
#endif /* CONFIG_NETFILTER */
这些变量被netfilter使用(防火墙代码),内核编译选项是“Device Drivers->Networking support-> Networking options-> Network packet filtering”和两个子选项“Network packet filtering debugging”和“Bridged IP/ARP packets filtering”
#ifdef CONFIG_NET_SCHED
__u16 tc_index;
#ifdef CONFIG_NET_CLS_ACT
__u16 tc_verd;
#endif
#endif
这两个变量被流量控制代码使用。tc_index只有在编译时定义了CONFIG_NET_SCHED符号才有效;tc_verd只有在编译时定义了CONFIG_NET_CLS_ACT符号才有效。这两个符号可以通过选择特定的编译选项来定义:
Networking --->
Networking options --->
QoS and/or fair queueing --->
QoS and/or fair queueing
Actions
QoS选项不能被编译成内核模块。原因就是,内核编译之后,由某个选项所控制的数据结构是不能动态变化的。一般来说,如果某个选项会修改内核数据结构(比如说,在sk_buff里面增加一个项tc_index),那么,包含这个选项的组件就不能被编译成内核模块。
你可能经常需要查找是哪个make menuconfig编译选项或者变种定义了某个#ifdef标记,以便理解内核中包含的某段代码。在2.6内核中,最快的,查找它们之间关联关系的方 法,就是查找分布在内核源代码树中的kconfig文件中是否定义了相应的符号(每个目录都有一个这样的文件)。在2.4内核中,你需要查看 Documentation/Configure.help文件。
Quality of Service(QoS)服务质量
本文部分原创,部分是参考别人的,若有侵犯版权,请告知,立即删除!!
- 大小: 7.9 KB
分享到:
相关推荐
理解 `struct sk_buff` 的设计原理及其内部结构对于深入研究 Linux 网络栈至关重要。 #### `struct sk_buff` 的重要性 `struct sk_buff` 是 Linux 网络子系统中最核心的数据结构之一,主要用于表示接收或发送的...
`sk_buff`结构体在`<include/linux/skbuff.h>`文件中定义,随着Linux内核版本的演进,其内部结构也经历了多次调整与优化。 #### 二、`sk_buff`结构的重要性 `sk_buff`结构之所以重要,主要因为它承载着数据包从低层...
3. truesize表示整个sk_buff结构所占用的内存大小,包括了分配给数据缓冲区的内存。 sk_buff还包含与引用计数相关的几个成员,比如users和cloned。这些成员用于跟踪sk_buff的使用情况,从而正确管理内存的释放。 ...
在Linux操作系统中,...理解`sk_buff`的结构和工作原理对于深入研究Linux网络编程和系统优化至关重要。阅读《Linux-Skb_buff.doc》文档将进一步深入解释这些概念,帮助开发者更好地理解和利用`sk_buff`进行网络编程。
sk_buff结构体定义在<include/linux/skbuff.h>头文件中,该数据结构允许网络协议栈以一致且高效的方式处理数据包。sk_buff在处理网络数据包时,提供了丰富的信息和控制机制,确保网络层间通信的正确性和效率。 sk_...
在Linux内核TCP/IP协议栈的静态分析中,不仅要理解上述的sk_buff结构,还要深入理解网络设备驱动的注册过程,以及网络层、TCP层、UDP层之间是如何双向调用和协同工作的。Linux内核的网络子系统是一个高度抽象和模块...
它在 `<include/linux/skbuff.h>` 头文件中定义,包含了丰富的成员变量,以满足不同层次的网络协议处理需求。 `sk_buff` 结构的成员变量分为多个类别: 1. **Layout 布局**:这部分成员变量主要涉及到缓冲区的数据...
其中,`makefile`教程和技术文档通常涉及构建和编译程序的过程,而`sk_buff`结构分析则与Linux内核中的网络数据包处理密切相关。 首先,让我们深入了解一下`makefile`。在程序开发中,`makefile`是一个至关重要的...
在`<include/linux/skbuff.h>`中定义,`skb_buff`不仅包含了网络数据包的基本信息,还具备了管理数据包在不同协议层之间传递的功能。该结构随着时间的推移不断演化,以适应新的特性和性能优化。 `skb_buff`的成员...
3. sk_buff(socket buffer):`sk_buff`是Linux内核中用于网络数据传输的数据结构,全称为socket buffer。它封装了网络数据包,并包含了数据包的相关信息,如头部、数据、控制信息等。`sk_buff`在内核的网络协议栈...
在Linux网络栈中,数据包的处理涉及多个关键的数据结构,包括`msghdr`、`socket`、`sk_buff`和`sock`等。 - **`msghdr`结构**:用于描述消息的元数据,包括消息名称、消息名称长度、消息控制信息等。例如: ```c ...
这些函数并不属于标准的C语言库,而是Linux内核网络栈中用于处理网络数据包Sk_buff(Socket Buffer)的数据结构的相关函数。Sk_buff是一种高效的数据结构,用于存储网络协议栈中的数据包。 1. **skb_header_pointer...
它在`<include/linux/skbuff.h>`中定义,包含大量的成员变量供网络代码中的各个子系统使用。随着Linux内核的发展,该结构经历了多次改动和优化,增加了新选项并改进了成员变量的布局。 **成员变量分类:** 1. **...
每个数据包分配一块内存作为缓存区,sk_buff结构的head、data、tail和end指针分别指示数据区域的开始、当前使用、结束位置。当数据包从网络设备接收时,数据会被放入sk_buff结构,然后根据目的IP地址进行路由决策。...