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