File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / mtr / packet / probe.c
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Sep 27 11:18:58 2023 UTC (18 months, 1 week ago) by misho
Branches: mtr, MAIN
CVS tags: v0_95, HEAD
Version 0.95

    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 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.
   17: */
   18: 
   19: #include "probe.h"
   20: 
   21: #include <arpa/inet.h>
   22: #include <assert.h>
   23: #include <errno.h>
   24: #ifdef HAVE_ERROR_H
   25: #include <error.h>
   26: #else
   27: #include "portability/error.h"
   28: #endif
   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"
   39: #include "sockaddr.h"
   40: 
   41: char *probe_err;
   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(
   96:     struct net_state_t *net_state,
   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)) {
  103:         probe_err = "decode address string remote";
  104:         return -1;
  105:     }
  106: 
  107:     if (param->local_address) {
  108:         if (decode_address_string
  109:             (param->ip_version, param->local_address, src_sockaddr)) {
  110:             probe_err = "decode address string local";
  111:             return -1;
  112:         }
  113:     } else {
  114:         probe_err = "find source address";
  115:         if (find_source_addr(src_sockaddr, dest_sockaddr)) {
  116:             //probe_err = "find source address";
  117:             return -1;
  118:         }
  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());
  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",
  236:                  mpls->label, mpls->traffic_class,
  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: {
  257:     char ip_text[INET6_ADDRSTRLEN];
  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";
  266:     } else if (icmp_type == ICMP_DEST_UNREACH) {
  267:         result = "no-route";
  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: 
  279:     if (inet_ntop(remote_addr->ss_family, sockaddr_addr_offset(remote_addr), ip_text, INET6_ADDRSTRLEN) ==
  280:         NULL) {
  281:         error(EXIT_FAILURE, errno, "inet_ntop failure");
  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;
  321: #ifdef __linux__
  322:     // The Linux code needs these.
  323:     struct sockaddr_in *srcaddr4;
  324:     struct sockaddr_in6 *srcaddr6;
  325: #endif
  326: 
  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:      */
  336:     *sockaddr_port_offset(&dest_with_port) = htons(1);
  337:     len = sockaddr_size(&dest_with_port);
  338: 
  339:     sock = socket(destaddr->ss_family, SOCK_DGRAM, IPPROTO_UDP);
  340:     if (sock == -1) {
  341:         probe_err = "open socket";
  342:         return -1;
  343:     }
  344: 
  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:         }
  361: 
  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
  370:         close(sock);
  371:         probe_err = "connect failed";
  372:         return -1;
  373: #endif
  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:      */
  382:     *sockaddr_port_offset(srcaddr) = 0;
  383: 
  384:     return 0;
  385: }

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