print-ahcp.c revision 1.2 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.2 2014/11/20 03:05:03 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 time_t t;
110 struct tm *tm;
111 char buf[BUFSIZE];
112
113 if (cp + 4 != ep)
114 goto corrupt;
115 ND_TCHECK2(*cp, 4);
116 t = EXTRACT_32BITS(cp);
117 if (NULL == (tm = gmtime(&t)))
118 ND_PRINT((ndo, ": gmtime() error"));
119 else if (0 == strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm))
120 ND_PRINT((ndo, ": strftime() error"));
121 else
122 ND_PRINT((ndo, ": %s UTC", buf));
123 return 0;
124
125 corrupt:
126 ND_PRINT((ndo, ": %s", cstr));
127 ND_TCHECK2(*cp, ep - cp);
128 return 0;
129 trunc:
130 ND_PRINT((ndo, "%s", tstr));
131 return -1;
132 }
133
134 static int
135 ahcp_seconds_print(netdissect_options *ndo, const u_char *cp, const u_char *ep) {
136 if (cp + 4 != ep)
137 goto corrupt;
138 ND_TCHECK2(*cp, 4);
139 ND_PRINT((ndo, ": %us", EXTRACT_32BITS(cp)));
140 return 0;
141
142 corrupt:
143 ND_PRINT((ndo, ": %s", cstr));
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 const char *sep = ": ";
154
155 while (cp < ep) {
156 if (cp + 16 > ep)
157 goto corrupt;
158 ND_TCHECK2(*cp, 16);
159 #ifdef INET6
160 ND_PRINT((ndo, "%s%s", sep, ip6addr_string(ndo, cp)));
161 #else
162 ND_PRINT((ndo, "%s(compiled w/o IPv6)", sep));
163 #endif /* INET6 */
164 cp += 16;
165 sep = ", ";
166 }
167 return 0;
168
169 corrupt:
170 ND_PRINT((ndo, ": %s", cstr));
171 ND_TCHECK2(*cp, ep - cp);
172 return 0;
173 trunc:
174 ND_PRINT((ndo, "%s", tstr));
175 return -1;
176 }
177
178 static int
179 ahcp_ipv4_addresses_print(netdissect_options *ndo, const u_char *cp, const u_char *ep) {
180 const char *sep = ": ";
181
182 while (cp < ep) {
183 if (cp + 4 > ep)
184 goto corrupt;
185 ND_TCHECK2(*cp, 4);
186 ND_PRINT((ndo, "%s%s", sep, ipaddr_string(ndo, cp)));
187 cp += 4;
188 sep = ", ";
189 }
190 return 0;
191
192 corrupt:
193 ND_PRINT((ndo, ": %s", cstr));
194 ND_TCHECK2(*cp, ep - cp);
195 return 0;
196 trunc:
197 ND_PRINT((ndo, "%s", tstr));
198 return -1;
199 }
200
201 static int
202 ahcp_ipv6_prefixes_print(netdissect_options *ndo, const u_char *cp, const u_char *ep) {
203 const char *sep = ": ";
204
205 while (cp < ep) {
206 if (cp + 17 > ep)
207 goto corrupt;
208 ND_TCHECK2(*cp, 17);
209 #ifdef INET6
210 ND_PRINT((ndo, "%s%s/%u", sep, ip6addr_string(ndo, cp), *(cp + 16)));
211 #else
212 ND_PRINT((ndo, "%s(compiled w/o IPv6)/%u", sep, *(cp + 16)));
213 #endif /* INET6 */
214 cp += 17;
215 sep = ", ";
216 }
217 return 0;
218
219 corrupt:
220 ND_PRINT((ndo, ": %s", cstr));
221 ND_TCHECK2(*cp, ep - cp);
222 return 0;
223 trunc:
224 ND_PRINT((ndo, "%s", tstr));
225 return -1;
226 }
227
228 static int
229 ahcp_ipv4_prefixes_print(netdissect_options *ndo, const u_char *cp, const u_char *ep) {
230 const char *sep = ": ";
231
232 while (cp < ep) {
233 if (cp + 5 > ep)
234 goto corrupt;
235 ND_TCHECK2(*cp, 5);
236 ND_PRINT((ndo, "%s%s/%u", sep, ipaddr_string(ndo, cp), *(cp + 4)));
237 cp += 5;
238 sep = ", ";
239 }
240 return 0;
241
242 corrupt:
243 ND_PRINT((ndo, ": %s", cstr));
244 ND_TCHECK2(*cp, ep - cp);
245 return 0;
246 trunc:
247 ND_PRINT((ndo, "%s", tstr));
248 return -1;
249 }
250
251 /* Data decoders signal truncated data with -1. */
252 static int
253 (* const data_decoders[AHCP1_OPT_MAX + 1])(netdissect_options *, const u_char *, const u_char *) = {
254 /* [AHCP1_OPT_PAD] = */ NULL,
255 /* [AHCP1_OPT_MANDATORY] = */ NULL,
256 /* [AHCP1_OPT_ORIGIN_TIME] = */ ahcp_time_print,
257 /* [AHCP1_OPT_EXPIRES] = */ ahcp_seconds_print,
258 /* [AHCP1_OPT_MY_IPV6_ADDRESS] = */ ahcp_ipv6_addresses_print,
259 /* [AHCP1_OPT_MY_IPV4_ADDRESS] = */ ahcp_ipv4_addresses_print,
260 /* [AHCP1_OPT_IPV6_PREFIX] = */ ahcp_ipv6_prefixes_print,
261 /* [AHCP1_OPT_IPV4_PREFIX] = */ NULL,
262 /* [AHCP1_OPT_IPV6_ADDRESS] = */ ahcp_ipv6_addresses_print,
263 /* [AHCP1_OPT_IPV4_ADDRESS] = */ ahcp_ipv4_addresses_print,
264 /* [AHCP1_OPT_IPV6_PREFIX_DELEGATION] = */ ahcp_ipv6_prefixes_print,
265 /* [AHCP1_OPT_IPV4_PREFIX_DELEGATION] = */ ahcp_ipv4_prefixes_print,
266 /* [AHCP1_OPT_NAME_SERVER] = */ ahcp_ipv6_addresses_print,
267 /* [AHCP1_OPT_NTP_SERVER] = */ ahcp_ipv6_addresses_print,
268 };
269
270 static void
271 ahcp1_options_print(netdissect_options *ndo, const u_char *cp, const u_char *ep) {
272 uint8_t option_no, option_len;
273
274 while (cp < ep) {
275 /* Option no */
276 ND_TCHECK2(*cp, 1);
277 option_no = *cp;
278 cp += 1;
279 ND_PRINT((ndo, "\n\t %s", tok2str(ahcp1_opt_str, "Unknown-%u", option_no)));
280 if (option_no == AHCP1_OPT_PAD || option_no == AHCP1_OPT_MANDATORY)
281 continue;
282 /* Length */
283 if (cp + 1 > ep)
284 goto corrupt;
285 ND_TCHECK2(*cp, 1);
286 option_len = *cp;
287 cp += 1;
288 if (cp + option_len > ep)
289 goto corrupt;
290 /* Value */
291 if (option_no <= AHCP1_OPT_MAX && data_decoders[option_no] != NULL) {
292 if (data_decoders[option_no](ndo, cp, cp + option_len) < 0)
293 break; /* truncated and already marked up */
294 } else {
295 ND_PRINT((ndo, " (Length %u)", option_len));
296 ND_TCHECK2(*cp, option_len);
297 }
298 cp += option_len;
299 }
300 return;
301
302 corrupt:
303 ND_PRINT((ndo, " %s", cstr));
304 ND_TCHECK2(*cp, ep - cp);
305 return;
306 trunc:
307 ND_PRINT((ndo, "%s", tstr));
308 }
309
310 static void
311 ahcp1_body_print(netdissect_options *ndo, const u_char *cp, const u_char *ep) {
312 uint8_t type, mbz;
313 uint16_t body_len;
314
315 if (cp + AHCP1_BODY_MIN_LEN > ep)
316 goto corrupt;
317 /* Type */
318 ND_TCHECK2(*cp, 1);
319 type = *cp;
320 cp += 1;
321 /* MBZ */
322 ND_TCHECK2(*cp, 1);
323 mbz = *cp;
324 cp += 1;
325 /* Length */
326 ND_TCHECK2(*cp, 2);
327 body_len = EXTRACT_16BITS(cp);
328 cp += 2;
329
330 if (ndo->ndo_vflag) {
331 ND_PRINT((ndo, "\n\t%s", tok2str(ahcp1_msg_str, "Unknown-%u", type)));
332 if (mbz != 0)
333 ND_PRINT((ndo, ", MBZ %u", mbz));
334 ND_PRINT((ndo, ", Length %u", body_len));
335 }
336 if (cp + body_len > ep)
337 goto corrupt;
338
339 /* Options */
340 if (ndo->ndo_vflag >= 2)
341 ahcp1_options_print(ndo, cp, cp + body_len); /* not ep (ignore extra data) */
342 else
343 ND_TCHECK2(*cp, body_len);
344 return;
345
346 corrupt:
347 ND_PRINT((ndo, " %s", cstr));
348 ND_TCHECK2(*cp, ep - cp);
349 return;
350 trunc:
351 ND_PRINT((ndo, "%s", tstr));
352 }
353
354 void
355 ahcp_print(netdissect_options *ndo, const u_char *cp, const u_int len) {
356 const u_char *ep = cp + len;
357 uint8_t version;
358
359 ND_PRINT((ndo, "AHCP"));
360 if (len < 2)
361 goto corrupt;
362 /* Magic */
363 ND_TCHECK2(*cp, 1);
364 if (*cp != AHCP_MAGIC_NUMBER)
365 goto corrupt;
366 cp += 1;
367 /* Version */
368 ND_TCHECK2(*cp, 1);
369 version = *cp;
370 cp += 1;
371 switch (version) {
372 case AHCP_VERSION_1: {
373 ND_PRINT((ndo, " Version 1"));
374 if (len < AHCP1_HEADER_FIX_LEN)
375 goto corrupt;
376 if (!ndo->ndo_vflag) {
377 ND_TCHECK2(*cp, AHCP1_HEADER_FIX_LEN - 2);
378 cp += AHCP1_HEADER_FIX_LEN - 2;
379 } else {
380 /* Hopcount */
381 ND_TCHECK2(*cp, 1);
382 ND_PRINT((ndo, "\n\tHopcount %u", *cp));
383 cp += 1;
384 /* Original Hopcount */
385 ND_TCHECK2(*cp, 1);
386 ND_PRINT((ndo, ", Original Hopcount %u", *cp));
387 cp += 1;
388 /* Nonce */
389 ND_TCHECK2(*cp, 4);
390 ND_PRINT((ndo, ", Nonce 0x%08x", EXTRACT_32BITS(cp)));
391 cp += 4;
392 /* Source Id */
393 ND_TCHECK2(*cp, 8);
394 ND_PRINT((ndo, ", Source Id %s", linkaddr_string(ndo, cp, 0, 8)));
395 cp += 8;
396 /* Destination Id */
397 ND_TCHECK2(*cp, 8);
398 ND_PRINT((ndo, ", Destination Id %s", linkaddr_string(ndo, cp, 0, 8)));
399 cp += 8;
400 }
401 /* Body */
402 ahcp1_body_print(ndo, cp, ep);
403 break;
404 }
405 default:
406 ND_PRINT((ndo, " Version %u (unknown)", version));
407 break;
408 }
409 return;
410
411 corrupt:
412 ND_PRINT((ndo, " %s", cstr));
413 ND_TCHECK2(*cp, ep - cp);
414 return;
415 trunc:
416 ND_PRINT((ndo, "%s", tstr));
417 }
418
419