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/* \summary: Ad hoc On-Demand Distance Vector (AODV) Routing printer */
34
35#ifdef HAVE_CONFIG_H
36#include <config.h>
37#endif
38
39#include "netdissect-stdinc.h"
40
41#include "netdissect.h"
42#include "addrtoname.h"
43#include "extract.h"
44
45/*
46 * RFC 3561
47 */
48struct aodv_rreq {
49	nd_uint8_t	rreq_type;	/* AODV message type (1) */
50	nd_uint8_t	rreq_flags;	/* various flags */
51	nd_uint8_t	rreq_zero0;	/* reserved, set to zero */
52	nd_uint8_t	rreq_hops;	/* number of hops from originator */
53	nd_uint32_t	rreq_id;	/* request ID */
54	nd_ipv4		rreq_da;	/* destination IPv4 address */
55	nd_uint32_t	rreq_ds;	/* destination sequence number */
56	nd_ipv4		rreq_oa;	/* originator IPv4 address */
57	nd_uint32_t	rreq_os;	/* originator sequence number */
58};
59struct aodv_rreq6 {
60	nd_uint8_t	rreq_type;	/* AODV message type (1) */
61	nd_uint8_t	rreq_flags;	/* various flags */
62	nd_uint8_t	rreq_zero0;	/* reserved, set to zero */
63	nd_uint8_t	rreq_hops;	/* number of hops from originator */
64	nd_uint32_t	rreq_id;	/* request ID */
65	nd_ipv6		rreq_da;	/* destination IPv6 address */
66	nd_uint32_t	rreq_ds;	/* destination sequence number */
67	nd_ipv6		rreq_oa;	/* originator IPv6 address */
68	nd_uint32_t	rreq_os;	/* originator sequence number */
69};
70struct aodv_rreq6_draft_01 {
71	nd_uint8_t	rreq_type;	/* AODV message type (16) */
72	nd_uint8_t	rreq_flags;	/* various flags */
73	nd_uint8_t	rreq_zero0;	/* reserved, set to zero */
74	nd_uint8_t	rreq_hops;	/* number of hops from originator */
75	nd_uint32_t	rreq_id;	/* request ID */
76	nd_uint32_t	rreq_ds;	/* destination sequence number */
77	nd_uint32_t	rreq_os;	/* originator sequence number */
78	nd_ipv6		rreq_da;	/* destination IPv6 address */
79	nd_ipv6		rreq_oa;	/* originator IPv6 address */
80};
81
82#define	RREQ_JOIN	0x80		/* join (reserved for multicast */
83#define	RREQ_REPAIR	0x40		/* repair (reserved for multicast */
84#define	RREQ_GRAT	0x20		/* gratuitous RREP */
85#define	RREQ_DEST	0x10		/* destination only */
86#define	RREQ_UNKNOWN	0x08		/* unknown destination sequence num */
87#define	RREQ_FLAGS_MASK	0xF8		/* mask for rreq_flags */
88
89struct aodv_rrep {
90	nd_uint8_t	rrep_type;	/* AODV message type (2) */
91	nd_uint8_t	rrep_flags;	/* various flags */
92	nd_uint8_t	rrep_ps;	/* prefix size */
93	nd_uint8_t	rrep_hops;	/* number of hops from o to d */
94	nd_ipv4		rrep_da;	/* destination IPv4 address */
95	nd_uint32_t	rrep_ds;	/* destination sequence number */
96	nd_ipv4		rrep_oa;	/* originator IPv4 address */
97	nd_uint32_t	rrep_life;	/* lifetime of this route */
98};
99struct aodv_rrep6 {
100	nd_uint8_t	rrep_type;	/* AODV message type (2) */
101	nd_uint8_t	rrep_flags;	/* various flags */
102	nd_uint8_t	rrep_ps;	/* prefix size */
103	nd_uint8_t	rrep_hops;	/* number of hops from o to d */
104	nd_ipv6		rrep_da;	/* destination IPv6 address */
105	nd_uint32_t	rrep_ds;	/* destination sequence number */
106	nd_ipv6		rrep_oa;	/* originator IPv6 address */
107	nd_uint32_t	rrep_life;	/* lifetime of this route */
108};
109struct aodv_rrep6_draft_01 {
110	nd_uint8_t	rrep_type;	/* AODV message type (17) */
111	nd_uint8_t	rrep_flags;	/* various flags */
112	nd_uint8_t	rrep_ps;	/* prefix size */
113	nd_uint8_t	rrep_hops;	/* number of hops from o to d */
114	nd_uint32_t	rrep_ds;	/* destination sequence number */
115	nd_ipv6		rrep_da;	/* destination IPv6 address */
116	nd_ipv6		rrep_oa;	/* originator IPv6 address */
117	nd_uint32_t	rrep_life;	/* lifetime of this route */
118};
119
120#define	RREP_REPAIR		0x80	/* repair (reserved for multicast */
121#define	RREP_ACK		0x40	/* acknowledgement required */
122#define	RREP_FLAGS_MASK		0xC0	/* mask for rrep_flags */
123#define	RREP_PREFIX_MASK	0x1F	/* mask for prefix size */
124
125struct rerr_unreach {
126	nd_ipv4		u_da;	/* IPv4 address */
127	nd_uint32_t	u_ds;	/* sequence number */
128};
129struct rerr_unreach6 {
130	nd_ipv6		u_da;	/* IPv6 address */
131	nd_uint32_t	u_ds;	/* sequence number */
132};
133struct rerr_unreach6_draft_01 {
134	nd_ipv6		u_da;	/* IPv6 address */
135	nd_uint32_t	u_ds;	/* sequence number */
136};
137
138struct aodv_rerr {
139	nd_uint8_t	rerr_type;	/* AODV message type (3 or 18) */
140	nd_uint8_t	rerr_flags;	/* various flags */
141	nd_uint8_t	rerr_zero0;	/* reserved, set to zero */
142	nd_uint8_t	rerr_dc;	/* destination count */
143};
144
145#define RERR_NODELETE		0x80	/* don't delete the link */
146#define RERR_FLAGS_MASK		0x80	/* mask for rerr_flags */
147
148struct aodv_rrep_ack {
149	nd_uint8_t	ra_type;
150	nd_uint8_t	ra_zero0;
151};
152
153#define	AODV_RREQ		1	/* route request */
154#define	AODV_RREP		2	/* route response */
155#define	AODV_RERR		3	/* error report */
156#define	AODV_RREP_ACK		4	/* route response acknowledgement */
157
158#define AODV_V6_DRAFT_01_RREQ		16	/* IPv6 route request */
159#define AODV_V6_DRAFT_01_RREP		17	/* IPv6 route response */
160#define AODV_V6_DRAFT_01_RERR		18	/* IPv6 error report */
161#define AODV_V6_DRAFT_01_RREP_ACK	19	/* IPV6 route response acknowledgment */
162
163struct aodv_ext {
164	nd_uint8_t	type;		/* extension type */
165	nd_uint8_t	length;		/* extension length */
166};
167
168struct aodv_hello {
169	struct	aodv_ext	eh;		/* extension header */
170	nd_uint32_t		interval;	/* expect my next hello in
171						 * (n) ms
172						 * NOTE: this is not aligned */
173};
174
175#define	AODV_EXT_HELLO	1
176
177static void
178aodv_extension(netdissect_options *ndo,
179               const struct aodv_ext *ep, u_int length)
180{
181	const struct aodv_hello *ah;
182
183	ND_TCHECK_SIZE(ep);
184	switch (GET_U_1(ep->type)) {
185	case AODV_EXT_HELLO:
186		ah = (const struct aodv_hello *)(const void *)ep;
187		ND_TCHECK_SIZE(ah);
188		if (length < sizeof(struct aodv_hello))
189			goto trunc;
190		if (GET_U_1(ep->length) < 4) {
191			ND_PRINT("\n\text HELLO - bad length %u",
192				 GET_U_1(ep->length));
193			break;
194		}
195		ND_PRINT("\n\text HELLO %u ms",
196		    GET_BE_U_4(ah->interval));
197		break;
198
199	default:
200		ND_PRINT("\n\text %u %u", GET_U_1(ep->type),
201			 GET_U_1(ep->length));
202		break;
203	}
204	return;
205
206trunc:
207	nd_print_trunc(ndo);
208}
209
210static void
211aodv_rreq(netdissect_options *ndo, const u_char *dat, u_int length)
212{
213	u_int i;
214	const struct aodv_rreq *ap = (const struct aodv_rreq *)dat;
215
216	ND_TCHECK_SIZE(ap);
217	if (length < sizeof(*ap))
218		goto trunc;
219	ND_PRINT(" rreq %u %s%s%s%s%shops %u id 0x%08x\n"
220	    "\tdst %s seq %u src %s seq %u", length,
221	    GET_U_1(ap->rreq_type) & RREQ_JOIN ? "[J]" : "",
222	    GET_U_1(ap->rreq_type) & RREQ_REPAIR ? "[R]" : "",
223	    GET_U_1(ap->rreq_type) & RREQ_GRAT ? "[G]" : "",
224	    GET_U_1(ap->rreq_type) & RREQ_DEST ? "[D]" : "",
225	    GET_U_1(ap->rreq_type) & RREQ_UNKNOWN ? "[U] " : " ",
226	    GET_U_1(ap->rreq_hops),
227	    GET_BE_U_4(ap->rreq_id),
228	    GET_IPADDR_STRING(ap->rreq_da),
229	    GET_BE_U_4(ap->rreq_ds),
230	    GET_IPADDR_STRING(ap->rreq_oa),
231	    GET_BE_U_4(ap->rreq_os));
232	i = length - sizeof(*ap);
233	if (i >= sizeof(struct aodv_ext))
234		aodv_extension(ndo, (const struct aodv_ext *)(dat + sizeof(*ap)), i);
235	return;
236
237trunc:
238	nd_print_trunc(ndo);
239}
240
241static void
242aodv_rrep(netdissect_options *ndo, const u_char *dat, u_int length)
243{
244	u_int i;
245	const struct aodv_rrep *ap = (const struct aodv_rrep *)dat;
246
247	ND_TCHECK_SIZE(ap);
248	if (length < sizeof(*ap))
249		goto trunc;
250	ND_PRINT(" rrep %u %s%sprefix %u hops %u\n"
251	    "\tdst %s dseq %u src %s %u ms", length,
252	    GET_U_1(ap->rrep_type) & RREP_REPAIR ? "[R]" : "",
253	    GET_U_1(ap->rrep_type) & RREP_ACK ? "[A] " : " ",
254	    GET_U_1(ap->rrep_ps) & RREP_PREFIX_MASK,
255	    GET_U_1(ap->rrep_hops),
256	    GET_IPADDR_STRING(ap->rrep_da),
257	    GET_BE_U_4(ap->rrep_ds),
258	    GET_IPADDR_STRING(ap->rrep_oa),
259	    GET_BE_U_4(ap->rrep_life));
260	i = length - sizeof(*ap);
261	if (i >= sizeof(struct aodv_ext))
262		aodv_extension(ndo, (const struct aodv_ext *)(dat + sizeof(*ap)), i);
263	return;
264
265trunc:
266	nd_print_trunc(ndo);
267}
268
269static void
270aodv_rerr(netdissect_options *ndo, const u_char *dat, u_int length)
271{
272	u_int i, dc;
273	const struct aodv_rerr *ap = (const struct aodv_rerr *)dat;
274	const struct rerr_unreach *dp;
275
276	ND_TCHECK_SIZE(ap);
277	if (length < sizeof(*ap))
278		goto trunc;
279	ND_PRINT(" rerr %s [items %u] [%u]:",
280	    GET_U_1(ap->rerr_flags) & RERR_NODELETE ? "[D]" : "",
281	    GET_U_1(ap->rerr_dc), length);
282	dp = (const struct rerr_unreach *)(dat + sizeof(*ap));
283	i = length - sizeof(*ap);
284	for (dc = GET_U_1(ap->rerr_dc); dc != 0; dc--) {
285		ND_TCHECK_SIZE(dp);
286		if (i < sizeof(*dp))
287			goto trunc;
288		ND_PRINT(" {%s}(%u)", GET_IPADDR_STRING(dp->u_da),
289		    GET_BE_U_4(dp->u_ds));
290		dp++;
291		i -= sizeof(*dp);
292	}
293	return;
294
295trunc:
296	nd_print_trunc(ndo);
297}
298
299static void
300aodv_v6_rreq(netdissect_options *ndo, const u_char *dat, u_int length)
301{
302	u_int i;
303	const struct aodv_rreq6 *ap = (const struct aodv_rreq6 *)dat;
304
305	ND_TCHECK_SIZE(ap);
306	if (length < sizeof(*ap))
307		goto trunc;
308	ND_PRINT(" v6 rreq %u %s%s%s%s%shops %u id 0x%08x\n"
309	    "\tdst %s seq %u src %s seq %u", length,
310	    GET_U_1(ap->rreq_type) & RREQ_JOIN ? "[J]" : "",
311	    GET_U_1(ap->rreq_type) & RREQ_REPAIR ? "[R]" : "",
312	    GET_U_1(ap->rreq_type) & RREQ_GRAT ? "[G]" : "",
313	    GET_U_1(ap->rreq_type) & RREQ_DEST ? "[D]" : "",
314	    GET_U_1(ap->rreq_type) & RREQ_UNKNOWN ? "[U] " : " ",
315	    GET_U_1(ap->rreq_hops),
316	    GET_BE_U_4(ap->rreq_id),
317	    GET_IP6ADDR_STRING(ap->rreq_da),
318	    GET_BE_U_4(ap->rreq_ds),
319	    GET_IP6ADDR_STRING(ap->rreq_oa),
320	    GET_BE_U_4(ap->rreq_os));
321	i = length - sizeof(*ap);
322	if (i >= sizeof(struct aodv_ext))
323		aodv_extension(ndo, (const struct aodv_ext *)(dat + sizeof(*ap)), i);
324	return;
325
326trunc:
327	nd_print_trunc(ndo);
328}
329
330static void
331aodv_v6_rrep(netdissect_options *ndo, const u_char *dat, u_int length)
332{
333	u_int i;
334	const struct aodv_rrep6 *ap = (const struct aodv_rrep6 *)dat;
335
336	ND_TCHECK_SIZE(ap);
337	if (length < sizeof(*ap))
338		goto trunc;
339	ND_PRINT(" rrep %u %s%sprefix %u hops %u\n"
340	   "\tdst %s dseq %u src %s %u ms", length,
341	    GET_U_1(ap->rrep_type) & RREP_REPAIR ? "[R]" : "",
342	    GET_U_1(ap->rrep_type) & RREP_ACK ? "[A] " : " ",
343	    GET_U_1(ap->rrep_ps) & RREP_PREFIX_MASK,
344	    GET_U_1(ap->rrep_hops),
345	    GET_IP6ADDR_STRING(ap->rrep_da),
346	    GET_BE_U_4(ap->rrep_ds),
347	    GET_IP6ADDR_STRING(ap->rrep_oa),
348	    GET_BE_U_4(ap->rrep_life));
349	i = length - sizeof(*ap);
350	if (i >= sizeof(struct aodv_ext))
351		aodv_extension(ndo, (const struct aodv_ext *)(dat + sizeof(*ap)), i);
352	return;
353
354trunc:
355	nd_print_trunc(ndo);
356}
357
358static void
359aodv_v6_rerr(netdissect_options *ndo, const u_char *dat, u_int length)
360{
361	u_int i, dc;
362	const struct aodv_rerr *ap = (const struct aodv_rerr *)dat;
363	const struct rerr_unreach6 *dp6;
364
365	ND_TCHECK_SIZE(ap);
366	if (length < sizeof(*ap))
367		goto trunc;
368	ND_PRINT(" rerr %s [items %u] [%u]:",
369	    GET_U_1(ap->rerr_flags) & RERR_NODELETE ? "[D]" : "",
370	    GET_U_1(ap->rerr_dc), length);
371	dp6 = (const struct rerr_unreach6 *)(const void *)(ap + 1);
372	i = length - sizeof(*ap);
373	for (dc = GET_U_1(ap->rerr_dc); dc != 0; dc--) {
374		ND_TCHECK_SIZE(dp6);
375		if (i < sizeof(*dp6))
376			goto trunc;
377		ND_PRINT(" {%s}(%u)", GET_IP6ADDR_STRING(dp6->u_da),
378			 GET_BE_U_4(dp6->u_ds));
379		dp6++;
380		i -= sizeof(*dp6);
381	}
382	return;
383
384trunc:
385	nd_print_trunc(ndo);
386}
387
388static void
389aodv_v6_draft_01_rreq(netdissect_options *ndo, const u_char *dat, u_int length)
390{
391	u_int i;
392	const struct aodv_rreq6_draft_01 *ap = (const struct aodv_rreq6_draft_01 *)dat;
393
394	ND_TCHECK_SIZE(ap);
395	if (length < sizeof(*ap))
396		goto trunc;
397	ND_PRINT(" rreq %u %s%s%s%s%shops %u id 0x%08x\n"
398	    "\tdst %s seq %u src %s seq %u", length,
399	    GET_U_1(ap->rreq_type) & RREQ_JOIN ? "[J]" : "",
400	    GET_U_1(ap->rreq_type) & RREQ_REPAIR ? "[R]" : "",
401	    GET_U_1(ap->rreq_type) & RREQ_GRAT ? "[G]" : "",
402	    GET_U_1(ap->rreq_type) & RREQ_DEST ? "[D]" : "",
403	    GET_U_1(ap->rreq_type) & RREQ_UNKNOWN ? "[U] " : " ",
404	    GET_U_1(ap->rreq_hops),
405	    GET_BE_U_4(ap->rreq_id),
406	    GET_IP6ADDR_STRING(ap->rreq_da),
407	    GET_BE_U_4(ap->rreq_ds),
408	    GET_IP6ADDR_STRING(ap->rreq_oa),
409	    GET_BE_U_4(ap->rreq_os));
410	i = length - sizeof(*ap);
411	if (i >= sizeof(struct aodv_ext))
412		aodv_extension(ndo, (const struct aodv_ext *)(dat + sizeof(*ap)), i);
413	return;
414
415trunc:
416	nd_print_trunc(ndo);
417}
418
419static void
420aodv_v6_draft_01_rrep(netdissect_options *ndo, const u_char *dat, u_int length)
421{
422	u_int i;
423	const struct aodv_rrep6_draft_01 *ap = (const struct aodv_rrep6_draft_01 *)dat;
424
425	ND_TCHECK_SIZE(ap);
426	if (length < sizeof(*ap))
427		goto trunc;
428	ND_PRINT(" rrep %u %s%sprefix %u hops %u\n"
429	   "\tdst %s dseq %u src %s %u ms", length,
430	    GET_U_1(ap->rrep_type) & RREP_REPAIR ? "[R]" : "",
431	    GET_U_1(ap->rrep_type) & RREP_ACK ? "[A] " : " ",
432	    GET_U_1(ap->rrep_ps) & RREP_PREFIX_MASK,
433	    GET_U_1(ap->rrep_hops),
434	    GET_IP6ADDR_STRING(ap->rrep_da),
435	    GET_BE_U_4(ap->rrep_ds),
436	    GET_IP6ADDR_STRING(ap->rrep_oa),
437	    GET_BE_U_4(ap->rrep_life));
438	i = length - sizeof(*ap);
439	if (i >= sizeof(struct aodv_ext))
440		aodv_extension(ndo, (const struct aodv_ext *)(dat + sizeof(*ap)), i);
441	return;
442
443trunc:
444	nd_print_trunc(ndo);
445}
446
447static void
448aodv_v6_draft_01_rerr(netdissect_options *ndo, const u_char *dat, u_int length)
449{
450	u_int i, dc;
451	const struct aodv_rerr *ap = (const struct aodv_rerr *)dat;
452	const struct rerr_unreach6_draft_01 *dp6;
453
454	ND_TCHECK_SIZE(ap);
455	if (length < sizeof(*ap))
456		goto trunc;
457	ND_PRINT(" rerr %s [items %u] [%u]:",
458	    GET_U_1(ap->rerr_flags) & RERR_NODELETE ? "[D]" : "",
459	    GET_U_1(ap->rerr_dc), length);
460	dp6 = (const struct rerr_unreach6_draft_01 *)(const void *)(ap + 1);
461	i = length - sizeof(*ap);
462	for (dc = GET_U_1(ap->rerr_dc); dc != 0; dc--) {
463		ND_TCHECK_SIZE(dp6);
464		if (i < sizeof(*dp6))
465			goto trunc;
466		ND_PRINT(" {%s}(%u)", GET_IP6ADDR_STRING(dp6->u_da),
467			 GET_BE_U_4(dp6->u_ds));
468		dp6++;
469		i -= sizeof(*dp6);
470	}
471	return;
472
473trunc:
474	nd_print_trunc(ndo);
475}
476
477void
478aodv_print(netdissect_options *ndo,
479           const u_char *dat, u_int length, int is_ip6)
480{
481	uint8_t msg_type;
482
483	ndo->ndo_protocol = "aodv";
484	/*
485	 * The message type is the first byte; make sure we have it
486	 * and then fetch it.
487	 */
488	msg_type = GET_U_1(dat);
489	ND_PRINT(" aodv");
490
491	switch (msg_type) {
492
493	case AODV_RREQ:
494		if (is_ip6)
495			aodv_v6_rreq(ndo, dat, length);
496		else
497			aodv_rreq(ndo, dat, length);
498		break;
499
500	case AODV_RREP:
501		if (is_ip6)
502			aodv_v6_rrep(ndo, dat, length);
503		else
504			aodv_rrep(ndo, dat, length);
505		break;
506
507	case AODV_RERR:
508		if (is_ip6)
509			aodv_v6_rerr(ndo, dat, length);
510		else
511			aodv_rerr(ndo, dat, length);
512		break;
513
514	case AODV_RREP_ACK:
515		ND_PRINT(" rrep-ack %u", length);
516		break;
517
518	case AODV_V6_DRAFT_01_RREQ:
519		aodv_v6_draft_01_rreq(ndo, dat, length);
520		break;
521
522	case AODV_V6_DRAFT_01_RREP:
523		aodv_v6_draft_01_rrep(ndo, dat, length);
524		break;
525
526	case AODV_V6_DRAFT_01_RERR:
527		aodv_v6_draft_01_rerr(ndo, dat, length);
528		break;
529
530	case AODV_V6_DRAFT_01_RREP_ACK:
531		ND_PRINT(" rrep-ack %u", length);
532		break;
533
534	default:
535		ND_PRINT(" type %u %u", msg_type, length);
536	}
537}
538