Annotation of embedaddon/mtr/packet/probe.c, revision 1.1.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>