Annotation of embedaddon/mtr/packet/probe.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 "probe.h"
20:
21: #include <arpa/inet.h>
22: #include <assert.h>
23: #include <errno.h>
1.1.1.2 ! misho 24: #ifdef HAVE_ERROR_H
! 25: #include <error.h>
! 26: #else
! 27: #include "portability/error.h"
! 28: #endif
1.1 misho 29: #include <stdio.h>
30: #include <stdlib.h>
31: #include <string.h>
32: #include <sys/socket.h>
33: #include <unistd.h>
34:
35: #include "command.h"
36: #include "platform.h"
37: #include "protocols.h"
38: #include "timeval.h"
1.1.1.2 ! misho 39: #include "sockaddr.h"
1.1 misho 40:
1.1.1.2 ! misho 41: char *probe_err;
1.1 misho 42:
43: /* Convert the destination address from text to sockaddr */
44: int decode_address_string(
45: int ip_version,
46: const char *address_string,
47: struct sockaddr_storage *address)
48: {
49: struct in_addr addr4;
50: struct in6_addr addr6;
51: struct sockaddr_in *sockaddr4;
52: struct sockaddr_in6 *sockaddr6;
53:
54: if (address == NULL) {
55: errno = EINVAL;
56: return -1;
57: }
58:
59: if (ip_version == 6) {
60: sockaddr6 = (struct sockaddr_in6 *) address;
61:
62: if (inet_pton(AF_INET6, address_string, &addr6) != 1) {
63: errno = EINVAL;
64: return -1;
65: }
66:
67: sockaddr6->sin6_family = AF_INET6;
68: sockaddr6->sin6_port = 0;
69: sockaddr6->sin6_flowinfo = 0;
70: sockaddr6->sin6_addr = addr6;
71: sockaddr6->sin6_scope_id = 0;
72: } else if (ip_version == 4) {
73: sockaddr4 = (struct sockaddr_in *) address;
74:
75: if (inet_pton(AF_INET, address_string, &addr4) != 1) {
76: errno = EINVAL;
77: return -1;
78: }
79:
80: sockaddr4->sin_family = AF_INET;
81: sockaddr4->sin_port = 0;
82: sockaddr4->sin_addr = addr4;
83: } else {
84: errno = EINVAL;
85: return -1;
86: }
87:
88: return 0;
89: }
90:
91: /*
92: Resolve the probe parameters into a remote and local address
93: for the probe.
94: */
95: int resolve_probe_addresses(
1.1.1.2 ! misho 96: struct net_state_t *net_state,
1.1 misho 97: const struct probe_param_t *param,
98: struct sockaddr_storage *dest_sockaddr,
99: struct sockaddr_storage *src_sockaddr)
100: {
101: if (decode_address_string
102: (param->ip_version, param->remote_address, dest_sockaddr)) {
1.1.1.2 ! misho 103: probe_err = "decode address string remote";
1.1 misho 104: return -1;
105: }
106:
107: if (param->local_address) {
108: if (decode_address_string
109: (param->ip_version, param->local_address, src_sockaddr)) {
1.1.1.2 ! misho 110: probe_err = "decode address string local";
1.1 misho 111: return -1;
112: }
113: } else {
1.1.1.2 ! misho 114: probe_err = "find source address";
1.1 misho 115: if (find_source_addr(src_sockaddr, dest_sockaddr)) {
1.1.1.2 ! misho 116: //probe_err = "find source address";
1.1 misho 117: return -1;
118: }
1.1.1.2 ! misho 119: probe_err = "";
! 120: }
! 121: /* DGRAM ICMP id is taken from src_port not from ICMP header */
! 122: if (param->protocol == IPPROTO_ICMP) {
! 123: if ( (src_sockaddr->ss_family == AF_INET && !net_state->platform.ip4_socket_raw) ||
! 124: (src_sockaddr->ss_family == AF_INET6 && !net_state->platform.ip6_socket_raw) )
! 125: *sockaddr_port_offset(src_sockaddr) = htons(getpid());
1.1 misho 126: }
127:
128: return 0;
129: }
130:
131: /* Allocate a structure for tracking a new probe */
132: struct probe_t *alloc_probe(
133: struct net_state_t *net_state,
134: int token)
135: {
136: struct probe_t *probe;
137:
138: if (net_state->outstanding_probe_count >= MAX_PROBES) {
139: return NULL;
140: }
141:
142: probe = malloc(sizeof(struct probe_t));
143: if (probe == NULL) {
144: return NULL;
145: }
146:
147: memset(probe, 0, sizeof(struct probe_t));
148: probe->token = token;
149:
150: platform_alloc_probe(net_state, probe);
151:
152: net_state->outstanding_probe_count++;
153: LIST_INSERT_HEAD(&net_state->outstanding_probes, probe,
154: probe_list_entry);
155:
156: return probe;
157: }
158:
159: /* Mark a probe tracking structure as unused */
160: void free_probe(
161: struct net_state_t *net_state,
162: struct probe_t *probe)
163: {
164: LIST_REMOVE(probe, probe_list_entry);
165: net_state->outstanding_probe_count--;
166:
167: platform_free_probe(probe);
168:
169: free(probe);
170: }
171:
172: /*
173: Find an existing probe structure by ICMP id and sequence number.
174: Returns NULL if non is found.
175: */
176: struct probe_t *find_probe(
177: struct net_state_t *net_state,
178: int protocol,
179: int id,
180: int sequence)
181: {
182: struct probe_t *probe;
183:
184: /*
185: ICMP has room for an id to check against our process, but
186: UDP doesn't.
187: */
188: if (protocol == IPPROTO_ICMP) {
189: /*
190: If the ICMP id doesn't match our process ID, it wasn't a
191: probe generated by this process, so ignore it.
192: */
193: if (id != htons(getpid())) {
194: return NULL;
195: }
196: }
197:
198: LIST_FOREACH(probe, &net_state->outstanding_probes, probe_list_entry) {
199: if (htons(probe->sequence) == sequence) {
200: return probe;
201: }
202: }
203:
204: return NULL;
205: }
206:
207: /*
208: Format a list of MPLS labels into a string appropriate for including
209: as an argument to a probe reply.
210: */
211: static
212: void format_mpls_string(
213: char *str,
214: int buffer_size,
215: int mpls_count,
216: const struct mpls_label_t *mpls_list)
217: {
218: int i;
219: char *append_pos = str;
220: const struct mpls_label_t *mpls;
221:
222: /* Start with an empty string */
223: str[0] = 0;
224:
225: for (i = 0; i < mpls_count; i++) {
226: mpls = &mpls_list[i];
227:
228: if (i > 0) {
229: strncat(append_pos, ",", buffer_size - 1);
230:
231: buffer_size -= strlen(append_pos);
232: append_pos += strlen(append_pos);
233: }
234:
235: snprintf(append_pos, buffer_size, "%d,%d,%d,%d",
1.1.1.2 ! misho 236: mpls->label, mpls->traffic_class,
1.1 misho 237: mpls->bottom_of_stack, mpls->ttl);
238:
239: buffer_size -= strlen(append_pos);
240: append_pos += strlen(append_pos);
241: }
242: }
243:
244: /*
245: After a probe reply has arrived, respond to the command request which
246: sent the probe.
247: */
248: void respond_to_probe(
249: struct net_state_t *net_state,
250: struct probe_t *probe,
251: int icmp_type,
252: const struct sockaddr_storage *remote_addr,
253: unsigned int round_trip_us,
254: int mpls_count,
255: const struct mpls_label_t *mpls)
256: {
1.1.1.2 ! misho 257: char ip_text[INET6_ADDRSTRLEN];
1.1 misho 258: char response[COMMAND_BUFFER_SIZE];
259: char mpls_str[COMMAND_BUFFER_SIZE];
260: int remaining_size;
261: const char *result;
262: const char *ip_argument;
263:
264: if (icmp_type == ICMP_TIME_EXCEEDED) {
265: result = "ttl-expired";
1.1.1.2 ! misho 266: } else if (icmp_type == ICMP_DEST_UNREACH) {
! 267: result = "no-route";
1.1 misho 268: } else {
269: assert(icmp_type == ICMP_ECHOREPLY);
270: result = "reply";
271: }
272:
273: if (remote_addr->ss_family == AF_INET6) {
274: ip_argument = "ip-6";
275: } else {
276: ip_argument = "ip-4";
277: }
278:
1.1.1.2 ! misho 279: if (inet_ntop(remote_addr->ss_family, sockaddr_addr_offset(remote_addr), ip_text, INET6_ADDRSTRLEN) ==
1.1 misho 280: NULL) {
1.1.1.2 ! misho 281: error(EXIT_FAILURE, errno, "inet_ntop failure");
1.1 misho 282: }
283:
284: snprintf(response, COMMAND_BUFFER_SIZE,
285: "%d %s %s %s round-trip-time %d",
286: probe->token, result, ip_argument, ip_text, round_trip_us);
287:
288: if (mpls_count) {
289: format_mpls_string(mpls_str, COMMAND_BUFFER_SIZE, mpls_count,
290: mpls);
291:
292: remaining_size = COMMAND_BUFFER_SIZE - strlen(response) - 1;
293: strncat(response, " mpls ", remaining_size);
294:
295: remaining_size = COMMAND_BUFFER_SIZE - strlen(response) - 1;
296: strncat(response, mpls_str, remaining_size);
297: }
298:
299: puts(response);
300: free_probe(net_state, probe);
301: }
302:
303: /*
304: Find the source address for transmitting to a particular destination
305: address. Remember that hosts can have multiple addresses, for example
306: a unique address for each network interface. So we will bind a UDP
307: socket to our destination and check the socket address after binding
308: to get the source for that destination, which will allow the kernel
309: to do the routing table work for us.
310:
311: (connecting UDP sockets, unlike TCP sockets, doesn't transmit any packets.
312: It's just an association.)
313: */
314: int find_source_addr(
315: struct sockaddr_storage *srcaddr,
316: const struct sockaddr_storage *destaddr)
317: {
318: int sock;
319: int len;
320: struct sockaddr_storage dest_with_port;
1.1.1.2 ! misho 321: #ifdef __linux__
! 322: // The Linux code needs these.
1.1 misho 323: struct sockaddr_in *srcaddr4;
324: struct sockaddr_in6 *srcaddr6;
1.1.1.2 ! misho 325: #endif
! 326:
1.1 misho 327:
328: dest_with_port = *destaddr;
329:
330: /*
331: MacOS requires a non-zero sin_port when used as an
332: address for a UDP connect. If we provide a zero port,
333: the connect will fail. We aren't actually sending
334: anything to the port.
335: */
1.1.1.2 ! misho 336: *sockaddr_port_offset(&dest_with_port) = htons(1);
! 337: len = sockaddr_size(&dest_with_port);
1.1 misho 338:
339: sock = socket(destaddr->ss_family, SOCK_DGRAM, IPPROTO_UDP);
340: if (sock == -1) {
1.1.1.2 ! misho 341: probe_err = "open socket";
1.1 misho 342: return -1;
343: }
344:
1.1.1.2 ! misho 345: if (connect(sock, (struct sockaddr *) &dest_with_port, len) == 0) {
! 346: if (getsockname(sock, (struct sockaddr *) srcaddr, &len)) {
! 347: close(sock);
! 348: probe_err = "getsockname";
! 349: return -1;
! 350: }
! 351: } else {
! 352: #ifdef __linux__
! 353: /* Linux doesn't require source address, so we can support
! 354: * a case when mtr is run against unreachable host (that can become
! 355: * reachable) */
! 356: if (errno != EHOSTUNREACH) {
! 357: probe_err = "not hostunreach";
! 358: close(sock);
! 359: return -1;
! 360: }
1.1 misho 361:
1.1.1.2 ! misho 362: if (destaddr->ss_family == AF_INET6) {
! 363: srcaddr6 = (struct sockaddr_in6 *) srcaddr;
! 364: srcaddr6->sin6_addr = in6addr_any;
! 365: } else {
! 366: srcaddr4 = (struct sockaddr_in *) srcaddr;
! 367: srcaddr4->sin_addr.s_addr = INADDR_ANY;
! 368: }
! 369: #else
1.1 misho 370: close(sock);
1.1.1.2 ! misho 371: probe_err = "connect failed";
1.1 misho 372: return -1;
1.1.1.2 ! misho 373: #endif
1.1 misho 374: }
375:
376: close(sock);
377:
378: /*
379: Zero the port, as we may later use this address to finding, and
380: we don't want to use the port from the socket we just created.
381: */
1.1.1.2 ! misho 382: *sockaddr_port_offset(srcaddr) = 0;
1.1 misho 383:
384: return 0;
385: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>