Annotation of embedaddon/mtr/packet/probe_unix.c, revision 1.1.1.2

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 <assert.h>
                     22: #include <errno.h>
                     23: #include <fcntl.h>
1.1.1.2 ! misho      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
1.1       misho      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"
1.1.1.2 ! misho      40: #include "sockaddr.h"
1.1       misho      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,
1.1.1.2 ! misho      50:     int sequence,
1.1       misho      51:     const char *packet,
                     52:     int packet_size,
                     53:     const struct sockaddr_storage *sockaddr)
                     54: {
1.1.1.2 ! misho      55:     struct sockaddr_storage dst;
1.1       misho      56:     int send_socket = 0;
                     57:     int sockaddr_length;
                     58: 
1.1.1.2 ! misho      59:     memcpy(&dst, sockaddr, sizeof(struct sockaddr_storage));
        !            60: 
1.1       misho      61:     if (sockaddr->ss_family == AF_INET6) {
                     62:         sockaddr_length = sizeof(struct sockaddr_in6);
                     63: 
                     64:         if (param->protocol == IPPROTO_ICMP) {
1.1.1.2 ! misho      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:             }
1.1       misho      70:         } else if (param->protocol == IPPROTO_UDP) {
1.1.1.2 ! misho      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:             }
1.1       misho      86:         }
                     87:     } else if (sockaddr->ss_family == AF_INET) {
                     88:         sockaddr_length = sizeof(struct sockaddr_in);
                     89: 
1.1.1.2 ! misho      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:         }
1.1       misho     108:     }
                    109: 
                    110:     if (send_socket == 0) {
                    111:         errno = EINVAL;
                    112:         return -1;
                    113:     }
                    114: 
                    115:     return sendto(send_socket, packet, packet_size, 0,
1.1.1.2 ! misho     116:                   (struct sockaddr *) &dst, sockaddr_length);
1.1       misho     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;
1.1.1.2 ! misho     136:     struct probe_t p0 = {.sequence = MIN_PORT };
1.1       misho     137:     ssize_t bytes_sent;
                    138:     int packet_size;
                    139: 
1.1.1.2 ! misho     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: 
1.1       misho     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";
1.1.1.2 ! misho     151:     param.is_probing_byte_order = true;
1.1       misho     152: 
1.1.1.2 ! misho     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));
1.1       misho     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: 
1.1.1.2 ! misho     164:     packet_size = construct_packet(net_state, NULL, &p0,
1.1       misho     165:                                    packet, PACKET_BUFFER_SIZE,
1.1.1.2 ! misho     166:                                    &param);
1.1       misho     167:     if (packet_size < 0) {
1.1.1.2 ! misho     168:       error(EXIT_FAILURE, errno, "Unable to send to localhost");
1.1       misho     169:     }
                    170: 
                    171:     bytes_sent =
1.1.1.2 ! misho     172:         send_packet(net_state, &param, MIN_PORT, packet, packet_size,
        !           173:                     &p0.remote_addr);
1.1       misho     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: 
1.1.1.2 ! misho     181:     packet_size = construct_packet(net_state, NULL, &p0,
1.1       misho     182:                                    packet, PACKET_BUFFER_SIZE,
1.1.1.2 ! misho     183:                                    &param);
1.1       misho     184:     if (packet_size < 0) {
1.1.1.2 ! misho     185:         error(EXIT_FAILURE, errno, "Unable to send to localhost");
1.1       misho     186:     }
                    187: 
                    188:     bytes_sent =
1.1.1.2 ! misho     189:         send_packet(net_state, &param, MIN_PORT, packet, packet_size,
        !           190:                     &p0.remote_addr);
1.1       misho     191:     if (bytes_sent < 0) {
1.1.1.2 ! misho     192:         error(EXIT_FAILURE, errno, "Unable to send with swapped length");
1.1       misho     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) {
1.1.1.2 ! misho     226:         error(EXIT_FAILURE, errno, "Unexpected socket F_GETFL error");
1.1       misho     227:     }
                    228: 
                    229:     if (fcntl(socket, F_SETFL, flags | O_NONBLOCK)) {
1.1.1.2 ! misho     230:         error(EXIT_FAILURE, errno, "Unexpected socket F_SETFL O_NONBLOCK error");
1.1       misho     231:     }
                    232: }
                    233: 
                    234: /*  Open the raw sockets for sending/receiving IPv4 packets  */
                    235: static
