Reputation: 593
I'm trying to catch all ICMP "destination unreachable - port unreachable" messages using RAW sockets in C++. And then process the underlying UDP protocol to find out for which port the destination was not reachable. So far I managed to receive the ICMP messages and filter them based on their type and code. However, I'm having hard time accessing the udp header. So far I tried these:
*(struct udphdr*)(icmp + sizeof(struct icmphdr) + sizeof(struct iphdr))
and
struct iphdr *ip = (struct iphdr*)ether_frame;
struct icmphdr *icmp = (struct icmphdr*)(ether_frame + ip->ihl*4);
struct iphdr *ip2 = (struct iphdr*)(icmp + sizeof(struct icmphdr))
struct udphdr *udphdr = (struct udphdr*)(ip2 + ip2->ihl*4)
But neither one worked. I'm sure it's just some simple pointers arithmetic mistake, but I cannot figure it out. So my questions are: How exactly are the protocols organized in ICMP response? How can I access the port number which was unreachable from a raw icmp response?
EDIT: The socket used for receiving was created using this->rcv_sd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP
and the response is received using recv(this->rcv_sd, ether_frame, IP_MAXPACKET, 0)
, where ether_frame
is a uint8_t
array of size IP_MAXPACKET
.
Upvotes: 1
Views: 591
Reputation: 595827
You are using pointer arithmetic, adding byte counts to struct pointers as-is, so statements like (icmp + sizeof(struct icmphdr))
and (ip2 + ip2->ihl*4)
are not doing what you think they are doing, causing you to end up with wrong addresses.
When you add a number to a typed pointer, the pointer is adjusted by the number of bytes of the underlying type multiplied by the specified number.
So, this:
struct iphdr *ip2 = (struct iphdr*)(icmp + sizeof(struct icmphdr))
Is equivalent to this:
struct iphdr *ip2 = (struct iphdr*)(((uint8_t*)icmp) + (sizeof(*icmp) * sizeof(struct icmphdr)))
And this:
struct udphdr *udphdr = (struct udphdr*)(ip2 + ip2->ihl*4)
Is equivalent to this:
struct udphdr *udphdr = (struct udphdr*)(((uint8_t*)ip2) + (sizeof(*ip2) * (ip2->ihl*4)))
Since you want to offset your struct pointers by bytes, not elements, you need to type-cast the struct pointers to byte pointers before adding the byte counts to them:
struct iphdr *ip2 = (struct iphdr*)(((uint8_t*)icmp) + sizeof(struct icmphdr))
// or simpler, using correct pointer arithmetic:
// struct iphdr *ip2 = (struct iphdr*)(icmp + 1)
struct udphdr *udphdr = (struct udphdr*)(((uint8_t*)ip2) + (ip2->ihl*4))
You don't have this issue when assigning your ip
and icmp
pointers from your ether_frame
buffer because ether_frame
is an array of uint8_t
, which decays as-is to a uint8_t*
pointer.
Upvotes: 3