xref: /openbsd/usr.sbin/tcpdump/print-nsh.c (revision 73471bf0)
1 /*	$OpenBSD: print-nsh.c,v 1.1 2019/12/03 01:43:33 dlg Exp $ */
2 
3 /*
4  * Copyright (c) 2019 David Gwynne <dlg@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /*
20  * RFC 8300 Network Service Header (NSH)
21  */
22 
23 #include <sys/time.h>
24 #include <sys/uio.h>
25 #include <sys/socket.h>
26 
27 #include <netinet/in.h>
28 #include <netinet/ip.h>
29 #include <arpa/inet.h>
30 
31 #include <stdio.h>
32 #include <string.h>
33 #include <ctype.h>
34 
35 #include "interface.h"
36 #include "addrtoname.h"
37 #include "extract.h"
38 
39 #ifndef roundup
40 #define	roundup(x, y)	((((x)+((y)-1))/(y))*(y))
41 #endif
42 
43 #ifndef nitems
44 #define nitems(_a)	(sizeof((_a)) / sizeof((_a)[0]))
45 #endif
46 
47 struct nsh_header {
48 	uint32_t	base;
49 #define NSH_VER_SHIFT		30
50 #define NSH_VER_MASK		(0x03 << NSH_VER_SHIFT)
51 #define NSH_VER_0		0x0
52 #define NSH_VER_RESERVED	(0x01 << NSH_VER_SHIFT)
53 #define NSH_OAM_SHIFT		29
54 #define NSH_OAM_MASK		(0x01 << NSH_OAM_SHIFT)
55 #define NSH_TTL_SHIFT		22
56 #define NSH_TTL_MASK		(0x3f << NSH_TTL_SHIFT)
57 #define NSH_LEN_SHIFT		16
58 #define NSH_LEN_MASK		(0x3f << NSH_LEN_SHIFT)
59 #define NSH_LEN_FACTOR		4
60 #define NSH_MDTYPE_SHIFT	8
61 #define NSH_MDTYPE_MASK		(0x0f << NSH_MDTYPE_SHIFT)
62 #define NSH_PROTO_SHIFT		0
63 #define NSH_PROTO_MASK		(0xff << NSH_PROTO_SHIFT)
64 
65 	uint32_t	sp;
66 #define NSH_SPI_SHIFT		8
67 #define NSH_SPI_MASK		(0xffffff << NSH_SPI_SHIFT)
68 #define NSH_SI_SHIFT		0
69 #define NSH_SI_MASK		(0xff << NSH_SI_SHIFT)
70 };
71 
72 #define NSH_PROTO_IPV4		0x01
73 #define NSH_PROTO_IPV6		0x02
74 #define NSH_PROTO_ETHERNET	0x03
75 #define NSH_PROTO_NSH		0x04
76 #define NSH_PROTO_MPLS		0x05
77 #define NSH_PROTO_EXP1		0xfe	/* Experiment 1 */
78 #define NSH_PROTO_EXP2		0xff	/* Experiment 2 */
79 
80 #define NSH_MDTYPE_RESERVED	0x0
81 #define NSH_MDTYPE_1		0x1
82 #define NSH_MDTYPE_2		0x2
83 #define NSH_MDTYPE_EXP		0xf	/* Experimentation */
84 
85 struct nsh_context_header {
86 	uint32_t	ch[4];
87 };
88 
89 struct nsh_md_header {
90 	uint16_t	class;
91 	uint8_t		type;
92 	uint8_t		len;
93 #define NSH_MD_LEN_MASK		0x7f
94 };
95 
96 static void	nsh_print_bytes(const void *, u_int);
97 
98 static void	nsh_print_mdtype1(const u_char *, u_int);
99 static void	nsh_print_mdtype2(const u_char *, u_int);
100 
101 void
102 nsh_print(const u_char *p, u_int length)
103 {
104 	struct nsh_header nsh;
105 	uint32_t field, len, proto;
106 	int l = snapend - p;
107 
108 	printf("NSH");
109 
110 	if (l < sizeof(nsh))
111 		goto trunc;
112 	if (length < sizeof(nsh)) {
113 		printf(" encapsulation truncated");
114 		return;
115 	}
116 
117 	nsh.base = EXTRACT_32BITS(p);
118 	nsh.sp = EXTRACT_32BITS(p + sizeof(nsh.base));
119 
120 	field = (nsh.base & NSH_VER_MASK) >> NSH_VER_SHIFT;
121 	switch (field) {
122 	case NSH_VER_0:
123 		break;
124 	case NSH_VER_RESERVED:
125 		printf(" Reserved version");
126 		return;
127 	default:
128 		printf(" Unknown version %u", field);
129 		return;
130 	}
131 
132 	field = (nsh.sp & NSH_SPI_MASK) >> NSH_SPI_SHIFT;
133 	printf(" spi %u", field);
134 	field = (nsh.sp & NSH_SI_MASK) >> NSH_SI_SHIFT;
135 	printf(" si %u", field);
136 
137 	len = ((nsh.base & NSH_LEN_MASK) >> NSH_LEN_SHIFT) * NSH_LEN_FACTOR;
138 	if (vflag > 1) {
139 		field = (nsh.base & NSH_TTL_MASK) >> NSH_TTL_SHIFT;
140 		printf(" (ttl %u, len %u)", field, len);
141 	}
142 
143 	if (l < len)
144 		goto trunc;
145 	if (length < len) {
146 		printf(" encapsulation truncated");
147 		return;
148 	}
149 
150 	p += sizeof(nsh);
151 	l -= sizeof(nsh);
152 	len -= sizeof(nsh);
153 
154 	field = (nsh.base & NSH_MDTYPE_MASK) >> NSH_MDTYPE_SHIFT;
155 	switch (field) {
156 	case NSH_MDTYPE_RESERVED:
157 		printf(" md-type-reserved");
158 		break;
159 	case NSH_MDTYPE_1:
160 		printf(" md1");
161 		if (vflag)
162 			nsh_print_mdtype1(p, len);
163 		break;
164 	case NSH_MDTYPE_2:
165 		printf(" md2");
166 		if (vflag)
167 			nsh_print_mdtype2(p, len);
168 		break;
169 	case NSH_MDTYPE_EXP:
170 		printf(" mdtype-experimentation");
171 		break;
172 	default:
173 		printf(" mdtype-unknown-0x%02x", field);
174 		break;
175 	}
176 
177 	printf("%s", vflag ? "\n    " : ": ");
178 
179 	p += len;
180 	l -= len;
181 	length -= len;
182 
183 	proto = (nsh.base & NSH_PROTO_MASK) >> NSH_PROTO_SHIFT;
184 
185 	if (nsh.base & NSH_OAM_MASK)
186 		printf("NSH OAM (proto 0x%0x, len %u)", proto, length);
187 	else {
188 		switch (field) {
189 		case NSH_PROTO_IPV4:
190 			ip_print(p, length);
191 			return;
192 		case NSH_PROTO_IPV6:
193 			ip_print(p, length);
194 			return;
195 		case NSH_PROTO_ETHERNET:
196 			ether_tryprint(p, length, 0);
197 			return;
198 		case NSH_PROTO_NSH:
199 			nsh_print(p, length);
200 			return;
201 		case NSH_PROTO_MPLS:
202 			mpls_print(p, length);
203 			return;
204 		case NSH_PROTO_EXP1:
205 			printf("NSH Experiment 1");
206 			break;
207 		case NSH_PROTO_EXP2:
208 			printf("NSH Experiment 2");
209 			break;
210 		default:
211 			printf("nsh-unknown-proto-0x%02x", field);
212 			break;
213 		}
214 	}
215 
216 	if (vflag)
217 		default_print_unaligned(p, length);
218 
219 	return;
220 trunc:
221 	printf(" [|nsh]");
222 }
223 
224 static void
225 nsh_print_mdtype1(const u_char *p, u_int len)
226 {
227 	const struct nsh_context_header *ctx;
228 	size_t i;
229 
230 	if (len != sizeof(*ctx))
231 		printf("nsh-mdtype1-length-%u (not %zu)", len, sizeof(*ctx));
232 
233 	printf("\n\tcontext");
234 
235 	ctx = (const struct nsh_context_header *)p;
236 	for (i = 0; i < nitems(ctx->ch); i++) {
237 		printf(" ");
238 		nsh_print_bytes(&ctx->ch[i], sizeof(ctx->ch[i]));
239 	}
240 }
241 
242 static void
243 nsh_print_mdtype2(const u_char *p, u_int l)
244 {
245 	if (l == 0)
246 		return;
247 
248 	do {
249 		struct nsh_md_header h;
250 		uint8_t len;
251 
252 		if (l < sizeof(h))
253 			goto trunc;
254 
255 		memcpy(&h, p, sizeof(h));
256 		p += sizeof(h);
257 		l -= sizeof(h);
258 
259 		h.class = ntohs(h.class);
260 		len = h.len & NSH_MD_LEN_MASK;
261 		printf("\n\tmd class %u type %u", h.class, h.type);
262 		if (len > 0) {
263 			printf(" ");
264 			nsh_print_bytes(p, len);
265 		}
266 
267 		len = roundup(len, 4);
268 		if (l < len)
269 			goto trunc;
270 
271 		p += len;
272 		l -= len;
273 	} while (l > 0);
274 
275 	return;
276 trunc:
277 	printf("[|nsh md]");
278 }
279 
280 static void
281 nsh_print_bytes(const void *b, u_int l)
282 {
283 	const uint8_t *p = b;
284 	u_int i;
285 
286 	for (i = 0; i < l; i++) {
287 		int ch = p[i];
288 #if 0
289 		if (isprint(ch) && !isspace(ch))
290 			putchar(ch);
291 		else {
292 			switch (ch) {
293 			case '\\':
294 				printf("\\\\");
295 				break;
296 			case '\0':
297 				printf("\\0");
298 				break;
299 			default:
300 				printf("\\x%02x", ch);
301 				break;
302 			}
303 		}
304 #else
305 		printf("%02x", ch);
306 #endif
307 	}
308 }
309