Search code examples
cstructalignmentc-preprocessor

C structure alignment and network protocol headers


Solaris 10 SPARC
Sun Studio C compiler 12.3

On SPARC64 machines if you access a variable which isn't correctly aligned on the relevant 4 or 8 byte boundary, you will get a core dump. This requires the coder to jump through a few hoops to cope with this requirement, (but also makes you write portable code too).

If we have a C structure which models a network protocol header, (i.e. these 16 bits are a port, these 8 bits are the flags etc), if we then use alignment directives to suit a SPARC64 processor, will this still retain the byte mapping, or will everything break. Is there logic hiding the implementation of the byte storage from the layout of the struct.

typedef struct TCPHdr_
{
    uint16_t th_sport;  /**< source port */
    uint16_t th_dport;  /**< destination port */
    uint32_t th_seq;    /**< sequence number */
    uint32_t th_ack;    /**< acknowledgement number */
    uint8_t th_offx2;   /**< offset and reserved */
    uint8_t th_flags;   /**< pkt flags */
    uint16_t th_win;    /**< pkt window */
    uint16_t th_sum;    /**< checksum */
    uint16_t th_urp;    /**< urgent pointer */
} TCPHdr;

gets aligned like this:

typedef struct TCPHdr_
{
    uint16_t th_sport    __attribute__((aligned(8))); /**< source port */
    uint16_t th_dport    __attribute__((aligned(8))); /**< destination port */
    uint32_t th_seq      __attribute__((aligned(8))); /**< sequence number */
    uint32_t th_ack      __attribute__((aligned(8))); /**< acknowledgement number */
    uint8_t th_offx2     __attribute__((aligned(8))); /**< offset and reserved */
    uint8_t th_flags     __attribute__((aligned(8))); /**< pkt flags */
    uint16_t th_win      __attribute__((aligned(8))); /**< pkt window */
    uint16_t th_sum      __attribute__((aligned(8))); /**< checksum */
    uint16_t th_urp      __attribute__((aligned(8))); /**< urgent pointer */
} TCPHdr;

Mostly it's a query about code like this:

SET_PKT_LEN(p, sizeof(IPV6Hdr) + sizeof(TCPHdr) + payload_len);

and

p1->tcph = (TCPHdr *)raw_tcp;

where the raw bytes are converted to the struct or a sizeof() tests the size of the struct. Will it still work or will the new struct not be able to map the network bytes?


Solution

  • You can cast an unaligned structure to its aligned version but data would be wrong. You need to put data to the right positions in memory manually. E.g. a function *unaligned_to_aligned* can copy data field by field. It can use unions to avoid core dumps. When working with raw data from the network, take into account endianness. Network protocols and your platform can have different representation of numbers in memory and you may have to change the order of bytes in *int*s, *short*s etc.