views:

64

answers:

2

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?

A: 

You can't pass binary data accross architectures by just copying memory.

You need to separate out each component and copy it as an int/string/float etc, even then you need to take care about int size and byte ordering.

Unless you have a well defined standard binary format (like an image) it may be easier to send things as strings.

Martin Beckett
+2  A: 

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
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
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