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	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};
59struct aodv_rreq6 {
60	uint8_t		rreq_type;	/* AODV message type (1) */
61	uint8_t		rreq_flags;	/* various flags */
62	uint8_t		rreq_zero0;	/* reserved, set to zero */
63	uint8_t		rreq_hops;	/* number of hops from originator */
64	uint32_t	rreq_id;	/* request ID */
65	struct in6_addr	rreq_da;	/* destination IPv6 address */
66	uint32_t	rreq_ds;	/* destination sequence number */
67	struct in6_addr	rreq_oa;	/* originator IPv6 address */
68	uint32_t	rreq_os;	/* originator sequence number */
69};
70struct aodv_rreq6_draft_01 {
71	uint8_t		rreq_type;	/* AODV message type (16) */
72	uint8_t		rreq_flags;	/* various flags */
73	uint8_t		rreq_zero0;	/* reserved, set to zero */
74	uint8_t		rreq_hops;	/* number of hops from originator */
75	uint32_t	rreq_id;	/* request ID */
76	uint32_t	rreq_ds;	/* destination sequence number */
77	uint32_t	rreq_os;	/* originator sequence number */
78	struct in6_addr	rreq_da;	/* destination IPv6 address */
79	struct in6_addr	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	uint8_t		rrep_type;	/* AODV message type (2) */
91	uint8_t		rrep_flags;	/* various flags */
92	uint8_t		rrep_ps;	/* prefix size */
93	uint8_t		rrep_hops;	/* number of hops from o to d */
94	uint32_t	rrep_da;	/* destination IPv4 address */
95	uint32_t	rrep_ds;	/* destination sequence number */
96	uint32_t	rrep_oa;	/* originator IPv4 address */
97	uint32_t	rrep_life;	/* lifetime of this route */
98};
99struct aodv_rrep6 {
100	uint8_t		rrep_type;	/* AODV message type (2) */
101	uint8_t		rrep_flags;	/* various flags */
102	uint8_t		rrep_ps;	/* prefix size */
103	uint8_t		rrep_hops;	/* number of hops from o to d */
104	struct in6_addr	rrep_da;	/* destination IPv6 address */
105	uint32_t	rrep_ds;	/* destination sequence number */
106	struct in6_addr	rrep_oa;	/* originator IPv6 address */
107	uint32_t	rrep_life;	/* lifetime of this route */
108};
109struct aodv_rrep6_draft_01 {
110	uint8_t		rrep_type;	/* AODV message type (17) */
111	uint8_t		rrep_flags;	/* various flags */
112	uint8_t		rrep_ps;	/* prefix size */
113	uint8_t		rrep_hops;	/* number of hops from o to d */
114	uint32_t	rrep_ds;	/* destination sequence number */
115	struct in6_addr	rrep_da;	/* destination IPv6 address */
116	struct in6_addr	rrep_oa;	/* originator IPv6 address */
117	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	uint32_t	u_da;	/* IPv4 address */
127	uint32_t	u_ds;	/* sequence number */
128};
129struct rerr_unreach6 {
130	struct in6_addr	u_da;	/* IPv6 address */
131	uint32_t	u_ds;	/* sequence number */
132};
133struct rerr_unreach6_draft_01 {
134	struct in6_addr	u_da;	/* IPv6 address */
135	uint32_t	u_ds;	/* sequence number */
136};
137
138struct aodv_rerr {
139	uint8_t		rerr_type;	/* AODV message type (3 or 18) */
140	uint8_t		rerr_flags;	/* various flags */
141	uint8_t		rerr_zero0;	/* reserved, set to zero */
142	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	uint8_t		ra_type;
150	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	uint8_t		type;		/* extension type */
165	uint8_t		length;		/* extension length */
166};
167
168struct aodv_hello {
169	struct	aodv_ext	eh;		/* extension header */
170	uint8_t			interval[4];	/* 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(*ep);
184	switch (ep->type) {
185	case AODV_EXT_HELLO:
186		ah = (const struct aodv_hello *)(const void *)ep;
187		ND_TCHECK(*ah);
188		if (length < sizeof(struct aodv_hello))
189			goto trunc;
190		if (ep->length < 4) {
191			ND_PRINT((ndo, "\n\text HELLO - bad length %u", ep->length));
192			break;
193		}
194		ND_PRINT((ndo, "\n\text HELLO %ld ms",
195		    (unsigned long)EXTRACT_32BITS(&ah->interval)));
196		break;
197
198	default:
199		ND_PRINT((ndo, "\n\text %u %u", ep->type, ep->length));
200		break;
201	}
202	return;
203
204trunc:
205	ND_PRINT((ndo, " [|hello]"));
206}
207
208static void
209aodv_rreq(netdissect_options *ndo, const u_char *dat, u_int length)
210{
211	u_int i;
212	const struct aodv_rreq *ap = (const struct aodv_rreq *)dat;
213
214	ND_TCHECK(*ap);
215	if (length < sizeof(*ap))
216		goto trunc;
217	ND_PRINT((ndo, " rreq %u %s%s%s%s%shops %u id 0x%08lx\n"
218	    "\tdst %s seq %lu src %s seq %lu", length,
219	    ap->rreq_type & RREQ_JOIN ? "[J]" : "",
220	    ap->rreq_type & RREQ_REPAIR ? "[R]" : "",
221	    ap->rreq_type & RREQ_GRAT ? "[G]" : "",
222	    ap->rreq_type & RREQ_DEST ? "[D]" : "",
223	    ap->rreq_type & RREQ_UNKNOWN ? "[U] " : " ",
224	    ap->rreq_hops,
225	    (unsigned long)EXTRACT_32BITS(&ap->rreq_id),
226	    ipaddr_string(ndo, &ap->rreq_da),
227	    (unsigned long)EXTRACT_32BITS(&ap->rreq_ds),
228	    ipaddr_string(ndo, &ap->rreq_oa),
229	    (unsigned long)EXTRACT_32BITS(&ap->rreq_os)));
230	i = length - sizeof(*ap);
231	if (i >= sizeof(struct aodv_ext))
232		aodv_extension(ndo, (const struct aodv_ext *)(dat + sizeof(*ap)), i);
233	return;
234
235trunc:
236	ND_PRINT((ndo, " [|rreq"));
237}
238
239static void
240aodv_rrep(netdissect_options *ndo, const u_char *dat, u_int length)
241{
242	u_int i;
243	const struct aodv_rrep *ap = (const struct aodv_rrep *)dat;
244
245	ND_TCHECK(*ap);
246	if (length < sizeof(*ap))
247		goto trunc;
248	ND_PRINT((ndo, " rrep %u %s%sprefix %u hops %u\n"
249	    "\tdst %s dseq %lu src %s %lu ms", length,
250	    ap->rrep_type & RREP_REPAIR ? "[R]" : "",
251	    ap->rrep_type & RREP_ACK ? "[A] " : " ",
252	    ap->rrep_ps & RREP_PREFIX_MASK,
253	    ap->rrep_hops,
254	    ipaddr_string(ndo, &ap->rrep_da),
255	    (unsigned long)EXTRACT_32BITS(&ap->rrep_ds),
256	    ipaddr_string(ndo, &ap->rrep_oa),
257	    (unsigned long)EXTRACT_32BITS(&ap->rrep_life)));
258	i = length - sizeof(*ap);
259	if (i >= sizeof(struct aodv_ext))
260		aodv_extension(ndo, (const struct aodv_ext *)(dat + sizeof(*ap)), i);
261	return;
262
263trunc:
264	ND_PRINT((ndo, " [|rreq"));
265}
266
267static void
268aodv_rerr(netdissect_options *ndo, const u_char *dat, u_int length)
269{
270	u_int i, dc;
271	const struct aodv_rerr *ap = (const struct aodv_rerr *)dat;
272	const struct rerr_unreach *dp;
273
274	ND_TCHECK(*ap);
275	if (length < sizeof(*ap))
276		goto trunc;
277	ND_PRINT((ndo, " rerr %s [items %u] [%u]:",
278	    ap->rerr_flags & RERR_NODELETE ? "[D]" : "",
279	    ap->rerr_dc, length));
280	dp = (const struct rerr_unreach *)(dat + sizeof(*ap));
281	i = length - sizeof(*ap);
282	for (dc = ap->rerr_dc; dc != 0; dc--) {
283		ND_TCHECK(*dp);
284		if (i < sizeof(*dp))
285			goto trunc;
286		ND_PRINT((ndo, " {%s}(%ld)", ipaddr_string(ndo, &dp->u_da),
287		    (unsigned long)EXTRACT_32BITS(&dp->u_ds)));
288		dp++;
289		i -= sizeof(*dp);
290	}
291	return;
292
293trunc:
294	ND_PRINT((ndo, "[|rerr]"));
295}
296
297static void
298aodv_v6_rreq(netdissect_options *ndo, const u_char *dat, u_int length)
299{
300	u_int i;
301	const struct aodv_rreq6 *ap = (const struct aodv_rreq6 *)dat;
302
303	ND_TCHECK(*ap);
304	if (length < sizeof(*ap))
305		goto trunc;
306	ND_PRINT((ndo, " v6 rreq %u %s%s%s%s%shops %u id 0x%08lx\n"
307	    "\tdst %s seq %lu src %s seq %lu", length,
308	    ap->rreq_type & RREQ_JOIN ? "[J]" : "",
309	    ap->rreq_type & RREQ_REPAIR ? "[R]" : "",
310	    ap->rreq_type & RREQ_GRAT ? "[G]" : "",
311	    ap->rreq_type & RREQ_DEST ? "[D]" : "",
312	    ap->rreq_type & RREQ_UNKNOWN ? "[U] " : " ",
313	    ap->rreq_hops,
314	    (unsigned long)EXTRACT_32BITS(&ap->rreq_id),
315	    ip6addr_string(ndo, &ap->rreq_da),
316	    (unsigned long)EXTRACT_32BITS(&ap->rreq_ds),
317	    ip6addr_string(ndo, &ap->rreq_oa),
318	    (unsigned long)EXTRACT_32BITS(&ap->rreq_os)));
319	i = length - sizeof(*ap);
320	if (i >= sizeof(struct aodv_ext))
321		aodv_extension(ndo, (const struct aodv_ext *)(dat + sizeof(*ap)), i);
322	return;
323
324trunc:
325	ND_PRINT((ndo, " [|rreq"));
326}
327
328static void
329aodv_v6_rrep(netdissect_options *ndo, const u_char *dat, u_int length)
330{
331	u_int i;
332	const struct aodv_rrep6 *ap = (const struct aodv_rrep6 *)dat;
333
334	ND_TCHECK(*ap);
335	if (length < sizeof(*ap))
336		goto trunc;
337	ND_PRINT((ndo, " rrep %u %s%sprefix %u hops %u\n"
338	   "\tdst %s dseq %lu src %s %lu ms", length,
339	    ap->rrep_type & RREP_REPAIR ? "[R]" : "",
340	    ap->rrep_type & RREP_ACK ? "[A] " : " ",
341	    ap->rrep_ps & RREP_PREFIX_MASK,
342	    ap->rrep_hops,
343	    ip6addr_string(ndo, &ap->rrep_da),
344	    (unsigned long)EXTRACT_32BITS(&ap->rrep_ds),
345	    ip6addr_string(ndo, &ap->rrep_oa),
346	    (unsigned long)EXTRACT_32BITS(&ap->rrep_life)));
347	i = length - sizeof(*ap);
348	if (i >= sizeof(struct aodv_ext))
349		aodv_extension(ndo, (const struct aodv_ext *)(dat + sizeof(*ap)), i);
350	return;
351
352trunc:
353	ND_PRINT((ndo, " [|rreq"));
354}
355
356static void
357aodv_v6_rerr(netdissect_options *ndo, const u_char *dat, u_int length)
358{
359	u_int i, dc;
360	const struct aodv_rerr *ap = (const struct aodv_rerr *)dat;
361	const struct rerr_unreach6 *dp6;
362
363	ND_TCHECK(*ap);
364	if (length < sizeof(*ap))
365		goto trunc;
366	ND_PRINT((ndo, " rerr %s [items %u] [%u]:",
367	    ap->rerr_flags & RERR_NODELETE ? "[D]" : "",
368	    ap->rerr_dc, length));
369	dp6 = (const struct rerr_unreach6 *)(const void *)(ap + 1);
370	i = length - sizeof(*ap);
371	for (dc = ap->rerr_dc; dc != 0; dc--) {
372		ND_TCHECK(*dp6);
373		if (i < sizeof(*dp6))
374			goto trunc;
375		ND_PRINT((ndo, " {%s}(%ld)", ip6addr_string(ndo, &dp6->u_da),
376		    (unsigned long)EXTRACT_32BITS(&dp6->u_ds)));
377		dp6++;
378		i -= sizeof(*dp6);
379	}
380	return;
381
382trunc:
383	ND_PRINT((ndo, "[|rerr]"));
384}
385
386static void
387aodv_v6_draft_01_rreq(netdissect_options *ndo, const u_char *dat, u_int length)
388{
389	u_int i;
390	const struct aodv_rreq6_draft_01 *ap = (const struct aodv_rreq6_draft_01 *)dat;
391
392	ND_TCHECK(*ap);
393	if (length < sizeof(*ap))
394		goto trunc;
395	ND_PRINT((ndo, " rreq %u %s%s%s%s%shops %u id 0x%08lx\n"
396	    "\tdst %s seq %lu src %s seq %lu", length,
397	    ap->rreq_type & RREQ_JOIN ? "[J]" : "",
398	    ap->rreq_type & RREQ_REPAIR ? "[R]" : "",
399	    ap->rreq_type & RREQ_GRAT ? "[G]" : "",
400	    ap->rreq_type & RREQ_DEST ? "[D]" : "",
401	    ap->rreq_type & RREQ_UNKNOWN ? "[U] " : " ",
402	    ap->rreq_hops,
403	    (unsigned long)EXTRACT_32BITS(&ap->rreq_id),
404	    ip6addr_string(ndo, &ap->rreq_da),
405	    (unsigned long)EXTRACT_32BITS(&ap->rreq_ds),
406	    ip6addr_string(ndo, &ap->rreq_oa),
407	    (unsigned long)EXTRACT_32BITS(&ap->rreq_os)));
408	i = length - sizeof(*ap);
409	if (i >= sizeof(struct aodv_ext))
410		aodv_extension(ndo, (const struct aodv_ext *)(dat + sizeof(*ap)), i);
411	return;
412
413trunc:
414	ND_PRINT((ndo, " [|rreq"));
415}
416
417static void
418aodv_v6_draft_01_rrep(netdissect_options *ndo, const u_char *dat, u_int length)
419{
420	u_int i;
421	const struct aodv_rrep6_draft_01 *ap = (const struct aodv_rrep6_draft_01 *)dat;
422
423	ND_TCHECK(*ap);
424	if (length < sizeof(*ap))
425		goto trunc;
426	ND_PRINT((ndo, " rrep %u %s%sprefix %u hops %u\n"
427	   "\tdst %s dseq %lu src %s %lu ms", length,
428	    ap->rrep_type & RREP_REPAIR ? "[R]" : "",
429	    ap->rrep_type & RREP_ACK ? "[A] " : " ",
430	    ap->rrep_ps & RREP_PREFIX_MASK,
431	    ap->rrep_hops,
432	    ip6addr_string(ndo, &ap->rrep_da),
433	    (unsigned long)EXTRACT_32BITS(&ap->rrep_ds),
434	    ip6addr_string(ndo, &ap->rrep_oa),
435	    (unsigned long)EXTRACT_32BITS(&ap->rrep_life)));
436	i = length - sizeof(*ap);
437	if (i >= sizeof(struct aodv_ext))
438		aodv_extension(ndo, (const struct aodv_ext *)(dat + sizeof(*ap)), i);
439	return;
440
441trunc:
442	ND_PRINT((ndo, " [|rreq"));
443}
444
445static void
446aodv_v6_draft_01_rerr(netdissect_options *ndo, const u_char *dat, u_int length)
447{
448	u_int i, dc;
449	const struct aodv_rerr *ap = (const struct aodv_rerr *)dat;
450	const struct rerr_unreach6_draft_01 *dp6;
451
452	ND_TCHECK(*ap);
453	if (length < sizeof(*ap))
454		goto trunc;
455	ND_PRINT((ndo, " rerr %s [items %u] [%u]:",
456	    ap->rerr_flags & RERR_NODELETE ? "[D]" : "",
457	    ap->rerr_dc, length));
458	dp6 = (const struct rerr_unreach6_draft_01 *)(const void *)(ap + 1);
459	i = length - sizeof(*ap);
460	for (dc = ap->rerr_dc; dc != 0; dc--) {
461		ND_TCHECK(*dp6);
462		if (i < sizeof(*dp6))
463			goto trunc;
464		ND_PRINT((ndo, " {%s}(%ld)", ip6addr_string(ndo, &dp6->u_da),
465		    (unsigned long)EXTRACT_32BITS(&dp6->u_ds)));
466		dp6++;
467		i -= sizeof(*dp6);
468	}
469	return;
470
471trunc:
472	ND_PRINT((ndo, "[|rerr]"));
473}
474
475void
476aodv_print(netdissect_options *ndo,
477           const u_char *dat, u_int length, int is_ip6)
478{
479	uint8_t msg_type;
480
481	/*
482	 * The message type is the first byte; make sure we have it
483	 * and then fetch it.
484	 */
485	ND_TCHECK(*dat);
486	msg_type = *dat;
487	ND_PRINT((ndo, " aodv"));
488
489	switch (msg_type) {
490
491	case AODV_RREQ:
492		if (is_ip6)
493			aodv_v6_rreq(ndo, dat, length);
494		else
495			aodv_rreq(ndo, dat, length);
496		break;
497
498	case AODV_RREP:
499		if (is_ip6)
500			aodv_v6_rrep(ndo, dat, length);
501		else
502			aodv_rrep(ndo, dat, length);
503		break;
504
505	case AODV_RERR:
506		if (is_ip6)
507			aodv_v6_rerr(ndo, dat, length);
508		else
509			aodv_rerr(ndo, dat, length);
510		break;
511
512	case AODV_RREP_ACK:
513		ND_PRINT((ndo, " rrep-ack %u", length));
514		break;
515
516	case AODV_V6_DRAFT_01_RREQ:
517		aodv_v6_draft_01_rreq(ndo, dat, length);
518		break;
519
520	case AODV_V6_DRAFT_01_RREP:
521		aodv_v6_draft_01_rrep(ndo, dat, length);
522		break;
523
524	case AODV_V6_DRAFT_01_RERR:
525		aodv_v6_draft_01_rerr(ndo, dat, length);
526		break;
527
528	case AODV_V6_DRAFT_01_RREP_ACK:
529		ND_PRINT((ndo, " rrep-ack %u", length));
530		break;
531
532	default:
533		ND_PRINT((ndo, " type %u %u", msg_type, length));
534	}
535	return;
536
537trunc:
538	ND_PRINT((ndo, " [|aodv]"));
539}
540