1 /* $OpenBSD: print-nsh.c,v 1.2 2023/02/28 10:04:50 claudio 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
nsh_print(const u_char * p,u_int length)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(p, l);
218
219 return;
220 trunc:
221 printf(" [|nsh]");
222 }
223
224 static void
nsh_print_mdtype1(const u_char * p,u_int len)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
nsh_print_mdtype2(const u_char * p,u_int l)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
nsh_print_bytes(const void * b,u_int l)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