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