1

I'm a little confused regarding data alignment. On x86, we typically take alignment for granted. However, I'm programming on a system that is very strict and will error out if I try to access unaligned data.

Heres my problem:

First, I'm going to show you some structs I have:

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 */
 };

I'm dealing with pcap. Pcap will return a pointer to a data packet to me:

u_char *packet;

Lets pretend the packet is a couple hundred bytes. What I typically do is cast that packet to several struct pointers, so I can access the data directly.

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

Ok. So everything looks great right? On x86, everything appears to work right. On any other archetecture that has strict alignment, I have issues when accessing some values and it will typically result in a sigbus. For example:

sip->ip_len = 0x32AA;

or

u_short val = sip->ip_len;

results in an error. I'm guessing its because it's misaligned in memory from the cast. Whats typically the best way to handle this when doing these kind of casts?

  • In gcc, you have __attribute__((packed)) to tell the compiler to align the structures tightly, without any padding. – Plumenator Aug 04 '11 at 15:56
  • 1
    `__attribute__((packed))` would make no difference in this case, as those structures have no padding. –  Dec 14 '13 at 19:57

1 Answers1

2

The easy way is to use memcpy

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

This assumes that your two machines use the same byte order, and have been careful to take account of structure padding.

The harder and more general way to handle this is to construct the values from the individual bytes:

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

Of course, you'd probably use a function or macro to abstract this.

Doug Currie
  • 40,708
  • 1
  • 95
  • 119
  • 2
    Rather than using `+` and `<<`, you can do the `memcpy()` to an intermediate variable, then use `ntohs()` to convert from network to host byte order. – caf Oct 03 '10 at 05:52
  • Yes, but that assumes that the structures were initially filled in using network order (big endian). I got the impression from the question that they were filled in using x86 (little endian) order. – Doug Currie Oct 03 '10 at 18:07
  • From the question, "pcap will return a pointer to a data packet to me" means the code is receiving packets; the Ethernet type, and fields in the IP header, are all big-endian, not little-endian. The issue about x86 vs. non-x86 is with alignment requirements, not with the byte order; x86 doesn't (by default) require alignment, but *some* other instruction set architectures (for example, SPARC - which happens to be big-endian by default) do require it. –  Dec 14 '13 at 19:53