• IPv4 header and memory alignment...

    From TDH1978@21:1/5 to All on Mon Mar 11 18:57:42 2019
    This question is in regards to the iphdr struct that can be found in /usr/include/linux/ip.h and /usr/include/netinet/ip.h.  In some
    open-source projects, I have seen this header mapped onto packet data,
    to extract individual IPv4 header fields, like so:


        struct iphdr
        {
            unsigned int ihl:4;
            unsigned int version:4;
            uint8_t tos;
            uint16_t tot_len;
            uint16_t id;
            uint16_t frag_off;
            uint8_t ttl;
            uint8_t protocol;
            uint16_t check;
            uint32_t saddr;
            uint32_t daddr;
        };


        struct iphdr* hdr = (struct iphdr*) packet_data;
      
        uint32_t ipv4_src_addr = hdr->saddr;
        uint32_t ipv4_dst_addr = hdr->daddr;
       
       
    Is this safe?  Given that the packet data could start at any memory
    location, what guarantee is there that the 32-bit 'saddr' and 'daddr'
    fields are properly aligned in memory at 32-bit boundaries?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Jorgen Grahn@21:1/5 to All on Tue Mar 12 09:20:52 2019
    On Mon, 2019-03-11, TDH1978 wrote:
    This question is in regards to the iphdr struct that can be found in /usr/include/linux/ip.h and /usr/include/netinet/ip.h.  In some
    open-source projects, I have seen this header mapped onto packet data,
    to extract individual IPv4 header fields, like so:


        struct iphdr
        {
            unsigned int ihl:4;
            unsigned int version:4;
            uint8_t tos;
            uint16_t tot_len;
            uint16_t id;
            uint16_t frag_off;
            uint8_t ttl;
            uint8_t protocol;
            uint16_t check;
            uint32_t saddr;
            uint32_t daddr;
        };


        struct iphdr* hdr = (struct iphdr*) packet_data;
      
        uint32_t ipv4_src_addr = hdr->saddr;
        uint32_t ipv4_dst_addr = hdr->daddr;
       
       
    Is this safe?  Given that the packet data could start at any memory location, what guarantee is there that the 32-bit 'saddr' and 'daddr'
    fields are properly aligned in memory at 32-bit boundaries?

    In the Linux kernel, they make sure it's safe by aligning that memory,
    and knowing things about how the compiler treats structs, more than
    what the C language guarantees.

    In my own code, I prefer not to rely on such things. It gets messy,
    and you still need to take care of endianness. I write my own accessor functions instead:

    /* assuming buf points to a full IPv4 header
    */
    struct in_addr get_saddr(const unsigned char* buf);

    As a bonus, you get a better type than uint32_t.

    /Jorgen

    --
    // Jorgen Grahn <grahn@ Oo o. . .
    \X/ snipabacken.se> O o .

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From TDH1978@21:1/5 to Jorgen Grahn on Thu Mar 14 21:37:46 2019
    On 2019-03-12 09:20:52 +0000, Jorgen Grahn said:

    In the Linux kernel, they make sure it's safe by aligning that memory,
    and knowing things about how the compiler treats structs, more than
    what the C language guarantees.

    In my own code, I prefer not to rely on such things. It gets messy,
    and you still need to take care of endianness. I write my own accessor functions instead:

    /* assuming buf points to a full IPv4 header
    */
    struct in_addr get_saddr(const unsigned char* buf);

    As a bonus, you get a better type than uint32_t.

    Thank you for your reply. I agree your method is safer but I assume
    that, by calling your accessor functions, you will incur a slight
    run-time overhead. If you do this for, say, a billion incoming
    packets, this overhead may become significant.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Jorgen Grahn@21:1/5 to All on Fri Mar 15 14:04:02 2019
    On Fri, 2019-03-15, TDH1978 wrote:
    On 2019-03-12 09:20:52 +0000, Jorgen Grahn said:

    In the Linux kernel, they make sure it's safe by aligning that memory,
    and knowing things about how the compiler treats structs, more than
    what the C language guarantees.

    In my own code, I prefer not to rely on such things. It gets messy,
    and you still need to take care of endianness. I write my own accessor
    functions instead:

    /* assuming buf points to a full IPv4 header
    */
    struct in_addr get_saddr(const unsigned char* buf);

    As a bonus, you get a better type than uint32_t.

    Thank you for your reply. I agree your method is safer but I assume
    that, by calling your accessor functions, you will incur a slight
    run-time overhead. If you do this for, say, a billion incoming
    packets, this overhead may become significant.

    Probably only if you're writing an IP stack. If you act on the
    information you extract I think that processing cost will quickly
    overshadow this inefficiency in the parsing.

    Profiling might be a good idea.

    /Jorgen

    --
    // Jorgen Grahn <grahn@ Oo o. . .
    \X/ snipabacken.se> O o .

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)