Annotation of embedaddon/mtr/packet/construct_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 "construct_unix.h"
! 20:
! 21: #include <errno.h>
! 22: #include <stdio.h>
! 23: #include <string.h>
! 24: #include <sys/socket.h>
! 25: #include <unistd.h>
! 26:
! 27: #include "protocols.h"
! 28:
! 29: /* A source of data for computing a checksum */
! 30: struct checksum_source_t {
! 31: const void *data;
! 32: size_t size;
! 33: };
! 34:
! 35: /* Compute the IP checksum (or ICMP checksum) of a packet. */
! 36: static
! 37: uint16_t compute_checksum(
! 38: const void *packet,
! 39: int size)
! 40: {
! 41: const uint8_t *packet_bytes = (uint8_t *) packet;
! 42: uint32_t sum = 0;
! 43: int i;
! 44:
! 45: for (i = 0; i < size; i++) {
! 46: if ((i & 1) == 0) {
! 47: sum += packet_bytes[i] << 8;
! 48: } else {
! 49: sum += packet_bytes[i];
! 50: }
! 51: }
! 52:
! 53: /*
! 54: Sums which overflow a 16-bit value have the high bits
! 55: added back into the low 16 bits.
! 56: */
! 57: while (sum >> 16) {
! 58: sum = (sum >> 16) + (sum & 0xffff);
! 59: }
! 60:
! 61: /*
! 62: The value stored is the one's complement of the
! 63: mathematical sum.
! 64: */
! 65: return (~sum & 0xffff);
! 66: }
! 67:
! 68: /* Encode the IP header length field in the order required by the OS. */
! 69: static
! 70: uint16_t length_byte_swap(
! 71: const struct net_state_t *net_state,
! 72: uint16_t length)
! 73: {
! 74: if (net_state->platform.ip_length_host_order) {
! 75: return length;
! 76: } else {
! 77: return htons(length);
! 78: }
! 79: }
! 80:
! 81: /* Construct a combined sockaddr from a source address and source port */
! 82: static
! 83: void construct_addr_port(
! 84: struct sockaddr_storage *addr_with_port,
! 85: const struct sockaddr_storage *addr,
! 86: int port)
! 87: {
! 88: struct sockaddr_in *addr4;
! 89: struct sockaddr_in6 *addr6;
! 90:
! 91: memcpy(addr_with_port, addr, sizeof(struct sockaddr_storage));
! 92:
! 93: if (addr->ss_family == AF_INET6) {
! 94: addr6 = (struct sockaddr_in6 *) addr_with_port;
! 95: addr6->sin6_port = htons(port);
! 96: } else {
! 97: addr4 = (struct sockaddr_in *) addr_with_port;
! 98: addr4->sin_port = htons(port);
! 99: }
! 100: }
! 101:
! 102: /* Construct a header for IP version 4 */
! 103: static
! 104: void construct_ip4_header(
! 105: const struct net_state_t *net_state,
! 106: char *packet_buffer,
! 107: int packet_size,
! 108: const struct sockaddr_storage *srcaddr,
! 109: const struct sockaddr_storage *destaddr,
! 110: const struct probe_param_t *param)
! 111: {
! 112: struct IPHeader *ip;
! 113: struct sockaddr_in *srcaddr4 = (struct sockaddr_in *) srcaddr;
! 114: struct sockaddr_in *destaddr4 = (struct sockaddr_in *) destaddr;
! 115:
! 116: ip = (struct IPHeader *) &packet_buffer[0];
! 117:
! 118: memset(ip, 0, sizeof(struct IPHeader));
! 119:
! 120: ip->version = 0x45;
! 121: ip->tos = param->type_of_service;
! 122: ip->len = length_byte_swap(net_state, packet_size);
! 123: ip->ttl = param->ttl;
! 124: ip->protocol = param->protocol;
! 125: memcpy(&ip->saddr, &srcaddr4->sin_addr, sizeof(uint32_t));
! 126: memcpy(&ip->daddr, &destaddr4->sin_addr, sizeof(uint32_t));
! 127: }
! 128:
! 129: /* Construct an ICMP header for IPv4 */
! 130: static
! 131: void construct_icmp4_header(
! 132: const struct net_state_t *net_state,
! 133: int sequence,
! 134: char *packet_buffer,
! 135: int packet_size,
! 136: const struct probe_param_t *param)
! 137: {
! 138: struct ICMPHeader *icmp;
! 139: int icmp_size;
! 140:
! 141: icmp = (struct ICMPHeader *) &packet_buffer[sizeof(struct IPHeader)];
! 142: icmp_size = packet_size - sizeof(struct IPHeader);
! 143:
! 144: memset(icmp, 0, sizeof(struct ICMPHeader));
! 145:
! 146: icmp->type = ICMP_ECHO;
! 147: icmp->id = htons(getpid());
! 148: icmp->sequence = htons(sequence);
! 149: icmp->checksum = htons(compute_checksum(icmp, icmp_size));
! 150: }
! 151:
! 152: /* Construct an ICMP header for IPv6 */
! 153: static
! 154: int construct_icmp6_packet(
! 155: const struct net_state_t *net_state,
! 156: int sequence,
! 157: char *packet_buffer,
! 158: int packet_size,
! 159: const struct probe_param_t *param)
! 160: {
! 161: struct ICMPHeader *icmp;
! 162:
! 163: icmp = (struct ICMPHeader *) packet_buffer;
! 164:
! 165: memset(icmp, 0, sizeof(struct ICMPHeader));
! 166:
! 167: icmp->type = ICMP6_ECHO;
! 168: icmp->id = htons(getpid());
! 169: icmp->sequence = htons(sequence);
! 170:
! 171: return 0;
! 172: }
! 173:
! 174: /*
! 175: Set the port numbers for an outgoing UDP probe.
! 176: There is limited space in the header for a sequence number
! 177: to identify the probe upon return.
! 178:
! 179: We store the sequence number in the destination port, the local
! 180: port, or the checksum. The location chosen depends upon which
! 181: probe parameters have been requested.
! 182: */
! 183: static
! 184: void set_udp_ports(
! 185: struct UDPHeader *udp,
! 186: int sequence,
! 187: const struct probe_param_t *param)
! 188: {
! 189: if (param->dest_port) {
! 190: udp->dstport = htons(param->dest_port);
! 191:
! 192: if (param->local_port) {
! 193: udp->srcport = htons(param->local_port);
! 194: udp->checksum = htons(sequence);
! 195: } else {
! 196: udp->srcport = htons(sequence);
! 197: udp->checksum = 0;
! 198: }
! 199: } else {
! 200: udp->dstport = htons(sequence);
! 201:
! 202: if (param->local_port) {
! 203: udp->srcport = htons(param->local_port);
! 204: } else {
! 205: udp->srcport = htons(getpid());
! 206: }
! 207:
! 208: udp->checksum = 0;
! 209: }
! 210: }
! 211:
! 212: /*
! 213: Construct a header for UDP probes, using the port number associated
! 214: with the probe.
! 215: */
! 216: static
! 217: void construct_udp4_header(
! 218: const struct net_state_t *net_state,
! 219: int sequence,
! 220: char *packet_buffer,
! 221: int packet_size,
! 222: const struct probe_param_t *param)
! 223: {
! 224: struct UDPHeader *udp;
! 225: int udp_size;
! 226:
! 227: udp = (struct UDPHeader *) &packet_buffer[sizeof(struct IPHeader)];
! 228: udp_size = packet_size - sizeof(struct IPHeader);
! 229:
! 230: memset(udp, 0, sizeof(struct UDPHeader));
! 231:
! 232: set_udp_ports(udp, sequence, param);
! 233: udp->length = htons(udp_size);
! 234: }
! 235:
! 236: /* Construct a header for UDPv6 probes */
! 237: static
! 238: int construct_udp6_packet(
! 239: const struct net_state_t *net_state,
! 240: int sequence,
! 241: char *packet_buffer,
! 242: int packet_size,
! 243: const struct probe_param_t *param)
! 244: {
! 245: int udp_socket = net_state->platform.udp6_send_socket;
! 246: struct UDPHeader *udp;
! 247: int udp_size;
! 248:
! 249: udp = (struct UDPHeader *) packet_buffer;
! 250: udp_size = packet_size;
! 251:
! 252: memset(udp, 0, sizeof(struct UDPHeader));
! 253:
! 254: set_udp_ports(udp, sequence, param);
! 255: udp->length = htons(udp_size);
! 256:
! 257: /*
! 258: Instruct the kernel to put the pseudoheader checksum into the
! 259: UDP header.
! 260: */
! 261: int chksum_offset = (char *) &udp->checksum - (char *) udp;
! 262: if (setsockopt(udp_socket, IPPROTO_IPV6,
! 263: IPV6_CHECKSUM, &chksum_offset, sizeof(int))) {
! 264: return -1;
! 265: }
! 266:
! 267: return 0;
! 268: }
! 269:
! 270: /*
! 271: Set the socket options for an outgoing stream protocol socket based on
! 272: the packet parameters.
! 273: */
! 274: static
! 275: int set_stream_socket_options(
! 276: int stream_socket,
! 277: const struct probe_param_t *param)
! 278: {
! 279: int level;
! 280: int opt;
! 281: int reuse = 1;
! 282:
! 283: /* Allow binding to a local port previously in use */
! 284: #ifdef SO_REUSEPORT
! 285: /*
! 286: FreeBSD wants SO_REUSEPORT in addition to SO_REUSEADDR to
! 287: bind to the same port
! 288: */
! 289: if (setsockopt(stream_socket, SOL_SOCKET, SO_REUSEPORT,
! 290: &reuse, sizeof(int)) == -1) {
! 291:
! 292: return -1;
! 293: }
! 294: #endif
! 295:
! 296: if (setsockopt(stream_socket, SOL_SOCKET, SO_REUSEADDR,
! 297: &reuse, sizeof(int)) == -1) {
! 298:
! 299: return -1;
! 300: }
! 301:
! 302: /* Set the number of hops the probe will transit across */
! 303: if (param->ip_version == 6) {
! 304: level = IPPROTO_IPV6;
! 305: opt = IPV6_UNICAST_HOPS;
! 306: } else {
! 307: level = IPPROTO_IP;
! 308: opt = IP_TTL;
! 309: }
! 310:
! 311: if (setsockopt(stream_socket, level, opt, ¶m->ttl, sizeof(int)) ==
! 312: -1) {
! 313:
! 314: return -1;
! 315: }
! 316:
! 317: /* Set the "type of service" field of the IP header */
! 318: if (param->ip_version == 6) {
! 319: level = IPPROTO_IPV6;
! 320: opt = IPV6_TCLASS;
! 321: } else {
! 322: level = IPPROTO_IP;
! 323: opt = IP_TOS;
! 324: }
! 325:
! 326: if (setsockopt(stream_socket, level, opt,
! 327: ¶m->type_of_service, sizeof(int)) == -1) {
! 328:
! 329: return -1;
! 330: }
! 331: #ifdef SO_MARK
! 332: if (param->routing_mark) {
! 333: if (setsockopt(stream_socket, SOL_SOCKET,
! 334: SO_MARK, ¶m->routing_mark, sizeof(int))) {
! 335: return -1;
! 336: }
! 337: }
! 338: #endif
! 339:
! 340: return 0;
! 341: }
! 342:
! 343: /*
! 344: Open a TCP or SCTP socket, respecting the probe paramters as much as
! 345: we can, and use it as an outgoing probe.
! 346: */
! 347: static
! 348: int open_stream_socket(
! 349: const struct net_state_t *net_state,
! 350: int protocol,
! 351: int port,
! 352: const struct sockaddr_storage *src_sockaddr,
! 353: const struct sockaddr_storage *dest_sockaddr,
! 354: const struct probe_param_t *param)
! 355: {
! 356: int stream_socket;
! 357: int addr_len;
! 358: int dest_port;
! 359: struct sockaddr_storage dest_port_addr;
! 360: struct sockaddr_storage src_port_addr;
! 361:
! 362: if (param->ip_version == 6) {
! 363: stream_socket = socket(AF_INET6, SOCK_STREAM, protocol);
! 364: addr_len = sizeof(struct sockaddr_in6);
! 365: } else if (param->ip_version == 4) {
! 366: stream_socket = socket(AF_INET, SOCK_STREAM, protocol);
! 367: addr_len = sizeof(struct sockaddr_in);
! 368: } else {
! 369: errno = EINVAL;
! 370: return -1;
! 371: }
! 372:
! 373: if (stream_socket == -1) {
! 374: return -1;
! 375: }
! 376:
! 377: set_socket_nonblocking(stream_socket);
! 378:
! 379: if (set_stream_socket_options(stream_socket, param)) {
! 380: close(stream_socket);
! 381: return -1;
! 382: }
! 383:
! 384: /*
! 385: Bind to a known local port so we can identify which probe
! 386: causes a TTL expiration.
! 387: */
! 388: construct_addr_port(&src_port_addr, src_sockaddr, port);
! 389: if (bind(stream_socket, (struct sockaddr *) &src_port_addr, addr_len)) {
! 390: close(stream_socket);
! 391: return -1;
! 392: }
! 393:
! 394: if (param->dest_port) {
! 395: dest_port = param->dest_port;
! 396: } else {
! 397: /* Use http if no port is specified */
! 398: dest_port = HTTP_PORT;
! 399: }
! 400:
! 401: /* Attempt a connection */
! 402: construct_addr_port(&dest_port_addr, dest_sockaddr, dest_port);
! 403: if (connect
! 404: (stream_socket, (struct sockaddr *) &dest_port_addr, addr_len)) {
! 405:
! 406: /* EINPROGRESS simply means the connection is in progress */
! 407: if (errno != EINPROGRESS) {
! 408: close(stream_socket);
! 409: return -1;
! 410: }
! 411: }
! 412:
! 413: return stream_socket;
! 414: }
! 415:
! 416: /*
! 417: Determine the size of the constructed packet based on the packet
! 418: parameters. This is the amount of space the packet *we* construct
! 419: uses, and doesn't include any headers the operating system tacks
! 420: onto the packet. (Such as the IPv6 header on non-Linux operating
! 421: systems.)
! 422: */
! 423: static
! 424: int compute_packet_size(
! 425: const struct net_state_t *net_state,
! 426: const struct probe_param_t *param)
! 427: {
! 428: int packet_size;
! 429:
! 430: if (param->protocol == IPPROTO_TCP) {
! 431: return 0;
! 432: }
! 433: #ifdef IPPROTO_SCTP
! 434: if (param->protocol == IPPROTO_SCTP) {
! 435: return 0;
! 436: }
! 437: #endif
! 438:
! 439: /* Start by determining the full size, including omitted headers */
! 440: if (param->ip_version == 6) {
! 441: packet_size = sizeof(struct IP6Header);
! 442: } else if (param->ip_version == 4) {
! 443: packet_size = sizeof(struct IPHeader);
! 444: } else {
! 445: errno = EINVAL;
! 446: return -1;
! 447: }
! 448:
! 449: if (param->protocol == IPPROTO_ICMP) {
! 450: packet_size += sizeof(struct ICMPHeader);
! 451: } else if (param->protocol == IPPROTO_UDP) {
! 452: packet_size += sizeof(struct UDPHeader);
! 453:
! 454: /* We may need to put the sequence number in the payload */
! 455: packet_size += sizeof(int);
! 456: } else {
! 457: errno = EINVAL;
! 458: return -1;
! 459: }
! 460:
! 461: /*
! 462: If the requested size from send-probe is greater, extend the
! 463: packet size.
! 464: */
! 465: if (param->packet_size > packet_size) {
! 466: packet_size = param->packet_size;
! 467: }
! 468:
! 469: /*
! 470: Since we don't explicitly construct the IPv6 header, we
! 471: need to account for it in our transmitted size.
! 472: */
! 473: if (param->ip_version == 6) {
! 474: packet_size -= sizeof(struct IP6Header);
! 475: }
! 476:
! 477: return packet_size;
! 478: }
! 479:
! 480: /* Construct a packet for an IPv4 probe */
! 481: static
! 482: int construct_ip4_packet(
! 483: const struct net_state_t *net_state,
! 484: int *packet_socket,
! 485: int sequence,
! 486: char *packet_buffer,
! 487: int packet_size,
! 488: const struct sockaddr_storage *src_sockaddr,
! 489: const struct sockaddr_storage *dest_sockaddr,
! 490: const struct probe_param_t *param)
! 491: {
! 492: int send_socket = net_state->platform.ip4_send_socket;
! 493: bool is_stream_protocol = false;
! 494:
! 495: if (param->protocol == IPPROTO_TCP) {
! 496: is_stream_protocol = true;
! 497: #ifdef IPPROTO_SCTP
! 498: } else if (param->protocol == IPPROTO_SCTP) {
! 499: is_stream_protocol = true;
! 500: #endif
! 501: } else {
! 502: construct_ip4_header(net_state, packet_buffer, packet_size,
! 503: src_sockaddr, dest_sockaddr, param);
! 504:
! 505: if (param->protocol == IPPROTO_ICMP) {
! 506: construct_icmp4_header(net_state, sequence, packet_buffer,
! 507: packet_size, param);
! 508: } else if (param->protocol == IPPROTO_UDP) {
! 509: construct_udp4_header(net_state, sequence, packet_buffer,
! 510: packet_size, param);
! 511: } else {
! 512: errno = EINVAL;
! 513: return -1;
! 514: }
! 515: }
! 516:
! 517: if (is_stream_protocol) {
! 518: send_socket =
! 519: open_stream_socket(net_state, param->protocol, sequence,
! 520: src_sockaddr, dest_sockaddr, param);
! 521:
! 522: if (send_socket == -1) {
! 523: return -1;
! 524: }
! 525:
! 526: *packet_socket = send_socket;
! 527: return 0;
! 528: }
! 529:
! 530: /*
! 531: The routing mark requires CAP_NET_ADMIN, as opposed to the
! 532: CAP_NET_RAW which we are sometimes explicitly given.
! 533: If we don't have CAP_NET_ADMIN, this will fail, so we'll
! 534: only set the mark if the user has explicitly requested it.
! 535:
! 536: Unfortunately, this means that once the mark is set, it won't
! 537: be set on the socket again until a new mark is explicitly
! 538: specified.
! 539: */
! 540: #ifdef SO_MARK
! 541: if (param->routing_mark) {
! 542: if (setsockopt(send_socket, SOL_SOCKET,
! 543: SO_MARK, ¶m->routing_mark, sizeof(int))) {
! 544: return -1;
! 545: }
! 546: }
! 547: #endif
! 548:
! 549: return 0;
! 550: }
! 551:
! 552: /* Construct a packet for an IPv6 probe */
! 553: static
! 554: int construct_ip6_packet(
! 555: const struct net_state_t *net_state,
! 556: int *packet_socket,
! 557: int sequence,
! 558: char *packet_buffer,
! 559: int packet_size,
! 560: const struct sockaddr_storage *src_sockaddr,
! 561: const struct sockaddr_storage *dest_sockaddr,
! 562: const struct probe_param_t *param)
! 563: {
! 564: int send_socket;
! 565: bool is_stream_protocol = false;
! 566: bool bind_send_socket = true;
! 567: struct sockaddr_storage current_sockaddr;
! 568: int current_sockaddr_len;
! 569:
! 570: if (param->protocol == IPPROTO_TCP) {
! 571: is_stream_protocol = true;
! 572: #ifdef IPPROTO_SCTP
! 573: } else if (param->protocol == IPPROTO_SCTP) {
! 574: is_stream_protocol = true;
! 575: #endif
! 576: } else if (param->protocol == IPPROTO_ICMP) {
! 577: send_socket = net_state->platform.icmp6_send_socket;
! 578:
! 579: if (construct_icmp6_packet
! 580: (net_state, sequence, packet_buffer, packet_size, param)) {
! 581: return -1;
! 582: }
! 583: } else if (param->protocol == IPPROTO_UDP) {
! 584: send_socket = net_state->platform.udp6_send_socket;
! 585:
! 586: if (construct_udp6_packet
! 587: (net_state, sequence, packet_buffer, packet_size, param)) {
! 588: return -1;
! 589: }
! 590: } else {
! 591: errno = EINVAL;
! 592: return -1;
! 593: }
! 594:
! 595: if (is_stream_protocol) {
! 596: send_socket =
! 597: open_stream_socket(net_state, param->protocol, sequence,
! 598: src_sockaddr, dest_sockaddr, param);
! 599:
! 600: if (send_socket == -1) {
! 601: return -1;
! 602: }
! 603:
! 604: *packet_socket = send_socket;
! 605: return 0;
! 606: }
! 607:
! 608: /*
! 609: Check the current socket address, and if it is the same
! 610: as the source address we intend, we will skip the bind.
! 611: This is to accomodate Solaris, which, as of Solaris 11.3,
! 612: will return an EINVAL error on bind if the socket is already
! 613: bound, even if the same address is used.
! 614: */
! 615: current_sockaddr_len = sizeof(struct sockaddr_in6);
! 616: if (getsockname(send_socket, (struct sockaddr *) ¤t_sockaddr,
! 617: ¤t_sockaddr_len) == 0) {
! 618:
! 619: if (memcmp(¤t_sockaddr,
! 620: src_sockaddr, sizeof(struct sockaddr_in6)) == 0) {
! 621: bind_send_socket = false;
! 622: }
! 623: }
! 624:
! 625: /* Bind to our local address */
! 626: if (bind_send_socket) {
! 627: if (bind(send_socket, (struct sockaddr *) src_sockaddr,
! 628: sizeof(struct sockaddr_in6))) {
! 629: return -1;
! 630: }
! 631: }
! 632:
! 633: /* The traffic class in IPv6 is analagous to ToS in IPv4 */
! 634: if (setsockopt(send_socket, IPPROTO_IPV6,
! 635: IPV6_TCLASS, ¶m->type_of_service, sizeof(int))) {
! 636: return -1;
! 637: }
! 638:
! 639: /* Set the time-to-live */
! 640: if (setsockopt(send_socket, IPPROTO_IPV6,
! 641: IPV6_UNICAST_HOPS, ¶m->ttl, sizeof(int))) {
! 642: return -1;
! 643: }
! 644: #ifdef SO_MARK
! 645: if (param->routing_mark) {
! 646: if (setsockopt(send_socket,
! 647: SOL_SOCKET, SO_MARK, ¶m->routing_mark,
! 648: sizeof(int))) {
! 649: return -1;
! 650: }
! 651: }
! 652: #endif
! 653:
! 654: return 0;
! 655: }
! 656:
! 657: /* Construct a probe packet based on the probe parameters */
! 658: int construct_packet(
! 659: const struct net_state_t *net_state,
! 660: int *packet_socket,
! 661: int sequence,
! 662: char *packet_buffer,
! 663: int packet_buffer_size,
! 664: const struct sockaddr_storage *dest_sockaddr,
! 665: const struct sockaddr_storage *src_sockaddr,
! 666: const struct probe_param_t *param)
! 667: {
! 668: int packet_size;
! 669:
! 670: packet_size = compute_packet_size(net_state, param);
! 671: if (packet_size < 0) {
! 672: return -1;
! 673: }
! 674:
! 675: if (packet_buffer_size < packet_size) {
! 676: errno = EINVAL;
! 677: return -1;
! 678: }
! 679:
! 680: memset(packet_buffer, param->bit_pattern, packet_size);
! 681:
! 682: if (param->ip_version == 6) {
! 683: if (construct_ip6_packet(net_state, packet_socket, sequence,
! 684: packet_buffer, packet_size,
! 685: src_sockaddr, dest_sockaddr, param)) {
! 686: return -1;
! 687: }
! 688: } else if (param->ip_version == 4) {
! 689: if (construct_ip4_packet(net_state, packet_socket, sequence,
! 690: packet_buffer, packet_size,
! 691: src_sockaddr, dest_sockaddr, param)) {
! 692: return -1;
! 693: }
! 694: } else {
! 695: errno = EINVAL;
! 696: return -1;
! 697: }
! 698:
! 699: return packet_size;
! 700: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>