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