Annotation of embedaddon/mtr/packet/deconstruct_unix.c, revision 1.1.1.2
1.1 misho 1: /*
2: mtr -- a network diagnostic tool
3: Copyright (C) 2016 Matt Kimball
4:
5: This program is free software; you can redistribute it and/or modify
6: it under the terms of the GNU General Public License version 2 as
7: published by the Free Software Foundation.
8:
9: This program is distributed in the hope that it will be useful,
10: but WITHOUT ANY WARRANTY; without even the implied warranty of
11: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12: GNU General Public License for more details.
13:
1.1.1.2 ! misho 14: You should have received a copy of the GNU General Public License along
! 15: with this program; if not, write to the Free Software Foundation, Inc.,
! 16: 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
1.1 misho 17: */
18:
19: #include "deconstruct_unix.h"
20:
21: #include <stdio.h>
22: #include <stdlib.h>
23: #include <string.h>
24:
25: #include "protocols.h"
1.1.1.2 ! misho 26: #include "sockaddr.h"
1.1 misho 27:
28: #define MAX_MPLS_LABELS 8
29:
30: /*
31: Given an ICMP id + ICMP sequence, find the match probe we've
32: transmitted and if found, respond to the command which sent it
33: */
34: static
35: void find_and_receive_probe(
36: struct net_state_t *net_state,
37: const struct sockaddr_storage *remote_addr,
38: struct timeval *timestamp,
39: int icmp_type,
40: int protocol,
41: int icmp_id,
42: int icmp_sequence,
43: int mpls_count,
44: struct mpls_label_t *mpls)
45: {
46: struct probe_t *probe;
47:
48: probe = find_probe(net_state, protocol, icmp_id, icmp_sequence);
49: if (probe == NULL) {
50: return;
51: }
52:
53: receive_probe(net_state, probe, icmp_type,
54: remote_addr, timestamp, mpls_count, mpls);
55: }
56:
57: /*
58: Handle a UDP packet received embedded in an ICMP reply.
59: The sequence number identifying the probe might be in
60: the source port number, the destination port number, or
61: the checksum. We'll check all three.
62: */
63: static
64: void handle_inner_udp_packet(
65: struct net_state_t *net_state,
66: const struct sockaddr_storage *remote_addr,
67: int icmp_result,
1.1.1.2 ! misho 68: int af,
! 69: const void *ip,
1.1 misho 70: const struct UDPHeader *udp,
71: int udp_length,
72: struct timeval *timestamp,
73: int mpls_count,
74: struct mpls_label_t *mpls)
75: {
76: struct probe_t *probe;
77:
78: probe = find_probe(net_state, IPPROTO_UDP, 0, udp->dstport);
79: if (probe == NULL) {
80: probe = find_probe(net_state, IPPROTO_UDP, 0, udp->srcport);
81: }
82: if (probe == NULL) {
83: probe = find_probe(net_state, IPPROTO_UDP, 0, udp->checksum);
84: }
1.1.1.2 ! misho 85: if (probe == NULL)
! 86: return;
! 87:
! 88: if (probe->remote_addr.ss_family != remote_addr->ss_family)
! 89: return;
! 90:
! 91: if (udp->dstport != *sockaddr_port_offset(&probe->remote_addr) )
! 92: return;
! 93:
! 94: if (udp->srcport != *sockaddr_port_offset(&probe->local_addr) )
! 95: return;
! 96:
! 97: void *saddr, *daddr;
! 98: if (af == AF_INET)
! 99: {
! 100: saddr = &((struct IPHeader *)ip)->saddr;
! 101: daddr = &((struct IPHeader *)ip)->daddr;
! 102: }else
! 103: if (af == AF_INET6)
! 104: {
! 105: daddr = &((struct IP6Header *)ip)->daddr;
! 106: saddr = &((struct IP6Header *)ip)->saddr;
! 107: }else
! 108: {
! 109: return;
! 110: }
1.1 misho 111:
1.1.1.2 ! misho 112: if( memcmp(sockaddr_addr_offset(&probe->remote_addr),
! 113: daddr,
! 114: sockaddr_addr_size(&probe->remote_addr)) != 0 )
! 115: return;
! 116:
! 117: if( memcmp(sockaddr_addr_offset(&probe->local_addr),
! 118: saddr,
! 119: sockaddr_addr_size(&probe->local_addr)) != 0)
! 120: return;
! 121:
! 122: /* probe is not null */
! 123: receive_probe(net_state, probe, icmp_result,
1.1 misho 124: remote_addr, timestamp, mpls_count, mpls);
1.1.1.2 ! misho 125: }
! 126:
! 127: void handle_error_queue_packet(
! 128: struct net_state_t *net_state,
! 129: const struct sockaddr_storage *remote_addr,
! 130: int icmp_result,
! 131: int proto,
! 132: char *packet,
! 133: int packet_length,
! 134: struct timeval *timestamp)
! 135: {
! 136: if (proto == IPPROTO_UDP) {
! 137: handle_inner_udp_packet(net_state, remote_addr, ICMP_TIME_EXCEEDED, 0, NULL,
! 138: (struct UDPHeader *)packet, packet_length, timestamp, 0, NULL);
! 139: } else if (proto == IPPROTO_ICMP || proto == IPPROTO_ICMPV6) {
! 140: const struct ICMPHeader *icmp = (struct ICMPHeader *)packet;
! 141: find_and_receive_probe(net_state, remote_addr, timestamp,
! 142: ICMP_TIME_EXCEEDED, IPPROTO_ICMP, icmp->id,
! 143: icmp->sequence, 0, NULL);
1.1 misho 144: }
1.1.1.2 ! misho 145:
1.1 misho 146: }
147:
148: /*
149: We've received an ICMP message with an embedded IP packet.
150: We will try to determine which of our outgoing probes
151: corresponds to the embedded IP packet and record the response.
152: */
153: static
154: void handle_inner_ip4_packet(
155: struct net_state_t *net_state,
156: const struct sockaddr_storage *remote_addr,
157: int icmp_result,
158: const struct IPHeader *ip,
159: int packet_length,
160: struct timeval *timestamp,
161: int mpls_count,
162: struct mpls_label_t *mpls)
163: {
164: const int ip_icmp_size =
165: sizeof(struct IPHeader) + sizeof(struct ICMPHeader);
166: const int ip_udp_size =
167: sizeof(struct IPHeader) + sizeof(struct UDPHeader);
168: const int ip_tcp_size =
169: sizeof(struct IPHeader) + sizeof(struct TCPHeader);
170: const struct ICMPHeader *icmp;
171: const struct UDPHeader *udp;
172: const struct TCPHeader *tcp;
173: int udp_length;
174: #ifdef IPPROTO_SCTP
175: const int ip_sctp_size =
176: sizeof(struct IPHeader) + sizeof(struct SCTPHeader);
177: const struct SCTPHeader *sctp;
178: #endif
179:
180: if (ip->protocol == IPPROTO_ICMP) {
181: if (packet_length < ip_icmp_size) {
182: return;
183: }
184:
185: icmp = (struct ICMPHeader *) (ip + 1);
186:
187: find_and_receive_probe(net_state, remote_addr, timestamp,
188: icmp_result, IPPROTO_ICMP, icmp->id,
189: icmp->sequence, mpls_count, mpls);
190: } else if (ip->protocol == IPPROTO_UDP) {
191: if (packet_length < ip_udp_size) {
192: return;
193: }
194:
195: udp = (struct UDPHeader *) (ip + 1);
196: udp_length = packet_length - sizeof(struct IPHeader);
197:
1.1.1.2 ! misho 198: handle_inner_udp_packet(net_state, remote_addr, icmp_result, AF_INET, ip, udp,
1.1 misho 199: udp_length, timestamp, mpls_count, mpls);
200: } else if (ip->protocol == IPPROTO_TCP) {
201: if (packet_length < ip_tcp_size) {
202: return;
203: }
204:
205: tcp = (struct TCPHeader *) (ip + 1);
206:
207: find_and_receive_probe(net_state, remote_addr, timestamp,
208: icmp_result, IPPROTO_TCP, 0, tcp->srcport,
209: mpls_count, mpls);
210: #ifdef IPPROTO_SCTP
211: } else if (ip->protocol == IPPROTO_SCTP) {
212: if (packet_length < ip_sctp_size) {
213: return;
214: }
215:
216: sctp = (struct SCTPHeader *) (ip + 1);
217:
218: find_and_receive_probe(net_state, remote_addr, timestamp,
219: icmp_result, IPPROTO_SCTP, 0, sctp->srcport,
220: mpls_count, mpls);
221: #endif
222: }
223: }
224:
225: /*
226: Examine the IPv6 header embedded in a returned ICMPv6 packet
227: in order to match it with a probe which we previously sent.
228: */
229: static
230: void handle_inner_ip6_packet(
231: struct net_state_t *net_state,
232: const struct sockaddr_storage *remote_addr,
233: int icmp_result,
234: const struct IP6Header *ip,
235: int packet_length,
236: struct timeval *timestamp,
237: int mpls_count,
238: struct mpls_label_t *mpls)
239: {
240: const int ip_icmp_size =
241: sizeof(struct IP6Header) + sizeof(struct ICMPHeader);
242: const int ip_udp_size =
243: sizeof(struct IP6Header) + sizeof(struct UDPHeader);
244: const int ip_tcp_size =
245: sizeof(struct IP6Header) + sizeof(struct TCPHeader);
246: const struct ICMPHeader *icmp;
247: const struct UDPHeader *udp;
248: const struct TCPHeader *tcp;
249: int udp_length;
250: #ifdef IPPROTO_SCTP
251: const int ip_sctp_size =
252: sizeof(struct IPHeader) + sizeof(struct SCTPHeader);
253: const struct SCTPHeader *sctp;
254: #endif
255:
256: if (ip->protocol == IPPROTO_ICMPV6) {
257: if (packet_length < ip_icmp_size) {
258: return;
259: }
260:
261: icmp = (struct ICMPHeader *) (ip + 1);
262:
263: find_and_receive_probe(net_state, remote_addr, timestamp,
264: icmp_result, IPPROTO_ICMP, icmp->id,
265: icmp->sequence, mpls_count, mpls);
266: } else if (ip->protocol == IPPROTO_UDP) {
267: if (packet_length < ip_udp_size) {
268: return;
269: }
270:
271: udp = (struct UDPHeader *) (ip + 1);
272: udp_length = packet_length - sizeof(struct IP6Header);
273:
1.1.1.2 ! misho 274: handle_inner_udp_packet(net_state, remote_addr, icmp_result, AF_INET6, ip, udp,
1.1 misho 275: udp_length, timestamp, mpls_count, mpls);
276: } else if (ip->protocol == IPPROTO_TCP) {
277: if (packet_length < ip_tcp_size) {
278: return;
279: }
280:
281: tcp = (struct TCPHeader *) (ip + 1);
282: find_and_receive_probe(net_state, remote_addr, timestamp,
283: icmp_result, IPPROTO_TCP, 0, tcp->srcport,
284: mpls_count, mpls);
285: #ifdef IPPROTO_SCTP
286: } else if (ip->protocol == IPPROTO_SCTP) {
287: if (packet_length < ip_sctp_size) {
288: return;
289: }
290:
291: sctp = (struct SCTPHeader *) (ip + 1);
292:
293: find_and_receive_probe(net_state, remote_addr, timestamp,
294: icmp_result, IPPROTO_SCTP, 0, sctp->srcport,
295: mpls_count, mpls);
296: #endif
297: }
298: }
299:
300: /* Convert an ICMP MPLS extension object into an mpls_label_t structure */
301: static
302: int decode_mpls_object(
303: struct ICMPExtensionObject *icmp_obj,
304: int obj_len,
305: struct mpls_label_t *mpls,
306: int mpls_count)
307: {
308: int label_bytes;
309: int labels_present;
310: int i;
311: struct ICMPExtMPLSLabel *ext_mpls;
312: struct ICMPExtMPLSLabel *ext_label;
313: struct mpls_label_t *label;
314:
315: label_bytes = obj_len - sizeof(struct ICMPExtensionObject);
316: labels_present = label_bytes / sizeof(struct ICMPExtMPLSLabel);
317:
318: ext_mpls = (struct ICMPExtMPLSLabel *) (icmp_obj + 1);
319: for (i = 0; i < mpls_count && i < labels_present; i++) {
320: ext_label = &ext_mpls[i];
321: label = &mpls[i];
322:
323: memset(label, 0, sizeof(struct mpls_label_t));
324:
325: label->label =
326: ext_label->label[0] << 12 |
327: ext_label->label[1] << 4 | ext_label->label[2] >> 4;
1.1.1.2 ! misho 328: label->traffic_class = (ext_label->label[2] & 0x0E) >> 1;
1.1 misho 329: label->bottom_of_stack = ext_label->label[2] & 0x01;
330: label->ttl = ext_label->ttl;
331: }
332:
333: return i;
334: }
335:
336: /* Extract MPLS labels from the ICMP extension header, if present */
337: static
338: int decode_mpls_labels(
339: const struct ICMPHeader *icmp,
340: int packet_length,
341: struct mpls_label_t *mpls,
342: int mpls_count)
343: {
344: const int icmp_orig_icmp_ext_size =
345: sizeof(struct ICMPHeader) + ICMP_ORIGINAL_DATAGRAM_MIN_SIZE +
346: sizeof(struct ICMPExtensionHeader);
347: char *inner_packet;
348: char *icmp_object_bytes;
349: struct ICMPExtensionHeader *icmp_ext;
350: struct ICMPExtensionObject *icmp_obj;
351: int remaining_size;
352: int obj_len;
353:
354: if (packet_length < icmp_orig_icmp_ext_size) {
355: return 0;
356: }
357:
358: inner_packet = (char *) (icmp + 1);
359: icmp_ext = (struct ICMPExtensionHeader *)
360: (inner_packet + ICMP_ORIGINAL_DATAGRAM_MIN_SIZE);
361:
362: if ((icmp_ext->version & 0xF0) != 0x20) {
363: return 0;
364: }
365:
366: remaining_size = packet_length - icmp_orig_icmp_ext_size;
367: icmp_object_bytes = (char *) (icmp_ext + 1);
368:
369: /*
370: Iterate through the chain of extension objects, looking for
371: an MPLS label extension.
372: */
373: while (remaining_size >= sizeof(struct ICMPExtensionObject)) {
374: icmp_obj = (struct ICMPExtensionObject *) icmp_object_bytes;
375: obj_len = ntohs(icmp_obj->len);
376:
377: if (obj_len > remaining_size) {
378: return 0;
379: }
380: if (obj_len < sizeof(struct ICMPExtensionObject)) {
381: return 0;
382: }
383:
384: if (icmp_obj->classnum == ICMP_EXT_MPLS_CLASSNUM &&
385: icmp_obj->ctype == ICMP_EXT_MPLS_CTYPE) {
386:
387: return decode_mpls_object(icmp_obj, obj_len, mpls, mpls_count);
388: }
389:
390: remaining_size -= obj_len;
391: icmp_object_bytes += obj_len;
392: }
393:
394: return 0;
395: }
396:
397: /*
398: Decode the ICMP header received and try to find a probe which it
399: is in response to.
400: */
401: static
402: void handle_received_icmp4_packet(
403: struct net_state_t *net_state,
404: const struct sockaddr_storage *remote_addr,
405: const struct ICMPHeader *icmp,
406: int packet_length,
407: struct timeval *timestamp)
408: {
1.1.1.2 ! misho 409: int icmp_ip_size = 0;
1.1 misho 410: const struct IPHeader *inner_ip;
411: int inner_size = packet_length - sizeof(struct ICMPHeader);
412: int mpls_count;
413: struct mpls_label_t mpls[MAX_MPLS_LABELS];
414:
1.1.1.2 ! misho 415: if (net_state->platform.ip4_socket_raw) {
! 416: icmp_ip_size += sizeof(struct IPHeader);
! 417: }
! 418: icmp_ip_size += sizeof(struct ICMPHeader);
1.1 misho 419: mpls_count =
420: decode_mpls_labels(icmp, packet_length, mpls, MAX_MPLS_LABELS);
421:
422: /* If we get an echo reply, our probe reached the destination host */
423: if (icmp->type == ICMP_ECHOREPLY) {
424: find_and_receive_probe(net_state, remote_addr, timestamp,
425: ICMP_ECHOREPLY, IPPROTO_ICMP, icmp->id,
426: icmp->sequence, mpls_count, mpls);
427: }
428:
429: if (packet_length < icmp_ip_size) {
430: return;
431: }
432: inner_ip = (struct IPHeader *) (icmp + 1);
433:
434: /*
435: If we get a time exceeded, we got a response from an intermediate
436: host along the path to our destination.
437: */
438: if (icmp->type == ICMP_TIME_EXCEEDED) {
439: /*
440: The IP packet inside the ICMP response contains our original
441: IP header. That's where we can get our original ID and
442: sequence number.
443: */
444: handle_inner_ip4_packet(net_state, remote_addr,
445: ICMP_TIME_EXCEEDED, inner_ip, inner_size,
446: timestamp, mpls_count, mpls);
447: }
448:
449: if (icmp->type == ICMP_DEST_UNREACH) {
450: /*
451: We'll get a ICMP_PORT_UNREACH when a non-ICMP probe
452: reaches its final destination. (Assuming that port isn't
453: open on the destination host.)
454: */
455: if (icmp->code == ICMP_PORT_UNREACH) {
456: handle_inner_ip4_packet(net_state, remote_addr,
457: ICMP_ECHOREPLY, inner_ip, inner_size,
458: timestamp, mpls_count, mpls);
1.1.1.2 ! misho 459: } else {
! 460: /*
! 461: ICMP_DEST_UNREACH subtypes other than port unreachable
! 462: indicate an exceptional condition, and will be reported
! 463: as a "no route to host" probe response.
! 464: */
! 465: handle_inner_ip4_packet(net_state, remote_addr,
! 466: ICMP_DEST_UNREACH, inner_ip, inner_size,
! 467: timestamp, mpls_count, mpls);
1.1 misho 468: }
469: }
470: }
471:
472: /*
473: Decode the ICMPv6 header. The code duplication with ICMPv4 is
474: unfortunate, but small details in structure size and ICMP
475: constants differ.
476: */
477: static
478: void handle_received_icmp6_packet(
479: struct net_state_t *net_state,
480: const struct sockaddr_storage *remote_addr,
481: const struct ICMPHeader *icmp,
482: int packet_length,
483: struct timeval *timestamp)
484: {
485: const int icmp_ip_size =
486: sizeof(struct ICMPHeader) + sizeof(struct IP6Header);
487: const struct IP6Header *inner_ip;
488: int inner_size = packet_length - sizeof(struct ICMPHeader);
489: int mpls_count;
490: struct mpls_label_t mpls[MAX_MPLS_LABELS];
491:
492: mpls_count =
493: decode_mpls_labels(icmp, packet_length, mpls, MAX_MPLS_LABELS);
494:
495: if (icmp->type == ICMP6_ECHOREPLY) {
496: find_and_receive_probe(net_state, remote_addr, timestamp,
497: ICMP_ECHOREPLY, IPPROTO_ICMP, icmp->id,
498: icmp->sequence, mpls_count, mpls);
499: }
500:
501: if (packet_length < icmp_ip_size) {
502: return;
503: }
504: inner_ip = (struct IP6Header *) (icmp + 1);
505:
506: if (icmp->type == ICMP6_TIME_EXCEEDED) {
507: handle_inner_ip6_packet(net_state, remote_addr,
508: ICMP_TIME_EXCEEDED, inner_ip, inner_size,
509: timestamp, mpls_count, mpls);
510: }
511:
512: if (icmp->type == ICMP6_DEST_UNREACH) {
513: if (icmp->code == ICMP6_PORT_UNREACH) {
514: handle_inner_ip6_packet(net_state, remote_addr,
515: ICMP_ECHOREPLY, inner_ip, inner_size,
516: timestamp, mpls_count, mpls);
1.1.1.2 ! misho 517: } else {
! 518: handle_inner_ip6_packet(net_state, remote_addr,
! 519: ICMP_DEST_UNREACH, inner_ip, inner_size,
! 520: timestamp, mpls_count, mpls);
1.1 misho 521: }
522: }
523: }
524:
525: /*
526: We've received a new IPv4 ICMP packet.
527: We'll check to see that it is a response to one of our probes, and
528: if so, report the result of the probe to our command stream.
529: */
530: void handle_received_ip4_packet(
531: struct net_state_t *net_state,
532: const struct sockaddr_storage *remote_addr,
533: const void *packet,
534: int packet_length,
535: struct timeval *timestamp)
536: {
1.1.1.2 ! misho 537: int ip_icmp_size = 0;
1.1 misho 538: const struct IPHeader *ip;
539: const struct ICMPHeader *icmp;
540: int icmp_length;
541:
1.1.1.2 ! misho 542: if (net_state->platform.ip4_socket_raw) {
! 543: ip_icmp_size += sizeof(struct IPHeader);
! 544: }
! 545: ip_icmp_size += sizeof(struct ICMPHeader);
! 546:
1.1 misho 547: /* Ensure that we don't access memory beyond the bounds of the packet */
548: if (packet_length < ip_icmp_size) {
549: return;
550: }
551:
1.1.1.2 ! misho 552: if (net_state->platform.ip4_socket_raw) {
! 553: ip = (struct IPHeader *) packet;
! 554: if (ip->protocol != IPPROTO_ICMP) {
! 555: return;
! 556: }
1.1 misho 557:
1.1.1.2 ! misho 558: icmp = (struct ICMPHeader *) (ip + 1);
! 559: icmp_length = packet_length - sizeof(struct IPHeader);
! 560: } else {
! 561: icmp = (struct ICMPHeader *) packet;
! 562: icmp_length = packet_length;
! 563: }
1.1 misho 564:
565: handle_received_icmp4_packet(net_state, remote_addr, icmp, icmp_length,
566: timestamp);
567: }
568:
569: /*
570: Unlike ICMPv6 raw sockets, unlike ICMPv4, don't include the IP header
571: in received packets, so we can assume the packet we got starts
572: with the ICMP packet.
573: */
574: void handle_received_ip6_packet(
575: struct net_state_t *net_state,
576: const struct sockaddr_storage *remote_addr,
577: const void *packet,
578: int packet_length,
579: struct timeval *timestamp)
580: {
581: const struct ICMPHeader *icmp;
582:
583: icmp = (struct ICMPHeader *) packet;
584:
585: handle_received_icmp6_packet(net_state, remote_addr, icmp,
586: packet_length, timestamp);
587: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>