Safe, efficient way to access unrelated data in a network packet from C

I am writing a program in C for Linux on an ARM9 processor. The program is designed to access network packets, which include a sequence of tagged data, for example:

<fieldID><length><data><fieldID><length><data> ...

The fieldID and length fields are uint16_t. Data can be 1 or more bytes (up to 64k if full length is used, but it is not).

As long as it <data>has an even number of bytes, I don't see a problem. But if I have a 1- or 3- or 5-byte <data>section, the next 16-bit field identifier does not end at the 16-bit boundary, and I expect alignment problems. It has been a while since I did something like this from scratch, so I'm a little unsure of the details. Any feedback is appreciated. Thanks.

+4
source share
4 answers

To avoid alignment problems in this case, refer to all data as unsigned char *. So:

unsigned char *p;
//...
uint16_t id = p[0] | (p[1] << 8);
p += 2;

The above example assumes a small data representation size, where the low byte comes first in a multibyte number.

+6
source

The easiest way is to manually rebuild uint16_ts due to speed:

uint8_t *packet = ...;
uint16_t fieldID = (packet[0] << 8) | packet[1];  // assumes big-endian host order
uint16_t length = (packet[2] << 8) | packet[2];
uint8_t *data = packet + 4;
packet += 4 + length;

If your processor supports it, you can pun or use the union (but beware of a strict alias ).

uint16_t fieldID = htons(*(uint16_t *)packet);
uint16_t length = htons(*(uint16_t *)(packet + 2));

, (, - ), , .

, , :

static char static_buffer[65540];
memcpy(static_buffer, packet, packet_size);  // make sure packet_size <= 65540
uint16_t fieldId = htons(*(uint16_t *)static_buffer);
uint16_t length = htons(*(uint16_t *)(static_buffer + 2));

№1, .

+4

( / , , , ), . - :

uint16_t unaligned_uint16( void* p)
{
    // this assumes big-endian values in data stream
    //  (which is common, but not universal in network
    //  communications) - this may or may not be 
    //  appropriate in your case

    unsigned char* pByte = (unsigned char*) p;

    uint16_t val = (pByte[0] << 8) | pByte[1];

    return val;
}
+4

, , , , .

By fixing endian-ness problems, you can memcpy from the "real" byte pointer to what you want / need to properly align, and everything will be fine.

(this works because the generated code will load / store data in bytes that are safe for alignment. This is when the generated assembly has instructions for loading and storing 16/32/64 bits of memory incorrectly, everything falls apart).

+1
source

All Articles