Annotation of embedaddon/mtr/packet/construct_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 "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"
1.1.1.2 ! misho      28: #include "sockaddr.h"
        !            29: 
        !            30: /* For Mac OS X and FreeBSD */
        !            31: #ifndef SOL_IP
        !            32: #define SOL_IP IPPROTO_IP
        !            33: #endif
1.1       misho      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));
1.1.1.2 ! misho      95:     *sockaddr_port_offset(addr_with_port) = htons(port);
1.1       misho      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,
1.1.1.2 ! misho     102:     const struct probe_t *probe,
1.1       misho     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;
1.1.1.2 ! misho     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));
1.1       misho     125: }
                    126: 
                    127: /*  Construct an ICMP header for IPv4  */
                    128: static
                    129: void construct_icmp4_header(
                    130:     const struct net_state_t *net_state,
1.1.1.2 ! misho     131:     struct probe_t *probe,
1.1       misho     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: 
1.1.1.2 ! misho     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:     }
1.1       misho     146: 
                    147:     memset(icmp, 0, sizeof(struct ICMPHeader));
                    148: 
                    149:     icmp->type = ICMP_ECHO;
                    150:     icmp->id = htons(getpid());
1.1.1.2 ! misho     151:     icmp->sequence = htons(probe->sequence);
1.1       misho     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,
1.1.1.2 ! misho     159:     struct probe_t *probe,
1.1       misho     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());
1.1.1.2 ! misho     172:     icmp->sequence = htons(probe->sequence);
1.1       misho     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,
1.1.1.2 ! misho     189:     struct probe_t *probe,
1.1       misho     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);
1.1.1.2 ! misho     197:             udp->checksum = htons(probe->sequence);
1.1       misho     198:         } else {
1.1.1.2 ! misho     199:             udp->srcport = htons(probe->sequence);
1.1       misho     200:             udp->checksum = 0;
                    201:         }
                    202:     } else {
1.1.1.2 ! misho     203:         udp->dstport = htons(probe->sequence);
1.1       misho     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:     }
1.1.1.2 ! misho     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);
1.1       misho     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,
1.1.1.2 ! misho     243:     struct probe_t *probe,
1.1       misho     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: 
1.1.1.2 ! misho     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:     }
1.1       misho     258: 
                    259:     memset(udp, 0, sizeof(struct UDPHeader));
                    260: 
