xref: /freebsd/contrib/tcpdump/print-aodv.c (revision 596596fec79f04e1f413850b44159224ff1fb8dc)
1 /*
2  * Copyright (c) 2003 Bruce M. Simpson <bms@spc.org>
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  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *        This product includes software developed by Bruce M. Simpson.
16  * 4. Neither the name of Bruce M. Simpson nor the names of co-
17  *    contributors may be used to endorse or promote products derived
18  *    from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY Bruce M. Simpson AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL Bruce M. Simpson OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #define NETDISSECT_REWORKED
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
37 
38 #include <tcpdump-stdinc.h>
39 
40 /* for offsetof */
41 #include <stddef.h>
42 
43 #include "interface.h"
44 #include "addrtoname.h"
45 #include "extract.h"			/* must come after interface.h */
46 
47 
48 struct aodv_rreq {
49 	uint8_t		rreq_type;	/* AODV message type (1) */
50 	uint8_t		rreq_flags;	/* various flags */
51 	uint8_t		rreq_zero0;	/* reserved, set to zero */
52 	uint8_t		rreq_hops;	/* number of hops from originator */
53 	uint32_t	rreq_id;	/* request ID */
54 	uint32_t	rreq_da;	/* destination IPv4 address */
55 	uint32_t	rreq_ds;	/* destination sequence number */
56 	uint32_t	rreq_oa;	/* originator IPv4 address */
57 	uint32_t	rreq_os;	/* originator sequence number */
58 };
59 #ifdef INET6
60 struct aodv_rreq6 {
61 	uint8_t		rreq_type;	/* AODV message type (1) */
62 	uint8_t		rreq_flags;	/* various flags */
63 	uint8_t		rreq_zero0;	/* reserved, set to zero */
64 	uint8_t		rreq_hops;	/* number of hops from originator */
65 	uint32_t	rreq_id;	/* request ID */
66 	struct in6_addr	rreq_da;	/* destination IPv6 address */
67 	uint32_t	rreq_ds;	/* destination sequence number */
68 	struct in6_addr	rreq_oa;	/* originator IPv6 address */
69 	uint32_t	rreq_os;	/* originator sequence number */
70 };
71 struct aodv_rreq6_draft_01 {
72 	uint8_t		rreq_type;	/* AODV message type (16) */
73 	uint8_t		rreq_flags;	/* various flags */
74 	uint8_t		rreq_zero0;	/* reserved, set to zero */
75 	uint8_t		rreq_hops;	/* number of hops from originator */
76 	uint32_t	rreq_id;	/* request ID */
77 	uint32_t	rreq_ds;	/* destination sequence number */
78 	uint32_t	rreq_os;	/* originator sequence number */
79 	struct in6_addr	rreq_da;	/* destination IPv6 address */
80 	struct in6_addr	rreq_oa;	/* originator IPv6 address */
81 };
82 #endif
83 
84 #define	RREQ_JOIN	0x80		/* join (reserved for multicast */
85 #define	RREQ_REPAIR	0x40		/* repair (reserved for multicast */
86 #define	RREQ_GRAT	0x20		/* gratuitous RREP */
87 #define	RREQ_DEST	0x10		/* destination only */
88 #define	RREQ_UNKNOWN	0x08		/* unknown destination sequence num */
89 #define	RREQ_FLAGS_MASK	0xF8		/* mask for rreq_flags */
90 
91 struct aodv_rrep {
92 	uint8_t		rrep_type;	/* AODV message type (2) */
93 	uint8_t		rrep_flags;	/* various flags */
94 	uint8_t		rrep_ps;	/* prefix size */
95 	uint8_t		rrep_hops;	/* number of hops from o to d */
96 	uint32_t	rrep_da;	/* destination IPv4 address */
97 	uint32_t	rrep_ds;	/* destination sequence number */
98 	uint32_t	rrep_oa;	/* originator IPv4 address */
99 	uint32_t	rrep_life;	/* lifetime of this route */
100 };
101 #ifdef INET6
102 struct aodv_rrep6 {
103 	uint8_t		rrep_type;	/* AODV message type (2) */
104 	uint8_t		rrep_flags;	/* various flags */
105 	uint8_t		rrep_ps;	/* prefix size */
106 	uint8_t		rrep_hops;	/* number of hops from o to d */
107 	struct in6_addr	rrep_da;	/* destination IPv6 address */
108 	uint32_t	rrep_ds;	/* destination sequence number */
109 	struct in6_addr	rrep_oa;	/* originator IPv6 address */
110 	uint32_t	rrep_life;	/* lifetime of this route */
111 };
112 struct aodv_rrep6_draft_01 {
113 	uint8_t		rrep_type;	/* AODV message type (17) */
114 	uint8_t		rrep_flags;	/* various flags */
115 	uint8_t		rrep_ps;	/* prefix size */
116 	uint8_t		rrep_hops;	/* number of hops from o to d */
117 	uint32_t	rrep_ds;	/* destination sequence number */
118 	struct in6_addr	rrep_da;	/* destination IPv6 address */
119 	struct in6_addr	rrep_oa;	/* originator IPv6 address */
120 	uint32_t	rrep_life;	/* lifetime of this route */
121 };
122 #endif
123 
124 #define	RREP_REPAIR		0x80	/* repair (reserved for multicast */
125 #define	RREP_ACK		0x40	/* acknowledgement required */
126 #define	RREP_FLAGS_MASK		0xC0	/* mask for rrep_flags */
127 #define	RREP_PREFIX_MASK	0x1F	/* mask for prefix size */
128 
129 struct rerr_unreach {
130 	uint32_t	u_da;	/* IPv4 address */
131 	uint32_t	u_ds;	/* sequence number */
132 };
133 #ifdef INET6
134 struct rerr_unreach6 {
135 	struct in6_addr	u_da;	/* IPv6 address */
136 	uint32_t	u_ds;	/* sequence number */
137 };
138 struct rerr_unreach6_draft_01 {
139 	struct in6_addr	u_da;	/* IPv6 address */
140 	uint32_t	u_ds;	/* sequence number */
141 };
142 #endif
143 
144 struct aodv_rerr {
145 	uint8_t		rerr_type;	/* AODV message type (3 or 18) */
146 	uint8_t		rerr_flags;	/* various flags */
147 	uint8_t		rerr_zero0;	/* reserved, set to zero */
148 	uint8_t		rerr_dc;	/* destination count */
149 	union {
150 		struct	rerr_unreach dest[1];
151 #ifdef INET6
152 		struct	rerr_unreach6 dest6[1];
153 		struct	rerr_unreach6_draft_01 dest6_draft_01[1];
154 #endif
155 	} r;
156 };
157 
158 #define RERR_NODELETE		0x80	/* don't delete the link */
159 #define RERR_FLAGS_MASK		0x80	/* mask for rerr_flags */
160 
161 struct aodv_rrep_ack {
162 	uint8_t		ra_type;
163 	uint8_t		ra_zero0;
164 };
165 
166 union aodv {
167 	struct aodv_rreq rreq;
168 	struct aodv_rrep rrep;
169 	struct aodv_rerr rerr;
170 	struct aodv_rrep_ack rrep_ack;
171 #ifdef INET6
172 	struct aodv_rreq6 rreq6;
173 	struct aodv_rreq6_draft_01 rreq6_draft_01;
174 	struct aodv_rrep6 rrep6;
175 	struct aodv_rrep6_draft_01 rrep6_draft_01;
176 #endif
177 };
178 
179 #define	AODV_RREQ		1	/* route request */
180 #define	AODV_RREP		2	/* route response */
181 #define	AODV_RERR		3	/* error report */
182 #define	AODV_RREP_ACK		4	/* route response acknowledgement */
183 
184 #define AODV_V6_DRAFT_01_RREQ		16	/* IPv6 route request */
185 #define AODV_V6_DRAFT_01_RREP		17	/* IPv6 route response */
186 #define AODV_V6_DRAFT_01_RERR		18	/* IPv6 error report */
187 #define AODV_V6_DRAFT_01_RREP_ACK	19	/* IPV6 route response acknowledgment */
188 
189 struct aodv_ext {
190 	uint8_t		type;		/* extension type */
191 	uint8_t		length;		/* extension length */
192 };
193 
194 struct aodv_hello {
195 	struct	aodv_ext	eh;		/* extension header */
196 	uint8_t			interval[4];	/* expect my next hello in
197 						 * (n) ms
198 						 * NOTE: this is not aligned */
199 };
200 
201 #define	AODV_EXT_HELLO	1
202 
203 static void
204 aodv_extension(netdissect_options *ndo,
205                const struct aodv_ext *ep, u_int length)
206 {
207 	u_int i;
208 	const struct aodv_hello *ah;
209 
210 	switch (ep->type) {
211 	case AODV_EXT_HELLO:
212 		if (ndo->ndo_snapend < (u_char *) ep) {
213 			ND_PRINT((ndo, " [|hello]"));
214 			return;
215 		}
216 		i = min(length, (u_int)(ndo->ndo_snapend - (u_char *)ep));
217 		if (i < sizeof(struct aodv_hello)) {
218 			ND_PRINT((ndo, " [|hello]"));
219 			return;
220 		}
221 		i -= sizeof(struct aodv_hello);
222 		ah = (void *)ep;
223 		ND_PRINT((ndo, "\n\text HELLO %ld ms",
224 		    (unsigned long)EXTRACT_32BITS(&ah->interval)));
225 		break;
226 
227 	default:
228 		ND_PRINT((ndo, "\n\text %u %u", ep->type, ep->length));
229 		break;
230 	}
231 }
232 
233 static void
234 aodv_rreq(netdissect_options *ndo,
235           const union aodv *ap, const u_char *dat, u_int length)
236 {
237 	u_int i;
238 
239 	if (ndo->ndo_snapend < dat) {
240 		ND_PRINT((ndo, " [|aodv]"));
241 		return;
242 	}
243 	i = min(length, (u_int)(ndo->ndo_snapend - dat));
244 	if (i < sizeof(ap->rreq)) {
245 		ND_PRINT((ndo, " [|rreq]"));
246 		return;
247 	}
248 	i -= sizeof(ap->rreq);
249 	ND_PRINT((ndo, " rreq %u %s%s%s%s%shops %u id 0x%08lx\n"
250 	    "\tdst %s seq %lu src %s seq %lu", length,
251 	    ap->rreq.rreq_type & RREQ_JOIN ? "[J]" : "",
252 	    ap->rreq.rreq_type & RREQ_REPAIR ? "[R]" : "",
253 	    ap->rreq.rreq_type & RREQ_GRAT ? "[G]" : "",
254 	    ap->rreq.rreq_type & RREQ_DEST ? "[D]" : "",
255 	    ap->rreq.rreq_type & RREQ_UNKNOWN ? "[U] " : " ",
256 	    ap->rreq.rreq_hops,
257 	    (unsigned long)EXTRACT_32BITS(&ap->rreq.rreq_id),
258 	    ipaddr_string(ndo, &ap->rreq.rreq_da),
259 	    (unsigned long)EXTRACT_32BITS(&ap->rreq.rreq_ds),
260 	    ipaddr_string(ndo, &ap->rreq.rreq_oa),
261 	    (unsigned long)EXTRACT_32BITS(&ap->rreq.rreq_os)));
262 	if (i >= sizeof(struct aodv_ext))
263 		aodv_extension(ndo, (void *)(&ap->rreq + 1), i);
264 }
265 
266 static void
267 aodv_rrep(netdissect_options *ndo,
268           const union aodv *ap, const u_char *dat, u_int length)
269 {
270 	u_int i;
271 
272 	if (ndo->ndo_snapend < dat) {
273 		ND_PRINT((ndo, " [|aodv]"));
274 		return;
275 	}
276 	i = min(length, (u_int)(ndo->ndo_snapend - dat));
277 	if (i < sizeof(ap->rrep)) {
278 		ND_PRINT((ndo, " [|rrep]"));
279 		return;
280 	}
281 	i -= sizeof(ap->rrep);
282 	ND_PRINT((ndo, " rrep %u %s%sprefix %u hops %u\n"
283 	    "\tdst %s dseq %lu src %s %lu ms", length,
284 	    ap->rrep.rrep_type & RREP_REPAIR ? "[R]" : "",
285 	    ap->rrep.rrep_type & RREP_ACK ? "[A] " : " ",
286 	    ap->rrep.rrep_ps & RREP_PREFIX_MASK,
287 	    ap->rrep.rrep_hops,
288 	    ipaddr_string(ndo, &ap->rrep.rrep_da),
289 	    (unsigned long)EXTRACT_32BITS(&ap->rrep.rrep_ds),
290 	    ipaddr_string(ndo, &ap->rrep.rrep_oa),
291 	    (unsigned long)EXTRACT_32BITS(&ap->rrep.rrep_life)));
292 	if (i >= sizeof(struct aodv_ext))
293 		aodv_extension(ndo, (void *)(&ap->rrep + 1), i);
294 }
295 
296 static void
297 aodv_rerr(netdissect_options *ndo,
298           const union aodv *ap, const u_char *dat, u_int length)
299 {
300 	u_int i;
301 	const struct rerr_unreach *dp = NULL;
302 	int n, trunc;
303 
304 	if (ndo->ndo_snapend < dat) {
305 		ND_PRINT((ndo, " [|aodv]"));
306 		return;
307 	}
308 	i = min(length, (u_int)(ndo->ndo_snapend - dat));
309 	if (i < offsetof(struct aodv_rerr, r)) {
310 		ND_PRINT((ndo, " [|rerr]"));
311 		return;
312 	}
313 	i -= offsetof(struct aodv_rerr, r);
314 	dp = &ap->rerr.r.dest[0];
315 	n = ap->rerr.rerr_dc * sizeof(ap->rerr.r.dest[0]);
316 	ND_PRINT((ndo, " rerr %s [items %u] [%u]:",
317 	    ap->rerr.rerr_flags & RERR_NODELETE ? "[D]" : "",
318 	    ap->rerr.rerr_dc, length));
319 	trunc = n - (i/sizeof(ap->rerr.r.dest[0]));
320 	for (; i >= sizeof(ap->rerr.r.dest[0]);
321 	    ++dp, i -= sizeof(ap->rerr.r.dest[0])) {
322 		ND_PRINT((ndo, " {%s}(%ld)", ipaddr_string(ndo, &dp->u_da),
323 		    (unsigned long)EXTRACT_32BITS(&dp->u_ds)));
324 	}
325 	if (trunc)
326 		ND_PRINT((ndo, "[|rerr]"));
327 }
328 
329 static void
330 #ifdef INET6
331 aodv_v6_rreq(netdissect_options *ndo,
332              const union aodv *ap, const u_char *dat, u_int length)
333 #else
334 aodv_v6_rreq(netdissect_options *ndo,
335              const union aodv *ap _U_, const u_char *dat _U_, u_int length)
336 #endif
337 {
338 #ifdef INET6
339 	u_int i;
340 
341 	if (ndo->ndo_snapend < dat) {
342 		ND_PRINT((ndo, " [|aodv]"));
343 		return;
344 	}
345 	i = min(length, (u_int)(ndo->ndo_snapend - dat));
346 	if (i < sizeof(ap->rreq6)) {
347 		ND_PRINT((ndo, " [|rreq6]"));
348 		return;
349 	}
350 	i -= sizeof(ap->rreq6);
351 	ND_PRINT((ndo, " v6 rreq %u %s%s%s%s%shops %u id 0x%08lx\n"
352 	    "\tdst %s seq %lu src %s seq %lu", length,
353 	    ap->rreq6.rreq_type & RREQ_JOIN ? "[J]" : "",
354 	    ap->rreq6.rreq_type & RREQ_REPAIR ? "[R]" : "",
355 	    ap->rreq6.rreq_type & RREQ_GRAT ? "[G]" : "",
356 	    ap->rreq6.rreq_type & RREQ_DEST ? "[D]" : "",
357 	    ap->rreq6.rreq_type & RREQ_UNKNOWN ? "[U] " : " ",
358 	    ap->rreq6.rreq_hops,
359 	    (unsigned long)EXTRACT_32BITS(&ap->rreq6.rreq_id),
360 	    ip6addr_string(ndo, &ap->rreq6.rreq_da),
361 	    (unsigned long)EXTRACT_32BITS(&ap->rreq6.rreq_ds),
362 	    ip6addr_string(ndo, &ap->rreq6.rreq_oa),
363 	    (unsigned long)EXTRACT_32BITS(&ap->rreq6.rreq_os)));
364 	if (i >= sizeof(struct aodv_ext))
365 		aodv_extension(ndo, (void *)(&ap->rreq6 + 1), i);
366 #else
367 	ND_PRINT((ndo, " v6 rreq %u", length));
368 #endif
369 }
370 
371 static void
372 #ifdef INET6
373 aodv_v6_rrep(netdissect_options *ndo,
374              const union aodv *ap, const u_char *dat, u_int length)
375 #else
376 aodv_v6_rrep(netdissect_options *ndo,
377              const union aodv *ap _U_, const u_char *dat _U_, u_int length)
378 #endif
379 {
380 #ifdef INET6
381 	u_int i;
382 
383 	if (ndo->ndo_snapend < dat) {
384 		ND_PRINT((ndo, " [|aodv]"));
385 		return;
386 	}
387 	i = min(length, (u_int)(ndo->ndo_snapend - dat));
388 	if (i < sizeof(ap->rrep6)) {
389 		ND_PRINT((ndo, " [|rrep6]"));
390 		return;
391 	}
392 	i -= sizeof(ap->rrep6);
393 	ND_PRINT((ndo, " rrep %u %s%sprefix %u hops %u\n"
394 	   "\tdst %s dseq %lu src %s %lu ms", length,
395 	    ap->rrep6.rrep_type & RREP_REPAIR ? "[R]" : "",
396 	    ap->rrep6.rrep_type & RREP_ACK ? "[A] " : " ",
397 	    ap->rrep6.rrep_ps & RREP_PREFIX_MASK,
398 	    ap->rrep6.rrep_hops,
399 	    ip6addr_string(ndo, &ap->rrep6.rrep_da),
400 	    (unsigned long)EXTRACT_32BITS(&ap->rrep6.rrep_ds),
401 	    ip6addr_string(ndo, &ap->rrep6.rrep_oa),
402 	    (unsigned long)EXTRACT_32BITS(&ap->rrep6.rrep_life)));
403 	if (i >= sizeof(struct aodv_ext))
404 		aodv_extension(ndo, (void *)(&ap->rrep6 + 1), i);
405 #else
406 	ND_PRINT((ndo, " rrep %u", length));
407 #endif
408 }
409 
410 static void
411 #ifdef INET6
412 aodv_v6_rerr(netdissect_options *ndo,
413              const union aodv *ap, u_int length)
414 #else
415 aodv_v6_rerr(netdissect_options *ndo,
416              const union aodv *ap _U_, u_int length)
417 #endif
418 {
419 #ifdef INET6
420 	const struct rerr_unreach6 *dp6 = NULL;
421 	int i, j, n, trunc;
422 
423 	i = length - offsetof(struct aodv_rerr, r);
424 	j = sizeof(ap->rerr.r.dest6[0]);
425 	dp6 = &ap->rerr.r.dest6[0];
426 	n = ap->rerr.rerr_dc * j;
427 	ND_PRINT((ndo, " rerr %s [items %u] [%u]:",
428 	    ap->rerr.rerr_flags & RERR_NODELETE ? "[D]" : "",
429 	    ap->rerr.rerr_dc, length));
430 	trunc = n - (i/j);
431 	for (; i -= j >= 0; ++dp6) {
432 		ND_PRINT((ndo, " {%s}(%ld)", ip6addr_string(ndo, &dp6->u_da),
433 		    (unsigned long)EXTRACT_32BITS(&dp6->u_ds)));
434 	}
435 	if (trunc)
436 		ND_PRINT((ndo, "[|rerr]"));
437 #else
438 	ND_PRINT((ndo, " rerr %u", length));
439 #endif
440 }
441 
442 static void
443 #ifdef INET6
444 aodv_v6_draft_01_rreq(netdissect_options *ndo,
445                       const union aodv *ap, const u_char *dat, u_int length)
446 #else
447 aodv_v6_draft_01_rreq(netdissect_options *ndo,
448                       const union aodv *ap _U_, const u_char *dat _U_,
449     u_int length)
450 #endif
451 {
452 #ifdef INET6
453 	u_int i;
454 
455 	if (ndo->ndo_snapend < dat) {
456 		ND_PRINT((ndo, " [|aodv]"));
457 		return;
458 	}
459 	i = min(length, (u_int)(ndo->ndo_snapend - dat));
460 	if (i < sizeof(ap->rreq6_draft_01)) {
461 		ND_PRINT((ndo, " [|rreq6]"));
462 		return;
463 	}
464 	i -= sizeof(ap->rreq6_draft_01);
465 	ND_PRINT((ndo, " rreq %u %s%s%s%s%shops %u id 0x%08lx\n"
466 	    "\tdst %s seq %lu src %s seq %lu", length,
467 	    ap->rreq6_draft_01.rreq_type & RREQ_JOIN ? "[J]" : "",
468 	    ap->rreq6_draft_01.rreq_type & RREQ_REPAIR ? "[R]" : "",
469 	    ap->rreq6_draft_01.rreq_type & RREQ_GRAT ? "[G]" : "",
470 	    ap->rreq6_draft_01.rreq_type & RREQ_DEST ? "[D]" : "",
471 	    ap->rreq6_draft_01.rreq_type & RREQ_UNKNOWN ? "[U] " : " ",
472 	    ap->rreq6_draft_01.rreq_hops,
473 	    (unsigned long)EXTRACT_32BITS(&ap->rreq6_draft_01.rreq_id),
474 	    ip6addr_string(ndo, &ap->rreq6_draft_01.rreq_da),
475 	    (unsigned long)EXTRACT_32BITS(&ap->rreq6_draft_01.rreq_ds),
476 	    ip6addr_string(ndo, &ap->rreq6_draft_01.rreq_oa),
477 	    (unsigned long)EXTRACT_32BITS(&ap->rreq6_draft_01.rreq_os)));
478 	if (i >= sizeof(struct aodv_ext))
479 		aodv_extension(ndo, (void *)(&ap->rreq6_draft_01 + 1), i);
480 #else
481 	ND_PRINT((ndo, " rreq %u", length));
482 #endif
483 }
484 
485 static void
486 #ifdef INET6
487 aodv_v6_draft_01_rrep(netdissect_options *ndo,
488                       const union aodv *ap, const u_char *dat, u_int length)
489 #else
490 aodv_v6_draft_01_rrep(netdissect_options *ndo,
491                       const union aodv *ap _U_, const u_char *dat _U_,
492     u_int length)
493 #endif
494 {
495 #ifdef INET6
496 	u_int i;
497 
498 	if (ndo->ndo_snapend < dat) {
499 		ND_PRINT((ndo, " [|aodv]"));
500 		return;
501 	}
502 	i = min(length, (u_int)(ndo->ndo_snapend - dat));
503 	if (i < sizeof(ap->rrep6_draft_01)) {
504 		ND_PRINT((ndo, " [|rrep6]"));
505 		return;
506 	}
507 	i -= sizeof(ap->rrep6_draft_01);
508 	ND_PRINT((ndo, " rrep %u %s%sprefix %u hops %u\n"
509 	   "\tdst %s dseq %lu src %s %lu ms", length,
510 	    ap->rrep6_draft_01.rrep_type & RREP_REPAIR ? "[R]" : "",
511 	    ap->rrep6_draft_01.rrep_type & RREP_ACK ? "[A] " : " ",
512 	    ap->rrep6_draft_01.rrep_ps & RREP_PREFIX_MASK,
513 	    ap->rrep6_draft_01.rrep_hops,
514 	    ip6addr_string(ndo, &ap->rrep6_draft_01.rrep_da),
515 	    (unsigned long)EXTRACT_32BITS(&ap->rrep6_draft_01.rrep_ds),
516 	    ip6addr_string(ndo, &ap->rrep6_draft_01.rrep_oa),
517 	    (unsigned long)EXTRACT_32BITS(&ap->rrep6_draft_01.rrep_life)));
518 	if (i >= sizeof(struct aodv_ext))
519 		aodv_extension(ndo, (void *)(&ap->rrep6_draft_01 + 1), i);
520 #else
521 	ND_PRINT((ndo, " rrep %u", length));
522 #endif
523 }
524 
525 static void
526 #ifdef INET6
527 aodv_v6_draft_01_rerr(netdissect_options *ndo,
528                       const union aodv *ap, u_int length)
529 #else
530 aodv_v6_draft_01_rerr(netdissect_options *ndo,
531                       const union aodv *ap _U_, u_int length)
532 #endif
533 {
534 #ifdef INET6
535 	const struct rerr_unreach6_draft_01 *dp6 = NULL;
536 	int i, j, n, trunc;
537 
538 	i = length - offsetof(struct aodv_rerr, r);
539 	j = sizeof(ap->rerr.r.dest6_draft_01[0]);
540 	dp6 = &ap->rerr.r.dest6_draft_01[0];
541 	n = ap->rerr.rerr_dc * j;
542 	ND_PRINT((ndo, " rerr %s [items %u] [%u]:",
543 	    ap->rerr.rerr_flags & RERR_NODELETE ? "[D]" : "",
544 	    ap->rerr.rerr_dc, length));
545 	trunc = n - (i/j);
546 	for (; i -= j >= 0; ++dp6) {
547 		ND_PRINT((ndo, " {%s}(%ld)", ip6addr_string(ndo, &dp6->u_da),
548 		    (unsigned long)EXTRACT_32BITS(&dp6->u_ds)));
549 	}
550 	if (trunc)
551 		ND_PRINT((ndo, "[|rerr]"));
552 #else
553 	ND_PRINT((ndo, " rerr %u", length));
554 #endif
555 }
556 
557 void
558 aodv_print(netdissect_options *ndo,
559            const u_char *dat, u_int length, int is_ip6)
560 {
561 	const union aodv *ap;
562 
563 	ap = (union aodv *)dat;
564 	if (ndo->ndo_snapend < dat) {
565 		ND_PRINT((ndo, " [|aodv]"));
566 		return;
567 	}
568 	if (min(length, (u_int)(ndo->ndo_snapend - dat)) < sizeof(ap->rrep_ack)) {
569 		ND_PRINT((ndo, " [|aodv]"));
570 		return;
571 	}
572 	ND_PRINT((ndo, " aodv"));
573 
574 	switch (ap->rerr.rerr_type) {
575 
576 	case AODV_RREQ:
577 		if (is_ip6)
578 			aodv_v6_rreq(ndo, ap, dat, length);
579 		else
580 			aodv_rreq(ndo, ap, dat, length);
581 		break;
582 
583 	case AODV_RREP:
584 		if (is_ip6)
585 			aodv_v6_rrep(ndo, ap, dat, length);
586 		else
587 			aodv_rrep(ndo, ap, dat, length);
588 		break;
589 
590 	case AODV_RERR:
591 		if (is_ip6)
592 			aodv_v6_rerr(ndo, ap, length);
593 		else
594 			aodv_rerr(ndo, ap, dat, length);
595 		break;
596 
597 	case AODV_RREP_ACK:
598 		ND_PRINT((ndo, " rrep-ack %u", length));
599 		break;
600 
601 	case AODV_V6_DRAFT_01_RREQ:
602 		aodv_v6_draft_01_rreq(ndo, ap, dat, length);
603 		break;
604 
605 	case AODV_V6_DRAFT_01_RREP:
606 		aodv_v6_draft_01_rrep(ndo, ap, dat, length);
607 		break;
608 
609 	case AODV_V6_DRAFT_01_RERR:
610 		aodv_v6_draft_01_rerr(ndo, ap, length);
611 		break;
612 
613 	case AODV_V6_DRAFT_01_RREP_ACK:
614 		ND_PRINT((ndo, " rrep-ack %u", length));
615 		break;
616 
617 	default:
618 		ND_PRINT((ndo, " %u %u", ap->rreq.rreq_type, length));
619 	}
620 }
621