1/*	$OpenBSD: print-gre.c,v 1.6 2002/10/30 03:04:04 fgsch Exp $	*/
2
3/*
4 * Copyright (c) 2002 Jason L. Wright (jason@thought.net)
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/* \summary: Generic Routing Encapsulation (GRE) printer */
30
31/*
32 * netdissect printer for GRE - Generic Routing Encapsulation
33 * RFC1701 (GRE), RFC1702 (GRE IPv4), and RFC2637 (Enhanced GRE)
34 */
35
36#ifdef HAVE_CONFIG_H
37#include <config.h>
38#endif
39
40#include "netdissect-stdinc.h"
41
42#include "netdissect.h"
43#include "addrtostr.h"
44#include "extract.h"
45#include "ethertype.h"
46
47
48#define	GRE_CP		0x8000		/* checksum present */
49#define	GRE_RP		0x4000		/* routing present */
50#define	GRE_KP		0x2000		/* key present */
51#define	GRE_SP		0x1000		/* sequence# present */
52#define	GRE_sP		0x0800		/* source routing */
53#define	GRE_AP		0x0080		/* acknowledgment# present */
54
55static const struct tok gre_flag_values[] = {
56    { GRE_CP, "checksum present"},
57    { GRE_RP, "routing present"},
58    { GRE_KP, "key present"},
59    { GRE_SP, "sequence# present"},
60    { GRE_sP, "source routing present"},
61    { GRE_AP, "ack present"},
62    { 0, NULL }
63};
64
65#define	GRE_RECRS_MASK	0x0700		/* recursion count */
66#define	GRE_VERS_MASK	0x0007		/* protocol version */
67
68/* source route entry types */
69#define	GRESRE_IP	0x0800		/* IP */
70#define	GRESRE_ASN	0xfffe		/* ASN */
71
72static void gre_print_0(netdissect_options *, const u_char *, u_int);
73static void gre_print_1(netdissect_options *, const u_char *, u_int);
74static int gre_sre_print(netdissect_options *, uint16_t, uint8_t, uint8_t, const u_char *, u_int);
75static int gre_sre_ip_print(netdissect_options *, uint8_t, uint8_t, const u_char *, u_int);
76static int gre_sre_asn_print(netdissect_options *, uint8_t, uint8_t, const u_char *, u_int);
77
78void
79gre_print(netdissect_options *ndo, const u_char *bp, u_int length)
80{
81	u_int len = length, vers;
82
83	ndo->ndo_protocol = "gre";
84	ND_TCHECK_2(bp);
85	if (len < 2)
86		goto trunc;
87	vers = GET_BE_U_2(bp) & GRE_VERS_MASK;
88	ND_PRINT("GREv%u",vers);
89
90	switch(vers) {
91	case 0:
92		gre_print_0(ndo, bp, len);
93		break;
94	case 1:
95		gre_print_1(ndo, bp, len);
96		break;
97	default:
98		ND_PRINT(" ERROR: unknown-version");
99		break;
100	}
101	return;
102
103trunc:
104	nd_print_trunc(ndo);
105}
106
107static void
108gre_print_0(netdissect_options *ndo, const u_char *bp, u_int length)
109{
110	u_int len = length;
111	uint16_t flags, prot;
112
113	/* 16 bits ND_TCHECKed in gre_print() */
114	flags = GET_BE_U_2(bp);
115	if (ndo->ndo_vflag)
116		ND_PRINT(", Flags [%s]",
117			 bittok2str(gre_flag_values,"none",flags));
118
119	len -= 2;
120	bp += 2;
121
122	ND_TCHECK_2(bp);
123	if (len < 2)
124		goto trunc;
125	prot = GET_BE_U_2(bp);
126	len -= 2;
127	bp += 2;
128
129	if ((flags & GRE_CP) | (flags & GRE_RP)) {
130		ND_TCHECK_2(bp);
131		if (len < 2)
132			goto trunc;
133		if (ndo->ndo_vflag)
134			ND_PRINT(", sum 0x%x", GET_BE_U_2(bp));
135		bp += 2;
136		len -= 2;
137
138		ND_TCHECK_2(bp);
139		if (len < 2)
140			goto trunc;
141		ND_PRINT(", off 0x%x", GET_BE_U_2(bp));
142		bp += 2;
143		len -= 2;
144	}
145
146	if (flags & GRE_KP) {
147		ND_TCHECK_4(bp);
148		if (len < 4)
149			goto trunc;
150		ND_PRINT(", key=0x%x", GET_BE_U_4(bp));
151		bp += 4;
152		len -= 4;
153	}
154
155	if (flags & GRE_SP) {
156		ND_TCHECK_4(bp);
157		if (len < 4)
158			goto trunc;
159		ND_PRINT(", seq %u", GET_BE_U_4(bp));
160		bp += 4;
161		len -= 4;
162	}
163
164	if (flags & GRE_RP) {
165		for (;;) {
166			uint16_t af;
167			uint8_t sreoff;
168			uint8_t srelen;
169
170			ND_TCHECK_4(bp);
171			if (len < 4)
172				goto trunc;
173			af = GET_BE_U_2(bp);
174			sreoff = GET_U_1(bp + 2);
175			srelen = GET_U_1(bp + 3);
176			bp += 4;
177			len -= 4;
178
179			if (af == 0 && srelen == 0)
180				break;
181
182			if (!gre_sre_print(ndo, af, sreoff, srelen, bp, len))
183				goto trunc;
184
185			if (len < srelen)
186				goto trunc;
187			bp += srelen;
188			len -= srelen;
189		}
190	}
191
192	if (ndo->ndo_eflag)
193		ND_PRINT(", proto %s (0x%04x)",
194			 tok2str(ethertype_values,"unknown",prot), prot);
195
196	ND_PRINT(", length %u",length);
197
198	if (ndo->ndo_vflag < 1)
199		ND_PRINT(": "); /* put in a colon as protocol demarc */
200	else
201		ND_PRINT("\n\t"); /* if verbose go multiline */
202
203	switch (prot) {
204	case ETHERTYPE_IP:
205		ip_print(ndo, bp, len);
206		break;
207	case ETHERTYPE_IPV6:
208		ip6_print(ndo, bp, len);
209		break;
210	case ETHERTYPE_MPLS:
211		mpls_print(ndo, bp, len);
212		break;
213	case ETHERTYPE_IPX:
214		ipx_print(ndo, bp, len);
215		break;
216	case ETHERTYPE_ATALK:
217		atalk_print(ndo, bp, len);
218		break;
219	case ETHERTYPE_GRE_ISO:
220		isoclns_print(ndo, bp, len);
221		break;
222	case ETHERTYPE_TEB:
223		ether_print(ndo, bp, len, ND_BYTES_AVAILABLE_AFTER(bp), NULL, NULL);
224		break;
225	default:
226		ND_PRINT("gre-proto-0x%x", prot);
227	}
228	return;
229
230trunc:
231	nd_print_trunc(ndo);
232}
233
234static void
235gre_print_1(netdissect_options *ndo, const u_char *bp, u_int length)
236{
237	u_int len = length;
238	uint16_t flags, prot;
239
240	/* 16 bits ND_TCHECKed in gre_print() */
241	flags = GET_BE_U_2(bp);
242	len -= 2;
243	bp += 2;
244
245	if (ndo->ndo_vflag)
246		ND_PRINT(", Flags [%s]",
247			 bittok2str(gre_flag_values,"none",flags));
248
249	ND_TCHECK_2(bp);
250	if (len < 2)
251		goto trunc;
252	prot = GET_BE_U_2(bp);
253	len -= 2;
254	bp += 2;
255
256
257	if (flags & GRE_KP) {
258		uint32_t k;
259
260		ND_TCHECK_4(bp);
261		if (len < 4)
262			goto trunc;
263		k = GET_BE_U_4(bp);
264		ND_PRINT(", call %u", k & 0xffff);
265		len -= 4;
266		bp += 4;
267	}
268
269	if (flags & GRE_SP) {
270		ND_TCHECK_4(bp);
271		if (len < 4)
272			goto trunc;
273		ND_PRINT(", seq %u", GET_BE_U_4(bp));
274		bp += 4;
275		len -= 4;
276	}
277
278	if (flags & GRE_AP) {
279		ND_TCHECK_4(bp);
280		if (len < 4)
281			goto trunc;
282		ND_PRINT(", ack %u", GET_BE_U_4(bp));
283		bp += 4;
284		len -= 4;
285	}
286
287	if ((flags & GRE_SP) == 0)
288		ND_PRINT(", no-payload");
289
290	if (ndo->ndo_eflag)
291		ND_PRINT(", proto %s (0x%04x)",
292			 tok2str(ethertype_values,"unknown",prot), prot);
293
294	ND_PRINT(", length %u",length);
295
296	if ((flags & GRE_SP) == 0)
297		return;
298
299	if (ndo->ndo_vflag < 1)
300		ND_PRINT(": "); /* put in a colon as protocol demarc */
301	else
302		ND_PRINT("\n\t"); /* if verbose go multiline */
303
304	switch (prot) {
305	case ETHERTYPE_PPP:
306		ppp_print(ndo, bp, len);
307		break;
308	default:
309		ND_PRINT("gre-proto-0x%x", prot);
310		break;
311	}
312	return;
313
314trunc:
315	nd_print_trunc(ndo);
316}
317
318static int
319gre_sre_print(netdissect_options *ndo, uint16_t af, uint8_t sreoff,
320	      uint8_t srelen, const u_char *bp, u_int len)
321{
322	int ret;
323
324	switch (af) {
325	case GRESRE_IP:
326		ND_PRINT(", (rtaf=ip");
327		ret = gre_sre_ip_print(ndo, sreoff, srelen, bp, len);
328		ND_PRINT(")");
329		break;
330	case GRESRE_ASN:
331		ND_PRINT(", (rtaf=asn");
332		ret = gre_sre_asn_print(ndo, sreoff, srelen, bp, len);
333		ND_PRINT(")");
334		break;
335	default:
336		ND_PRINT(", (rtaf=0x%x)", af);
337		ret = 1;
338	}
339	return (ret);
340}
341
342static int
343gre_sre_ip_print(netdissect_options *ndo, uint8_t sreoff, uint8_t srelen,
344		 const u_char *bp, u_int len)
345{
346	const u_char *up = bp;
347	char buf[INET_ADDRSTRLEN];
348
349	if (sreoff & 3) {
350		ND_PRINT(", badoffset=%u", sreoff);
351		return (1);
352	}
353	if (srelen & 3) {
354		ND_PRINT(", badlength=%u", srelen);
355		return (1);
356	}
357	if (sreoff >= srelen) {
358		ND_PRINT(", badoff/len=%u/%u", sreoff, srelen);
359		return (1);
360	}
361
362	while (srelen != 0) {
363		ND_TCHECK_4(bp);
364		if (len < 4)
365			return (0);
366
367		addrtostr(bp, buf, sizeof(buf));
368		ND_PRINT(" %s%s",
369			 ((bp - up) == sreoff) ? "*" : "", buf);
370
371		bp += 4;
372		len -= 4;
373		srelen -= 4;
374	}
375	return (1);
376trunc:
377	return 0;
378}
379
380static int
381gre_sre_asn_print(netdissect_options *ndo, uint8_t sreoff, uint8_t srelen,
382		  const u_char *bp, u_int len)
383{
384	const u_char *up = bp;
385
386	if (sreoff & 1) {
387		ND_PRINT(", badoffset=%u", sreoff);
388		return (1);
389	}
390	if (srelen & 1) {
391		ND_PRINT(", badlength=%u", srelen);
392		return (1);
393	}
394	if (sreoff >= srelen) {
395		ND_PRINT(", badoff/len=%u/%u", sreoff, srelen);
396		return (1);
397	}
398
399	while (srelen != 0) {
400		ND_TCHECK_2(bp);
401		if (len < 2)
402			return (0);
403
404		ND_PRINT(" %s%x",
405			 ((bp - up) == sreoff) ? "*" : "", GET_BE_U_2(bp));
406
407		bp += 2;
408		len -= 2;
409		srelen -= 2;
410	}
411	return (1);
412trunc:
413	return 0;
414}
415