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.