2010-10-03 63 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