File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / mtr / packet / construct_unix.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Mar 17 00:07:30 2021 UTC (3 years, 3 months ago) by misho
Branches: mtr, MAIN
CVS tags: v0_94, HEAD
mtr 0.94

    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 "construct_unix.h"
   20: 
   21: #include <errno.h>
   22: #include <stdio.h>
   23: #include <string.h>
   24: #include <sys/socket.h>
   25: #include <unistd.h>
   26: 
   27: #include "protocols.h"
   28: #include "sockaddr.h"
   29: 
   30: /* For Mac OS X and FreeBSD */
   31: #ifndef SOL_IP
   32: #define SOL_IP IPPROTO_IP
   33: #endif
   34: 
   35: /*  A source of data for computing a checksum  */
   36: struct checksum_source_t {
   37:     const void *data;
   38:     size_t size;
   39: };
   40: 
   41: /*  Compute the IP checksum (or ICMP checksum) of a packet.  */
   42: static
   43: uint16_t compute_checksum(
   44:     const void *packet,
   45:     int size)
   46: {
   47:     const uint8_t *packet_bytes = (uint8_t *) packet;
   48:     uint32_t sum = 0;
   49:     int i;
   50: 
   51:     for (i = 0; i < size; i++) {
   52:         if ((i & 1) == 0) {
   53:             sum += packet_bytes[i] << 8;
   54:         } else {
   55:             sum += packet_bytes[i];
   56:         }
   57:     }
   58: 
   59:     /*
   60:        Sums which overflow a 16-bit value have the high bits
   61:        added back into the low 16 bits.
   62:      */
   63:     while (sum >> 16) {
   64:         sum = (sum >> 16) + (sum & 0xffff);
   65:     }
   66: 
   67:     /*
   68:        The value stored is the one's complement of the
   69:        mathematical sum.
   70:      */
   71:     return (~sum & 0xffff);
   72: }
   73: 
   74: /*  Encode the IP header length field in the order required by the OS.  */
   75: static
   76: uint16_t length_byte_swap(
   77:     const struct net_state_t *net_state,
   78:     uint16_t length)
   79: {
   80:     if (net_state->platform.ip_length_host_order) {
   81:         return length;
   82:     } else {
   83:         return htons(length);
   84:     }
   85: }
   86: 
   87: /*  Construct a combined sockaddr from a source address and source port  */
   88: static
   89: void construct_addr_port(
   90:     struct sockaddr_storage *addr_with_port,
   91:     const struct sockaddr_storage *addr,
   92:     int port)
   93: {
   94:     memcpy(addr_with_port, addr, sizeof(struct sockaddr_storage));
   95:     *sockaddr_port_offset(addr_with_port) = htons(port);
   96: }
   97: 
   98: /*  Construct a header for IP version 4  */
   99: static
  100: void construct_ip4_header(
  101:     const struct net_state_t *net_state,
  102:     const struct probe_t *probe,
  103:     char *packet_buffer,
  104:     int packet_size,
  105:     const struct probe_param_t *param)
  106: {
  107:     struct IPHeader *ip;
  108: 
  109:     ip = (struct IPHeader *) &packet_buffer[0];
  110: 
  111:     memset(ip, 0, sizeof(struct IPHeader));
  112: 
  113:     ip->version = 0x45;
  114:     ip->tos = param->type_of_service;
  115:     ip->len = length_byte_swap(net_state, packet_size);
  116:     ip->ttl = param->ttl;
  117:     ip->protocol = param->protocol;
  118: //    ip->id = htons(getpid());
  119:     memcpy(&ip->saddr,
  120:            sockaddr_addr_offset(&probe->local_addr),
  121:            sockaddr_addr_size(&probe->local_addr));
  122:     memcpy(&ip->daddr,
  123:            sockaddr_addr_offset(&probe->remote_addr),
  124:            sockaddr_addr_size(&probe->remote_addr));
  125: }
  126: 
  127: /*  Construct an ICMP header for IPv4  */
  128: static
  129: void construct_icmp4_header(
  130:     const struct net_state_t *net_state,
  131:     struct probe_t *probe,
  132:     char *packet_buffer,
  133:     int packet_size,
  134:     const struct probe_param_t *param)
  135: {
  136:     struct ICMPHeader *icmp;
  137:     int icmp_size;
  138: 
  139:     if (net_state->platform.ip4_socket_raw) {
  140:         icmp = (struct ICMPHeader *) &packet_buffer[sizeof(struct IPHeader)];
  141:         icmp_size = packet_size - sizeof(struct IPHeader);
  142:     } else {
  143:         icmp = (struct ICMPHeader *) &packet_buffer[0];
  144:         icmp_size = packet_size;
  145:     }
  146: 
  147:     memset(icmp, 0, sizeof(struct ICMPHeader));
  148: 
  149:     icmp->type = ICMP_ECHO;
  150:     icmp->id = htons(getpid());
  151:     icmp->sequence = htons(probe->sequence);
  152:     icmp->checksum = htons(compute_checksum(icmp, icmp_size));
  153: }
  154: 
  155: /*  Construct an ICMP header for IPv6  */
  156: static
  157: int construct_icmp6_packet(
  158:     const struct net_state_t *net_state,
  159:     struct probe_t *probe,
  160:     char *packet_buffer,
  161:     int packet_size,
  162:     const struct probe_param_t *param)
  163: {
  164:     struct ICMPHeader *icmp;
  165: 
  166:     icmp = (struct ICMPHeader *) packet_buffer;
  167: 
  168:     memset(icmp, 0, sizeof(struct ICMPHeader));
  169: 
  170:     icmp->type = ICMP6_ECHO;
  171:     icmp->id = htons(getpid());
  172:     icmp->sequence = htons(probe->sequence);
  173: 
  174:     return 0;
  175: }
  176: 
  177: /*
  178:     Set the port numbers for an outgoing UDP probe.
  179:     There is limited space in the header for a sequence number
  180:     to identify the probe upon return.
  181: 
  182:     We store the sequence number in the destination port, the local
  183:     port, or the checksum.  The location chosen depends upon which
  184:     probe parameters have been requested.
  185: */
  186: static
  187: void set_udp_ports(
  188:     struct UDPHeader *udp,
  189:     struct probe_t *probe,
  190:     const struct probe_param_t *param)
  191: {
  192:     if (param->dest_port) {
  193:         udp->dstport = htons(param->dest_port);
  194: 
  195:         if (param->local_port) {
  196:             udp->srcport = htons(param->local_port);
  197:             udp->checksum = htons(probe->sequence);
  198:         } else {
  199:             udp->srcport = htons(probe->sequence);
  200:             udp->checksum = 0;
  201:         }
  202:     } else {
  203:         udp->dstport = htons(probe->sequence);
  204: 
  205:         if (param->local_port) {
  206:             udp->srcport = htons(param->local_port);
  207:         } else {
  208:             udp->srcport = htons(getpid());
  209:         }
  210: 
  211:         udp->checksum = 0;
  212:     }
  213:     *sockaddr_port_offset(&probe->local_addr) = udp->srcport;
  214:     *sockaddr_port_offset(&probe->remote_addr) = udp->dstport;
  215: }
  216: 
  217: /* Prepend pseudoheader to the udp datagram and calculate checksum */
  218: static
  219: int udp4_checksum(void *pheader, void *udata, int psize, int dsize,
  220:                   int alt_checksum)
  221: {
  222:     unsigned int totalsize = psize + dsize;
  223:     unsigned char csumpacket[totalsize];
  224: 
  225:     memcpy(csumpacket, pheader, psize); /* pseudo header */
  226:     memcpy(csumpacket+psize, udata, dsize);   /* udp header & payload */
  227: 
  228:     if (alt_checksum && dsize >= sizeof(struct UDPHeader) + 2) {
  229:         csumpacket[psize + sizeof(struct UDPHeader)] = 0;
  230:         csumpacket[psize + sizeof(struct UDPHeader) + 1] = 0;
  231:     }
  232: 
  233:     return compute_checksum(csumpacket, totalsize);
  234: }
  235: 
  236: /*
  237:     Construct a header for UDP probes, using the port number associated
  238:     with the probe.
  239: */
  240: static
  241: void construct_udp4_header(
  242:     const struct net_state_t *net_state,
  243:     struct probe_t *probe,
  244:     char *packet_buffer,
  245:     int packet_size,
  246:     const struct probe_param_t *param)
  247: {
  248:     struct UDPHeader *udp;
  249:     int udp_size;
  250: 
  251:     if (net_state->platform.ip4_socket_raw) {
  252:         udp = (struct UDPHeader *) &packet_buffer[sizeof(struct IPHeader)];
  253:         udp_size = packet_size - sizeof(struct IPHeader);
  254:     } else {
  255:         udp = (struct UDPHeader *) &packet_buffer[0];
  256:         udp_size = packet_size;
  257:     }
  258: 
  259:     memset(udp, 0, sizeof(struct UDPHeader));
  260: 
  261:     set_udp_ports(udp, probe, param);
  262:     udp->length = htons(udp_size);
  263: 
  264:     /* calculate udp checksum */
  265:     struct UDPPseudoHeader udph = {
  266:         .saddr = *(uint32_t *)sockaddr_addr_offset(&probe->local_addr),
  267:         .daddr = *(uint32_t *)sockaddr_addr_offset(&probe->remote_addr),
  268:         .zero = 0,
  269:         .protocol = 17,
  270:         .len = udp->length
  271:     };
  272: 
  273:     /* get position to write checksum */
  274:     uint16_t *checksum_off = &udp->checksum;
  275: 
  276:     if (udp->checksum != 0)
  277:     { /* checksum is sequence number - correct the payload to match the checksum
  278:          checksum_off is udp payload */
  279:         checksum_off = (uint16_t *)&packet_buffer[packet_size -
  280:                                                   udp_size +
  281:                                                   sizeof(struct UDPHeader)];
  282:     }
  283:     *checksum_off = htons(udp4_checksum(&udph, udp,
  284:                                         sizeof(struct UDPPseudoHeader),
  285:                                         udp_size, udp->checksum != 0));
  286: }
  287: 
  288: /*  Construct a header for UDPv6 probes  */
  289: static
  290: int construct_udp6_packet(
  291:     const struct net_state_t *net_state,
  292:     struct probe_t *probe,
  293:     char *packet_buffer,
  294:     int packet_size,
  295:     const struct probe_param_t *param)
  296: {
  297:     int udp_socket = net_state->platform.udp6_send_socket;
  298:     struct UDPHeader *udp;
  299:     int udp_size;
  300: 
  301:     udp = (struct UDPHeader *) packet_buffer;
  302:     udp_size = packet_size;
  303: 
  304:     memset(udp, 0, sizeof(struct UDPHeader));
  305: 
  306:     set_udp_ports(udp, probe, param);
  307:     udp->length = htons(udp_size);
  308: 
  309:     if (net_state->platform.ip6_socket_raw) {
  310:         /*
  311:            Instruct the kernel to put the pseudoheader checksum into the
  312:            UDP header, this is only needed when using RAW socket.
  313:          */
  314:         int chksum_offset = (char *) &udp->checksum - (char *) udp;
  315:         if (setsockopt(udp_socket, IPPROTO_IPV6,
  316:                        IPV6_CHECKSUM, &chksum_offset, sizeof(int))) {
  317:             return -1;
  318:         }
  319:     }
  320: 
  321:     return 0;
  322: }
  323: 
  324: /*
  325:     Set the socket options for an outgoing stream protocol socket based on
  326:     the packet parameters.
  327: */
  328: static
  329: int set_stream_socket_options(
  330:     int stream_socket,
  331:     const struct probe_param_t *param)
  332: {
  333:     int level;
  334:     int opt;
  335:     int reuse = 1;
  336: 
  337:     /*  Allow binding to a local port previously in use  */
  338: #ifdef SO_REUSEPORT
  339:     /*
  340:        FreeBSD wants SO_REUSEPORT in addition to SO_REUSEADDR to
  341:        bind to the same port
  342:      */
  343:     if (setsockopt(stream_socket, SOL_SOCKET, SO_REUSEPORT,
  344:                    &reuse, sizeof(int)) == -1) {
  345: 
  346:         return -1;
  347:     }
  348: #endif
  349: 
  350:     if (setsockopt(stream_socket, SOL_SOCKET, SO_REUSEADDR,
  351:                    &reuse, sizeof(int)) == -1) {
  352: 
  353:         return -1;
  354:     }
  355: 
  356:     /*  Set the number of hops the probe will transit across  */
  357:     if (param->ip_version == 6) {
  358:         level = IPPROTO_IPV6;
  359:         opt = IPV6_UNICAST_HOPS;
  360:     } else {
  361:         level = IPPROTO_IP;
  362:         opt = IP_TTL;
  363:     }
  364: 
  365:     if (setsockopt(stream_socket, level, opt, &param->ttl, sizeof(int)) ==
  366:         -1) {
  367: 
  368:         return -1;
  369:     }
  370: 
  371:     /*  Set the "type of service" field of the IP header  */
  372:     if (param->ip_version == 6) {
  373:         level = IPPROTO_IPV6;
  374:         opt = IPV6_TCLASS;
  375:     } else {
  376:         level = IPPROTO_IP;
  377:         opt = IP_TOS;
  378:     }
  379: 
  380:     if (setsockopt(stream_socket, level, opt,
  381:                    &param->type_of_service, sizeof(int)) == -1) {
  382: 
  383:         return -1;
  384:     }
  385: #ifdef SO_MARK
  386:     if (param->routing_mark) {
  387:         if (setsockopt(stream_socket, SOL_SOCKET,
  388:                        SO_MARK, &param->routing_mark, sizeof(int))) {
  389:             return -1;
  390:         }
  391:     }
  392: #endif
  393: 
  394:     return 0;
  395: }
  396: 
  397: /*
  398:     Open a TCP or SCTP socket, respecting the probe paramters as much as
  399:     we can, and use it as an outgoing probe.
  400: */
  401: static
  402: int open_stream_socket(
  403:     const struct net_state_t *net_state,
  404:     int protocol,
  405:     int port,
  406:     const struct sockaddr_storage *src_sockaddr,
  407:     const struct sockaddr_storage *dest_sockaddr,
  408:     const struct probe_param_t *param)
  409: {
  410:     int stream_socket;
  411:     int addr_len;
  412:     int dest_port;
  413:     struct sockaddr_storage dest_port_addr;
  414:     struct sockaddr_storage src_port_addr;
  415: 
  416:     if (param->ip_version == 6) {
  417:         stream_socket = socket(AF_INET6, SOCK_STREAM, protocol);
  418:         addr_len = sizeof(struct sockaddr_in6);
  419:     } else if (param->ip_version == 4) {
  420:         stream_socket = socket(AF_INET, SOCK_STREAM, protocol);
  421:         addr_len = sizeof(struct sockaddr_in);
  422:     } else {
  423:         errno = EINVAL;
  424:         return -1;
  425:     }
  426: 
  427:     if (stream_socket == -1) {
  428:         return -1;
  429:     }
  430: 
  431:     set_socket_nonblocking(stream_socket);
  432: 
  433:     if (set_stream_socket_options(stream_socket, param)) {
  434:         close(stream_socket);
  435:         return -1;
  436:     }
  437: 
  438:     /*
  439:        Bind to a known local port so we can identify which probe
  440:        causes a TTL expiration.
  441:      */
  442:     construct_addr_port(&src_port_addr, src_sockaddr, port);
  443:     if (bind(stream_socket, (struct sockaddr *) &src_port_addr, addr_len)) {
  444:         close(stream_socket);
  445:         return -1;
  446:     }
  447: 
  448:     if (param->dest_port) {
  449:         dest_port = param->dest_port;
  450:     } else {
  451:         /*  Use http if no port is specified  */
  452:         dest_port = HTTP_PORT;
  453:     }
  454: 
  455:     /*  Attempt a connection  */
  456:     construct_addr_port(&dest_port_addr, dest_sockaddr, dest_port);
  457:     if (connect
  458:         (stream_socket, (struct sockaddr *) &dest_port_addr, addr_len)) {
  459: 
  460:         /*  EINPROGRESS simply means the connection is in progress  */
  461:         if (errno != EINPROGRESS) {
  462:             close(stream_socket);
  463:             return -1;
  464:         }
  465:     }
  466: 
  467:     return stream_socket;
  468: }
  469: 
  470: /*
  471:     Determine the size of the constructed packet based on the packet
  472:     parameters.  This is the amount of space the packet *we* construct
  473:     uses, and doesn't include any headers the operating system tacks
  474:     onto the packet.  (Such as the IPv6 header on non-Linux operating
  475:     systems.)
  476: */
  477: static
  478: int compute_packet_size(
  479:     const struct net_state_t *net_state,
  480:     const struct probe_param_t *param)
  481: {
  482:     int packet_size = 0;
  483: 
  484:     if (param->protocol == IPPROTO_TCP) {
  485:         return 0;
  486:     }
  487: #ifdef IPPROTO_SCTP
  488:     if (param->protocol == IPPROTO_SCTP) {
  489:         return 0;
  490:     }
  491: #endif
  492: 
  493:     /*  Start by determining the full size, including omitted headers  */
  494:     if (param->ip_version == 6) {
  495:         if (net_state->platform.ip6_socket_raw) {
  496:             packet_size += sizeof(struct IP6Header);
  497:         }
  498:     } else if (param->ip_version == 4) {
  499:         if (net_state->platform.ip4_socket_raw) {
  500:             packet_size += sizeof(struct IPHeader);
  501:         }
  502:     } else {
  503:         errno = EINVAL;
  504:         return -1;
  505:     }
  506: 
  507:     if (param->protocol == IPPROTO_ICMP) {
  508:         packet_size += sizeof(struct ICMPHeader);
  509:     } else if (param->protocol == IPPROTO_UDP) {
  510:         packet_size += sizeof(struct UDPHeader);
  511: 
  512:         /*  We may need to put the sequence number in the payload  */
  513:         packet_size += sizeof(int);
  514:     } else {
  515:         errno = EINVAL;
  516:         return -1;
  517:     }
  518: 
  519:     /*
  520:        If the requested size from send-probe is greater, extend the
  521:        packet size.
  522:      */
  523:     if (param->packet_size > packet_size) {
  524:         packet_size = param->packet_size;
  525:     }
  526: 
  527:     /*
  528:        Since we don't explicitly construct the IPv6 header, we
  529:        need to account for it in our transmitted size.
  530:      */
  531:     if (param->ip_version == 6 && net_state->platform.ip6_socket_raw) {
  532:         packet_size -= sizeof(struct IP6Header);
  533:     }
  534: 
  535:     return packet_size;
  536: }
  537: 
  538: /*  Construct a packet for an IPv4 probe  */
  539: static
  540: int construct_ip4_packet(
  541:     const struct net_state_t *net_state,
  542:     int *packet_socket,
  543:     struct probe_t *probe,
  544:     char *packet_buffer,
  545:     int packet_size,
  546:     const struct probe_param_t *param)
  547: {
  548:     int send_socket = net_state->platform.ip4_send_socket;
  549:     bool is_stream_protocol = false;
  550:     int tos, ttl, socket;
  551:     bool bind_send_socket = false;
  552:     struct sockaddr_storage current_sockaddr;
  553:     int current_sockaddr_len;
  554: 
  555:     if (param->protocol == IPPROTO_TCP) {
  556:         is_stream_protocol = true;
  557: #ifdef IPPROTO_SCTP
  558:     } else if (param->protocol == IPPROTO_SCTP) {
  559:         is_stream_protocol = true;
  560: #endif
  561:     } else {
  562:         if (net_state->platform.ip4_socket_raw) {
  563:             construct_ip4_header(net_state, probe, packet_buffer, packet_size,
  564:                                   param);
  565:         }
  566:         if (param->protocol == IPPROTO_ICMP) {
  567:             construct_icmp4_header(net_state, probe, packet_buffer,
  568:                                    packet_size, param);
  569:         } else if (param->protocol == IPPROTO_UDP) {
  570:             construct_udp4_header(net_state, probe, packet_buffer,
  571:                                   packet_size, param);
  572:         } else {
  573:             errno = EINVAL;
  574:             return -1;
  575:         }
  576:     }
  577: 
  578:     if (is_stream_protocol) {
  579:         send_socket =
  580:             open_stream_socket(net_state, param->protocol, probe->sequence,
  581:                                &probe->local_addr, &probe->remote_addr, param);
  582: 
  583:         if (send_socket == -1) {
  584:             return -1;
  585:         }
  586: 
  587:         *packet_socket = send_socket;
  588:         return 0;
  589:     }
  590: 
  591:     /*
  592:        The routing mark requires CAP_NET_ADMIN, as opposed to the
  593:        CAP_NET_RAW which we are sometimes explicitly given.
  594:        If we don't have CAP_NET_ADMIN, this will fail, so we'll 
  595:        only set the mark if the user has explicitly requested it.
  596: 
  597:        Unfortunately, this means that once the mark is set, it won't
  598:        be set on the socket again until a new mark is explicitly
  599:        specified.
  600:      */
  601: #ifdef SO_MARK
  602:     if (param->routing_mark) {
  603:         if (setsockopt(send_socket, SOL_SOCKET,
  604:                        SO_MARK, &param->routing_mark, sizeof(int))) {
  605:             return -1;
  606:         }
  607:     }
  608: #endif
  609: 
  610:     /*
  611:        Bind src port when not using raw socket to pass in ICMP id, kernel
  612:        get ICMP id from src_port when using DGRAM socket.
  613:      */
  614:     if (!net_state->platform.ip4_socket_raw &&
  615:             param->protocol == IPPROTO_ICMP &&
  616:             !param->is_probing_byte_order) {
  617:         current_sockaddr_len = sizeof(struct sockaddr_in);
  618:         bind_send_socket = true;
  619:         socket = net_state->platform.ip4_txrx_icmp_socket;
  620:         if (getsockname(socket, (struct sockaddr *) &current_sockaddr,
  621:                         &current_sockaddr_len)) {
  622:             return -1;
  623:         }
  624:         struct sockaddr_in *sin_cur =
  625:             (struct sockaddr_in *) &current_sockaddr;
  626: 
  627:         /* avoid double bind */
  628:         if (sin_cur->sin_port) {
  629:             bind_send_socket = false;
  630:         }
  631:     }
  632: 
  633:     /*  Bind to our local address  */
  634:     if (bind_send_socket && bind(socket, (struct sockaddr *)&probe->local_addr,
  635:                 sizeof(struct sockaddr_in))) {
  636:         return -1;
  637:     }
  638: 
  639:     /* set TOS and TTL for non-raw socket */
  640:     if (!net_state->platform.ip4_socket_raw && !param->is_probing_byte_order) {
  641:         if (param->protocol == IPPROTO_ICMP) {
  642:             socket = net_state->platform.ip4_txrx_icmp_socket;
  643:         } else if (param->protocol == IPPROTO_UDP) {
  644:             socket = net_state->platform.ip4_txrx_udp_socket;
  645:         } else {
  646:             return 0;
  647:         }
  648:         tos = param->type_of_service;
  649:         if (setsockopt(socket, SOL_IP, IP_TOS, &tos, sizeof(int))) {
  650:             return -1;
  651:         }
  652:         ttl = param->ttl;
  653:         if (setsockopt(socket, SOL_IP, IP_TTL,
  654:                        &ttl, sizeof(int)) == -1) {
  655:             return -1;
  656:         }
  657:     }
  658: 
  659:     return 0;
  660: }
  661: 
  662: /*  Construct a packet for an IPv6 probe  */
  663: static
  664: int construct_ip6_packet(
  665:     const struct net_state_t *net_state,
  666:     int *packet_socket,
  667:     struct probe_t *probe,
  668:     char *packet_buffer,
  669:     int packet_size,
  670:     const struct probe_param_t *param)
  671: {
  672:     int send_socket;
  673:     bool is_stream_protocol = false;
  674:     bool bind_send_socket = true;
  675:     struct sockaddr_storage current_sockaddr;
  676:     int current_sockaddr_len;
  677: 
  678:     if (param->protocol == IPPROTO_TCP) {
  679:         is_stream_protocol = true;
  680: #ifdef IPPROTO_SCTP
  681:     } else if (param->protocol == IPPROTO_SCTP) {
  682:         is_stream_protocol = true;
  683: #endif
  684:     } else if (param->protocol == IPPROTO_ICMP) {
  685:         if (net_state->platform.ip6_socket_raw) {
  686:             send_socket = net_state->platform.icmp6_send_socket;
  687:         } else {
  688:             send_socket = net_state->platform.ip6_txrx_icmp_socket;
  689:         }
  690: 
  691:         if (construct_icmp6_packet
  692:             (net_state, probe, packet_buffer, packet_size, param)) {
  693:             return -1;
  694:         }
  695:     } else if (param->protocol == IPPROTO_UDP) {
  696:         if (net_state->platform.ip6_socket_raw) {
  697:             send_socket = net_state->platform.udp6_send_socket;
  698:         } else {
  699:             send_socket = net_state->platform.ip6_txrx_udp_socket;
  700:         }
  701: 
  702:         if (construct_udp6_packet
  703:             (net_state, probe, packet_buffer, packet_size, param)) {
  704:             return -1;
  705:         }
  706:     } else {
  707:         errno = EINVAL;
  708:         return -1;
  709:     }
  710: 
  711:     if (is_stream_protocol) {
  712:         send_socket =
  713:             open_stream_socket(net_state, param->protocol, probe->sequence,
  714:                                &probe->local_addr, &probe->remote_addr, param);
  715: 
  716:         if (send_socket == -1) {
  717:             return -1;
  718:         }
  719: 
  720:         *packet_socket = send_socket;
  721:         return 0;
  722:     }
  723: 
  724:     /*
  725:        Check the current socket address, and if it is the same
  726:        as the source address we intend, we will skip the bind.
  727:        This is to accommodate Solaris, which, as of Solaris 11.3,
  728:        will return an EINVAL error on bind if the socket is already
  729:        bound, even if the same address is used.
  730:      */
  731:     current_sockaddr_len = sizeof(struct sockaddr_in6);
  732:     if (getsockname(send_socket, (struct sockaddr *) &current_sockaddr,
  733:                     &current_sockaddr_len) == 0) {
  734:         struct sockaddr_in6 *sin6_cur = (struct sockaddr_in6 *) &current_sockaddr;
  735: 
  736:         if (net_state->platform.ip6_socket_raw) {
  737:             if (memcmp(&current_sockaddr,
  738:                        &probe->local_addr, sizeof(struct sockaddr_in6)) == 0) {
  739:                 bind_send_socket = false;
  740:             }
  741:         } else {
  742:             /* avoid double bind for DGRAM socket */
  743:             if (sin6_cur->sin6_port) {
  744:                 bind_send_socket = false;
  745:             }
  746:         }
  747:     }
  748: 
  749:     /*  Bind to our local address  */
  750:     if (bind_send_socket) {
  751:         if (bind(send_socket, (struct sockaddr *) &probe->local_addr,
  752:                  sizeof(struct sockaddr_in6))) {
  753:             return -1;
  754:         }
  755:     }
  756: 
  757:     /*  The traffic class in IPv6 is analogous to ToS in IPv4  */
  758:     if (setsockopt(send_socket, IPPROTO_IPV6,
  759:                    IPV6_TCLASS, &param->type_of_service, sizeof(int))) {
  760:         return -1;
  761:     }
  762: 
  763:     /*  Set the time-to-live  */
  764:     if (setsockopt(send_socket, IPPROTO_IPV6,
  765:                    IPV6_UNICAST_HOPS, &param->ttl, sizeof(int))) {
  766:         return -1;
  767:     }
  768: #ifdef SO_MARK
  769:     if (param->routing_mark) {
  770:         if (setsockopt(send_socket,
  771:                        SOL_SOCKET, SO_MARK, &param->routing_mark,
  772:                        sizeof(int))) {
  773:             return -1;
  774:         }
  775:     }
  776: #endif
  777: 
  778:     return 0;
  779: }
  780: 
  781: /*  Construct a probe packet based on the probe parameters  */
  782: int construct_packet(
  783:     const struct net_state_t *net_state,
  784:     int *packet_socket,
  785:     struct probe_t *probe,
  786:     char *packet_buffer,
  787:     int packet_buffer_size,
  788:     const struct probe_param_t *param)
  789: {
  790:     int packet_size;
  791: 
  792:     packet_size = compute_packet_size(net_state, param);
  793:     if (packet_size < 0) {
  794:         return -1;
  795:     }
  796: 
  797:     if (packet_buffer_size < packet_size) {
  798:         errno = EINVAL;
  799:         return -1;
  800:     }
  801: 
  802:     memset(packet_buffer, param->bit_pattern, packet_size);
  803: 
  804:     if (param->ip_version == 6) {
  805:         if (construct_ip6_packet(net_state, packet_socket, probe,
  806:                                  packet_buffer, packet_size,
  807:                                  param)) {
  808:             return -1;
  809:         }
  810:     } else if (param->ip_version == 4) {
  811:         if (construct_ip4_packet(net_state, packet_socket, probe,
  812:                                  packet_buffer, packet_size,
  813:                                  param)) {
  814:             return -1;
  815:         }
  816:     } else {
  817:         errno = EINVAL;
  818:         return -1;
  819:     }
  820: 
  821:     return packet_size;
  822: }

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