1.1.1.2 ! misho     236: int open_ip4_sockets_raw(
1.1       misho     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) {
1.1.1.2 ! misho     245:         send_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
        !           246:         if (send_socket == -1) {
        !           247:             return -1;
        !           248:         }
1.1       misho     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;
1.1.1.2 ! misho     273:     net_state->platform.ip4_socket_raw = true;
1.1       misho     274:     net_state->platform.ip4_send_socket = send_socket;
                    275:     net_state->platform.ip4_recv_socket = recv_socket;
                    276: 
                    277:     return 0;
                    278: }
                    279: 
1.1.1.2 ! misho     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: 
1.1       misho     332: /*  Open the raw sockets for sending/receiving IPv6 packets  */
                    333: static
1.1.1.2 ! misho     334: int open_ip6_sockets_raw(
1.1       misho     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;
1.1.1.2 ! misho     362:     net_state->platform.ip6_socket_raw = true;
1.1       misho     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: 
1.1.1.2 ! misho     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: 
1.1       misho     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: 
1.1.1.2 ! misho     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
1.1       misho     436:     }
1.1.1.2 ! misho     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
1.1       misho     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) {
1.1.1.2 ! misho     452:         error(0, ip4_err, "Failure to open IPv4 sockets");
        !           453:         error(0, ip6_err, "Failure to open IPv6 sockets");
1.1       misho     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: {
1.1.1.2 ! misho     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:     }
1.1       misho     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;
1.1.1.2 ! misho     558:     int trytimes;
1.1       misho     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: 
1.1.1.2 ! misho     567:     if (resolve_probe_addresses(net_state, param, &probe->remote_addr,
        !           568:                 &probe->local_addr)) {
1.1       misho     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)) {
1.1.1.2 ! misho     575:         error(EXIT_FAILURE, errno, "gettimeofday failure");
1.1       misho     576:     }
                    577: 
1.1.1.2 ! misho     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:     }
1.1       misho     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) {
1.1.1.2 ! misho     622:         if (send_packet(net_state, param, probe->sequence,
1.1       misho     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)) {
1.1.1.2 ! misho     679:             error(EXIT_FAILURE, errno, "gettimeofday failure");
1.1       misho     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
1.1.1.2 ! misho     695:     handle any responses to probes we have previously sent.
1.1       misho     696: */
                    697: static
1.1.1.2 ! misho     698: void receive_replies_from_recv_socket(
1.1       misho     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;
1.1.1.2 ! misho     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
1.1       misho     714: 
                    715:     /*  Read until no more packets are available  */
                    716:     while (true) {
1.1.1.2 ! misho     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);
1.1       misho     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)) {
1.1.1.2 ! misho     738:             error(EXIT_FAILURE, errno, "gettimeofday failure");
1.1       misho     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) {
1.1.1.2 ! misho     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);
1.1       misho     763:                 continue;
                    764:             }
                    765: 
1.1.1.2 ! misho     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");
1.1       misho     786:         }
                    787: 
1.1.1.2 ! misho     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
1.1       misho     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 {
1.1.1.2 ! misho     869:             error(EXIT_FAILURE, errno, "probe socket select error");
1.1       misho     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)) {
1.1.1.2 ! misho     881:         error(EXIT_FAILURE, errno, "probe socket SO_ERROR");
1.1       misho     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) {
1.1.1.2 ! misho     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:         }
1.1       misho     921:     }
                    922: 
                    923:     if (net_state->platform.ip6_present) {
1.1.1.2 ! misho     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:         }
1.1       misho     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)) {
1.1.1.2 ! misho     989:         error(EXIT_FAILURE, errno, "gettimeofday failure");
1.1       misho     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)) {
1.1.1.2 ! misho    1022:         error(EXIT_FAILURE, errno, "gettimeofday failure");
1.1       misho    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>