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