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