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>