1、skb结构体
skb定义
linux/skbuff.h
重要的数据成员包括(暂不分析,完整结构见文章最后的附录)
struct sk_buff {
...
__u16 transport_header;
__u16 network_header;
__u16 mac_header;
...
/* These elements must be at the end, see alloc_skb() for details. */
sk_buff_data_t tail;
sk_buff_data_t end;
unsigned char *head,
*data;
unsigned int truesize;
...
}
1.1 以太网(mac)头部
1.1.1 结构及获取
linux/if_ether.h中,定义其结构为:
struct ethhdr {
unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ //目的mac地址
unsigned char h_source[ETH_ALEN]; /* source ether addr */ //源mac地址
__be16 h_proto; /* packet type ID field */
} __attribute__((packed));
#define ETH_ALEN 6 /* Octets in one ethernet addr */
常见协议h_proto类型有:
#define ETH_P_IP 0x0800 /* Internet Protocol packet */
#define ETH_P_ARP 0x0806 /* Address Resolution packet */
#define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */
#define ETH_P_IPV6 0x86DD /* IPv6 over bluebook */
获取方式(仅列举常用):
struct ethhdr * peth_hdr = NULL;
peth_hdr = eth_hdr(skb);
打印mac地址方法:
printk("dst_addr %pM src_addr %pM \n", peth_hdr->h_dest, peth_hdr->h_source);
1.1.2 vlan头部
linux/if_vlan.h 中定义
/**
* struct vlan_ethhdr - vlan ethernet header (ethhdr + vlan_hdr)
* @h_dest: destination ethernet address
* @h_source: source ethernet address
* @h_vlan_proto: ethernet protocol
* @h_vlan_TCI: priority and VLAN ID
* @h_vlan_encapsulated_proto: packet type ID or len
*/
struct vlan_ethhdr {
unsigned char h_dest[ETH_ALEN];
unsigned char h_source[ETH_ALEN];
__be16 h_vlan_proto;
__be16 h_vlan_TCI;
__be16 h_vlan_encapsulated_proto;
//这个后面紧跟着__be16 h_proto; /* packet type ID field */
};
只是为了擦除vlan头部,可以如下操作:
if (peth_hdr->h_proto == htons(ETH_P_8021Q))
peth_hdr = (struct ethhdr *)((unsigned char*)peth_hdr + VLAN_HLEN);
#define VLAN_HLEN 4 /* The additional bytes required by VLAN
* (in addition to the Ethernet header)
*/
之后重新判断h_proto即可,此时不能获取h_dest、h_source,因为这两个值已经不对了。
1.1.3 ip头部
判断是否为IP(ipv4)协议
struct iphdr * pip_hdr = NULL;
if(peth_hdr->h_proto == htons(ETH_P_IP)) {
pip_hdr = (struct iphdr *) ((unsigned char*)peth_hdr + sizeof(struct ethhdr));
}
linux/ip.h中定义:
struct iphdr {
__u8 version:4,
ihl:4;
__u8 tos;
__be16 tot_len;
__be16 id;
__be16 frag_off;
__u8 ttl; //生存时间
__u8 protocol; //协议类型
__sum16 check;
__be32 saddr; //源Ip
__be32 daddr; //目的IP
/*The options start here. */
};
ip打印方法:
u32 saddr, daddr;
saddr = ntohl(pip_hdr->saddr);
daddr = ntohl(pip_hdr->daddr);
printk("sa %pI4h da %pI4h\n", &saddr, &daddr);
常见的ip protocol协议类型
IPPROTO_ICMP = 1, /* Internet Control Message Protocol */
IPPROTO_IGMP = 2, /* Internet Group Management Protocol */
IPPROTO_TCP = 6, /* Transmission Control Protocol */
IPPROTO_UDP = 17, /* User Datagram Protocol */
1.1.4 ICMP头部
ICMP协议属于IP协议分支,一般ping指令发出的包均为icmp协议包,linux/icmp.h中:
struct icmphdr {
__u8 type; //icmp报文类型
__u8 code;
__sum16 checksum;
union {
struct {
__be16 id;
__be16 sequence; //序列号
} echo;
__be32 gateway;
struct {
__be16 __unused;
__be16 mtu;
} frag;
__u8 reserved[4];
} un;
};
icmp type常见报文类型
#define ICMP_ECHOREPLY 0 /* Echo Reply */ //ping回复
#define ICMP_DEST_UNREACH 3 /* Destination Unreachable */ //ping不通时
#define ICMP_ECHO 8 /* Echo Request */ //ping请求
1.1.5 IGMP头部
IGMP协议属于IP协议分支,用于路由器间的组播协议,linux/igmp.h中:
struct igmphdr {
__u8 type;
__u8 code; /* For newer IGMP */
__sum16 csum;
__be32 group;
};
参考相关协议,暂不过多分析
1.1.6 TCP/UDP头部
暂不分析,后续需要使用时再补充
附录
sk_buff完整结构
struct sk_buff {
union {
struct {
/* These two members must be first. */
struct sk_buff *next;
struct sk_buff *prev;
union {
struct net_device *dev;
/* Some protocols might use this space to store information,
* while device pointer would be NULL.
* UDP receive path is one user.
*/
unsigned long dev_scratch;
};
};
struct rb_node rbnode; /* used in netem, ip4 defrag, and tcp stack */
struct list_head list;
};
union {
struct sock *sk;
int ip_defrag_offset;
};
union {
ktime_t tstamp;
u64 skb_mstamp;
};
/*
* 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] __aligned(8);
unsigned long _skb_refdst;
void (*destructor)(struct sk_buff *skb);
bool (*vendor_free)(struct sk_buff *skb, bool force_release);
__u32 vendor_free_priv[4];
#ifdef CONFIG_XFRM
struct sec_path *sp;
#endif
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
unsigned long _nfct;
#endif
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
struct nf_bridge_info *nf_bridge;
#endif
unsigned int len,
data_len;
__u16 mac_len,
hdr_len;
/* Following fields are _not_ copied in __copy_skb_header()
* Note that queue_mapping is here mostly to fill a hole.
*/
__u16 queue_mapping;
/* if you move cloned around you also must adapt those constants */
#ifdef __BIG_ENDIAN_BITFIELD
#define CLONED_MASK (1 << 7)
#else
#define CLONED_MASK 1
#endif
#define CLONED_OFFSET() offsetof(struct sk_buff, __cloned_offset)
__u8 __cloned_offset[0];
__u8 cloned:1,
nohdr:1,
fclone:2,
peeked:1,
head_frag:1,
xmit_more:1,
pfmemalloc:1;
/* fields enclosed in headers_start/headers_end are copied
* using a single memcpy() in __copy_skb_header()
*/
/* private: */
__u32 headers_start[0];
/* public: */
/* if you move pkt_type around you also must adapt those constants */
#ifdef __BIG_ENDIAN_BITFIELD
#define PKT_TYPE_MAX (7 << 5)
#else
#define PKT_TYPE_MAX 7
#endif
#define PKT_TYPE_OFFSET() offsetof(struct sk_buff, __pkt_type_offset)
__u8 __pkt_type_offset[0];
__u8 pkt_type:3;
__u8 ignore_df:1;
__u8 nf_trace:1;
__u8 ip_summed:2;
__u8 ooo_okay:1;
__u8 l4_hash:1;
__u8 sw_hash:1;
__u8 wifi_acked_valid:1;
__u8 wifi_acked:1;
__u8 no_fcs:1;
/* Indicates the inner headers are valid in the skbuff. */
__u8 encapsulation:1;
__u8 encap_hdr_csum:1;
__u8 csum_valid:1;
__u8 csum_complete_sw:1;
__u8 csum_level:2;
__u8 csum_not_inet:1;
__u8 dst_pending_confirm:1;
#ifdef CONFIG_IPV6_NDISC_NODETYPE
__u8 ndisc_nodetype:2;
#endif
__u8 ipvs_property:1;
__u8 inner_protocol_type:1;
__u8 remcsum_offload:1;
#ifdef CONFIG_NET_SWITCHDEV
__u8 offload_fwd_mark:1;
#endif
#ifdef CONFIG_NET_CLS_ACT
__u8 tc_skip_classify:1;
__u8 tc_at_ingress:1;
__u8 tc_redirected:1;
__u8 tc_from_ingress:1;
#endif
__u8 gro_skip:1;
#ifdef CONFIG_NET_SCHED
__u16 tc_index; /* traffic control index */
#endif
union {
__wsum csum;
struct {
__u16 csum_start;
__u16 csum_offset;
};
};
__u32 priority;
int skb_iif;
__u32 hash;
__be16 vlan_proto;
__u16 vlan_tci;
#if defined(CONFIG_NET_RX_BUSY_POLL) || defined(CONFIG_XPS)
union {
unsigned int napi_id;
unsigned int sender_cpu;
};
#endif
#ifdef CONFIG_NETWORK_SECMARK
__u32 secmark;
#endif
union {
__u32 mark;
__u32 reserved_tailroom;
};
union {
__be16 inner_protocol;
__u8 inner_ipproto;
};
__u16 inner_transport_header;
__u16 inner_network_header;
__u16 inner_mac_header;
__be16 protocol;
__u16 transport_header;
__u16 network_header;
__u16 mac_header;
/* private: */
__u32 headers_end[0];
/* public: */
/* These elements must be at the end, see alloc_skb() for details. */
sk_buff_data_t tail;
sk_buff_data_t end;
unsigned char *head,
*data;
unsigned int truesize;
refcount_t users;
};