Linux中iphdr、tcphdr结构体的__LITTLE_ENDIAN_BITFIELD和__BIG_ENDIAN_BITFIELD

代码

在/usr/include/uapi/linux/ip.h和/usr/include/uapi/linux/tcp.h中分别有如下代码定义了ip以及tcp头部
iphdr:

struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
	__u8	ihl:4,
			version:4;
#elif defined (__BIG_ENDIAN_BITFIELD)
	__u8	version:4,
  			ihl:4;
#else
#error	"Please fix <asm/byteorder.h>"
#endif
	__u8	tos;
	__be16	tot_len;
	__be16	id;
	__be16	frag_off;
	__u8	ttl;
	__u8	protocol;
	__sum16	check;
	__be32	saddr;
	__be32	daddr;
	/*The options start here. */
};

tcphdr:

struct tcphdr {
        __u16        source;
        __u16        dest;
        __u32        seq;
        __u32        ack_seq;
#if defined(__LITTLE_ENDIAN_BITFIELD)
        __u16   res1:4,
                doff:4,
                fin:1,
                syn:1,
                rst:1,
                psh:1,
                ack:1,
                urg:1,
                ece:1,
                cwr:1;
#elif defined(__BIG_ENDIAN_BITFIELD)
        __u16   doff:4,
                res1:4,
                cwr:1,
                ece:1,
                urg:1,
                ack:1,
                psh:1,
                rst:1,
                syn:1,
                fin:1;
#else
#error        "Adjust your <asm/byteorder.h> defines"
#endif       
        __u16        window;
        __u16        check;
        __u16        urg_ptr;
};

这其中:4等是C语言中的位域,表示取二进制中的低四位(在大端序中这个低四位是存储在高地址的)。

疑问

初读这些代码,我还并不理解为何有大小端的区别,"big endian"和"little endian"的区别是在按字节之间的存储顺序上。比如0x12345678
在"little endian"上表示为(假设基址为0x100):

0x100 0x78 (01111000)
0x101 0x56
0x102 0x34
0x103 0x12

在“big endian"上表示为:

0x100 0x12
0x101 0x34
0x102 0x56
0x103 0x78 (01111000)

显然对单个字节来说,其中的位存储还是相同的,所以我并不理解为何在Linux的代码中,要把它们一些字节的位域定义成相反的顺序。

解答

经过我搜集资料后终于找到了答案:

譬如ip头部ip_hdr,假设仅仅是直接对ihl或者version进行操作,确实无需分辨大小端字节序,Linux代码中那样写反而有些多此一举。

但使用者可能使用memcpy来直接对这开头的8位进行赋值操作,而这在大端序和小端序的机器上会产生不同的情况。

举个经典的例子,比如说下述代码:

u_int16_t t = 0x1;
u_int8_t x[2];
memcpy(x, t);

在小端序的机器上结果应该是

x[0] x[1]
10 … 00

而在大端序的机器上执行结果会变为:

x[0] x[1]
00 … 01

注:x[1]的地址都是比x[0]高的。

