I'd avoid mapping packets onto structs. A lot can go wrong due to alignment issues.
And, nctnico is wrong about this again.
I have never seen any issue with mapping packets onto structs. Actually, the whole internet (BSD, linux) works on C code written that way. Endianness is handled with simple swapping macros which are conditionally defined based on the architecture. Alignment is not an issue at all as I will explain below.
On the contrary, large parsers, when written by a human,
almost always contain mistakes. It's not just one or two times someone got wrong the amount of shift, forgot parantheses, stepped into integer promotion rule traps or got some type cast wrong, typed < instead of <<, or, most usually, just forgot to update the damn parser when the interface changed. If the type changed but variable name did not, you don't even get a warning!
Then, alignment. When using normal struct, compiler pads the variables so they are self-aligning. This becomes a problem when different types of architectures with different padding rules communicate with each other. Solution: packed attribute.
With packed attribute, padding is removed and fields of the struct may not be self-aligned, except by luck. But contrary to what nctnico repeatedly claims, being corrected, but nevertheless continuing to supply the same wrong information, this is not a problem. nctnico's mistake happens when he thinks packed attribute only controls the padding when the struct is created and that's it. This is not true. packed attribute is part of the type qualification, so compiler knows the struct is packed everywhere it is used, and can always create correct access instructions. And trust me, compilers are nearly bug-free and will create correct code much more likely than nctnico writing low-level bit manipulation code by hand, trying to outsmart the compiler.
One rule results from this: casting away the packed attribute is dangerous. This happens if you take a pointer of a packed struct
member and use that. But this is also why compilers warn about this. Seemingly gcc does this even with minimal warnings enabled (no -Wall):
typedef struct __attribute__((packed))
{
char c1;
int i;
char c2;
} s_t;
int main()
{
extern void f1(s_t * s);
extern void f2(int * i);
s_t s = {0};
f1(&s); // correct use
f2(&s.i); // possible alignment error
return 0;
}
hrst@jorma:~$ gcc t3.c
t3.c: In function ‘main’:
t3.c:17:5: warning: taking address of packed member of ‘struct <anonymous>’ may result in an unaligned pointer value [-Waddress-of-packed-member]
17 | f2(&s.i);
| ^~~~
This is not a reason not to use packed structs. With similar reasoning, any qualifier like volatile is dangerous because you can cast it away (and compiler will warn you, too).
TLDR:
* use data types to convey the intent and let the compiler generate the memory access code for you.
* do not try to outsmart the compiler by manipulating bits, unless you really have to. While popular decades ago, it is prone to error.
* C is higher level language than many think. Use this to your advantage.
* packed struct does automatically exactly what nctnico is doing manually. This is a compromise in performance compared to normal struct, as compiler cannot and will not make unsafe assumptions about alignment.