2.6内核的sk_buff结构分析
2010-10-26 11:58
435 查看
原文
http://andychenkan.blog.163.com/blog/static/56300913200961695519363/
struct sk_buff {
/* These two members must be first. */
//以下两个变量用于将sk_buff链接到一个双向循环链表中
struct sk_buff *next;
struct sk_buff *prev;
//此报文所属的sock结构,此值在本机发出的报文中有效,从网络设备收到
//的报文此值为空
struct sock *sk;
//此报文收到时的时间
ktime_t tstamp;
//收到此报文的网络设备
struct net_device *dev;
//此报文的路由
union {
struct dst_entry *dst;
struct rtable *rtable;
};
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[48];
//len表示存储区的数据长度和分片长度之和
unsigned int len,
//da
ta_len表示分片长度
data_len;
//mac_len表示mac头的长度
__u16 mac_len,
hdr_len;
union {
__wsum csum;
struct {
__u16 csum_start;
__u16 csum_offset;
};
};
__u32 priority;
__u8 local_df:1,
cloned:1,
ip_summed:2,
nohdr:1,
nfctinfo:3;
//pkt_type,数据报的类型。这个值在网卡驱动程序中由函数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。
__u8 pkt_type:3,
fclone:2,
ipvs_property:1,
peeked:1,
nf_trace:1;
//protocol, 它的值是以太网首部的第三个成员,即帧类型,对于IP数据来讲,
//就是ETH_P_IP(0x8000),对ARP数据报来讲,就是ETH_P_ARP(0x8086)
__be16 protocol;
void (*destructor)(struct sk_buff *skb);
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
struct nf_conntrack *nfct;
struct sk_buff *nfct_reasm;
#endif
#ifdef CONFIG_BRIDGE_NETFILTER
struct nf_bridge_info *nf_bridge;
#endif
int iif;
__u16 queue_mapping;
#ifdef CONFIG_NET_SCHED
__u16 tc_index; /* traffic control index */
#ifdef CONFIG_NET_CLS_ACT
__u16 tc_verd; /* traffic control verdict */
#endif
#endif
#ifdef CONFIG_IPV6_NDISC_NODETYPE
__u8 ndisc_nodetype:2;
#endif
#if defined(CONFIG_MAC80211) || defined(CONFIG_MAC80211_MODULE)
__u8 do_not_encrypt:1;
#endif
/* 0/13/14 bit hole */
#ifdef CONFIG_NET_DMA
dma_cookie_t dma_cookie;
#endif
#ifdef CONFIG_NETWORK_SECMARK
__u32 secmark;
#endif
__u32 mark;
__u16 vlan_tci;
//传输层报文头
sk_buff_da
ta_t transport_header;
//网络报文头
sk_buff_da
ta_t network_header;
//链路报文头
sk_buff_da
ta_t mac_header;
/* These elements must be at the end, see alloc_skb() for details. */
//以下四个变量指向此报文的存储区
//网络报文在存储空间里的存放的顺序
//依次是:链路层的头部,网络层的头部,传输层的头部,传输层的数据
//tail指向网络报文的结束地址
sk_buff_data_t tail;
//end指向存储空间的结束地址
sk_buff_data_t end;
//head指向存储空间的起始地址
unsigned char *head,
//data指向网络报文的起始地址
*data;
//truesize表示存储区总长度(即end-head)和sk_buff本身长度之和
unsigned int truesize;
//表示该sk_buff结构实例被引用的计数。这个是结构本身的引用计数,
//而不是其对应的存储区的引用计数。
atomic_t users;
};
用红色标记这三个成员,分别是传输头,网络头以及mac头相对于Sk_buff的head的偏移。有了这三个成员,可以说为内核编程人员提供了更便利的获取传输层、网络层和MAC层头的偏移。并且,内核也新增了几个函数,来提供获取这些偏移的接口:
#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://andychenkan.blog.163.com/blog/static/56300913200961695519363/
struct sk_buff {
/* These two members must be first. */
//以下两个变量用于将sk_buff链接到一个双向循环链表中
struct sk_buff *next;
struct sk_buff *prev;
//此报文所属的sock结构,此值在本机发出的报文中有效,从网络设备收到
//的报文此值为空
struct sock *sk;
//此报文收到时的时间
ktime_t tstamp;
//收到此报文的网络设备
struct net_device *dev;
//此报文的路由
union {
struct dst_entry *dst;
struct rtable *rtable;
};
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[48];
//len表示存储区的数据长度和分片长度之和
unsigned int len,
//da
ta_len表示分片长度
data_len;
//mac_len表示mac头的长度
__u16 mac_len,
hdr_len;
union {
__wsum csum;
struct {
__u16 csum_start;
__u16 csum_offset;
};
};
__u32 priority;
__u8 local_df:1,
cloned:1,
ip_summed:2,
nohdr:1,
nfctinfo:3;
//pkt_type,数据报的类型。这个值在网卡驱动程序中由函数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。
__u8 pkt_type:3,
fclone:2,
ipvs_property:1,
peeked:1,
nf_trace:1;
//protocol, 它的值是以太网首部的第三个成员,即帧类型,对于IP数据来讲,
//就是ETH_P_IP(0x8000),对ARP数据报来讲,就是ETH_P_ARP(0x8086)
__be16 protocol;
void (*destructor)(struct sk_buff *skb);
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
struct nf_conntrack *nfct;
struct sk_buff *nfct_reasm;
#endif
#ifdef CONFIG_BRIDGE_NETFILTER
struct nf_bridge_info *nf_bridge;
#endif
int iif;
__u16 queue_mapping;
#ifdef CONFIG_NET_SCHED
__u16 tc_index; /* traffic control index */
#ifdef CONFIG_NET_CLS_ACT
__u16 tc_verd; /* traffic control verdict */
#endif
#endif
#ifdef CONFIG_IPV6_NDISC_NODETYPE
__u8 ndisc_nodetype:2;
#endif
#if defined(CONFIG_MAC80211) || defined(CONFIG_MAC80211_MODULE)
__u8 do_not_encrypt:1;
#endif
/* 0/13/14 bit hole */
#ifdef CONFIG_NET_DMA
dma_cookie_t dma_cookie;
#endif
#ifdef CONFIG_NETWORK_SECMARK
__u32 secmark;
#endif
__u32 mark;
__u16 vlan_tci;
//传输层报文头
sk_buff_da
ta_t transport_header;
//网络报文头
sk_buff_da
ta_t network_header;
//链路报文头
sk_buff_da
ta_t mac_header;
/* These elements must be at the end, see alloc_skb() for details. */
//以下四个变量指向此报文的存储区
//网络报文在存储空间里的存放的顺序
//依次是:链路层的头部,网络层的头部,传输层的头部,传输层的数据
//tail指向网络报文的结束地址
sk_buff_data_t tail;
//end指向存储空间的结束地址
sk_buff_data_t end;
//head指向存储空间的起始地址
unsigned char *head,
//data指向网络报文的起始地址
*data;
//truesize表示存储区总长度(即end-head)和sk_buff本身长度之和
unsigned int truesize;
//表示该sk_buff结构实例被引用的计数。这个是结构本身的引用计数,
//而不是其对应的存储区的引用计数。
atomic_t users;
};
用红色标记这三个成员,分别是传输头,网络头以及mac头相对于Sk_buff的head的偏移。有了这三个成员,可以说为内核编程人员提供了更便利的获取传输层、网络层和MAC层头的偏移。并且,内核也新增了几个函数,来提供获取这些偏移的接口:
#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"
相关文章推荐
- 内核中struct sk_buff结构分析
- Linux2.6.39内核sk_buff的结构分析
- sk_buff结构分析
- 【Linux 内核网络协议栈源码剖析】网络栈主要结构介绍(socket、sock、sk_buff,etc)
- sk_buff结构和函数分析
- Linux内核--网络协议栈深入分析(一)--与sk_buff有关的几个重要的数据结构
- Linux内核--网络协议栈深入分析(一)--与sk_buff有关的几个重要的数据结构
- sk_buff结构分析
- linux2.6.24内核源代码分析(1)——扒一扒sk_buff
- Linux内核--网络协议栈深入分析(一)--与sk_buff有关的几个重要的数据结构
- sk_buff结构分析
- 【Linux 内核网络协议栈源码剖析】网络栈主要结构介绍(socket、sock、sk_buff,etc)
- Linux 2.6.32 sk_buff 的结构分析
- sk_buff结构分析
- sk_buff结构分析
- Linux内核--网络协议栈深入分析(一)--与sk_buff有关的几个重要的数据结构
- linux2.6内核 list_head结构分析
- linux协议栈之链路层上的数据传输-----sk_buff结构分析
- sk_buff 结构分析
- Linux内核-从sk_buff{}结构学习“双循环双链表”的实现