1.1.1.2 ! misho     261:     set_udp_ports(udp, probe, param);
1.1       misho     262:     udp->length = htons(udp_size);
1.1.1.2 ! misho     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));
1.1       misho     286: }
                    287: 
                    288: /*  Construct a header for UDPv6 probes  */
                    289: static
                    290: int construct_udp6_packet(
                    291:     const struct net_state_t *net_state,
1.1.1.2 ! misho     292:     struct probe_t *probe,
1.1       misho     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: 
1.1.1.2 ! misho     306:     set_udp_ports(udp, probe, param);
1.1       misho     307:     udp->length = htons(udp_size);
                    308: 
1.1.1.2 ! misho     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:         }
1.1       misho     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: {
1.1.1.2 ! misho     482:     int packet_size = 0;
1.1       misho     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) {
1.1.1.2 ! misho     495:         if (net_state->platform.ip6_socket_raw) {
        !           496:             packet_size += sizeof(struct IP6Header);
        !           497:         }
1.1       misho     498:     } else if (param->ip_version == 4) {
1.1.1.2 ! misho     499:         if (net_state->platform.ip4_socket_raw) {
        !           500:             packet_size += sizeof(struct IPHeader);
        !           501:         }
1.1       misho     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:      */
1.1.1.2 ! misho     531:     if (param->ip_version == 6 && net_state->platform.ip6_socket_raw) {
1.1       misho     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,
1.1.1.2 ! misho     543:     struct probe_t *probe,
1.1       misho     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;
1.1.1.2 ! misho     550:     int tos, ttl, socket;
        !           551:     bool bind_send_socket = false;
        !           552:     struct sockaddr_storage current_sockaddr;
        !           553:     int current_sockaddr_len;
1.1       misho     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 {
1.1.1.2 ! misho     562:         if (net_state->platform.ip4_socket_raw) {
        !           563:             construct_ip4_header(net_state, probe, packet_buffer, packet_size,
        !           564:                                   param);
        !           565:         }
1.1       misho     566:         if (param->protocol == IPPROTO_ICMP) {
1.1.1.2 ! misho     567:             construct_icmp4_header(net_state, probe, packet_buffer,
1.1       misho     568:                                    packet_size, param);
                    569:         } else if (param->protocol == IPPROTO_UDP) {
1.1.1.2 ! misho     570:             construct_udp4_header(net_state, probe, packet_buffer,
1.1       misho     571:                                   packet_size, param);
                    572:         } else {
                    573:             errno = EINVAL;
                    574:             return -1;
                    575:         }
                    576:     }
                    577: 
                    578:     if (is_stream_protocol) {
                    579:         send_socket =
1.1.1.2 ! misho     580:             open_stream_socket(net_state, param->protocol, probe->sequence,
        !           581:                                &probe->local_addr, &probe->remote_addr, param);
1.1       misho     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: 
1.1.1.2 ! misho     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: 
1.1       misho     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,
1.1.1.2 ! misho     667:     struct probe_t *probe,
1.1       misho     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) {
1.1.1.2 ! misho     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:         }
1.1       misho     690: 
                    691:         if (construct_icmp6_packet
1.1.1.2 ! misho     692:             (net_state, probe, packet_buffer, packet_size, param)) {
1.1       misho     693:             return -1;
                    694:         }
                    695:     } else if (param->protocol == IPPROTO_UDP) {
1.1.1.2 ! misho     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:         }
1.1       misho     701: 
                    702:         if (construct_udp6_packet
1.1.1.2 ! misho     703:             (net_state, probe, packet_buffer, packet_size, param)) {
1.1       misho     704:             return -1;
                    705:         }
                    706:     } else {
                    707:         errno = EINVAL;
                    708:         return -1;
                    709:     }
                    710: 
                    711:     if (is_stream_protocol) {
                    712:         send_socket =
1.1.1.2 ! misho     713:             open_stream_socket(net_state, param->protocol, probe->sequence,
        !           714:                                &probe->local_addr, &probe->remote_addr, param);
1.1       misho     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.
1.1.1.2 ! misho     727:        This is to accommodate Solaris, which, as of Solaris 11.3,
1.1       misho     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) {
1.1.1.2 ! misho     734:         struct sockaddr_in6 *sin6_cur = (struct sockaddr_in6 *) &current_sockaddr;
1.1       misho     735: 
1.1.1.2 ! misho     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:             }
1.1       misho     746:         }
                    747:     }
                    748: 
                    749:     /*  Bind to our local address  */
                    750:     if (bind_send_socket) {
1.1.1.2 ! misho     751:         if (bind(send_socket, (struct sockaddr *) &probe->local_addr,
1.1       misho     752:                  sizeof(struct sockaddr_in6))) {
                    753:             return -1;
                    754:         }
                    755:     }
                    756: 
1.1.1.2 ! misho     757:     /*  The traffic class in IPv6 is analogous to ToS in IPv4  */
1.1       misho     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,
1.1.1.2 ! misho     785:     struct probe_t *probe,
1.1       misho     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) {
1.1.1.2 ! misho     805:         if (construct_ip6_packet(net_state, packet_socket, probe,
1.1       misho     806:                                  packet_buffer, packet_size,
1.1.1.2 ! misho     807:                                  param)) {
1.1       misho     808:             return -1;
                    809:         }
                    810:     } else if (param->ip_version == 4) {
1.1.1.2 ! misho     811:         if (construct_ip4_packet(net_state, packet_socket, probe,
1.1       misho     812:                                  packet_buffer, packet_size,
1.1.1.2 ! misho     813:                                  param)) {
1.1       misho     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>