xref: /minix/external/bsd/tcpdump/dist/print-ahcp.c (revision fb9c64b2)
1 /*
2  * This module implements decoding of AHCP (Ad Hoc Configuration Protocol) based
3  * on draft-chroboczek-ahcp-00 and source code of ahcpd-0.53.
4  *
5  *
6  * Copyright (c) 2013 The TCPDUMP project
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __RCSID("$NetBSD: print-ahcp.c,v 1.3 2015/03/31 21:59:35 christos Exp $");
35 #endif
36 
37 #define NETDISSECT_REWORKED
38 #ifdef HAVE_CONFIG_H
39 #include "config.h"
40 #endif
41 
42 #include <tcpdump-stdinc.h>
43 
44 #include "interface.h"
45 #include "extract.h"
46 #include "addrtoname.h"
47 
48 static const char tstr[] = " [|ahcp]";
49 static const char cstr[] = "(corrupt)";
50 
51 #define AHCP_MAGIC_NUMBER 43
52 #define AHCP_VERSION_1 1
53 #define AHCP1_HEADER_FIX_LEN 24
54 #define AHCP1_BODY_MIN_LEN 4
55 
56 #define AHCP1_MSG_DISCOVER 0
57 #define AHCP1_MSG_OFFER    1
58 #define AHCP1_MSG_REQUEST  2
59 #define AHCP1_MSG_ACK      3
60 #define AHCP1_MSG_NACK     4
61 #define AHCP1_MSG_RELEASE  5
62 
63 static const struct tok ahcp1_msg_str[] = {
64 	{ AHCP1_MSG_DISCOVER, "Discover" },
65 	{ AHCP1_MSG_OFFER,    "Offer"    },
66 	{ AHCP1_MSG_REQUEST,  "Request"  },
67 	{ AHCP1_MSG_ACK,      "Ack"      },
68 	{ AHCP1_MSG_NACK,     "Nack"     },
69 	{ AHCP1_MSG_RELEASE,  "Release"  },
70 	{ 0, NULL }
71 };
72 
73 #define AHCP1_OPT_PAD                     0
74 #define AHCP1_OPT_MANDATORY               1
75 #define AHCP1_OPT_ORIGIN_TIME             2
76 #define AHCP1_OPT_EXPIRES                 3
77 #define AHCP1_OPT_MY_IPV6_ADDRESS         4
78 #define AHCP1_OPT_MY_IPV4_ADDRESS         5
79 #define AHCP1_OPT_IPV6_PREFIX             6
80 #define AHCP1_OPT_IPV4_PREFIX             7
81 #define AHCP1_OPT_IPV6_ADDRESS            8
82 #define AHCP1_OPT_IPV4_ADDRESS            9
83 #define AHCP1_OPT_IPV6_PREFIX_DELEGATION 10
84 #define AHCP1_OPT_IPV4_PREFIX_DELEGATION 11
85 #define AHCP1_OPT_NAME_SERVER            12
86 #define AHCP1_OPT_NTP_SERVER             13
87 #define AHCP1_OPT_MAX                    13
88 
89 static const struct tok ahcp1_opt_str[] = {
90 	{ AHCP1_OPT_PAD,                    "Pad"                    },
91 	{ AHCP1_OPT_MANDATORY,              "Mandatory"              },
92 	{ AHCP1_OPT_ORIGIN_TIME,            "Origin Time"            },
93 	{ AHCP1_OPT_EXPIRES,                "Expires"                },
94 	{ AHCP1_OPT_MY_IPV6_ADDRESS,        "My-IPv6-Address"        },
95 	{ AHCP1_OPT_MY_IPV4_ADDRESS,        "My-IPv4-Address"        },
96 	{ AHCP1_OPT_IPV6_PREFIX,            "IPv6 Prefix"            },
97 	{ AHCP1_OPT_IPV4_PREFIX,            "IPv4 Prefix"            },
98 	{ AHCP1_OPT_IPV6_ADDRESS,           "IPv6 Address"           },
99 	{ AHCP1_OPT_IPV4_ADDRESS,           "IPv4 Address"           },
100 	{ AHCP1_OPT_IPV6_PREFIX_DELEGATION, "IPv6 Prefix Delegation" },
101 	{ AHCP1_OPT_IPV4_PREFIX_DELEGATION, "IPv4 Prefix Delegation" },
102 	{ AHCP1_OPT_NAME_SERVER,            "Name Server"            },
103 	{ AHCP1_OPT_NTP_SERVER,             "NTP Server"             },
104 	{ 0, NULL }
105 };
106 
107 static int
108 ahcp_time_print(netdissect_options *ndo, const u_char *cp, const u_char *ep)
109 {
110 	time_t t;
111 	struct tm *tm;
112 	char buf[BUFSIZE];
113 
114 	if (cp + 4 != ep)
115 		goto corrupt;
116 	ND_TCHECK2(*cp, 4);
117 	t = EXTRACT_32BITS(cp);
118 	if (NULL == (tm = gmtime(&t)))
119 		ND_PRINT((ndo, ": gmtime() error"));
120 	else if (0 == strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm))
121 		ND_PRINT((ndo, ": strftime() error"));
122 	else
123 		ND_PRINT((ndo, ": %s UTC", buf));
124 	return 0;
125 
126 corrupt:
127 	ND_PRINT((ndo, ": %s", cstr));
128 	ND_TCHECK2(*cp, ep - cp);
129 	return 0;
130 trunc:
131 	ND_PRINT((ndo, "%s", tstr));
132 	return -1;
133 }
134 
135 static int
136 ahcp_seconds_print(netdissect_options *ndo, const u_char *cp, const u_char *ep)
137 {
138 	if (cp + 4 != ep)
139 		goto corrupt;
140 	ND_TCHECK2(*cp, 4);
141 	ND_PRINT((ndo, ": %us", EXTRACT_32BITS(cp)));
142 	return 0;
143 
144 corrupt:
145 	ND_PRINT((ndo, ": %s", cstr));
146 	ND_TCHECK2(*cp, ep - cp);
147 	return 0;
148 trunc:
149 	ND_PRINT((ndo, "%s", tstr));
150 	return -1;
151 }
152 
153 static int
154 ahcp_ipv6_addresses_print(netdissect_options *ndo, const u_char *cp, const u_char *ep)
155 {
156 	const char *sep = ": ";
157 
158 	while (cp < ep) {
159 		if (cp + 16 > ep)
160 			goto corrupt;
161 		ND_TCHECK2(*cp, 16);
162 #ifdef INET6
163 		ND_PRINT((ndo, "%s%s", sep, ip6addr_string(ndo, cp)));
164 #else
165 		ND_PRINT((ndo, "%s(compiled w/o IPv6)", sep));
166 #endif /* INET6 */
167 		cp += 16;
168 		sep = ", ";
169 	}
170 	return 0;
171 
172 corrupt:
173 	ND_PRINT((ndo, ": %s", cstr));
174 	ND_TCHECK2(*cp, ep - cp);
175 	return 0;
176 trunc:
177 	ND_PRINT((ndo, "%s", tstr));
178 	return -1;
179 }
180 
181 static int
182 ahcp_ipv4_addresses_print(netdissect_options *ndo, const u_char *cp, const u_char *ep)
183 {
184 	const char *sep = ": ";
185 
186 	while (cp < ep) {
187 		if (cp + 4 > ep)
188 			goto corrupt;
189 		ND_TCHECK2(*cp, 4);
190 		ND_PRINT((ndo, "%s%s", sep, ipaddr_string(ndo, cp)));
191 		cp += 4;
192 		sep = ", ";
193 	}
194 	return 0;
195 
196 corrupt:
197 	ND_PRINT((ndo, ": %s", cstr));
198 	ND_TCHECK2(*cp, ep - cp);
199 	return 0;
200 trunc:
201 	ND_PRINT((ndo, "%s", tstr));
202 	return -1;
203 }
204 
205 static int
206 ahcp_ipv6_prefixes_print(netdissect_options *ndo, const u_char *cp, const u_char *ep)
207 {
208 	const char *sep = ": ";
209 
210 	while (cp < ep) {
211 		if (cp + 17 > ep)
212 			goto corrupt;
213 		ND_TCHECK2(*cp, 17);
214 #ifdef INET6
215 		ND_PRINT((ndo, "%s%s/%u", sep, ip6addr_string(ndo, cp), *(cp + 16)));
216 #else
217 		ND_PRINT((ndo, "%s(compiled w/o IPv6)/%u", sep, *(cp + 16)));
218 #endif /* INET6 */
219 		cp += 17;
220 		sep = ", ";
221 	}
222 	return 0;
223 
224 corrupt:
225 	ND_PRINT((ndo, ": %s", cstr));
226 	ND_TCHECK2(*cp, ep - cp);
227 	return 0;
228 trunc:
229 	ND_PRINT((ndo, "%s", tstr));
230 	return -1;
231 }
232 
233 static int
234 ahcp_ipv4_prefixes_print(netdissect_options *ndo, const u_char *cp, const u_char *ep)
235 {
236 	const char *sep = ": ";
237 
238 	while (cp < ep) {
239 		if (cp + 5 > ep)
240 			goto corrupt;
241 		ND_TCHECK2(*cp, 5);
242 		ND_PRINT((ndo, "%s%s/%u", sep, ipaddr_string(ndo, cp), *(cp + 4)));
243 		cp += 5;
244 		sep = ", ";
245 	}
246 	return 0;
247 
248 corrupt:
249 	ND_PRINT((ndo, ": %s", cstr));
250 	ND_TCHECK2(*cp, ep - cp);
251 	return 0;
252 trunc:
253 	ND_PRINT((ndo, "%s", tstr));
254 	return -1;
255 }
256 
257 /* Data decoders signal truncated data with -1. */
258 static int
259 (* const data_decoders[AHCP1_OPT_MAX + 1])(netdissect_options *, const u_char *, const u_char *) = {
260 	/* [AHCP1_OPT_PAD]                    = */  NULL,
261 	/* [AHCP1_OPT_MANDATORY]              = */  NULL,
262 	/* [AHCP1_OPT_ORIGIN_TIME]            = */  ahcp_time_print,
263 	/* [AHCP1_OPT_EXPIRES]                = */  ahcp_seconds_print,
264 	/* [AHCP1_OPT_MY_IPV6_ADDRESS]        = */  ahcp_ipv6_addresses_print,
265 	/* [AHCP1_OPT_MY_IPV4_ADDRESS]        = */  ahcp_ipv4_addresses_print,
266 	/* [AHCP1_OPT_IPV6_PREFIX]            = */  ahcp_ipv6_prefixes_print,
267 	/* [AHCP1_OPT_IPV4_PREFIX]            = */  NULL,
268 	/* [AHCP1_OPT_IPV6_ADDRESS]           = */  ahcp_ipv6_addresses_print,
269 	/* [AHCP1_OPT_IPV4_ADDRESS]           = */  ahcp_ipv4_addresses_print,
270 	/* [AHCP1_OPT_IPV6_PREFIX_DELEGATION] = */  ahcp_ipv6_prefixes_print,
271 	/* [AHCP1_OPT_IPV4_PREFIX_DELEGATION] = */  ahcp_ipv4_prefixes_print,
272 	/* [AHCP1_OPT_NAME_SERVER]            = */  ahcp_ipv6_addresses_print,
273 	/* [AHCP1_OPT_NTP_SERVER]             = */  ahcp_ipv6_addresses_print,
274 };
275 
276 static void
277 ahcp1_options_print(netdissect_options *ndo, const u_char *cp, const u_char *ep)
278 {
279 	uint8_t option_no, option_len;
280 
281 	while (cp < ep) {
282 		/* Option no */
283 		ND_TCHECK2(*cp, 1);
284 		option_no = *cp;
285 		cp += 1;
286 		ND_PRINT((ndo, "\n\t %s", tok2str(ahcp1_opt_str, "Unknown-%u", option_no)));
287 		if (option_no == AHCP1_OPT_PAD || option_no == AHCP1_OPT_MANDATORY)
288 			continue;
289 		/* Length */
290 		if (cp + 1 > ep)
291 			goto corrupt;
292 		ND_TCHECK2(*cp, 1);
293 		option_len = *cp;
294 		cp += 1;
295 		if (cp + option_len > ep)
296 			goto corrupt;
297 		/* Value */
298 		if (option_no <= AHCP1_OPT_MAX && data_decoders[option_no] != NULL) {
299 			if (data_decoders[option_no](ndo, cp, cp + option_len) < 0)
300 				break; /* truncated and already marked up */
301 		} else {
302 			ND_PRINT((ndo, " (Length %u)", option_len));
303 			ND_TCHECK2(*cp, option_len);
304 		}
305 		cp += option_len;
306 	}
307 	return;
308 
309 corrupt:
310 	ND_PRINT((ndo, " %s", cstr));
311 	ND_TCHECK2(*cp, ep - cp);
312 	return;
313 trunc:
314 	ND_PRINT((ndo, "%s", tstr));
315 }
316 
317 static void
318 ahcp1_body_print(netdissect_options *ndo, const u_char *cp, const u_char *ep)
319 {
320 	uint8_t type, mbz;
321 	uint16_t body_len;
322 
323 	if (cp + AHCP1_BODY_MIN_LEN > ep)
324 		goto corrupt;
325 	/* Type */
326 	ND_TCHECK2(*cp, 1);
327 	type = *cp;
328 	cp += 1;
329 	/* MBZ */
330 	ND_TCHECK2(*cp, 1);
331 	mbz = *cp;
332 	cp += 1;
333 	/* Length */
334 	ND_TCHECK2(*cp, 2);
335 	body_len = EXTRACT_16BITS(cp);
336 	cp += 2;
337 
338 	if (ndo->ndo_vflag) {
339 		ND_PRINT((ndo, "\n\t%s", tok2str(ahcp1_msg_str, "Unknown-%u", type)));
340 		if (mbz != 0)
341 			ND_PRINT((ndo, ", MBZ %u", mbz));
342 		ND_PRINT((ndo, ", Length %u", body_len));
343 	}
344 	if (cp + body_len > ep)
345 		goto corrupt;
346 
347 	/* Options */
348 	if (ndo->ndo_vflag >= 2)
349 		ahcp1_options_print(ndo, cp, cp + body_len); /* not ep (ignore extra data) */
350 	else
351 		ND_TCHECK2(*cp, body_len);
352 	return;
353 
354 corrupt:
355 	ND_PRINT((ndo, " %s", cstr));
356 	ND_TCHECK2(*cp, ep - cp);
357 	return;
358 trunc:
359 	ND_PRINT((ndo, "%s", tstr));
360 }
361 
362 void
363 ahcp_print(netdissect_options *ndo, const u_char *cp, const u_int len)
364 {
365 	const u_char *ep = cp + len;
366 	uint8_t version;
367 
368 	ND_PRINT((ndo, "AHCP"));
369 	if (len < 2)
370 		goto corrupt;
371 	/* Magic */
372 	ND_TCHECK2(*cp, 1);
373 	if (*cp != AHCP_MAGIC_NUMBER)
374 		goto corrupt;
375 	cp += 1;
376 	/* Version */
377 	ND_TCHECK2(*cp, 1);
378 	version = *cp;
379 	cp += 1;
380 	switch (version) {
381 		case AHCP_VERSION_1: {
382 			ND_PRINT((ndo, " Version 1"));
383 			if (len < AHCP1_HEADER_FIX_LEN)
384 				goto corrupt;
385 			if (!ndo->ndo_vflag) {
386 				ND_TCHECK2(*cp, AHCP1_HEADER_FIX_LEN - 2);
387 				cp += AHCP1_HEADER_FIX_LEN - 2;
388 			} else {
389 				/* Hopcount */
390 				ND_TCHECK2(*cp, 1);
391 				ND_PRINT((ndo, "\n\tHopcount %u", *cp));
392 				cp += 1;
393 				/* Original Hopcount */
394 				ND_TCHECK2(*cp, 1);
395 				ND_PRINT((ndo, ", Original Hopcount %u", *cp));
396 				cp += 1;
397 				/* Nonce */
398 				ND_TCHECK2(*cp, 4);
399 				ND_PRINT((ndo, ", Nonce 0x%08x", EXTRACT_32BITS(cp)));
400 				cp += 4;
401 				/* Source Id */
402 				ND_TCHECK2(*cp, 8);
403 				ND_PRINT((ndo, ", Source Id %s", linkaddr_string(ndo, cp, 0, 8)));
404 				cp += 8;
405 				/* Destination Id */
406 				ND_TCHECK2(*cp, 8);
407 				ND_PRINT((ndo, ", Destination Id %s", linkaddr_string(ndo, cp, 0, 8)));
408 				cp += 8;
409 			}
410 			/* Body */
411 			ahcp1_body_print(ndo, cp, ep);
412 			break;
413 		}
414 		default:
415 			ND_PRINT((ndo, " Version %u (unknown)", version));
416 			break;
417 	}
418 	return;
419 
420 corrupt:
421 	ND_PRINT((ndo, " %s", cstr));
422 	ND_TCHECK2(*cp, ep - cp);
423 	return;
424 trunc:
425 	ND_PRINT((ndo, "%s", tstr));
426 }
427