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