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