xref: /freebsd/contrib/tcpdump/print-ahcp.c (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
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 /* \summary: Ad Hoc Configuration Protocol (AHCP) printer */
29 
30 /* Based on draft-chroboczek-ahcp-00 and source code of ahcpd-0.53 */
31 
32 #include <config.h>
33 
34 #include "netdissect-stdinc.h"
35 
36 #define ND_LONGJMP_FROM_TCHECK
37 #include "netdissect.h"
38 #include "extract.h"
39 #include "addrtoname.h"
40 
41 
42 #define AHCP_MAGIC_NUMBER 43
43 #define AHCP_VERSION_1 1
44 #define AHCP1_HEADER_FIX_LEN 24
45 #define AHCP1_BODY_MIN_LEN 4
46 
47 #define AHCP1_MSG_DISCOVER 0
48 #define AHCP1_MSG_OFFER    1
49 #define AHCP1_MSG_REQUEST  2
50 #define AHCP1_MSG_ACK      3
51 #define AHCP1_MSG_NACK     4
52 #define AHCP1_MSG_RELEASE  5
53 
54 static const struct tok ahcp1_msg_str[] = {
55 	{ AHCP1_MSG_DISCOVER, "Discover" },
56 	{ AHCP1_MSG_OFFER,    "Offer"    },
57 	{ AHCP1_MSG_REQUEST,  "Request"  },
58 	{ AHCP1_MSG_ACK,      "Ack"      },
59 	{ AHCP1_MSG_NACK,     "Nack"     },
60 	{ AHCP1_MSG_RELEASE,  "Release"  },
61 	{ 0, NULL }
62 };
63 
64 #define AHCP1_OPT_PAD                     0
65 #define AHCP1_OPT_MANDATORY               1
66 #define AHCP1_OPT_ORIGIN_TIME             2
67 #define AHCP1_OPT_EXPIRES                 3
68 #define AHCP1_OPT_MY_IPV6_ADDRESS         4
69 #define AHCP1_OPT_MY_IPV4_ADDRESS         5
70 #define AHCP1_OPT_IPV6_PREFIX             6
71 #define AHCP1_OPT_IPV4_PREFIX             7
72 #define AHCP1_OPT_IPV6_ADDRESS            8
73 #define AHCP1_OPT_IPV4_ADDRESS            9
74 #define AHCP1_OPT_IPV6_PREFIX_DELEGATION 10
75 #define AHCP1_OPT_IPV4_PREFIX_DELEGATION 11
76 #define AHCP1_OPT_NAME_SERVER            12
77 #define AHCP1_OPT_NTP_SERVER             13
78 #define AHCP1_OPT_MAX                    13
79 
80 static const struct tok ahcp1_opt_str[] = {
81 	{ AHCP1_OPT_PAD,                    "Pad"                    },
82 	{ AHCP1_OPT_MANDATORY,              "Mandatory"              },
83 	{ AHCP1_OPT_ORIGIN_TIME,            "Origin Time"            },
84 	{ AHCP1_OPT_EXPIRES,                "Expires"                },
85 	{ AHCP1_OPT_MY_IPV6_ADDRESS,        "My-IPv6-Address"        },
86 	{ AHCP1_OPT_MY_IPV4_ADDRESS,        "My-IPv4-Address"        },
87 	{ AHCP1_OPT_IPV6_PREFIX,            "IPv6 Prefix"            },
88 	{ AHCP1_OPT_IPV4_PREFIX,            "IPv4 Prefix"            },
89 	{ AHCP1_OPT_IPV6_ADDRESS,           "IPv6 Address"           },
90 	{ AHCP1_OPT_IPV4_ADDRESS,           "IPv4 Address"           },
91 	{ AHCP1_OPT_IPV6_PREFIX_DELEGATION, "IPv6 Prefix Delegation" },
92 	{ AHCP1_OPT_IPV4_PREFIX_DELEGATION, "IPv4 Prefix Delegation" },
93 	{ AHCP1_OPT_NAME_SERVER,            "Name Server"            },
94 	{ AHCP1_OPT_NTP_SERVER,             "NTP Server"             },
95 	{ 0, NULL }
96 };
97 
98 static void
99 ahcp_time_print(netdissect_options *ndo,
100                 const u_char *cp, uint8_t len)
101 {
102 	time_t t;
103 	char buf[sizeof("-yyyyyyyyyy-mm-dd hh:mm:ss UTC")];
104 
105 	if (len != 4)
106 		goto invalid;
107 	t = GET_BE_U_4(cp);
108 	ND_PRINT(": %s",
109 	    nd_format_time(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S UTC",
110 	      gmtime(&t)));
111 	return;
112 
113 invalid:
114 	nd_print_invalid(ndo);
115 	ND_TCHECK_LEN(cp, len);
116 }
117 
118 static void
119 ahcp_seconds_print(netdissect_options *ndo,
120                    const u_char *cp, uint8_t len)
121 {
122 	if (len != 4)
123 		goto invalid;
124 	ND_PRINT(": %us", GET_BE_U_4(cp));
125 	return;
126 
127 invalid:
128 	nd_print_invalid(ndo);
129 	ND_TCHECK_LEN(cp, len);
130 }
131 
132 static void
133 ahcp_ipv6_addresses_print(netdissect_options *ndo,
134                           const u_char *cp, uint8_t len)
135 {
136 	const char *sep = ": ";
137 
138 	while (len) {
139 		if (len < 16)
140 			goto invalid;
141 		ND_PRINT("%s%s", sep, GET_IP6ADDR_STRING(cp));
142 		cp += 16;
143 		len -= 16;
144 		sep = ", ";
145 	}
146 	return;
147 
148 invalid:
149 	nd_print_invalid(ndo);
150 	ND_TCHECK_LEN(cp, len);
151 }
152 
153 static void
154 ahcp_ipv4_addresses_print(netdissect_options *ndo,
155                           const u_char *cp, uint8_t len)
156 {
157 	const char *sep = ": ";
158 
159 	while (len) {
160 		if (len < 4)
161 			goto invalid;
162 		ND_PRINT("%s%s", sep, GET_IPADDR_STRING(cp));
163 		cp += 4;
164 		len -= 4;
165 		sep = ", ";
166 	}
167 	return;
168 
169 invalid:
170 	nd_print_invalid(ndo);
171 	ND_TCHECK_LEN(cp, len);
172 }
173 
174 static void
175 ahcp_ipv6_prefixes_print(netdissect_options *ndo,
176                          const u_char *cp, uint8_t len)
177 {
178 	const char *sep = ": ";
179 
180 	while (len) {
181 		if (len < 17)
182 			goto invalid;
183 		ND_PRINT("%s%s/%u", sep, GET_IP6ADDR_STRING(cp), GET_U_1(cp + 16));
184 		cp += 17;
185 		len -= 17;
186 		sep = ", ";
187 	}
188 	return;
189 
190 invalid:
191 	nd_print_invalid(ndo);
192 	ND_TCHECK_LEN(cp, len);
193 }
194 
195 static void
196 ahcp_ipv4_prefixes_print(netdissect_options *ndo,
197                          const u_char *cp, uint8_t len)
198 {
199 	const char *sep = ": ";
200 
201 	while (len) {
202 		if (len < 5)
203 			goto invalid;
204 		ND_PRINT("%s%s/%u", sep, GET_IPADDR_STRING(cp), GET_U_1(cp + 4));
205 		cp += 5;
206 		len -= 5;
207 		sep = ", ";
208 	}
209 	return;
210 
211 invalid:
212 	nd_print_invalid(ndo);
213 	ND_TCHECK_LEN(cp, len);
214 }
215 
216 static void
217 (* const data_decoders[AHCP1_OPT_MAX + 1])(netdissect_options *, const u_char *, uint8_t) = {
218 	/* [AHCP1_OPT_PAD]                    = */  NULL,
219 	/* [AHCP1_OPT_MANDATORY]              = */  NULL,
220 	/* [AHCP1_OPT_ORIGIN_TIME]            = */  ahcp_time_print,
221 	/* [AHCP1_OPT_EXPIRES]                = */  ahcp_seconds_print,
222 	/* [AHCP1_OPT_MY_IPV6_ADDRESS]        = */  ahcp_ipv6_addresses_print,
223 	/* [AHCP1_OPT_MY_IPV4_ADDRESS]        = */  ahcp_ipv4_addresses_print,
224 	/* [AHCP1_OPT_IPV6_PREFIX]            = */  ahcp_ipv6_prefixes_print,
225 	/* [AHCP1_OPT_IPV4_PREFIX]            = */  NULL,
226 	/* [AHCP1_OPT_IPV6_ADDRESS]           = */  ahcp_ipv6_addresses_print,
227 	/* [AHCP1_OPT_IPV4_ADDRESS]           = */  ahcp_ipv4_addresses_print,
228 	/* [AHCP1_OPT_IPV6_PREFIX_DELEGATION] = */  ahcp_ipv6_prefixes_print,
229 	/* [AHCP1_OPT_IPV4_PREFIX_DELEGATION] = */  ahcp_ipv4_prefixes_print,
230 	/* [AHCP1_OPT_NAME_SERVER]            = */  ahcp_ipv6_addresses_print,
231 	/* [AHCP1_OPT_NTP_SERVER]             = */  ahcp_ipv6_addresses_print,
232 };
233 
234 static void
235 ahcp1_options_print(netdissect_options *ndo,
236                     const u_char *cp, uint16_t len)
237 {
238 	while (len) {
239 		uint8_t option_no, option_len;
240 
241 		/* Option no */
242 		option_no = GET_U_1(cp);
243 		cp += 1;
244 		len -= 1;
245 		ND_PRINT("\n\t %s", tok2str(ahcp1_opt_str, "Unknown-%u", option_no));
246 		if (option_no == AHCP1_OPT_PAD || option_no == AHCP1_OPT_MANDATORY)
247 			continue;
248 		/* Length */
249 		if (!len)
250 			goto invalid;
251 		option_len = GET_U_1(cp);
252 		cp += 1;
253 		len -= 1;
254 		if (option_len > len)
255 			goto invalid;
256 		/* Value */
257 		if (option_no <= AHCP1_OPT_MAX && data_decoders[option_no] != NULL) {
258 			data_decoders[option_no](ndo, cp, option_len);
259 		} else {
260 			ND_PRINT(" (Length %u)", option_len);
261 			ND_TCHECK_LEN(cp, option_len);
262 		}
263 		cp += option_len;
264 		len -= option_len;
265 	}
266 	return;
267 
268 invalid:
269 	nd_print_invalid(ndo);
270 	ND_TCHECK_LEN(cp, len);
271 }
272 
273 static void
274 ahcp1_body_print(netdissect_options *ndo,
275                  const u_char *cp, u_int len)
276 {
277 	uint8_t type, mbz;
278 	uint16_t body_len;
279 
280 	if (len < AHCP1_BODY_MIN_LEN)
281 		goto invalid;
282 	/* Type */
283 	type = GET_U_1(cp);
284 	cp += 1;
285 	len -= 1;
286 	/* MBZ */
287 	mbz = GET_U_1(cp);
288 	cp += 1;
289 	len -= 1;
290 	/* Length */
291 	body_len = GET_BE_U_2(cp);
292 	cp += 2;
293 	len -= 2;
294 
295 	if (ndo->ndo_vflag) {
296 		ND_PRINT("\n\t%s", tok2str(ahcp1_msg_str, "Unknown-%u", type));
297 		if (mbz != 0)
298 			ND_PRINT(", MBZ %u", mbz);
299 		ND_PRINT(", Length %u", body_len);
300 	}
301 	if (body_len > len)
302 		goto invalid;
303 
304 	/* Options */
305 	/* Here use "body_len", not "len" (ignore any extra data). */
306 	if (ndo->ndo_vflag >= 2)
307 		ahcp1_options_print(ndo, cp, body_len);
308 	else
309 		ND_TCHECK_LEN(cp, body_len);
310 	return;
311 
312 invalid:
313 	nd_print_invalid(ndo);
314 	ND_TCHECK_LEN(cp, len);
315 
316 }
317 
318 void
319 ahcp_print(netdissect_options *ndo,
320            const u_char *cp, u_int len)
321 {
322 	uint8_t version;
323 
324 	ndo->ndo_protocol = "ahcp";
325 	nd_print_protocol_caps(ndo);
326 	if (len < 2)
327 		goto invalid;
328 	/* Magic */
329 	if (GET_U_1(cp) != AHCP_MAGIC_NUMBER)
330 		goto invalid;
331 	cp += 1;
332 	len -= 1;
333 	/* Version */
334 	version = GET_U_1(cp);
335 	cp += 1;
336 	len -= 1;
337 	switch (version) {
338 		case AHCP_VERSION_1: {
339 			ND_PRINT(" Version 1");
340 			if (len < AHCP1_HEADER_FIX_LEN - 2)
341 				goto invalid;
342 			if (!ndo->ndo_vflag) {
343 				ND_TCHECK_LEN(cp, AHCP1_HEADER_FIX_LEN - 2);
344 				cp += AHCP1_HEADER_FIX_LEN - 2;
345 				len -= AHCP1_HEADER_FIX_LEN - 2;
346 			} else {
347 				/* Hopcount */
348 				ND_PRINT("\n\tHopcount %u", GET_U_1(cp));
349 				cp += 1;
350 				len -= 1;
351 				/* Original Hopcount */
352 				ND_PRINT(", Original Hopcount %u", GET_U_1(cp));
353 				cp += 1;
354 				len -= 1;
355 				/* Nonce */
356 				ND_PRINT(", Nonce 0x%08x", GET_BE_U_4(cp));
357 				cp += 4;
358 				len -= 4;
359 				/* Source Id */
360 				ND_PRINT(", Source Id %s", GET_LINKADDR_STRING(cp, LINKADDR_OTHER, 8));
361 				cp += 8;
362 				len -= 8;
363 				/* Destination Id */
364 				ND_PRINT(", Destination Id %s", GET_LINKADDR_STRING(cp, LINKADDR_OTHER, 8));
365 				cp += 8;
366 				len -= 8;
367 			}
368 			/* Body */
369 			ahcp1_body_print(ndo, cp, len);
370 			break;
371 		}
372 		default:
373 			ND_PRINT(" Version %u (unknown)", version);
374 			ND_TCHECK_LEN(cp, len);
375 			break;
376 	}
377 	return;
378 
379 invalid:
380 	nd_print_invalid(ndo);
381 	ND_TCHECK_LEN(cp, len);
382 }
383