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