xref: /openbsd-src/usr.sbin/tcpdump/print-nsh.c (revision 2b7cc9f7bdbb904bcb6bd8f480378653a3842200)
1*2b7cc9f7Sclaudio /*	$OpenBSD: print-nsh.c,v 1.2 2023/02/28 10:04:50 claudio Exp $ */
27da8f7c0Sdlg 
37da8f7c0Sdlg /*
47da8f7c0Sdlg  * Copyright (c) 2019 David Gwynne <dlg@openbsd.org>
57da8f7c0Sdlg  *
67da8f7c0Sdlg  * Permission to use, copy, modify, and distribute this software for any
77da8f7c0Sdlg  * purpose with or without fee is hereby granted, provided that the above
87da8f7c0Sdlg  * copyright notice and this permission notice appear in all copies.
97da8f7c0Sdlg  *
107da8f7c0Sdlg  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
117da8f7c0Sdlg  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
127da8f7c0Sdlg  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
137da8f7c0Sdlg  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
147da8f7c0Sdlg  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
157da8f7c0Sdlg  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
167da8f7c0Sdlg  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
177da8f7c0Sdlg  */
187da8f7c0Sdlg 
197da8f7c0Sdlg /*
207da8f7c0Sdlg  * RFC 8300 Network Service Header (NSH)
217da8f7c0Sdlg  */
227da8f7c0Sdlg 
237da8f7c0Sdlg #include <sys/time.h>
247da8f7c0Sdlg #include <sys/uio.h>
257da8f7c0Sdlg #include <sys/socket.h>
267da8f7c0Sdlg 
277da8f7c0Sdlg #include <netinet/in.h>
287da8f7c0Sdlg #include <netinet/ip.h>
297da8f7c0Sdlg #include <arpa/inet.h>
307da8f7c0Sdlg 
317da8f7c0Sdlg #include <stdio.h>
327da8f7c0Sdlg #include <string.h>
337da8f7c0Sdlg #include <ctype.h>
347da8f7c0Sdlg 
357da8f7c0Sdlg #include "interface.h"
367da8f7c0Sdlg #include "addrtoname.h"
377da8f7c0Sdlg #include "extract.h"
387da8f7c0Sdlg 
397da8f7c0Sdlg #ifndef roundup
407da8f7c0Sdlg #define	roundup(x, y)	((((x)+((y)-1))/(y))*(y))
417da8f7c0Sdlg #endif
427da8f7c0Sdlg 
437da8f7c0Sdlg #ifndef nitems
447da8f7c0Sdlg #define nitems(_a)	(sizeof((_a)) / sizeof((_a)[0]))
457da8f7c0Sdlg #endif
467da8f7c0Sdlg 
477da8f7c0Sdlg struct nsh_header {
487da8f7c0Sdlg 	uint32_t	base;
497da8f7c0Sdlg #define NSH_VER_SHIFT		30
507da8f7c0Sdlg #define NSH_VER_MASK		(0x03 << NSH_VER_SHIFT)
517da8f7c0Sdlg #define NSH_VER_0		0x0
527da8f7c0Sdlg #define NSH_VER_RESERVED	(0x01 << NSH_VER_SHIFT)
537da8f7c0Sdlg #define NSH_OAM_SHIFT		29
547da8f7c0Sdlg #define NSH_OAM_MASK		(0x01 << NSH_OAM_SHIFT)
557da8f7c0Sdlg #define NSH_TTL_SHIFT		22
567da8f7c0Sdlg #define NSH_TTL_MASK		(0x3f << NSH_TTL_SHIFT)
577da8f7c0Sdlg #define NSH_LEN_SHIFT		16
587da8f7c0Sdlg #define NSH_LEN_MASK		(0x3f << NSH_LEN_SHIFT)
597da8f7c0Sdlg #define NSH_LEN_FACTOR		4
607da8f7c0Sdlg #define NSH_MDTYPE_SHIFT	8
617da8f7c0Sdlg #define NSH_MDTYPE_MASK		(0x0f << NSH_MDTYPE_SHIFT)
627da8f7c0Sdlg #define NSH_PROTO_SHIFT		0
637da8f7c0Sdlg #define NSH_PROTO_MASK		(0xff << NSH_PROTO_SHIFT)
647da8f7c0Sdlg 
657da8f7c0Sdlg 	uint32_t	sp;
667da8f7c0Sdlg #define NSH_SPI_SHIFT		8
677da8f7c0Sdlg #define NSH_SPI_MASK		(0xffffff << NSH_SPI_SHIFT)
687da8f7c0Sdlg #define NSH_SI_SHIFT		0
697da8f7c0Sdlg #define NSH_SI_MASK		(0xff << NSH_SI_SHIFT)
707da8f7c0Sdlg };
717da8f7c0Sdlg 
727da8f7c0Sdlg #define NSH_PROTO_IPV4		0x01
737da8f7c0Sdlg #define NSH_PROTO_IPV6		0x02
747da8f7c0Sdlg #define NSH_PROTO_ETHERNET	0x03
757da8f7c0Sdlg #define NSH_PROTO_NSH		0x04
767da8f7c0Sdlg #define NSH_PROTO_MPLS		0x05
777da8f7c0Sdlg #define NSH_PROTO_EXP1		0xfe	/* Experiment 1 */
787da8f7c0Sdlg #define NSH_PROTO_EXP2		0xff	/* Experiment 2 */
797da8f7c0Sdlg 
807da8f7c0Sdlg #define NSH_MDTYPE_RESERVED	0x0
817da8f7c0Sdlg #define NSH_MDTYPE_1		0x1
827da8f7c0Sdlg #define NSH_MDTYPE_2		0x2
837da8f7c0Sdlg #define NSH_MDTYPE_EXP		0xf	/* Experimentation */
847da8f7c0Sdlg 
857da8f7c0Sdlg struct nsh_context_header {
867da8f7c0Sdlg 	uint32_t	ch[4];
877da8f7c0Sdlg };
887da8f7c0Sdlg 
897da8f7c0Sdlg struct nsh_md_header {
907da8f7c0Sdlg 	uint16_t	class;
917da8f7c0Sdlg 	uint8_t		type;
927da8f7c0Sdlg 	uint8_t		len;
937da8f7c0Sdlg #define NSH_MD_LEN_MASK		0x7f
947da8f7c0Sdlg };
957da8f7c0Sdlg 
967da8f7c0Sdlg static void	nsh_print_bytes(const void *, u_int);
977da8f7c0Sdlg 
987da8f7c0Sdlg static void	nsh_print_mdtype1(const u_char *, u_int);
997da8f7c0Sdlg static void	nsh_print_mdtype2(const u_char *, u_int);
1007da8f7c0Sdlg 
1017da8f7c0Sdlg void
nsh_print(const u_char * p,u_int length)1027da8f7c0Sdlg nsh_print(const u_char *p, u_int length)
1037da8f7c0Sdlg {
1047da8f7c0Sdlg 	struct nsh_header nsh;
1057da8f7c0Sdlg 	uint32_t field, len, proto;
1067da8f7c0Sdlg 	int l = snapend - p;
1077da8f7c0Sdlg 
1087da8f7c0Sdlg 	printf("NSH");
1097da8f7c0Sdlg 
1107da8f7c0Sdlg 	if (l < sizeof(nsh))
1117da8f7c0Sdlg 		goto trunc;
1127da8f7c0Sdlg 	if (length < sizeof(nsh)) {
1137da8f7c0Sdlg 		printf(" encapsulation truncated");
1147da8f7c0Sdlg 		return;
1157da8f7c0Sdlg 	}
1167da8f7c0Sdlg 
1177da8f7c0Sdlg 	nsh.base = EXTRACT_32BITS(p);
1187da8f7c0Sdlg 	nsh.sp = EXTRACT_32BITS(p + sizeof(nsh.base));
1197da8f7c0Sdlg 
1207da8f7c0Sdlg 	field = (nsh.base & NSH_VER_MASK) >> NSH_VER_SHIFT;
1217da8f7c0Sdlg 	switch (field) {
1227da8f7c0Sdlg 	case NSH_VER_0:
1237da8f7c0Sdlg 		break;
1247da8f7c0Sdlg 	case NSH_VER_RESERVED:
1257da8f7c0Sdlg 		printf(" Reserved version");
1267da8f7c0Sdlg 		return;
1277da8f7c0Sdlg 	default:
1287da8f7c0Sdlg 		printf(" Unknown version %u", field);
1297da8f7c0Sdlg 		return;
1307da8f7c0Sdlg 	}
1317da8f7c0Sdlg 
1327da8f7c0Sdlg 	field = (nsh.sp & NSH_SPI_MASK) >> NSH_SPI_SHIFT;
1337da8f7c0Sdlg 	printf(" spi %u", field);
1347da8f7c0Sdlg 	field = (nsh.sp & NSH_SI_MASK) >> NSH_SI_SHIFT;
1357da8f7c0Sdlg 	printf(" si %u", field);
1367da8f7c0Sdlg 
1377da8f7c0Sdlg 	len = ((nsh.base & NSH_LEN_MASK) >> NSH_LEN_SHIFT) * NSH_LEN_FACTOR;
1387da8f7c0Sdlg 	if (vflag > 1) {
1397da8f7c0Sdlg 		field = (nsh.base & NSH_TTL_MASK) >> NSH_TTL_SHIFT;
1407da8f7c0Sdlg 		printf(" (ttl %u, len %u)", field, len);
1417da8f7c0Sdlg 	}
1427da8f7c0Sdlg 
1437da8f7c0Sdlg 	if (l < len)
1447da8f7c0Sdlg 		goto trunc;
1457da8f7c0Sdlg 	if (length < len) {
1467da8f7c0Sdlg 		printf(" encapsulation truncated");
1477da8f7c0Sdlg 		return;
1487da8f7c0Sdlg 	}
1497da8f7c0Sdlg 
1507da8f7c0Sdlg 	p += sizeof(nsh);
1517da8f7c0Sdlg 	l -= sizeof(nsh);
1527da8f7c0Sdlg 	len -= sizeof(nsh);
1537da8f7c0Sdlg 
1547da8f7c0Sdlg 	field = (nsh.base & NSH_MDTYPE_MASK) >> NSH_MDTYPE_SHIFT;
1557da8f7c0Sdlg 	switch (field) {
1567da8f7c0Sdlg 	case NSH_MDTYPE_RESERVED:
1577da8f7c0Sdlg 		printf(" md-type-reserved");
1587da8f7c0Sdlg 		break;
1597da8f7c0Sdlg 	case NSH_MDTYPE_1:
1607da8f7c0Sdlg 		printf(" md1");
1617da8f7c0Sdlg 		if (vflag)
1627da8f7c0Sdlg 			nsh_print_mdtype1(p, len);
1637da8f7c0Sdlg 		break;
1647da8f7c0Sdlg 	case NSH_MDTYPE_2:
1657da8f7c0Sdlg 		printf(" md2");
1667da8f7c0Sdlg 		if (vflag)
1677da8f7c0Sdlg 			nsh_print_mdtype2(p, len);
1687da8f7c0Sdlg 		break;
1697da8f7c0Sdlg 	case NSH_MDTYPE_EXP:
1707da8f7c0Sdlg 		printf(" mdtype-experimentation");
1717da8f7c0Sdlg 		break;
1727da8f7c0Sdlg 	default:
1737da8f7c0Sdlg 		printf(" mdtype-unknown-0x%02x", field);
1747da8f7c0Sdlg 		break;
1757da8f7c0Sdlg 	}
1767da8f7c0Sdlg 
1777da8f7c0Sdlg 	printf("%s", vflag ? "\n    " : ": ");
1787da8f7c0Sdlg 
1797da8f7c0Sdlg 	p += len;
1807da8f7c0Sdlg 	l -= len;
1817da8f7c0Sdlg 	length -= len;
1827da8f7c0Sdlg 
1837da8f7c0Sdlg 	proto = (nsh.base & NSH_PROTO_MASK) >> NSH_PROTO_SHIFT;
1847da8f7c0Sdlg 
1857da8f7c0Sdlg 	if (nsh.base & NSH_OAM_MASK)
1867da8f7c0Sdlg 		printf("NSH OAM (proto 0x%0x, len %u)", proto, length);
1877da8f7c0Sdlg 	else {
1887da8f7c0Sdlg 		switch (field) {
1897da8f7c0Sdlg 		case NSH_PROTO_IPV4:
1907da8f7c0Sdlg 			ip_print(p, length);
1917da8f7c0Sdlg 			return;
1927da8f7c0Sdlg 		case NSH_PROTO_IPV6:
1937da8f7c0Sdlg 			ip_print(p, length);
1947da8f7c0Sdlg 			return;
1957da8f7c0Sdlg 		case NSH_PROTO_ETHERNET:
1967da8f7c0Sdlg 			ether_tryprint(p, length, 0);
1977da8f7c0Sdlg 			return;
1987da8f7c0Sdlg 		case NSH_PROTO_NSH:
1997da8f7c0Sdlg 			nsh_print(p, length);
2007da8f7c0Sdlg 			return;
2017da8f7c0Sdlg 		case NSH_PROTO_MPLS:
2027da8f7c0Sdlg 			mpls_print(p, length);
2037da8f7c0Sdlg 			return;
2047da8f7c0Sdlg 		case NSH_PROTO_EXP1:
2057da8f7c0Sdlg 			printf("NSH Experiment 1");
2067da8f7c0Sdlg 			break;
2077da8f7c0Sdlg 		case NSH_PROTO_EXP2:
2087da8f7c0Sdlg 			printf("NSH Experiment 2");
2097da8f7c0Sdlg 			break;
2107da8f7c0Sdlg 		default:
2117da8f7c0Sdlg 			printf("nsh-unknown-proto-0x%02x", field);
2127da8f7c0Sdlg 			break;
2137da8f7c0Sdlg 		}
2147da8f7c0Sdlg 	}
2157da8f7c0Sdlg 
2167da8f7c0Sdlg 	if (vflag)
217*2b7cc9f7Sclaudio 		default_print(p, l);
2187da8f7c0Sdlg 
2197da8f7c0Sdlg 	return;
2207da8f7c0Sdlg trunc:
2217da8f7c0Sdlg 	printf(" [|nsh]");
2227da8f7c0Sdlg }
2237da8f7c0Sdlg 
2247da8f7c0Sdlg static void
nsh_print_mdtype1(const u_char * p,u_int len)2257da8f7c0Sdlg nsh_print_mdtype1(const u_char *p, u_int len)
2267da8f7c0Sdlg {
2277da8f7c0Sdlg 	const struct nsh_context_header *ctx;
2287da8f7c0Sdlg 	size_t i;
2297da8f7c0Sdlg 
2307da8f7c0Sdlg 	if (len != sizeof(*ctx))
2317da8f7c0Sdlg 		printf("nsh-mdtype1-length-%u (not %zu)", len, sizeof(*ctx));
2327da8f7c0Sdlg 
2337da8f7c0Sdlg 	printf("\n\tcontext");
2347da8f7c0Sdlg 
2357da8f7c0Sdlg 	ctx = (const struct nsh_context_header *)p;
2367da8f7c0Sdlg 	for (i = 0; i < nitems(ctx->ch); i++) {
2377da8f7c0Sdlg 		printf(" ");
2387da8f7c0Sdlg 		nsh_print_bytes(&ctx->ch[i], sizeof(ctx->ch[i]));
2397da8f7c0Sdlg 	}
2407da8f7c0Sdlg }
2417da8f7c0Sdlg 
2427da8f7c0Sdlg static void
nsh_print_mdtype2(const u_char * p,u_int l)2437da8f7c0Sdlg nsh_print_mdtype2(const u_char *p, u_int l)
2447da8f7c0Sdlg {
2457da8f7c0Sdlg 	if (l == 0)
2467da8f7c0Sdlg 		return;
2477da8f7c0Sdlg 
2487da8f7c0Sdlg 	do {
2497da8f7c0Sdlg 		struct nsh_md_header h;
2507da8f7c0Sdlg 		uint8_t len;
2517da8f7c0Sdlg 
2527da8f7c0Sdlg 		if (l < sizeof(h))
2537da8f7c0Sdlg 			goto trunc;
2547da8f7c0Sdlg 
2557da8f7c0Sdlg 		memcpy(&h, p, sizeof(h));
2567da8f7c0Sdlg 		p += sizeof(h);
2577da8f7c0Sdlg 		l -= sizeof(h);
2587da8f7c0Sdlg 
2597da8f7c0Sdlg 		h.class = ntohs(h.class);
2607da8f7c0Sdlg 		len = h.len & NSH_MD_LEN_MASK;
2617da8f7c0Sdlg 		printf("\n\tmd class %u type %u", h.class, h.type);
2627da8f7c0Sdlg 		if (len > 0) {
2637da8f7c0Sdlg 			printf(" ");
2647da8f7c0Sdlg 			nsh_print_bytes(p, len);
2657da8f7c0Sdlg 		}
2667da8f7c0Sdlg 
2677da8f7c0Sdlg 		len = roundup(len, 4);
2687da8f7c0Sdlg 		if (l < len)
2697da8f7c0Sdlg 			goto trunc;
2707da8f7c0Sdlg 
2717da8f7c0Sdlg 		p += len;
2727da8f7c0Sdlg 		l -= len;
2737da8f7c0Sdlg 	} while (l > 0);
2747da8f7c0Sdlg 
2757da8f7c0Sdlg 	return;
2767da8f7c0Sdlg trunc:
2777da8f7c0Sdlg 	printf("[|nsh md]");
2787da8f7c0Sdlg }
2797da8f7c0Sdlg 
2807da8f7c0Sdlg static void
nsh_print_bytes(const void * b,u_int l)2817da8f7c0Sdlg nsh_print_bytes(const void *b, u_int l)
2827da8f7c0Sdlg {
2837da8f7c0Sdlg 	const uint8_t *p = b;
2847da8f7c0Sdlg 	u_int i;
2857da8f7c0Sdlg 
2867da8f7c0Sdlg 	for (i = 0; i < l; i++) {
2877da8f7c0Sdlg 		int ch = p[i];
2887da8f7c0Sdlg #if 0
2897da8f7c0Sdlg 		if (isprint(ch) && !isspace(ch))
2907da8f7c0Sdlg 			putchar(ch);
2917da8f7c0Sdlg 		else {
2927da8f7c0Sdlg 			switch (ch) {
2937da8f7c0Sdlg 			case '\\':
2947da8f7c0Sdlg 				printf("\\\\");
2957da8f7c0Sdlg 				break;
2967da8f7c0Sdlg 			case '\0':
2977da8f7c0Sdlg 				printf("\\0");
2987da8f7c0Sdlg 				break;
2997da8f7c0Sdlg 			default:
3007da8f7c0Sdlg 				printf("\\x%02x", ch);
3017da8f7c0Sdlg 				break;
3027da8f7c0Sdlg 			}
3037da8f7c0Sdlg 		}
3047da8f7c0Sdlg #else
3057da8f7c0Sdlg 		printf("%02x", ch);
3067da8f7c0Sdlg #endif
3077da8f7c0Sdlg 	}
3087da8f7c0Sdlg }
309