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