Annotation of embedaddon/mtr/packet/probe.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 "probe.h"
! 20:
! 21: #include <arpa/inet.h>
! 22: #include <assert.h>
! 23: #include <errno.h>
! 24: #include <stdio.h>
! 25: #include <stdlib.h>
! 26: #include <string.h>
! 27: #include <sys/socket.h>
! 28: #include <unistd.h>
! 29:
! 30: #include "command.h"
! 31: #include "platform.h"
! 32: #include "protocols.h"
! 33: #include "timeval.h"
! 34:
! 35: #define IP_TEXT_LENGTH 64
! 36:
! 37: /* Convert the destination address from text to sockaddr */
! 38: int decode_address_string(
! 39: int ip_version,
! 40: const char *address_string,
! 41: struct sockaddr_storage *address)
! 42: {
! 43: struct in_addr addr4;
! 44: struct in6_addr addr6;
! 45: struct sockaddr_in *sockaddr4;
! 46: struct sockaddr_in6 *sockaddr6;
! 47:
! 48: if (address == NULL) {
! 49: errno = EINVAL;
! 50: return -1;
! 51: }
! 52:
! 53: if (ip_version == 6) {
! 54: sockaddr6 = (struct sockaddr_in6 *) address;
! 55:
! 56: if (inet_pton(AF_INET6, address_string, &addr6) != 1) {
! 57: errno = EINVAL;
! 58: return -1;
! 59: }
! 60:
! 61: sockaddr6->sin6_family = AF_INET6;
! 62: sockaddr6->sin6_port = 0;
! 63: sockaddr6->sin6_flowinfo = 0;
! 64: sockaddr6->sin6_addr = addr6;
! 65: sockaddr6->sin6_scope_id = 0;
! 66: } else if (ip_version == 4) {
! 67: sockaddr4 = (struct sockaddr_in *) address;
! 68:
! 69: if (inet_pton(AF_INET, address_string, &addr4) != 1) {
! 70: errno = EINVAL;
! 71: return -1;
! 72: }
! 73:
! 74: sockaddr4->sin_family = AF_INET;
! 75: sockaddr4->sin_port = 0;
! 76: sockaddr4->sin_addr = addr4;
! 77: } else {
! 78: errno = EINVAL;
! 79: return -1;
! 80: }
! 81:
! 82: return 0;
! 83: }
! 84:
! 85: /*
! 86: Resolve the probe parameters into a remote and local address
! 87: for the probe.
! 88: */
! 89: int resolve_probe_addresses(
! 90: const struct probe_param_t *param,
! 91: struct sockaddr_storage *dest_sockaddr,
! 92: struct sockaddr_storage *src_sockaddr)
! 93: {
! 94: if (decode_address_string
! 95: (param->ip_version, param->remote_address, dest_sockaddr)) {
! 96: return -1;
! 97: }
! 98:
! 99: if (param->local_address) {
! 100: if (decode_address_string
! 101: (param->ip_version, param->local_address, src_sockaddr)) {
! 102: return -1;
! 103: }
! 104: } else {
! 105: if (find_source_addr(src_sockaddr, dest_sockaddr)) {
! 106: return -1;
! 107: }
! 108: }
! 109:
! 110: return 0;
! 111: }
! 112:
! 113: /* Allocate a structure for tracking a new probe */
! 114: struct probe_t *alloc_probe(
! 115: struct net_state_t *net_state,
! 116: int token)
! 117: {
! 118: struct probe_t *probe;
! 119:
! 120: if (net_state->outstanding_probe_count >= MAX_PROBES) {
! 121: return NULL;
! 122: }
! 123:
! 124: probe = malloc(sizeof(struct probe_t));
! 125: if (probe == NULL) {
! 126: return NULL;
! 127: }
! 128:
! 129: memset(probe, 0, sizeof(struct probe_t));
! 130: probe->token = token;
! 131:
! 132: platform_alloc_probe(net_state, probe);
! 133:
! 134: net_state->outstanding_probe_count++;
! 135: LIST_INSERT_HEAD(&net_state->outstanding_probes, probe,
! 136: probe_list_entry);
! 137:
! 138: return probe;
! 139: }
! 140:
! 141: /* Mark a probe tracking structure as unused */
! 142: void free_probe(
! 143: struct net_state_t *net_state,
! 144: struct probe_t *probe)
! 145: {
! 146: LIST_REMOVE(probe, probe_list_entry);
! 147: net_state->outstanding_probe_count--;
! 148:
! 149: platform_free_probe(probe);
! 150:
! 151: free(probe);
! 152: }
! 153:
! 154: /*
! 155: Find an existing probe structure by ICMP id and sequence number.
! 156: Returns NULL if non is found.
! 157: */
! 158: struct probe_t *find_probe(
! 159: struct net_state_t *net_state,
! 160: int protocol,
! 161: int id,
! 162: int sequence)
! 163: {
! 164: struct probe_t *probe;
! 165:
! 166: /*
! 167: ICMP has room for an id to check against our process, but
! 168: UDP doesn't.
! 169: */
! 170: if (protocol == IPPROTO_ICMP) {
! 171: /*
! 172: If the ICMP id doesn't match our process ID, it wasn't a
! 173: probe generated by this process, so ignore it.
! 174: */
! 175: if (id != htons(getpid())) {
! 176: return NULL;
! 177: }
! 178: }
! 179:
! 180: LIST_FOREACH(probe, &net_state->outstanding_probes, probe_list_entry) {
! 181: if (htons(probe->sequence) == sequence) {
! 182: return probe;
! 183: }
! 184: }
! 185:
! 186: return NULL;
! 187: }
! 188:
! 189: /*
! 190: Format a list of MPLS labels into a string appropriate for including
! 191: as an argument to a probe reply.
! 192: */
! 193: static
! 194: void format_mpls_string(
! 195: char *str,
! 196: int buffer_size,
! 197: int mpls_count,
! 198: const struct mpls_label_t *mpls_list)
! 199: {
! 200: int i;
! 201: char *append_pos = str;
! 202: const struct mpls_label_t *mpls;
! 203:
! 204: /* Start with an empty string */
! 205: str[0] = 0;
! 206:
! 207: for (i = 0; i < mpls_count; i++) {
! 208: mpls = &mpls_list[i];
! 209:
! 210: if (i > 0) {
! 211: strncat(append_pos, ",", buffer_size - 1);
! 212:
! 213: buffer_size -= strlen(append_pos);
! 214: append_pos += strlen(append_pos);
! 215: }
! 216:
! 217: snprintf(append_pos, buffer_size, "%d,%d,%d,%d",
! 218: mpls->label, mpls->experimental_use,
! 219: mpls->bottom_of_stack, mpls->ttl);
! 220:
! 221: buffer_size -= strlen(append_pos);
! 222: append_pos += strlen(append_pos);
! 223: }
! 224: }
! 225:
! 226: /*
! 227: After a probe reply has arrived, respond to the command request which
! 228: sent the probe.
! 229: */
! 230: void respond_to_probe(
! 231: struct net_state_t *net_state,
! 232: struct probe_t *probe,
! 233: int icmp_type,
! 234: const struct sockaddr_storage *remote_addr,
! 235: unsigned int round_trip_us,
! 236: int mpls_count,
! 237: const struct mpls_label_t *mpls)
! 238: {
! 239: char ip_text[IP_TEXT_LENGTH];
! 240: char response[COMMAND_BUFFER_SIZE];
! 241: char mpls_str[COMMAND_BUFFER_SIZE];
! 242: int remaining_size;
! 243: const char *result;
! 244: const char *ip_argument;
! 245: struct sockaddr_in *sockaddr4;
! 246: struct sockaddr_in6 *sockaddr6;
! 247: void *addr;
! 248:
! 249: if (icmp_type == ICMP_TIME_EXCEEDED) {
! 250: result = "ttl-expired";
! 251: } else {
! 252: assert(icmp_type == ICMP_ECHOREPLY);
! 253: result = "reply";
! 254: }
! 255:
! 256: if (remote_addr->ss_family == AF_INET6) {
! 257: ip_argument = "ip-6";
! 258: sockaddr6 = (struct sockaddr_in6 *) remote_addr;
! 259: addr = &sockaddr6->sin6_addr;
! 260: } else {
! 261: ip_argument = "ip-4";
! 262: sockaddr4 = (struct sockaddr_in *) remote_addr;
! 263: addr = &sockaddr4->sin_addr;
! 264: }
! 265:
! 266: if (inet_ntop(remote_addr->ss_family, addr, ip_text, IP_TEXT_LENGTH) ==
! 267: NULL) {
! 268:
! 269: perror("inet_ntop failure");
! 270: exit(EXIT_FAILURE);
! 271: }
! 272:
! 273: snprintf(response, COMMAND_BUFFER_SIZE,
! 274: "%d %s %s %s round-trip-time %d",
! 275: probe->token, result, ip_argument, ip_text, round_trip_us);
! 276:
! 277: if (mpls_count) {
! 278: format_mpls_string(mpls_str, COMMAND_BUFFER_SIZE, mpls_count,
! 279: mpls);
! 280:
! 281: remaining_size = COMMAND_BUFFER_SIZE - strlen(response) - 1;
! 282: strncat(response, " mpls ", remaining_size);
! 283:
! 284: remaining_size = COMMAND_BUFFER_SIZE - strlen(response) - 1;
! 285: strncat(response, mpls_str, remaining_size);
! 286: }
! 287:
! 288: puts(response);
! 289: free_probe(net_state, probe);
! 290: }
! 291:
! 292: /*
! 293: Find the source address for transmitting to a particular destination
! 294: address. Remember that hosts can have multiple addresses, for example
! 295: a unique address for each network interface. So we will bind a UDP
! 296: socket to our destination and check the socket address after binding
! 297: to get the source for that destination, which will allow the kernel
! 298: to do the routing table work for us.
! 299:
! 300: (connecting UDP sockets, unlike TCP sockets, doesn't transmit any packets.
! 301: It's just an association.)
! 302: */
! 303: int find_source_addr(
! 304: struct sockaddr_storage *srcaddr,
! 305: const struct sockaddr_storage *destaddr)
! 306: {
! 307: int sock;
! 308: int len;
! 309: struct sockaddr_in *destaddr4;
! 310: struct sockaddr_in6 *destaddr6;
! 311: struct sockaddr_storage dest_with_port;
! 312: struct sockaddr_in *srcaddr4;
! 313: struct sockaddr_in6 *srcaddr6;
! 314:
! 315: dest_with_port = *destaddr;
! 316:
! 317: /*
! 318: MacOS requires a non-zero sin_port when used as an
! 319: address for a UDP connect. If we provide a zero port,
! 320: the connect will fail. We aren't actually sending
! 321: anything to the port.
! 322: */
! 323: if (destaddr->ss_family == AF_INET6) {
! 324: destaddr6 = (struct sockaddr_in6 *) &dest_with_port;
! 325: destaddr6->sin6_port = htons(1);
! 326:
! 327: len = sizeof(struct sockaddr_in6);
! 328: } else {
! 329: destaddr4 = (struct sockaddr_in *) &dest_with_port;
! 330: destaddr4->sin_port = htons(1);
! 331:
! 332: len = sizeof(struct sockaddr_in);
! 333: }
! 334:
! 335: sock = socket(destaddr->ss_family, SOCK_DGRAM, IPPROTO_UDP);
! 336: if (sock == -1) {
! 337: return -1;
! 338: }
! 339:
! 340: if (connect(sock, (struct sockaddr *) &dest_with_port, len)) {
! 341: close(sock);
! 342: return -1;
! 343: }
! 344:
! 345: if (getsockname(sock, (struct sockaddr *) srcaddr, &len)) {
! 346: close(sock);
! 347: return -1;
! 348: }
! 349:
! 350: close(sock);
! 351:
! 352: /*
! 353: Zero the port, as we may later use this address to finding, and
! 354: we don't want to use the port from the socket we just created.
! 355: */
! 356: if (destaddr->ss_family == AF_INET6) {
! 357: srcaddr6 = (struct sockaddr_in6 *) srcaddr;
! 358:
! 359: srcaddr6->sin6_port = 0;
! 360: } else {
! 361: srcaddr4 = (struct sockaddr_in *) srcaddr;
! 362:
! 363: srcaddr4->sin_port = 0;
! 364: }
! 365:
! 366: return 0;
! 367: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>