2010-10-03 66 views
1

对于数据对齐我有点困惑。在x86上,我们通常会采取一致行为。但是,我在非常严格的系统上编程,如果尝试访问未对齐的数据,将会出错。与网络编程的数据对齐

我的继承人问题:

首先,我要告诉你一些结构,我有:

struct sniff_ethernet { 
    u_char ether_dhost[6]; /* Destination host address */ 
    u_char ether_shost[6]; /* Source host address */ 
    u_short ether_type; /* IP? ARP? RARP? etc */ 
}; 

struct sniff_ip { 
    u_char ip_vhl; /* version << 4 | header length >> 2 */ 
    u_char ip_tos; /* type of service */ 
    u_short ip_len; /* total length */ 
    u_short ip_id; /* identification */ 
    u_short ip_off; /* fragment offset field */ 
    u_char ip_ttl; /* time to live */ 
    u_char ip_p; /* protocol */ 
    u_short ip_sum; /* checksum */ 
    struct in_addr ip_src,ip_dst; /* source and dest address */ 
}; 

我处理PCAP。 PCAP将指向一个数据包回我:

u_char *packet; 

让我们假装的包是几百个字节。我通常所做的就是将该数据包转换为多个结构指针,以便我可以直接访问数据。

struct sniff_ethernet *seth = (struct sniff_ethernet *) packet; 
struct sniff_ip *sip = (struct sniff_ip *) (packet + 14); // 14 is the size of an ethernet header 

好的。所以一切看起来都很棒吧?在x86上,一切看起来都是正确的。在任何其他严格对齐的架构中,访问某些值时遇到问题,并且通常会导致sigbus。例如:

sip->ip_len = 0x32AA; 

u_short val = sip->ip_len; 

导致错误。我猜它是因为它在演员阵容中的错位。在做这些类型的演员时,什么是典型的最佳处理方式?

+0

在gcc中,__attribute __((packed))告诉编译器紧密排列结构,没有任何填充。 – Plumenator 2011-08-04 15:56:05

+0

'__attribute __((packed))'在这种情况下没有区别,因为这些结构没有填充。 – 2013-12-14 19:57:39

回答

2

最简单的方法是使用memcpy

struct sniff_ip sip; 
memcpy(&sip, packet + 14, sizeof(sip)); 

这是假设你的两台机器使用相同的字节顺序,并一直在谨慎考虑结构的填充。

处理这种情况更困难和更普遍的方式是从单个字节构成值:

u_short val; 
int offset = 14 + offsetof(sniff_ip, ip_len); 
val = packet[offset] + (packet[offset+1] << 8); // assuming little endian packet 

当然,你可能会使用一个函数或宏摘要。

+2

您可以使用'memcpy()'作为中间变量,然后使用'ntohs()'将网络转换为主机字节顺序,而不是使用'+'和'<<'。 – caf 2010-10-03 05:52:11

+0

是的,但假设结构最初是使用网络顺序填充(大端)。我从使用x86(小端)顺序填充的问题中获得了印象。 – 2010-10-03 18:07:38

+0

从这个问题,“pcap将返回一个指向数据包的指针给我”意味着代码正在接收数据包;以太网类型和IP报头中的字段都是大端,而不是小端。关于x86与非x86的问题与对齐需求有关,而不是字节顺序; x86不需要(默认情况下)需要对齐,但是*一些其他指令集体系结构(例如,SPARC--默认情况下恰好是big-endian)需要它。 – 2013-12-14 19:53:20