]> The Tcpdump Group git mirrors - tcpdump/blob - print-ahcp.c
Add the nd_print_invalid() function
[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 = EXTRACT_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", EXTRACT_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), EXTRACT_U_1(cp + 16));
203 cp += 17;
204 sep = ", ";
205 }
206 return 0;
207
208 invalid:
209 nd_print_invalid(ndo);
210 ND_TCHECK_LEN(cp, ep - cp);
211 return 0;
212 trunc:
213 nd_print_trunc(ndo);
214 return -1;
215 }
216
217 static int
218 ahcp_ipv4_prefixes_print(netdissect_options *ndo, const u_char *cp, const u_char *ep)
219 {
220 const char *sep = ": ";
221
222 while (cp < ep) {
223 if (cp + 5 > ep)
224 goto invalid;
225 ND_TCHECK_5(cp);
226 ND_PRINT("%s%s/%u", sep, ipaddr_string(ndo, cp), EXTRACT_U_1(cp + 4));
227 cp += 5;
228 sep = ", ";
229 }
230 return 0;
231
232 invalid:
233 nd_print_invalid(ndo);
234 ND_TCHECK_LEN(cp, ep - cp);
235 return 0;
236 trunc:
237 nd_print_trunc(ndo);
238 return -1;
239 }
240
241 /* Data decoders signal truncated data with -1. */
242 static int
243 (* const data_decoders[AHCP1_OPT_MAX + 1])(netdissect_options *, const u_char *, const u_char *) = {
244 /* [AHCP1_OPT_PAD] = */ NULL,
245 /* [AHCP1_OPT_MANDATORY] = */ NULL,
246 /* [AHCP1_OPT_ORIGIN_TIME] = */ ahcp_time_print,
247 /* [AHCP1_OPT_EXPIRES] = */ ahcp_seconds_print,
248 /* [AHCP1_OPT_MY_IPV6_ADDRESS] = */ ahcp_ipv6_addresses_print,
249 /* [AHCP1_OPT_MY_IPV4_ADDRESS] = */ ahcp_ipv4_addresses_print,
250 /* [AHCP1_OPT_IPV6_PREFIX] = */ ahcp_ipv6_prefixes_print,
251 /* [AHCP1_OPT_IPV4_PREFIX] = */ NULL,
252 /* [AHCP1_OPT_IPV6_ADDRESS] = */ ahcp_ipv6_addresses_print,
253 /* [AHCP1_OPT_IPV4_ADDRESS] = */ ahcp_ipv4_addresses_print,
254 /* [AHCP1_OPT_IPV6_PREFIX_DELEGATION] = */ ahcp_ipv6_prefixes_print,
255 /* [AHCP1_OPT_IPV4_PREFIX_DELEGATION] = */ ahcp_ipv4_prefixes_print,
256 /* [AHCP1_OPT_NAME_SERVER] = */ ahcp_ipv6_addresses_print,
257 /* [AHCP1_OPT_NTP_SERVER] = */ ahcp_ipv6_addresses_print,
258 };
259
260 static void
261 ahcp1_options_print(netdissect_options *ndo, const u_char *cp, const u_char *ep)
262 {
263 uint8_t option_no, option_len;
264
265 while (cp < ep) {
266 /* Option no */
267 ND_TCHECK_1(cp);
268 option_no = EXTRACT_U_1(cp);
269 cp += 1;
270 ND_PRINT("\n\t %s", tok2str(ahcp1_opt_str, "Unknown-%u", option_no));
271 if (option_no == AHCP1_OPT_PAD || option_no == AHCP1_OPT_MANDATORY)
272 continue;
273 /* Length */
274 if (cp + 1 > ep)
275 goto invalid;
276 ND_TCHECK_1(cp);
277 option_len = EXTRACT_U_1(cp);
278 cp += 1;
279 if (cp + option_len > ep)
280 goto invalid;
281 /* Value */
282 if (option_no <= AHCP1_OPT_MAX && data_decoders[option_no] != NULL) {
283 if (data_decoders[option_no](ndo, cp, cp + option_len) < 0)
284 break; /* truncated and already marked up */
285 } else {
286 ND_PRINT(" (Length %u)", option_len);
287 ND_TCHECK_LEN(cp, option_len);
288 }
289 cp += option_len;
290 }
291 return;
292
293 invalid:
294 nd_print_invalid(ndo);
295 ND_TCHECK_LEN(cp, ep - cp);
296 return;
297 trunc:
298 nd_print_trunc(ndo);
299 }
300
301 static void
302 ahcp1_body_print(netdissect_options *ndo, const u_char *cp, const u_char *ep)
303 {
304 uint8_t type, mbz;
305 uint16_t body_len;
306
307 if (cp + AHCP1_BODY_MIN_LEN > ep)
308 goto invalid;
309 /* Type */
310 ND_TCHECK_1(cp);
311 type = EXTRACT_U_1(cp);
312 cp += 1;
313 /* MBZ */
314 ND_TCHECK_1(cp);
315 mbz = EXTRACT_U_1(cp);
316 cp += 1;
317 /* Length */
318 ND_TCHECK_2(cp);
319 body_len = EXTRACT_BE_U_2(cp);
320 cp += 2;
321
322 if (ndo->ndo_vflag) {
323 ND_PRINT("\n\t%s", tok2str(ahcp1_msg_str, "Unknown-%u", type));
324 if (mbz != 0)
325 ND_PRINT(", MBZ %u", mbz);
326 ND_PRINT(", Length %u", body_len);
327 }
328 if (cp + body_len > ep)
329 goto invalid;
330
331 /* Options */
332 if (ndo->ndo_vflag >= 2)
333 ahcp1_options_print(ndo, cp, cp + body_len); /* not ep (ignore extra data) */
334 else
335 ND_TCHECK_LEN(cp, body_len);
336 return;
337
338 invalid:
339 nd_print_invalid(ndo);
340 ND_TCHECK_LEN(cp, ep - cp);
341 return;
342 trunc:
343 nd_print_trunc(ndo);
344 }
345
346 void
347 ahcp_print(netdissect_options *ndo, const u_char *cp, const u_int len)
348 {
349 const u_char *ep = ndo->ndo_snapend;
350 uint8_t version;
351
352 ndo->ndo_protocol = "ahcp";
353 ND_PRINT("AHCP");
354 if (len < 2)
355 goto invalid;
356 /* Magic */
357 ND_TCHECK_1(cp);
358 if (EXTRACT_U_1(cp) != AHCP_MAGIC_NUMBER)
359 goto invalid;
360 cp += 1;
361 /* Version */
362 ND_TCHECK_1(cp);
363 version = EXTRACT_U_1(cp);
364 cp += 1;
365 switch (version) {
366 case AHCP_VERSION_1: {
367 ND_PRINT(" Version 1");
368 if (len < AHCP1_HEADER_FIX_LEN)
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 } else {
374 /* Hopcount */
375 ND_TCHECK_1(cp);
376 ND_PRINT("\n\tHopcount %u", EXTRACT_U_1(cp));
377 cp += 1;
378 /* Original Hopcount */
379 ND_TCHECK_1(cp);
380 ND_PRINT(", Original Hopcount %u", EXTRACT_U_1(cp));
381 cp += 1;
382 /* Nonce */
383 ND_TCHECK_4(cp);
384 ND_PRINT(", Nonce 0x%08x", EXTRACT_BE_U_4(cp));
385 cp += 4;
386 /* Source Id */
387 ND_TCHECK_8(cp);
388 ND_PRINT(", Source Id %s", linkaddr_string(ndo, cp, 0, 8));
389 cp += 8;
390 /* Destination Id */
391 ND_TCHECK_8(cp);
392 ND_PRINT(", Destination Id %s", linkaddr_string(ndo, cp, 0, 8));
393 cp += 8;
394 }
395 /* Body */
396 ahcp1_body_print(ndo, cp, ep);
397 break;
398 }
399 default:
400 ND_PRINT(" Version %u (unknown)", version);
401 break;
402 }
403 return;
404
405 invalid:
406 nd_print_invalid(ndo);
407 ND_TCHECK_LEN(cp, ep - cp);
408 return;
409 trunc:
410 nd_print_trunc(ndo);
411 }