因此为了提高兼容性,使程序能够被小端序和大端序的机器共用。须要预先推断是大端序还是小端序。并调换ihr和version在内存中的位置。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`struct sk_buff` 是 Linux 内核用来表示网络数据包的数据结构。它是一个很重要的数据结构,因为 Linux 内核网络协议栈的所有数据包处理都是基于 sk_buff 进行的。 下面是 `struct sk_buff` 结构的定义: ```c struct sk_buff { struct sk_buff *next; struct sk_buff *prev; ktime_t tstamp; struct sock *sk; struct net_device *dev; char cb[48]; unsigned int len; unsigned int data_len; __u16 mac_len; __u16 hdr_len; union { __u16 all; struct { __u16 nfmark : 16; } nfctmark; struct { __u16 pkt_type : 3; __u16 ignore_df : 1; __u16 nf_trace : 1; __u16 ip_summed : 2; __u16 ooo_okay : 1; __u16 l4_rxhash : 1; __u16 sw_hash : 1; __u16 sw_hash_valid : 1; __u16 l5_hash_valid : 1; __u16 l4_hash_valid : 1; __u16 fclone : 2; __u16 frag_list : 2; __u16 rxhash : 1; __u16 loopback : 1; __u16 vlan_present : 1; __u16 vlan_tci : 16; __u16 inner_protocol : 16; __u16 inner_transport_header : 16; } parsed; } encapsulation; unsigned char protocol; unsigned char pkt_type: 3; unsigned char fclone: 2; unsigned char ip_summed: 2; unsigned char ooo_okay: 1; __u16 vlan_proto; __u16 vlan_tci; union { struct { __be16 h_vlan_TCI; __be16 h_vlan_encapsulated_proto; }; __be32 ipv4; struct ipv6hdr *ipv6h; struct arphdr *arph; struct tcphdr *hth; struct udphdr *huh; struct icmphdr *icmph; } protocol_headers; union { struct tcphdr *th; struct udphdr *uh; struct icmphdr *icmph; struct igmphdr *igmph; struct iphdr *iph; struct ipv6hdr *ipv6h; struct arphdr *arph; struct pppox_hdr *pppoe; struct snap_header *llc; struct cfm_pdu_header *cfm_pdu; struct batadv_unicast_packet *batman_adv; struct batadv_bcast_packet *batman_adv_bcast; struct batadv_icmp_packet *batman_adv_icmp; struct batadv_tvlv_packet *batman_adv_tvlv; struct batadv_frag_packet *batman_adv_frag; struct batadv_purge_packet *batman_adv_purge; struct batadv_gw_packet *batman_adv_gw; struct batadv_tt_change_packet *batman_adv_tt_change; struct batadv_mcast_packet *batman_adv_mcast; struct batadv_frag_list *batman_adv_frag_list; struct batadv_unicast_4addr_packet *batman_adv_unicast_4addr; struct batadv_bla_claim *batman_adv_bla_claim; struct batadv_bla_backbone_gw *batman_adv_bla_backbone_gw; struct batadv_bla_claim_reply *batman_adv_bla_claim_reply; struct batadv_bla_claim_confirm *batman_adv_bla_claim_confirm; struct batadv_bla_claim_ack *batman_adv_bla_claim_ack; struct batadv_bla_update *batman_adv_bla_update; struct batadv_bla_claim_broadcast *batman_adv_bla_claim_broadcast; struct batadv_bla_claim_broadcast_reply *batman_adv_bla_claim_broadcast_reply; } encapsulated; char *head; char *data; char *tail; char *end; unsigned int truesize; atomic_t users; }; ``` `struct sk_buff` 结构的字段含义如下: - `next` 和 `prev`:`struct sk_buff` 是一个双向链表,这两个字段用于链表操作; - `tstamp`:时间戳,用于记录数据包的接收或发送时间; - `sk`:指向网络套接字的指针; - `dev`:指向网络设备的指针; - `cb`:可选的控制块,用于保存一些协议栈内部使用的数据; - `len`:数据包的总长度; - `data_len`:数据包实际负载的长度; - `mac_len`:物理层帧头的长度; - `hdr_len`:网络层协议头的长度; - `encapsulation`:封装信息,用于保存协议头的解析结果; - `protocol`:网络协议号; - `pkt_type`:数据包类型; - `fclone`:用于指示是否进行数据包复制; - `ip_summed`:用于指示是否需要计算 IP 校验和; - `ooo_okay`:用于指示是否允许乱序到达的数据包; - `vlan_proto` 和 `vlan_tci`:VLAN 标签的协议号和标识符; - `protocol_headers`:协议头指针的联合; - `encapsulated`:封装协议的联合; - `head`:指向数据包缓冲区首地址的指针; - `data`:指向数据包负载首地址的指针; - `tail`:指向数据包负载末地址的指针; - `end`:指向数据包缓冲区末地址的指针; - `truesize`:数据包缓冲区的实际大小; - `users`:用于记录当前正在使用该数据包的线程数的原子计数器。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值