Annotation of embedaddon/mtr/packet/construct_unix.c, revision 1.1.1.3
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.
1.1.1.3 ! misho 594: If we don't have CAP_NET_ADMIN, this will fail, so we'll
1.1 misho 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>