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, ¶m->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: ¶m->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, ¶m->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, ¶m->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 *) ¤t_sockaddr,
! 621: ¤t_sockaddr_len)) {
! 622: return -1;
! 623: }
! 624: struct sockaddr_in *sin_cur =
! 625: (struct sockaddr_in *) ¤t_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 *) ¤t_sockaddr,
733: ¤t_sockaddr_len) == 0) {
1.1.1.2 ! misho 734: struct sockaddr_in6 *sin6_cur = (struct sockaddr_in6 *) ¤t_sockaddr;
1.1 misho 735:
1.1.1.2 ! misho 736: if (net_state->platform.ip6_socket_raw) {
! 737: if (memcmp(¤t_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, ¶m->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, ¶m->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, ¶m->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>