]> The Tcpdump Group git mirrors - tcpdump/blob - print-ahcp.c
remove redundant ND_TCHECK, let GET_ routines handle checks
[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 void
100 ahcp_time_print(netdissect_options *ndo,
101 const u_char *cp, uint8_t len)
102 {
103 time_t t;
104 struct tm *tm;
105 char buf[BUFSIZE];
106
107 if (len != 4)
108 goto invalid;
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;
117
118 invalid:
119 nd_print_invalid(ndo);
120 ND_TCHECK_LEN(cp, len);
121 return;
122 trunc:
123 nd_trunc(ndo);
124 }
125
126 static void
127 ahcp_seconds_print(netdissect_options *ndo,
128 const u_char *cp, uint8_t len)
129 {
130 if (len != 4)
131 goto invalid;
132 ND_PRINT(": %us", GET_BE_U_4(cp));
133 return;
134
135 invalid:
136 nd_print_invalid(ndo);
137 ND_TCHECK_LEN(cp, len);
138 return;
139 trunc:
140 nd_trunc(ndo);
141 }
142
143 static void
144 ahcp_ipv6_addresses_print(netdissect_options *ndo,
145 const u_char *cp, uint8_t len)
146 {
147 const char *sep = ": ";
148
149 while (len) {
150 if (len < 16)
151 goto invalid;
152 ND_PRINT("%s%s", sep, GET_IP6ADDR_STRING(cp));
153 cp += 16;
154 len -= 16;
155 sep = ", ";
156 }
157 return;
158
159 invalid:
160 nd_print_invalid(ndo);
161 ND_TCHECK_LEN(cp, len);
162 return;
163 trunc:
164 nd_trunc(ndo);
165 }
166
167 static void
168 ahcp_ipv4_addresses_print(netdissect_options *ndo,
169 const u_char *cp, uint8_t len)
170 {
171 const char *sep = ": ";
172
173 while (len) {
174 if (len < 4)
175 goto invalid;
176 ND_PRINT("%s%s", sep, GET_IPADDR_STRING(cp));
177 cp += 4;
178 len -= 4;
179 sep = ", ";
180 }
181 return;
182
183 invalid:
184 nd_print_invalid(ndo);
185 ND_TCHECK_LEN(cp, len);
186 return;
187 trunc:
188 nd_trunc(ndo);
189 }
190
191 static void
192 ahcp_ipv6_prefixes_print(netdissect_options *ndo,
193 const u_char *cp, uint8_t len)
194 {
195 const char *sep = ": ";
196
197 while (len) {
198 if (len < 17)
199 goto invalid;
200 ND_PRINT("%s%s/%u", sep, GET_IP6ADDR_STRING(cp), GET_U_1(cp + 16));
201 cp += 17;
202 len -= 17;
203 sep = ", ";
204 }
205 return;
206
207 invalid:
208 nd_print_invalid(ndo);
209 ND_TCHECK_LEN(cp, len);
210 return;
211 trunc:
212 nd_trunc(ndo);
213 }
214
215 static void
216 ahcp_ipv4_prefixes_print(netdissect_options *ndo,
217 const u_char *cp, uint8_t len)
218 {
219 const char *sep = ": ";
220
221 while (len) {
222 if (len < 5)
223 goto invalid;
224 ND_PRINT("%s%s/%u", sep, GET_IPADDR_STRING(cp), GET_U_1(cp + 4));
225 cp += 5;
226 len -= 5;
227 sep = ", ";
228 }
229 return;
230
231 invalid:
232 nd_print_invalid(ndo);
233 ND_TCHECK_LEN(cp, len);
234 return;
235 trunc:
236 nd_trunc(ndo);
237 }
238
239 static void
240 (* const data_decoders[AHCP1_OPT_MAX + 1])(netdissect_options *, const u_char *, uint8_t) = {
241 /* [AHCP1_OPT_PAD] = */ NULL,
242 /* [AHCP1_OPT_MANDATORY] = */ NULL,
243 /* [AHCP1_OPT_ORIGIN_TIME] = */ ahcp_time_print,
244 /* [AHCP1_OPT_EXPIRES] = */ ahcp_seconds_print,
245 /* [AHCP1_OPT_MY_IPV6_ADDRESS] = */ ahcp_ipv6_addresses_print,
246 /* [AHCP1_OPT_MY_IPV4_ADDRESS] = */ ahcp_ipv4_addresses_print,
247 /* [AHCP1_OPT_IPV6_PREFIX] = */ ahcp_ipv6_prefixes_print,
248 /* [AHCP1_OPT_IPV4_PREFIX] = */ NULL,
249 /* [AHCP1_OPT_IPV6_ADDRESS] = */ ahcp_ipv6_addresses_print,
250 /* [AHCP1_OPT_IPV4_ADDRESS] = */ ahcp_ipv4_addresses_print,
251 /* [AHCP1_OPT_IPV6_PREFIX_DELEGATION] = */ ahcp_ipv6_prefixes_print,
252 /* [AHCP1_OPT_IPV4_PREFIX_DELEGATION] = */ ahcp_ipv4_prefixes_print,
253 /* [AHCP1_OPT_NAME_SERVER] = */ ahcp_ipv6_addresses_print,
254 /* [AHCP1_OPT_NTP_SERVER] = */ ahcp_ipv6_addresses_print,
255 };
256
257 static void
258 ahcp1_options_print(netdissect_options *ndo,
259 const u_char *cp, uint16_t len)
260 {
261 while (len) {
262 uint8_t option_no, option_len;
263
264 /* Option no */
265 option_no = GET_U_1(cp);
266 cp += 1;
267 len -= 1;
268 ND_PRINT("\n\t %s", tok2str(ahcp1_opt_str, "Unknown-%u", option_no));
269 if (option_no == AHCP1_OPT_PAD || option_no == AHCP1_OPT_MANDATORY)
270 continue;
271 /* Length */
272 if (!len)
273 goto invalid;
274 option_len = GET_U_1(cp);
275 cp += 1;
276 len -= 1;
277 if (option_len > len)
278 goto invalid;
279 /* Value */
280 if (option_no <= AHCP1_OPT_MAX && data_decoders[option_no] != NULL) {
281 data_decoders[option_no](ndo, cp, option_len);
282 } else {
283 ND_PRINT(" (Length %u)", option_len);
284 ND_TCHECK_LEN(cp, option_len);
285 }
286 cp += option_len;
287 len -= option_len;
288 }
289 return;
290
291 invalid:
292 nd_print_invalid(ndo);
293 ND_TCHECK_LEN(cp, len);
294 return;
295 trunc:
296 nd_trunc(ndo);
297 }
298
299 static void
300 ahcp1_body_print(netdissect_options *ndo,
301 const u_char *cp, u_int len)
302 {
303 uint8_t type, mbz;
304 uint16_t body_len;
305
306 if (len < AHCP1_BODY_MIN_LEN)
307 goto invalid;
308 /* Type */
309 type = GET_U_1(cp);
310 cp += 1;
311 len -= 1;
312 /* MBZ */
313 mbz = GET_U_1(cp);
314 cp += 1;
315 len -= 1;
316 /* Length */
317 body_len = GET_BE_U_2(cp);
318 cp += 2;
319 len -= 2;
320
321 if (ndo->ndo_vflag) {
322 ND_PRINT("\n\t%s", tok2str(ahcp1_msg_str, "Unknown-%u", type));
323 if (mbz != 0)
324 ND_PRINT(", MBZ %u", mbz);
325 ND_PRINT(", Length %u", body_len);
326 }
327 if (body_len > len)
328 goto invalid;
329
330 /* Options */
331 /* Here use "body_len", not "len" (ignore any extra data). */
332 if (ndo->ndo_vflag >= 2)
333 ahcp1_options_print(ndo, cp, body_len);
334 else
335 ND_TCHECK_LEN(cp, body_len);
336 return;
337
338 invalid:
339 nd_print_invalid(ndo);
340 ND_TCHECK_LEN(cp, len);
341 return;
342 trunc:
343 nd_trunc(ndo);
344 }
345
346 void
347 ahcp_print(netdissect_options *ndo,
348 const u_char *cp, u_int len)
349 {
350 uint8_t version;
351
352 ndo->ndo_protocol = "ahcp";
353 nd_print_protocol_caps(ndo);
354 if (len < 2)
355 goto invalid;
356 /* Magic */
357 if (GET_U_1(cp) != AHCP_MAGIC_NUMBER)
358 goto invalid;
359 cp += 1;
360 len -= 1;
361 /* Version */
362 version = GET_U_1(cp);
363 cp += 1;
364 len -= 1;
365 switch (version) {
366 case AHCP_VERSION_1: {
367 ND_PRINT(" Version 1");
368 if (len < AHCP1_HEADER_FIX_LEN - 2)
369 goto invalid;
370 if (!ndo->ndo_vflag) {
371 ND_TCHECK_LEN(cp, AHCP1_HEADER_FIX_LEN - 2);
372 cp += AHCP1_HEADER_FIX_LEN - 2;
373 len -= AHCP1_HEADER_FIX_LEN - 2;
374 } else {
375 /* Hopcount */
376 ND_PRINT("\n\tHopcount %u", GET_U_1(cp));
377 cp += 1;
378 len -= 1;
379 /* Original Hopcount */
380 ND_PRINT(", Original Hopcount %u", GET_U_1(cp));
381 cp += 1;
382 len -= 1;
383 /* Nonce */
384 ND_PRINT(", Nonce 0x%08x", GET_BE_U_4(cp));
385 cp += 4;
386 len -= 4;
387 /* Source Id */
388 ND_PRINT(", Source Id %s", GET_LINKADDR_STRING(cp, LINKADDR_OTHER, 8));
389 cp += 8;
390 len -= 8;
391 /* Destination Id */
392 ND_PRINT(", Destination Id %s", GET_LINKADDR_STRING(cp, LINKADDR_OTHER, 8));
393 cp += 8;
394 len -= 8;
395 }
396 /* Body */
397 ahcp1_body_print(ndo, cp, len);
398 break;
399 }
400 default:
401 ND_PRINT(" Version %u (unknown)", version);
402 ND_TCHECK_LEN(cp, len);
403 break;
404 }
405 return;
406
407 invalid:
408 nd_print_invalid(ndo);
409 ND_TCHECK_LEN(cp, len);
410 return;
411 trunc:
412 nd_trunc(ndo);
413 }