1

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.

sveatlo
  • 543
  • 9
  • 27
  • 1
    "*How exactly are the protocols organized in ICMP response?*" - I suggest you use Wireshark or other similar packet sniffer+analyzer to see the actual packet structure for yourself. – Remy Lebeau Oct 31 '17 at 18:36
  • Of course I did that, but it seemed to me, that my first try (`(struct udphdr*)(icmp + sizeof(struct icmphdr) + sizeof(struct iphdr))`) was the most relevant to what I saw in wireshark. – sveatlo Oct 31 '17 at 18:37
  • yes, otherwise i couldn't filter them based on the icmp's type and code. I also looked at them in gdb and their content was (as expected) identical to what I saw in wireshark. – sveatlo Oct 31 '17 at 18:43
  • just a typo in the question. should be fixed now. – sveatlo Oct 31 '17 at 18:45
  • ICMP and UDP are different things. You can't simply get one from the other. – Jesper Juhl Oct 31 '17 at 18:58
  • @JesperJuhl: an ICMP "destination unreachable" or "port unreachable" packet contains a copy of the UDP/TCP packet that couldn't be delivered. The OP is trying to extract that inner packet. – Remy Lebeau Oct 31 '17 at 18:59
  • @Remy Lebeau ohh. I misunderstood then. I thought OP was trying to interpret the ICMP packet itself as a UDP packet. My bad. – Jesper Juhl Oct 31 '17 at 19:26
  • @sveatlo, ICMP has a structure, it stores the original packet (well, not the whole packet, only the first xxx bytes) in a field, so I suggest you to point there, you'll find the IP header of the packet in trouble in the data section of the ICMP packet. Also, the marked answer would be helpful:) – Luis Colorado Nov 01 '17 at 06:53

1 Answers1

3

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.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770