File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / mtr / packet / probe_unix.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 (8 months, 4 weeks 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 <assert.h>
   22: #include <errno.h>
   23: #include <fcntl.h>
   24: #ifdef HAVE_ERROR_H
   25: #include <error.h>
   26: #else
   27: #include "portability/error.h"
   28: #endif
   29: #ifdef HAVE_LINUX_ERRQUEUE_H
   30: #include <linux/errqueue.h>
   31: #endif
   32: #include <stdio.h>
   33: #include <stdlib.h>
   34: #include <string.h>
   35: #include <sys/socket.h>
   36: #include <unistd.h>
   37: 
   38: #include "platform.h"
   39: #include "protocols.h"
   40: #include "sockaddr.h"
   41: #include "construct_unix.h"
   42: #include "deconstruct_unix.h"
   43: #include "timeval.h"
   44: 
   45: /*  A wrapper around sendto for mixed IPv4 and IPv6 sending  */
   46: static
   47: int send_packet(
   48:     const struct net_state_t *net_state,
   49:     const struct probe_param_t *param,
   50:     int sequence,
   51:     const char *packet,
   52:     int packet_size,
   53:     const struct sockaddr_storage *sockaddr)
   54: {
   55:     struct sockaddr_storage dst;
   56:     int send_socket = 0;
   57:     int sockaddr_length;
   58: 
   59:     memcpy(&dst, sockaddr, sizeof(struct sockaddr_storage));
   60: 
   61:     if (sockaddr->ss_family == AF_INET6) {
   62:         sockaddr_length = sizeof(struct sockaddr_in6);
   63: 
   64:         if (param->protocol == IPPROTO_ICMP) {
   65:             if (net_state->platform.ip6_socket_raw) {
   66:                 send_socket = net_state->platform.icmp6_send_socket;
   67:             } else {
   68:                 send_socket = net_state->platform.ip6_txrx_icmp_socket;
   69:             }
   70:         } else if (param->protocol == IPPROTO_UDP) {
   71:             if (net_state->platform.ip6_socket_raw) {
   72:                 send_socket = net_state->platform.udp6_send_socket;
   73:                 /* we got a ipv6 udp raw socket
   74:                  * the remote port is in the payload
   75:                  * we do not set in the sockaddr
   76:                  */
   77:                 *sockaddr_port_offset(&dst) = 0;
   78:             } else {
   79:                 send_socket = net_state->platform.ip6_txrx_udp_socket;
   80:                 if (param->dest_port) {
   81:                     *sockaddr_port_offset(&dst) = htons(param->dest_port);
   82:                 } else {
   83:                     *sockaddr_port_offset(&dst) = sequence;
   84:                 }
   85:             }
   86:         }
   87:     } else if (sockaddr->ss_family == AF_INET) {
   88:         sockaddr_length = sizeof(struct sockaddr_in);
   89: 
   90:         if (net_state->platform.ip4_socket_raw) {
   91:             send_socket = net_state->platform.ip4_send_socket;
   92:         } else {
   93:             if (param->protocol == IPPROTO_ICMP) {
   94:                 if (param->is_probing_byte_order) {
   95:                     send_socket = net_state->platform.ip4_tmp_icmp_socket;;
   96:                 } else {
   97:                     send_socket = net_state->platform.ip4_txrx_icmp_socket;
   98:                 }
   99:             } else if (param->protocol == IPPROTO_UDP) {
  100:                 send_socket = net_state->platform.ip4_txrx_udp_socket;
  101:                 if (param->dest_port) {
  102:                     *sockaddr_port_offset(&dst) = htons(param->dest_port);
  103:                 } else {
  104:                     *sockaddr_port_offset(&dst) = sequence;
  105:                 }
  106:             }
  107:         }
  108:     }
  109: 
  110:     if (send_socket == 0) {
  111:         errno = EINVAL;
  112:         return -1;
  113:     }
  114: 
  115:     return sendto(send_socket, packet, packet_size, 0,
  116:                   (struct sockaddr *) &dst, sockaddr_length);
  117: }
  118: 
  119: /*
  120:     Nearly all fields in the IP header should be encoded in network byte
  121:     order prior to passing to send().  However, the required byte order of
  122:     the length field of the IP header is inconsistent between operating
  123:     systems and operating system versions.  FreeBSD 11 requires the length
  124:     field in network byte order, but some older versions of FreeBSD
  125:     require host byte order.  OS X requires the length field in host
  126:     byte order.  Linux will accept either byte order.
  127: 
  128:     Test for a byte order which works by sending a ping to localhost.
  129: */
  130: static
  131: void check_length_order(
  132:     struct net_state_t *net_state)
  133: {
  134:     char packet[PACKET_BUFFER_SIZE];
  135:     struct probe_param_t param;
  136:     struct probe_t p0 = {.sequence = MIN_PORT };
  137:     ssize_t bytes_sent;
  138:     int packet_size;
  139: 
  140: #ifdef __linux__
  141:     /*  Linux will accept either byte order and check below fails to work
  142:      *  in some cases due to sendto() returning EPERM. */
  143:     return;
  144: #endif
  145: 
  146:     memset(&param, 0, sizeof(struct probe_param_t));
  147:     param.ip_version = 4;
  148:     param.protocol = IPPROTO_ICMP;
  149:     param.ttl = 255;
  150:     param.remote_address = "127.0.0.1";
  151:     param.is_probing_byte_order = true;
  152: 
  153: 
  154:     if (resolve_probe_addresses(net_state, &param, &p0.remote_addr,
  155:                 &p0.local_addr)) {
  156:         fprintf(stderr, "Error decoding localhost address (%s/%s)\n",
  157:                 probe_err, strerror (errno));
  158:         exit(EXIT_FAILURE);
  159:     }
  160: 
  161:     /*  First attempt to ping the localhost with network byte order  */
  162:     net_state->platform.ip_length_host_order = false;
  163: 
  164:     packet_size = construct_packet(net_state, NULL, &p0,
  165:                                    packet, PACKET_BUFFER_SIZE,
  166:                                    &param);
  167:     if (packet_size < 0) {
  168:       error(EXIT_FAILURE, errno, "Unable to send to localhost");
  169:     }
  170: 
  171:     bytes_sent =
  172:         send_packet(net_state, &param, MIN_PORT, packet, packet_size,
  173:                     &p0.remote_addr);
  174:     if (bytes_sent > 0) {
  175:         return;
  176:     }
  177: 
  178:     /*  Since network byte order failed, try host byte order  */
  179:     net_state->platform.ip_length_host_order = true;
  180: 
  181:     packet_size = construct_packet(net_state, NULL, &p0,
  182:                                    packet, PACKET_BUFFER_SIZE,
  183:                                    &param);
  184:     if (packet_size < 0) {
  185:         error(EXIT_FAILURE, errno, "Unable to send to localhost");
  186:     }
  187: 
  188:     bytes_sent =
  189:         send_packet(net_state, &param, MIN_PORT, packet, packet_size,
  190:                     &p0.remote_addr);
  191:     if (bytes_sent < 0) {
  192:         error(EXIT_FAILURE, errno, "Unable to send with swapped length");
  193:     }
  194: }
  195: 
  196: /*
  197:     Check to see if SCTP is support.  We can't just rely on checking
  198:     if IPPROTO_SCTP is defined, because while that is necessary,
  199:     MacOS as of "Sierra" defines IPPROTO_SCTP, but creating an SCTP
  200:     socket results in an error.
  201: */
  202: static
  203: void check_sctp_support(
  204:     struct net_state_t *net_state)
  205: {
  206: #ifdef IPPROTO_SCTP
  207:     int sctp_socket;
  208: 
  209:     sctp_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP);
  210:     if (sctp_socket != -1) {
  211:         close(sctp_socket);
  212: 
  213:         net_state->platform.sctp_support = true;
  214:     }
  215: #endif
  216: }
  217: 
  218: /*  Set a socket to non-blocking mode  */
  219: void set_socket_nonblocking(
  220:     int socket)
  221: {
  222:     int flags;
  223: 
  224:     flags = fcntl(socket, F_GETFL, 0);
  225:     if (flags == -1) {
  226:         error(EXIT_FAILURE, errno, "Unexpected socket F_GETFL error");
  227:     }
  228: 
  229:     if (fcntl(socket, F_SETFL, flags | O_NONBLOCK)) {
  230:         error(EXIT_FAILURE, errno, "Unexpected socket F_SETFL O_NONBLOCK error");
  231:     }
  232: }
  233: 
  234: /*  Open the raw sockets for sending/receiving IPv4 packets  */
  235: static
  236: int open_ip4_sockets_raw(
  237:     struct net_state_t *net_state)
  238: {
  239:     int send_socket;
  240:     int recv_socket;
  241:     int trueopt = 1;
  242: 
  243:     send_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
  244:     if (send_socket == -1) {
  245:         send_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
  246:         if (send_socket == -1) {
  247:             return -1;
  248:         }
  249:     }
  250: 
  251:     /*
  252:        We will be including the IP header in transmitted packets.
  253:        Linux doesn't require this, but BSD derived network stacks do.
  254:      */
  255:     if (setsockopt
  256:         (send_socket, IPPROTO_IP, IP_HDRINCL, &trueopt, sizeof(int))) {
  257: 
  258:         close(send_socket);
  259:         return -1;
  260:     }
  261: 
  262:     /*
  263:        Open a second socket with IPPROTO_ICMP because we are only
  264:        interested in receiving ICMP packets, not all packets.
  265:      */
  266:     recv_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
  267:     if (recv_socket == -1) {
  268:         close(send_socket);
  269:         return -1;
  270:     }
  271: 
  272:     net_state->platform.ip4_present = true;
  273:     net_state->platform.ip4_socket_raw = true;
  274:     net_state->platform.ip4_send_socket = send_socket;
  275:     net_state->platform.ip4_recv_socket = recv_socket;
  276: 
  277:     return 0;
  278: }
  279: 
  280: #ifdef HAVE_LINUX_ERRQUEUE_H
  281: /*  Open DGRAM sockets for sending/receiving IPv4 packets  */
  282: static
  283: int open_ip4_sockets_dgram(
  284:     struct net_state_t *net_state)
  285: {
  286:     int udp_socket;
  287:     int icmp_socket, icmp_tmp_socket;
  288: #ifdef HAVE_LINUX_ERRQUEUE_H
  289:     int val = 1;
  290: #endif
  291: 
  292:     icmp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
  293:     if (icmp_socket == -1) {
  294:         return -1;
  295:     }
  296: #ifdef HAVE_LINUX_ERRQUEUE_H
  297:     if (setsockopt(icmp_socket, SOL_IP, IP_RECVERR, &val, sizeof(val)) < 0) {
  298:         return -1;
  299:     }
  300: #endif
  301: 
  302:     udp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  303:     if (udp_socket == -1) {
  304:         close(icmp_socket);
  305:         return -1;
  306:     }
  307: #ifdef HAVE_LINUX_ERRQUEUE_H
  308:     if (setsockopt(udp_socket, SOL_IP, IP_RECVERR, &val, sizeof(val)) < 0) {
  309:         close(icmp_socket);
  310:         close(udp_socket);
  311:         return -1;
  312:     }
  313: #endif
  314: 
  315:     icmp_tmp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
  316:     if (icmp_tmp_socket == -1) {
  317:         close(icmp_socket);
  318:         close(udp_socket);
  319:         return -1;
  320:     }
  321: 
  322:     net_state->platform.ip4_present = true;
  323:     net_state->platform.ip4_socket_raw = false;
  324:     net_state->platform.ip4_txrx_icmp_socket = icmp_socket;
  325:     net_state->platform.ip4_tmp_icmp_socket = icmp_tmp_socket;
  326:     net_state->platform.ip4_txrx_udp_socket = udp_socket;
  327: 
  328:     return 0;
  329: }
  330: #endif
  331: 
  332: /*  Open the raw sockets for sending/receiving IPv6 packets  */
  333: static
  334: int open_ip6_sockets_raw(
  335:     struct net_state_t *net_state)
  336: {
  337:     int send_socket_icmp;
  338:     int send_socket_udp;
  339:     int recv_socket;
  340: 
  341:     send_socket_icmp = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
  342:     if (send_socket_icmp == -1) {
  343:         return -1;
  344:     }
  345: 
  346:     send_socket_udp = socket(AF_INET6, SOCK_RAW, IPPROTO_UDP);
  347:     if (send_socket_udp == -1) {
  348:         close(send_socket_icmp);
  349: 
  350:         return -1;
  351:     }
  352: 
  353:     recv_socket = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
  354:     if (recv_socket == -1) {
  355:         close(send_socket_icmp);
  356:         close(send_socket_udp);
  357: 
  358:         return -1;
  359:     }
  360: 
  361:     net_state->platform.ip6_present = true;
  362:     net_state->platform.ip6_socket_raw = true;
  363:     net_state->platform.icmp6_send_socket = send_socket_icmp;
  364:     net_state->platform.udp6_send_socket = send_socket_udp;
  365:     net_state->platform.ip6_recv_socket = recv_socket;
  366: 
  367:     return 0;
  368: }
  369: 
  370: #ifdef HAVE_LINUX_ERRQUEUE_H
  371: /*  Open DGRAM sockets for sending/receiving IPv6 packets  */
  372: static
  373: int open_ip6_sockets_dgram(
  374:     struct net_state_t *net_state)
  375: {
  376:     int icmp_socket;
  377:     int udp_socket;
  378: #ifdef HAVE_LINUX_ERRQUEUE_H
  379:     int val = 1;
  380: #endif
  381: 
  382:     icmp_socket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
  383:     if (icmp_socket == -1) {
  384:         return -1;
  385:     }
  386: #ifdef HAVE_LINUX_ERRQUEUE_H
  387:     if (setsockopt(icmp_socket, SOL_IPV6, IPV6_RECVERR, &val, sizeof(val)) < 0) {
  388:         return -1;
  389:     }
  390: #endif
  391: 
  392:     udp_socket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
  393:     if (udp_socket == -1) {
  394:         close(icmp_socket);
  395:         return -1;
  396:     }
  397: #ifdef HAVE_LINUX_ERRQUEUE_H
  398:     if (setsockopt(udp_socket, SOL_IPV6, IPV6_RECVERR, &val, sizeof(val)) < 0) {
  399:         close(icmp_socket);
  400:         close(udp_socket);
  401:         return -1;
  402:     }
  403: #endif
  404: 
  405:     net_state->platform.ip6_present = true;
  406:     net_state->platform.ip6_socket_raw = false;
  407:     net_state->platform.ip6_txrx_icmp_socket = icmp_socket;
  408:     net_state->platform.ip6_txrx_udp_socket = udp_socket;
  409: 
  410:     return 0;
  411: }
  412: #endif
  413: 
  414: /*
  415:     The first half of the net state initialization.  Since this
  416:     happens with elevated privileges, this is kept as minimal
  417:     as possible to minimize security risk.
  418: */
  419: void init_net_state_privileged(
  420:     struct net_state_t *net_state)
  421: {
  422:     int ip4_err = 0;
  423:     int ip6_err = 0;
  424: 
  425:     memset(net_state, 0, sizeof(struct net_state_t));
  426: 
  427:     net_state->platform.next_sequence = MIN_PORT;
  428: 
  429:     if (open_ip4_sockets_raw(net_state)) {
  430: #ifdef HAVE_LINUX_ERRQUEUE_H
  431:         /* fall back to using unprivileged sockets */
  432:         if (open_ip4_sockets_dgram(net_state)) {
  433:             ip4_err = errno;
  434:         }
  435: #endif
  436:     }
  437:     if (open_ip6_sockets_raw(net_state)) {
  438: #ifdef HAVE_LINUX_ERRQUEUE_H
  439:         /* fall back to using unprivileged sockets */
  440:         if (open_ip6_sockets_dgram(net_state)) {
  441:             ip6_err = errno;
  442:         }
  443: #endif
  444:     }
  445: 
  446:     /*
  447:        If we couldn't open either IPv4 or IPv6 sockets, we can't do
  448:        much, so print errors and exit.
  449:      */
  450:     if (!net_state->platform.ip4_present
  451:         && !net_state->platform.ip6_present) {
  452:         error(0, ip4_err, "Failure to open IPv4 sockets");
  453:         error(0, ip6_err, "Failure to open IPv6 sockets");
  454:         exit(EXIT_FAILURE);
  455:     }
  456: }
  457: 
  458: /*
  459:     The second half of net state initialization, which is run
  460:     at normal privilege levels.
  461: */
  462: void init_net_state(
  463:     struct net_state_t *net_state)
  464: {
  465:     if (net_state->platform.ip4_socket_raw) {
  466:         set_socket_nonblocking(net_state->platform.ip4_recv_socket);
  467:     } else {
  468:         set_socket_nonblocking(net_state->platform.ip4_txrx_icmp_socket);
  469:         set_socket_nonblocking(net_state->platform.ip4_txrx_udp_socket);
  470:     }
  471:     if (net_state->platform.ip6_socket_raw) {
  472:         set_socket_nonblocking(net_state->platform.ip6_recv_socket);
  473:     } else {
  474:         set_socket_nonblocking(net_state->platform.ip6_txrx_icmp_socket);
  475:         set_socket_nonblocking(net_state->platform.ip6_txrx_udp_socket);
  476:     }
  477: 
  478:     if (net_state->platform.ip4_present) {
  479:         check_length_order(net_state);
  480:     }
  481: 
  482:     check_sctp_support(net_state);
  483: }
  484: 
  485: /*
  486:     Returns true if we were able to open sockets for a particular
  487:     IP protocol version.
  488: */
  489: bool is_ip_version_supported(
  490:     struct net_state_t *net_state,
  491:     int ip_version)
  492: {
  493:     if (ip_version == 4) {
  494:         return net_state->platform.ip4_present;
  495:     } else if (ip_version == 6) {
  496:         return net_state->platform.ip6_present;
  497:     } else {
  498:         return false;
  499:     }
  500: }
  501: 
  502: /*  Returns true if we can transmit probes using the specified protocol  */
  503: bool is_protocol_supported(
  504:     struct net_state_t * net_state,
  505:     int protocol)
  506: {
  507:     if (protocol == IPPROTO_ICMP) {
  508:         return true;
  509:     }
  510: 
  511:     if (protocol == IPPROTO_UDP) {
  512:         return true;
  513:     }
  514: 
  515:     if (protocol == IPPROTO_TCP) {
  516:         return true;
  517:     }
  518: #ifdef IPPROTO_SCTP
  519:     if (protocol == IPPROTO_SCTP) {
  520:         return net_state->platform.sctp_support;
  521:     }
  522: #endif
  523: 
  524:     return false;
  525: }
  526: 
  527: /*  Report an error during send_probe based on the errno value  */
  528: static
  529: void report_packet_error(
  530:     int command_token)
  531: {
  532:     if (errno == EINVAL) {
  533:         printf("%d invalid-argument\n", command_token);
  534:     } else if (errno == ENETDOWN) {
  535:         printf("%d network-down\n", command_token);
  536:     } else if (errno == ENETUNREACH) {
  537:         printf("%d no-route\n", command_token);
  538:     } else if (errno == EHOSTUNREACH) {
  539:         printf("%d no-route\n", command_token);
  540:     } else if (errno == EPERM) {
  541:         printf("%d permission-denied\n", command_token);
  542:     } else if (errno == EADDRINUSE) {
  543:         printf("%d address-in-use\n", command_token);
  544:     } else if (errno == EADDRNOTAVAIL) {
  545:         printf("%d address-not-available\n", command_token);
  546:     } else {
  547:         printf("%d unexpected-error errno %d\n", command_token, errno);
  548:     }
  549: }
  550: 
  551: /*  Craft a custom ICMP packet for a network probe.  */
  552: void send_probe(
  553:     struct net_state_t *net_state,
  554:     const struct probe_param_t *param)
  555: {
  556:     char packet[PACKET_BUFFER_SIZE];
  557:     struct probe_t *probe;
  558:     int trytimes;
  559:     int packet_size;
  560: 
  561:     probe = alloc_probe(net_state, param->command_token);
  562:     if (probe == NULL) {
  563:         printf("%d probes-exhausted\n", param->command_token);
  564:         return;
  565:     }
  566: 
  567:     if (resolve_probe_addresses(net_state, param, &probe->remote_addr,
  568:                 &probe->local_addr)) {
  569:         printf("%d invalid-argument\n", param->command_token);
  570:         free_probe(net_state, probe);
  571:         return;
  572:     }
  573: 
  574:     if (gettimeofday(&probe->platform.departure_time, NULL)) {
  575:         error(EXIT_FAILURE, errno, "gettimeofday failure");
  576:     }
  577: 
  578:     // there might be an off-by-one in the number of tries here.
  579:     // this is intentional.  It is no use exhausting the very last
  580:     // open port. Max 10 retries would've been acceptable too I think.
  581:     for (trytimes=MIN_PORT; trytimes < MAX_PORT; trytimes++) {
  582: 
  583:         packet_size = construct_packet(net_state, &probe->platform.socket,
  584:                          probe, packet, PACKET_BUFFER_SIZE,
  585:                          param);
  586: 
  587:         if (packet_size > 0) break; // no retry if we succeed.
  588: 
  589:         if ((param->protocol != IPPROTO_TCP) &&
  590:             (param->protocol != IPPROTO_SCTP)) break; // no retry if not TCP/SCTP
  591: 
  592:         if ((errno != EADDRINUSE) && (errno != EADDRNOTAVAIL)) {
  593:             break; // no retry
  594:         }
  595: 
  596:      	probe->sequence = net_state->platform.next_sequence++;
  597: 
  598:        	if (net_state->platform.next_sequence > MAX_PORT) {
  599:             net_state->platform.next_sequence = MIN_PORT;
  600:         }
  601:     }
  602: 
  603:     if (packet_size < 0) {
  604:         /*
  605:            When using a stream protocol, FreeBSD will return ECONNREFUSED
  606:            when connecting to localhost if the port doesn't exist,
  607:            even if the socket is non-blocking, so we should be
  608:            prepared for that.
  609:          */
  610:         if (errno == ECONNREFUSED) {
  611:             receive_probe(net_state, probe, ICMP_ECHOREPLY,
  612:                           &probe->remote_addr, NULL, 0, NULL);
  613:         } else {
  614:             report_packet_error(param->command_token);
  615:             free_probe(net_state, probe);
  616:         }
  617: 
  618:         return;
  619:     }
  620: 
  621:     if (packet_size > 0) {
  622:         if (send_packet(net_state, param, probe->sequence,
  623:                         packet, packet_size, &probe->remote_addr) == -1) {
  624: 
  625:             report_packet_error(param->command_token);
  626:             free_probe(net_state, probe);
  627:             return;
  628:         }
  629:     }
  630: 
  631:     probe->platform.timeout_time = probe->platform.departure_time;
  632:     probe->platform.timeout_time.tv_sec += param->timeout;
  633: }
  634: 
  635: /*  When allocating a probe, assign it a unique port number  */
  636: void platform_alloc_probe(
  637:     struct net_state_t *net_state,
  638:     struct probe_t *probe)
  639: {
  640:     probe->sequence = net_state->platform.next_sequence++;
  641: 
  642:     if (net_state->platform.next_sequence > MAX_PORT) {
  643:         net_state->platform.next_sequence = MIN_PORT;
  644:     }
  645: }
  646: 
  647: /*
  648:     When freeing the probe, close the socket for the probe,
  649:     if one has been opened
  650: */
  651: void platform_free_probe(
  652:     struct probe_t *probe)
  653: {
  654:     if (probe->platform.socket) {
  655:         close(probe->platform.socket);
  656:         probe->platform.socket = 0;
  657:     }
  658: }
  659: 
  660: /*
  661:     Compute the round trip time of a just-received probe and pass it
  662:     to the platform agnostic response handling.
  663: */
  664: void receive_probe(
  665:     struct net_state_t *net_state,
  666:     struct probe_t *probe,
  667:     int icmp_type,
  668:     const struct sockaddr_storage *remote_addr,
  669:     struct timeval *timestamp,
  670:     int mpls_count,
  671:     struct mpls_label_t *mpls)
  672: {
  673:     unsigned int round_trip_us;
  674:     struct timeval *departure_time = &probe->platform.departure_time;
  675:     struct timeval now;
  676: 
  677:     if (timestamp == NULL) {
  678:         if (gettimeofday(&now, NULL)) {
  679:             error(EXIT_FAILURE, errno, "gettimeofday failure");
  680:         }
  681: 
  682:         timestamp = &now;
  683:     }
  684: 
  685:     round_trip_us =
  686:         (timestamp->tv_sec - departure_time->tv_sec) * 1000000 +
  687:         timestamp->tv_usec - departure_time->tv_usec;
  688: 
  689:     respond_to_probe(net_state, probe, icmp_type,
  690:                      remote_addr, round_trip_us, mpls_count, mpls);
  691: }
  692: 
  693: /*
  694:     Read all available packets through our receiving raw socket, and
  695:     handle any responses to probes we have previously sent.
  696: */
  697: static
  698: void receive_replies_from_recv_socket(
  699:     struct net_state_t *net_state,
  700:     int socket,
  701:     received_packet_func_t handle_received_packet)
  702: {
  703:     char packet[PACKET_BUFFER_SIZE];
  704:     int packet_length;
  705:     struct sockaddr_storage remote_addr;
  706:     struct timeval timestamp;
  707:     int flag = 0;
  708: #ifdef HAVE_LINUX_ERRQUEUE_H
  709:     struct cmsghdr *cm;
  710:     struct sock_extended_err *ee = NULL;
  711:     bool icmp_connrefused_received = false;
  712:     bool icmp_hostunreach_received = false;
  713: #endif
  714: 
  715:     /*  Read until no more packets are available  */
  716:     while (true) {
  717:         struct iovec iov;
  718:         struct msghdr msg;
  719:         char control[1024];
  720: 
  721:         memset(&msg, 0, sizeof(msg));
  722:         memset(&iov, 0, sizeof(iov));
  723:         iov.iov_base = packet;
  724:         iov.iov_len = sizeof(packet);
  725:         msg.msg_iov = &iov;
  726:         msg.msg_iovlen = 1;
  727:         msg.msg_name = (struct sockaddr*) &remote_addr;
  728:         msg.msg_namelen = sizeof(remote_addr);
  729:         msg.msg_control = control;
  730:         msg.msg_controllen = sizeof(control);
  731:         packet_length = recvmsg(socket, &msg, flag);
  732: 
  733:         /*
  734:            Get the time immediately after reading the packet to
  735:            keep the timing as precise as we can.
  736:          */
  737:         if (gettimeofday(&timestamp, NULL)) {
  738:             error(EXIT_FAILURE, errno, "gettimeofday failure");
  739:         }
  740: 
  741:         if (packet_length == -1) {
  742:             /*
  743:                EAGAIN will be returned if there is no current packet
  744:                available.
  745:              */
  746:             if (errno == EAGAIN) {
  747:                 return;
  748:             }
  749: 
  750:             /*
  751:                EINTER will be returned if we received a signal during
  752:                receive.
  753:              */
  754:             if (errno == EINTR) {
  755:                 /* clear error */
  756:                 int so_err;
  757:                 socklen_t so_err_size = sizeof(so_err);
  758:                 int err;
  759: 
  760:                 do {
  761:                   err = getsockopt(socket, SOL_SOCKET, SO_ERROR, &so_err, &so_err_size);
  762:                 } while (err < 0 && errno == EINTR);
  763:                 continue;
  764:             }
  765: 
  766:             /* handle error received in error queue */
  767:             if (errno == EHOSTUNREACH) {
  768:                 /* potential error caused by ttl, read inner icmp hdr from err queue */
  769: #ifdef HAVE_LINUX_ERRQUEUE_H
  770:                 icmp_hostunreach_received = true;
  771:                 flag |= MSG_ERRQUEUE;
  772: #endif
  773:                 continue;
  774:             }
  775: 
  776:             if (errno == ECONNREFUSED) {
  777:                 /* udp packet reached dst, read inner udp hdr from err queue */
  778: #ifdef HAVE_LINUX_ERRQUEUE_H
  779:                 icmp_connrefused_received = true;
  780:                 flag |= MSG_ERRQUEUE;
  781: #endif
  782:                 continue;
  783:             }
  784: 
  785:             error(EXIT_FAILURE, errno, "Failure receiving replies");
  786:         }
  787: 
  788: #ifdef HAVE_LINUX_ERRQUEUE_H
  789:         /* get src ip for packets read from err queue */
  790:         if (flag & MSG_ERRQUEUE) {
  791:             for (cm = CMSG_FIRSTHDR(&msg); cm; cm = CMSG_NXTHDR(&msg, cm)) {
  792:                 if (cm->cmsg_level == SOL_IP) {
  793:                     if (cm->cmsg_type == IP_RECVERR) {
  794:                         ee = (struct sock_extended_err *) CMSG_DATA(cm);
  795:                     }
  796:                 }
  797:                 else if (cm->cmsg_level == SOL_IPV6) {
  798:                     if (cm->cmsg_type == IPV6_RECVERR) {
  799:                         ee = (struct sock_extended_err *) CMSG_DATA(cm);
  800:                     }
  801:                 }
  802:             }
  803:             if (ee) {
  804:                 memcpy(&remote_addr, SO_EE_OFFENDER(ee), sizeof(remote_addr));
  805:             }
  806:         }
  807: 
  808: #ifdef SO_PROTOCOL
  809:         if (icmp_connrefused_received) {
  810:             /* using ICMP type ICMP_ECHOREPLY is not a bug, it is an
  811:                indication of successfully reaching dst host.
  812:              */
  813:             handle_error_queue_packet(net_state, &remote_addr, ICMP_ECHOREPLY, IPPROTO_UDP,
  814:                     packet, packet_length, &timestamp);
  815:         } else if (icmp_hostunreach_received) {
  816:             /* handle packet based on send socket protocol */
  817:             int proto, length = sizeof(int);
  818: 
  819:             if (getsockopt(socket, SOL_SOCKET, SO_PROTOCOL, &proto, &length) < 0) {
  820:                 error(EXIT_FAILURE, errno, "getsockopt SO_PROTOCOL error");
  821:             }
  822:             handle_error_queue_packet(net_state, &remote_addr, ICMP_TIME_EXCEEDED, proto,
  823:                     packet, packet_length, &timestamp);
  824:         } else {
  825: #endif
  826: #endif
  827:             /* ICMP packets received from raw socket */
  828:             handle_received_packet(net_state, &remote_addr, packet,
  829:                                    packet_length, &timestamp);
  830: #ifdef HAVE_LINUX_ERRQUEUE_H
  831: #ifdef SO_PROTOCOL
  832:         }
  833: #endif
  834: #endif
  835:     }
  836: }
  837: 
  838: /*
  839:     Attempt to send using the probe's socket, in order to check whether
  840:     the connection has completed, for stream oriented protocols such as
  841:     TCP.
  842: */
  843: static
  844: void receive_replies_from_probe_socket(
  845:     struct net_state_t *net_state,
  846:     struct probe_t *probe)
  847: {
  848:     int probe_socket;
  849:     struct timeval zero_time;
  850:     int err;
  851:     int err_length = sizeof(int);
  852:     fd_set write_set;
  853: 
  854:     probe_socket = probe->platform.socket;
  855:     if (!probe_socket) {
  856:         return;
  857:     }
  858: 
  859:     FD_ZERO(&write_set);
  860:     FD_SET(probe_socket, &write_set);
  861: 
  862:     zero_time.tv_sec = 0;
  863:     zero_time.tv_usec = 0;
  864: 
  865:     if (select(probe_socket + 1, NULL, &write_set, NULL, &zero_time) == -1) {
  866:         if (errno == EAGAIN) {
  867:             return;
  868:         } else {
  869:             error(EXIT_FAILURE, errno, "probe socket select error");
  870:         }
  871:     }
  872: 
  873:     /*
  874:        If the socket is writable, the connection attempt has completed.
  875:      */
  876:     if (!FD_ISSET(probe_socket, &write_set)) {
  877:         return;
  878:     }
  879: 
  880:     if (getsockopt(probe_socket, SOL_SOCKET, SO_ERROR, &err, &err_length)) {
  881:         error(EXIT_FAILURE, errno, "probe socket SO_ERROR");
  882:     }
  883: 
  884:     /*
  885:        If the connection complete successfully, or was refused, we can
  886:        assume our probe arrived at the destination.
  887:      */
  888:     if (!err || err == ECONNREFUSED) {
  889:         receive_probe(net_state, probe, ICMP_ECHOREPLY,
  890:                       &probe->remote_addr, NULL, 0, NULL);
  891:     } else {
  892:         errno = err;
  893:         report_packet_error(probe->token);
  894:         free_probe(net_state, probe);
  895:     }
  896: }
  897: 
  898: /*  Check both the IPv4 and IPv6 sockets for incoming packets  */
  899: void receive_replies(
  900:     struct net_state_t *net_state)
  901: {
  902:     struct probe_t *probe;
  903:     struct probe_t *probe_safe_iter;
  904: 
  905:     if (net_state->platform.ip4_present) {
  906:         if (net_state->platform.ip4_socket_raw) {
  907:             receive_replies_from_recv_socket(net_state,
  908:                                              net_state->platform.
  909:                                              ip4_recv_socket,
  910:                                              handle_received_ip4_packet);
  911:         } else {
  912:             receive_replies_from_recv_socket(net_state,
  913:                                              net_state->platform.
  914:                                              ip4_txrx_icmp_socket,
  915:                                              handle_received_ip4_packet);
  916:             receive_replies_from_recv_socket(net_state,
  917:                                              net_state->platform.
  918:                                              ip4_txrx_udp_socket,
  919:                                              handle_received_ip4_packet);
  920:         }
  921:     }
  922: 
  923:     if (net_state->platform.ip6_present) {
  924:         if (net_state->platform.ip6_socket_raw) {
  925:             receive_replies_from_recv_socket(net_state,
  926:                                              net_state->platform.
  927:                                              ip6_recv_socket,
  928:                                              handle_received_ip6_packet);
  929:         } else {
  930:             receive_replies_from_recv_socket(net_state,
  931:                                              net_state->platform.
  932:                                              ip6_txrx_icmp_socket,
  933:                                              handle_received_ip6_packet);
  934:             receive_replies_from_recv_socket(net_state,
  935:                                              net_state->platform.
  936:                                              ip6_txrx_udp_socket,
  937:                                              handle_received_ip6_packet);
  938:         }
  939:     }
  940: 
  941:     LIST_FOREACH_SAFE(probe, &net_state->outstanding_probes,
  942:                       probe_list_entry, probe_safe_iter) {
  943: 
  944:         receive_replies_from_probe_socket(net_state, probe);
  945:     }
  946: }
  947: 
  948: /*
  949:     Put all of our probe sockets in the read set used for an upcoming
  950:     select so we can wake when any of them become readable.
  951: */
  952: int gather_probe_sockets(
  953:     const struct net_state_t *net_state,
  954:     fd_set * write_set)
  955: {
  956:     int probe_socket;
  957:     int nfds;
  958:     const struct probe_t *probe;
  959: 
  960:     nfds = 0;
  961: 
  962:     LIST_FOREACH(probe, &net_state->outstanding_probes, probe_list_entry) {
  963:         probe_socket = probe->platform.socket;
  964: 
  965:         if (probe_socket) {
  966:             FD_SET(probe_socket, write_set);
  967:             if (probe_socket >= nfds) {
  968:                 nfds = probe_socket + 1;
  969:             }
  970:         }
  971:     }
  972: 
  973:     return nfds;
  974: }
  975: 
  976: /*
  977:     Check for any probes for which we have not received a response
  978:     for some time, and report a time-out, assuming that we won't
  979:     receive a future reply.
  980: */
  981: void check_probe_timeouts(
  982:     struct net_state_t *net_state)
  983: {
  984:     struct timeval now;
  985:     struct probe_t *probe;
  986:     struct probe_t *probe_safe_iter;
  987: 
  988:     if (gettimeofday(&now, NULL)) {
  989:         error(EXIT_FAILURE, errno, "gettimeofday failure");
  990:     }
  991: 
  992:     LIST_FOREACH_SAFE(probe, &net_state->outstanding_probes,
  993:                       probe_list_entry, probe_safe_iter) {
  994: 
  995:         if (compare_timeval(probe->platform.timeout_time, now) < 0) {
  996:             /*  Report timeout to the command stream  */
  997:             printf("%d no-reply\n", probe->token);
  998: 
  999:             free_probe(net_state, probe);
 1000:         }
 1001:     }
 1002: }
 1003: 
 1004: /*
 1005:     Find the remaining time until the next probe times out.
 1006:     This may be a negative value if the next probe timeout has
 1007:     already elapsed.
 1008: 
 1009:     Returns false if no probes are currently outstanding, and true
 1010:     if a timeout value for the next probe exists.
 1011: */
 1012: bool get_next_probe_timeout(
 1013:     const struct net_state_t *net_state,
 1014:     struct timeval *timeout)
 1015: {
 1016:     bool have_timeout;
 1017:     const struct probe_t *probe;
 1018:     struct timeval now;
 1019:     struct timeval probe_timeout;
 1020: 
 1021:     if (gettimeofday(&now, NULL)) {
 1022:         error(EXIT_FAILURE, errno, "gettimeofday failure");
 1023:     }
 1024: 
 1025:     have_timeout = false;
 1026:     LIST_FOREACH(probe, &net_state->outstanding_probes, probe_list_entry) {
 1027:         probe_timeout.tv_sec =
 1028:             probe->platform.timeout_time.tv_sec - now.tv_sec;
 1029:         probe_timeout.tv_usec =
 1030:             probe->platform.timeout_time.tv_usec - now.tv_usec;
 1031: 
 1032:         normalize_timeval(&probe_timeout);
 1033:         if (have_timeout) {
 1034:             if (compare_timeval(probe_timeout, *timeout) < 0) {
 1035:                 /*  If this probe has a sooner timeout, store it instead  */
 1036:                 *timeout = probe_timeout;
 1037:             }
 1038:         } else {
 1039:             *timeout = probe_timeout;
 1040:             have_timeout = true;
 1041:         }
 1042:     }
 1043: 
 1044:     return have_timeout;
 1045: }

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