Home > OS >  sockets: right way to access IP and UDP headers on raw socket buffer
sockets: right way to access IP and UDP headers on raw socket buffer

Time:09-14

struct msghdr msg;
struct iovec iov;
unsigned char buf[BUFSIZE] = { '\0', };
ssize n;
int fd;
...

fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));

...

msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_flags = 0;

iov.iov_base = buf;
iov.iov_len = sizeof(buf);

...   
n = recvmsg(sockfd, &msg, 0);
...
parse_pkt(buf, BUFSIZE);

So far so good, the packet is received, and now I need to parse it:

static int parse_packet(unsigned char *pkt, int len)
{
    struct iphdr *ip = (struct iphdr *)(pkt   14);
    struct udphdr *udp;

    /* ip has valid pointer and we can explore IP header fields, ip pointer is NOT modified. */
    ...

    udp = (struct udphdr *)(ip   (ip->ihl << 2));
    /* at this point I'm expecting udp to point past IP header space. */
    ...

}

The problem I'm seeing is that udp does not point where I'm expecting, I don't get why: pkt contains the whole packet (including Ethernet header, no VLANs), so ip obtains a pointer past ether_header, so udp = (struct udphdr *)(ip (ip->ihl << 2)) should just skip over IP header size, but it does not!

What does work though is this:

struct iphdr *ip = (struct iphdr *)(pkt   14);
...
udp = (struct udphdr *)(pkt   14   (ip->ihl << 2));

What is it so, what am I doing wrong?

CodePudding user response:

When you do this:

udp = (struct udphdr *)(ip   (ip->ihl << 2));

You're doing pointer arithmetic in units of sizeof(*ip) instead of 1.

Your alternate works:

udp = (struct udphdr *)(pkt   14   (ip->ihl << 2));

Because pkt is an unsigned char * so pointer arithmetic is done in single byte units.

This would also work:

udp = (struct udphdr *)((unsigned char *)ip   (ip->ihl << 2));

As it allow you to perform pointer arithmetic in single byte units.

  • Related