Annotation of embedaddon/mtr/packet/construct_unix.c, revision 1.1.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, ¶m->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: ¶m->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, ¶m->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, ¶m->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 *) ¤t_sockaddr,
617: ¤t_sockaddr_len) == 0) {
618:
619: if (memcmp(¤t_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, ¶m->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, ¶m->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, ¶m->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>