Annotation of embedaddon/mtr/packet/probe.c, revision 1.1.1.3

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>