Annotation of embedaddon/mtr/packet/deconstruct_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 "deconstruct_unix.h"
20:
21: #include <stdio.h>
22: #include <stdlib.h>
23: #include <string.h>
24:
25: #include "protocols.h"
26:
27: #define MAX_MPLS_LABELS 8
28:
29: /*
30: Given an ICMP id + ICMP sequence, find the match probe we've
31: transmitted and if found, respond to the command which sent it
32: */
33: static
34: void find_and_receive_probe(
35: struct net_state_t *net_state,
36: const struct sockaddr_storage *remote_addr,
37: struct timeval *timestamp,
38: int icmp_type,
39: int protocol,
40: int icmp_id,
41: int icmp_sequence,
42: int mpls_count,
43: struct mpls_label_t *mpls)
44: {
45: struct probe_t *probe;
46:
47: probe = find_probe(net_state, protocol, icmp_id, icmp_sequence);
48: if (probe == NULL) {
49: return;
50: }
51:
52: receive_probe(net_state, probe, icmp_type,
53: remote_addr, timestamp, mpls_count, mpls);
54: }
55:
56: /*
57: Handle a UDP packet received embedded in an ICMP reply.
58: The sequence number identifying the probe might be in
59: the source port number, the destination port number, or
60: the checksum. We'll check all three.
61: */
62: static
63: void handle_inner_udp_packet(
64: struct net_state_t *net_state,
65: const struct sockaddr_storage *remote_addr,
66: int icmp_result,
67: const struct UDPHeader *udp,
68: int udp_length,
69: struct timeval *timestamp,
70: int mpls_count,
71: struct mpls_label_t *mpls)
72: {
73: struct probe_t *probe;
74:
75: probe = find_probe(net_state, IPPROTO_UDP, 0, udp->dstport);
76: if (probe == NULL) {
77: probe = find_probe(net_state, IPPROTO_UDP, 0, udp->srcport);
78: }
79: if (probe == NULL) {
80: probe = find_probe(net_state, IPPROTO_UDP, 0, udp->checksum);
81: }
82:
83: if (probe != NULL) {
84: receive_probe(net_state, probe, icmp_result,
85: remote_addr, timestamp, mpls_count, mpls);
86: }
87: }
88:
89: /*
90: We've received an ICMP message with an embedded IP packet.
91: We will try to determine which of our outgoing probes
92: corresponds to the embedded IP packet and record the response.
93: */
94: static
95: void handle_inner_ip4_packet(
96: struct net_state_t *net_state,
97: const struct sockaddr_storage *remote_addr,
98: int icmp_result,
99: const struct IPHeader *ip,
100: int packet_length,
101: struct timeval *timestamp,
102: int mpls_count,
103: struct mpls_label_t *mpls)
104: {
105: const int ip_icmp_size =
106: sizeof(struct IPHeader) + sizeof(struct ICMPHeader);
107: const int ip_udp_size =
108: sizeof(struct IPHeader) + sizeof(struct UDPHeader);
109: const int ip_tcp_size =
110: sizeof(struct IPHeader) + sizeof(struct TCPHeader);
111: const struct ICMPHeader *icmp;
112: const struct UDPHeader *udp;
113: const struct TCPHeader *tcp;
114: int udp_length;
115: #ifdef IPPROTO_SCTP
116: const int ip_sctp_size =
117: sizeof(struct IPHeader) + sizeof(struct SCTPHeader);
118: const struct SCTPHeader *sctp;
119: #endif
120:
121: if (ip->protocol == IPPROTO_ICMP) {
122: if (packet_length < ip_icmp_size) {
123: return;
124: }
125:
126: icmp = (struct ICMPHeader *) (ip + 1);
127:
128: find_and_receive_probe(net_state, remote_addr, timestamp,
129: icmp_result, IPPROTO_ICMP, icmp->id,
130: icmp->sequence, mpls_count, mpls);
131: } else if (ip->protocol == IPPROTO_UDP) {
132: if (packet_length < ip_udp_size) {
133: return;
134: }
135:
136: udp = (struct UDPHeader *) (ip + 1);
137: udp_length = packet_length - sizeof(struct IPHeader);
138:
139: handle_inner_udp_packet(net_state, remote_addr, icmp_result, udp,
140: udp_length, timestamp, mpls_count, mpls);
141: } else if (ip->protocol == IPPROTO_TCP) {
142: if (packet_length < ip_tcp_size) {
143: return;
144: }
145:
146: tcp = (struct TCPHeader *) (ip + 1);
147:
148: find_and_receive_probe(net_state, remote_addr, timestamp,
149: icmp_result, IPPROTO_TCP, 0, tcp->srcport,
150: mpls_count, mpls);
151: #ifdef IPPROTO_SCTP
152: } else if (ip->protocol == IPPROTO_SCTP) {
153: if (packet_length < ip_sctp_size) {
154: return;
155: }
156:
157: sctp = (struct SCTPHeader *) (ip + 1);
158:
159: find_and_receive_probe(net_state, remote_addr, timestamp,
160: icmp_result, IPPROTO_SCTP, 0, sctp->srcport,
161: mpls_count, mpls);
162: #endif
163: }
164: }
165:
166: /*
167: Examine the IPv6 header embedded in a returned ICMPv6 packet
168: in order to match it with a probe which we previously sent.
169: */
170: static
171: void handle_inner_ip6_packet(
172: struct net_state_t *net_state,
173: const struct sockaddr_storage *remote_addr,
174: int icmp_result,
175: const struct IP6Header *ip,
176: int packet_length,
177: struct timeval *timestamp,
178: int mpls_count,
179: struct mpls_label_t *mpls)
180: {
181: const int ip_icmp_size =
182: sizeof(struct IP6Header) + sizeof(struct ICMPHeader);
183: const int ip_udp_size =
184: sizeof(struct IP6Header) + sizeof(struct UDPHeader);
185: const int ip_tcp_size =
186: sizeof(struct IP6Header) + sizeof(struct TCPHeader);
187: const struct ICMPHeader *icmp;
188: const struct UDPHeader *udp;
189: const struct TCPHeader *tcp;
190: int udp_length;
191: #ifdef IPPROTO_SCTP
192: const int ip_sctp_size =
193: sizeof(struct IPHeader) + sizeof(struct SCTPHeader);
194: const struct SCTPHeader *sctp;
195: #endif
196:
197: if (ip->protocol == IPPROTO_ICMPV6) {
198: if (packet_length < ip_icmp_size) {
199: return;
200: }
201:
202: icmp = (struct ICMPHeader *) (ip + 1);
203:
204: find_and_receive_probe(net_state, remote_addr, timestamp,
205: icmp_result, IPPROTO_ICMP, icmp->id,
206: icmp->sequence, mpls_count, mpls);
207: } else if (ip->protocol == IPPROTO_UDP) {
208: if (packet_length < ip_udp_size) {
209: return;
210: }
211:
212: udp = (struct UDPHeader *) (ip + 1);
213: udp_length = packet_length - sizeof(struct IP6Header);
214:
215: handle_inner_udp_packet(net_state, remote_addr, icmp_result, udp,
216: udp_length, timestamp, mpls_count, mpls);
217: } else if (ip->protocol == IPPROTO_TCP) {
218: if (packet_length < ip_tcp_size) {
219: return;
220: }
221:
222: tcp = (struct TCPHeader *) (ip + 1);
223: find_and_receive_probe(net_state, remote_addr, timestamp,
224: icmp_result, IPPROTO_TCP, 0, tcp->srcport,
225: mpls_count, mpls);
226: #ifdef IPPROTO_SCTP
227: } else if (ip->protocol == IPPROTO_SCTP) {
228: if (packet_length < ip_sctp_size) {
229: return;
230: }
231:
232: sctp = (struct SCTPHeader *) (ip + 1);
233:
234: find_and_receive_probe(net_state, remote_addr, timestamp,
235: icmp_result, IPPROTO_SCTP, 0, sctp->srcport,
236: mpls_count, mpls);
237: #endif
238: }
239: }
240:
241: /* Convert an ICMP MPLS extension object into an mpls_label_t structure */
242: static
243: int decode_mpls_object(
244: struct ICMPExtensionObject *icmp_obj,
245: int obj_len,
246: struct mpls_label_t *mpls,
247: int mpls_count)
248: {
249: int label_bytes;
250: int labels_present;
251: int i;
252: struct ICMPExtMPLSLabel *ext_mpls;
253: struct ICMPExtMPLSLabel *ext_label;
254: struct mpls_label_t *label;
255:
256: label_bytes = obj_len - sizeof(struct ICMPExtensionObject);
257: labels_present = label_bytes / sizeof(struct ICMPExtMPLSLabel);
258:
259: ext_mpls = (struct ICMPExtMPLSLabel *) (icmp_obj + 1);
260: for (i = 0; i < mpls_count && i < labels_present; i++) {
261: ext_label = &ext_mpls[i];
262: label = &mpls[i];
263:
264: memset(label, 0, sizeof(struct mpls_label_t));
265:
266: label->label =
267: ext_label->label[0] << 12 |
268: ext_label->label[1] << 4 | ext_label->label[2] >> 4;
269: label->experimental_use = (ext_label->label[2] & 0x0E) >> 1;
270: label->bottom_of_stack = ext_label->label[2] & 0x01;
271: label->ttl = ext_label->ttl;
272: }
273:
274: return i;
275: }
276:
277: /* Extract MPLS labels from the ICMP extension header, if present */
278: static
279: int decode_mpls_labels(
280: const struct ICMPHeader *icmp,
281: int packet_length,
282: struct mpls_label_t *mpls,
283: int mpls_count)
284: {
285: const int icmp_orig_icmp_ext_size =
286: sizeof(struct ICMPHeader) + ICMP_ORIGINAL_DATAGRAM_MIN_SIZE +
287: sizeof(struct ICMPExtensionHeader);
288: char *inner_packet;
289: char *icmp_object_bytes;
290: struct ICMPExtensionHeader *icmp_ext;
291: struct ICMPExtensionObject *icmp_obj;
292: int remaining_size;
293: int obj_len;
294:
295: if (packet_length < icmp_orig_icmp_ext_size) {
296: return 0;
297: }
298:
299: inner_packet = (char *) (icmp + 1);
300: icmp_ext = (struct ICMPExtensionHeader *)
301: (inner_packet + ICMP_ORIGINAL_DATAGRAM_MIN_SIZE);
302:
303: if ((icmp_ext->version & 0xF0) != 0x20) {
304: return 0;
305: }
306:
307: remaining_size = packet_length - icmp_orig_icmp_ext_size;
308: icmp_object_bytes = (char *) (icmp_ext + 1);
309:
310: /*
311: Iterate through the chain of extension objects, looking for
312: an MPLS label extension.
313: */
314: while (remaining_size >= sizeof(struct ICMPExtensionObject)) {
315: icmp_obj = (struct ICMPExtensionObject *) icmp_object_bytes;
316: obj_len = ntohs(icmp_obj->len);
317:
318: if (obj_len > remaining_size) {
319: return 0;
320: }
321: if (obj_len < sizeof(struct ICMPExtensionObject)) {
322: return 0;
323: }
324:
325: if (icmp_obj->classnum == ICMP_EXT_MPLS_CLASSNUM &&
326: icmp_obj->ctype == ICMP_EXT_MPLS_CTYPE) {
327:
328: return decode_mpls_object(icmp_obj, obj_len, mpls, mpls_count);
329: }
330:
331: remaining_size -= obj_len;
332: icmp_object_bytes += obj_len;
333: }
334:
335: return 0;
336: }
337:
338: /*
339: Decode the ICMP header received and try to find a probe which it
340: is in response to.
341: */
342: static
343: void handle_received_icmp4_packet(
344: struct net_state_t *net_state,
345: const struct sockaddr_storage *remote_addr,
346: const struct ICMPHeader *icmp,
347: int packet_length,
348: struct timeval *timestamp)
349: {
350: const int icmp_ip_size =
351: sizeof(struct ICMPHeader) + sizeof(struct IPHeader);
352: const struct IPHeader *inner_ip;
353: int inner_size = packet_length - sizeof(struct ICMPHeader);
354: int mpls_count;
355: struct mpls_label_t mpls[MAX_MPLS_LABELS];
356:
357: mpls_count =
358: decode_mpls_labels(icmp, packet_length, mpls, MAX_MPLS_LABELS);
359:
360: /* If we get an echo reply, our probe reached the destination host */
361: if (icmp->type == ICMP_ECHOREPLY) {
362: find_and_receive_probe(net_state, remote_addr, timestamp,
363: ICMP_ECHOREPLY, IPPROTO_ICMP, icmp->id,
364: icmp->sequence, mpls_count, mpls);
365: }
366:
367: if (packet_length < icmp_ip_size) {
368: return;
369: }
370: inner_ip = (struct IPHeader *) (icmp + 1);
371:
372: /*
373: If we get a time exceeded, we got a response from an intermediate
374: host along the path to our destination.
375: */
376: if (icmp->type == ICMP_TIME_EXCEEDED) {
377: /*
378: The IP packet inside the ICMP response contains our original
379: IP header. That's where we can get our original ID and
380: sequence number.
381: */
382: handle_inner_ip4_packet(net_state, remote_addr,
383: ICMP_TIME_EXCEEDED, inner_ip, inner_size,
384: timestamp, mpls_count, mpls);
385: }
386:
387: if (icmp->type == ICMP_DEST_UNREACH) {
388: /*
389: We'll get a ICMP_PORT_UNREACH when a non-ICMP probe
390: reaches its final destination. (Assuming that port isn't
391: open on the destination host.)
392: */
393: if (icmp->code == ICMP_PORT_UNREACH) {
394: handle_inner_ip4_packet(net_state, remote_addr,
395: ICMP_ECHOREPLY, inner_ip, inner_size,
396: timestamp, mpls_count, mpls);
397: }
398: }
399: }
400:
401: /*
402: Decode the ICMPv6 header. The code duplication with ICMPv4 is
403: unfortunate, but small details in structure size and ICMP
404: constants differ.
405: */
406: static
407: void handle_received_icmp6_packet(
408: struct net_state_t *net_state,
409: const struct sockaddr_storage *remote_addr,
410: const struct ICMPHeader *icmp,
411: int packet_length,
412: struct timeval *timestamp)
413: {
414: const int icmp_ip_size =
415: sizeof(struct ICMPHeader) + sizeof(struct IP6Header);
416: const struct IP6Header *inner_ip;
417: int inner_size = packet_length - sizeof(struct ICMPHeader);
418: int mpls_count;
419: struct mpls_label_t mpls[MAX_MPLS_LABELS];
420:
421: mpls_count =
422: decode_mpls_labels(icmp, packet_length, mpls, MAX_MPLS_LABELS);
423:
424: if (icmp->type == ICMP6_ECHOREPLY) {
425: find_and_receive_probe(net_state, remote_addr, timestamp,
426: ICMP_ECHOREPLY, IPPROTO_ICMP, icmp->id,
427: icmp->sequence, mpls_count, mpls);
428: }
429:
430: if (packet_length < icmp_ip_size) {
431: return;
432: }
433: inner_ip = (struct IP6Header *) (icmp + 1);
434:
435: if (icmp->type == ICMP6_TIME_EXCEEDED) {
436: handle_inner_ip6_packet(net_state, remote_addr,
437: ICMP_TIME_EXCEEDED, inner_ip, inner_size,
438: timestamp, mpls_count, mpls);
439: }
440:
441: if (icmp->type == ICMP6_DEST_UNREACH) {
442: if (icmp->code == ICMP6_PORT_UNREACH) {
443: handle_inner_ip6_packet(net_state, remote_addr,
444: ICMP_ECHOREPLY, inner_ip, inner_size,
445: timestamp, mpls_count, mpls);
446: }
447: }
448: }
449:
450: /*
451: We've received a new IPv4 ICMP packet.
452: We'll check to see that it is a response to one of our probes, and
453: if so, report the result of the probe to our command stream.
454: */
455: void handle_received_ip4_packet(
456: struct net_state_t *net_state,
457: const struct sockaddr_storage *remote_addr,
458: const void *packet,
459: int packet_length,
460: struct timeval *timestamp)
461: {
462: const int ip_icmp_size =
463: sizeof(struct IPHeader) + sizeof(struct ICMPHeader);
464: const struct IPHeader *ip;
465: const struct ICMPHeader *icmp;
466: int icmp_length;
467:
468: /* Ensure that we don't access memory beyond the bounds of the packet */
469: if (packet_length < ip_icmp_size) {
470: return;
471: }
472:
473: ip = (struct IPHeader *) packet;
474: if (ip->protocol != IPPROTO_ICMP) {
475: return;
476: }
477:
478: icmp = (struct ICMPHeader *) (ip + 1);
479: icmp_length = packet_length - sizeof(struct IPHeader);
480:
481: handle_received_icmp4_packet(net_state, remote_addr, icmp, icmp_length,
482: timestamp);
483: }
484:
485: /*
486: Unlike ICMPv6 raw sockets, unlike ICMPv4, don't include the IP header
487: in received packets, so we can assume the packet we got starts
488: with the ICMP packet.
489: */
490: void handle_received_ip6_packet(
491: struct net_state_t *net_state,
492: const struct sockaddr_storage *remote_addr,
493: const void *packet,
494: int packet_length,
495: struct timeval *timestamp)
496: {
497: const struct ICMPHeader *icmp;
498:
499: icmp = (struct ICMPHeader *) packet;
500:
501: handle_received_icmp6_packet(net_state, remote_addr, icmp,
502: packet_length, timestamp);
503: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>