Annotation of embedaddon/mtr/packet/construct_unix.c, revision 1.1

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

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