13d25ea14Schristos /* 23d25ea14Schristos * Copyright (c) 2014 VMware, Inc. All Rights Reserved. 33d25ea14Schristos * 43d25ea14Schristos * Jesse Gross <jesse@nicira.com> 53d25ea14Schristos * 63d25ea14Schristos * Redistribution and use in source and binary forms, with or without 73d25ea14Schristos * modification, are permitted provided that: (1) source code 83d25ea14Schristos * distributions retain the above copyright notice and this paragraph 93d25ea14Schristos * in its entirety, and (2) distributions including binary code include 103d25ea14Schristos * the above copyright notice and this paragraph in its entirety in 113d25ea14Schristos * the documentation or other materials provided with the distribution. 123d25ea14Schristos * THIS SOFTWARE IS PROVIDED ``AS IS'' AND 133d25ea14Schristos * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT 143d25ea14Schristos * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 153d25ea14Schristos * FOR A PARTICULAR PURPOSE. 163d25ea14Schristos */ 173d25ea14Schristos 18fdccd7e4Schristos #include <sys/cdefs.h> 19fdccd7e4Schristos #ifndef lint 20*26ba0b50Schristos __RCSID("$NetBSD: print-geneve.c,v 1.5 2024/09/02 16:15:31 christos Exp $"); 21fdccd7e4Schristos #endif 22fdccd7e4Schristos 23dc860a36Sspz /* \summary: Generic Network Virtualization Encapsulation (Geneve) printer */ 24dc860a36Sspz 25c74ad251Schristos #include <config.h> 263d25ea14Schristos 27c74ad251Schristos #include "netdissect-stdinc.h" 283d25ea14Schristos 29784088dfSchristos #include "netdissect.h" 303d25ea14Schristos #include "extract.h" 313d25ea14Schristos #include "ethertype.h" 323d25ea14Schristos 333d25ea14Schristos /* 34784088dfSchristos * Geneve header, draft-ietf-nvo3-geneve 353d25ea14Schristos * 363d25ea14Schristos * 0 1 2 3 373d25ea14Schristos * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 383d25ea14Schristos * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 393d25ea14Schristos * |Ver| Opt Len |O|C| Rsvd. | Protocol Type | 403d25ea14Schristos * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 413d25ea14Schristos * | Virtual Network Identifier (VNI) | Reserved | 423d25ea14Schristos * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 433d25ea14Schristos * | Variable Length Options | 443d25ea14Schristos * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 453d25ea14Schristos * 463d25ea14Schristos * Options: 473d25ea14Schristos * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 483d25ea14Schristos * | Option Class | Type |R|R|R| Length | 493d25ea14Schristos * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 503d25ea14Schristos * | Variable Option Data | 513d25ea14Schristos * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 523d25ea14Schristos */ 533d25ea14Schristos 543d25ea14Schristos #define VER_SHIFT 6 553d25ea14Schristos #define HDR_OPTS_LEN_MASK 0x3F 563d25ea14Schristos 573d25ea14Schristos #define FLAG_OAM (1 << 7) 583d25ea14Schristos #define FLAG_CRITICAL (1 << 6) 593d25ea14Schristos #define FLAG_R1 (1 << 5) 603d25ea14Schristos #define FLAG_R2 (1 << 4) 613d25ea14Schristos #define FLAG_R3 (1 << 3) 623d25ea14Schristos #define FLAG_R4 (1 << 2) 633d25ea14Schristos #define FLAG_R5 (1 << 1) 643d25ea14Schristos #define FLAG_R6 (1 << 0) 653d25ea14Schristos 663d25ea14Schristos #define OPT_TYPE_CRITICAL (1 << 7) 673d25ea14Schristos #define OPT_LEN_MASK 0x1F 683d25ea14Schristos 693d25ea14Schristos static const struct tok geneve_flag_values[] = { 703d25ea14Schristos { FLAG_OAM, "O" }, 713d25ea14Schristos { FLAG_CRITICAL, "C" }, 723d25ea14Schristos { FLAG_R1, "R1" }, 733d25ea14Schristos { FLAG_R2, "R2" }, 743d25ea14Schristos { FLAG_R3, "R3" }, 753d25ea14Schristos { FLAG_R4, "R4" }, 763d25ea14Schristos { FLAG_R5, "R5" }, 773d25ea14Schristos { FLAG_R6, "R6" }, 783d25ea14Schristos { 0, NULL } 793d25ea14Schristos }; 803d25ea14Schristos 813d25ea14Schristos static const char * 823d25ea14Schristos format_opt_class(uint16_t opt_class) 833d25ea14Schristos { 84784088dfSchristos switch (opt_class) { 85784088dfSchristos case 0x0100: 86784088dfSchristos return "Linux"; 87784088dfSchristos case 0x0101: 88784088dfSchristos return "Open vSwitch"; 89784088dfSchristos case 0x0102: 90784088dfSchristos return "Open Virtual Networking (OVN)"; 91784088dfSchristos case 0x0103: 92784088dfSchristos return "In-band Network Telemetry (INT)"; 93784088dfSchristos case 0x0104: 94784088dfSchristos return "VMware"; 95784088dfSchristos default: 96784088dfSchristos if (opt_class <= 0x00ff) 973d25ea14Schristos return "Standard"; 98784088dfSchristos else if (opt_class >= 0xfff0) 993d25ea14Schristos return "Experimental"; 100784088dfSchristos } 101784088dfSchristos 1023d25ea14Schristos return "Unknown"; 1033d25ea14Schristos } 1043d25ea14Schristos 1053d25ea14Schristos static void 1063d25ea14Schristos geneve_opts_print(netdissect_options *ndo, const u_char *bp, u_int len) 1073d25ea14Schristos { 1083d25ea14Schristos const char *sep = ""; 1093d25ea14Schristos 1103d25ea14Schristos while (len > 0) { 1113d25ea14Schristos uint16_t opt_class; 1123d25ea14Schristos uint8_t opt_type; 1133d25ea14Schristos uint8_t opt_len; 1143d25ea14Schristos 115c74ad251Schristos ND_PRINT("%s", sep); 1163d25ea14Schristos sep = ", "; 1173d25ea14Schristos 118c74ad251Schristos opt_class = GET_BE_U_2(bp); 119c74ad251Schristos opt_type = GET_U_1(bp + 2); 120c74ad251Schristos opt_len = 4 + ((GET_U_1(bp + 3) & OPT_LEN_MASK) * 4); 1213d25ea14Schristos 122c74ad251Schristos ND_PRINT("class %s (0x%x) type 0x%x%s len %u", 1233d25ea14Schristos format_opt_class(opt_class), opt_class, opt_type, 124c74ad251Schristos opt_type & OPT_TYPE_CRITICAL ? "(C)" : "", opt_len); 1253d25ea14Schristos 1263d25ea14Schristos if (opt_len > len) { 127c74ad251Schristos ND_PRINT(" [bad length]"); 1283d25ea14Schristos return; 1293d25ea14Schristos } 1303d25ea14Schristos 1313d25ea14Schristos if (ndo->ndo_vflag > 1 && opt_len > 4) { 132784088dfSchristos const uint32_t *data = (const uint32_t *)(bp + 4); 1333d25ea14Schristos int i; 1343d25ea14Schristos 135c74ad251Schristos ND_PRINT(" data"); 1363d25ea14Schristos 1373d25ea14Schristos for (i = 4; i < opt_len; i += 4) { 138c74ad251Schristos ND_PRINT(" %08x", GET_BE_U_4(data)); 139784088dfSchristos data++; 1403d25ea14Schristos } 1413d25ea14Schristos } 1423d25ea14Schristos 1433d25ea14Schristos bp += opt_len; 1443d25ea14Schristos len -= opt_len; 1453d25ea14Schristos } 1463d25ea14Schristos } 1473d25ea14Schristos 1483d25ea14Schristos void 1493d25ea14Schristos geneve_print(netdissect_options *ndo, const u_char *bp, u_int len) 1503d25ea14Schristos { 1513d25ea14Schristos uint8_t ver_opt; 152784088dfSchristos u_int version; 1533d25ea14Schristos uint8_t flags; 1543d25ea14Schristos uint16_t prot; 1553d25ea14Schristos uint32_t vni; 1563d25ea14Schristos uint8_t reserved; 1573d25ea14Schristos u_int opts_len; 1583d25ea14Schristos 159c74ad251Schristos ndo->ndo_protocol = "geneve"; 160c74ad251Schristos ND_PRINT("Geneve"); 1613d25ea14Schristos 162c74ad251Schristos if (len < 8) { 163c74ad251Schristos ND_PRINT(" [length %u < 8]", len); 164c74ad251Schristos nd_print_invalid(ndo); 165c74ad251Schristos return; 166c74ad251Schristos } 1673d25ea14Schristos 168c74ad251Schristos ND_TCHECK_8(bp); 169c74ad251Schristos 170c74ad251Schristos ver_opt = GET_U_1(bp); 1713d25ea14Schristos bp += 1; 1723d25ea14Schristos len -= 1; 1733d25ea14Schristos 1743d25ea14Schristos version = ver_opt >> VER_SHIFT; 1753d25ea14Schristos if (version != 0) { 176c74ad251Schristos ND_PRINT(" ERROR: unknown-version %u", version); 1773d25ea14Schristos return; 1783d25ea14Schristos } 1793d25ea14Schristos 180c74ad251Schristos flags = GET_U_1(bp); 1813d25ea14Schristos bp += 1; 1823d25ea14Schristos len -= 1; 1833d25ea14Schristos 184c74ad251Schristos prot = GET_BE_U_2(bp); 1853d25ea14Schristos bp += 2; 1863d25ea14Schristos len -= 2; 1873d25ea14Schristos 188c74ad251Schristos vni = GET_BE_U_3(bp); 1893d25ea14Schristos bp += 3; 1903d25ea14Schristos len -= 3; 1913d25ea14Schristos 192c74ad251Schristos reserved = GET_U_1(bp); 1933d25ea14Schristos bp += 1; 1943d25ea14Schristos len -= 1; 1953d25ea14Schristos 196c74ad251Schristos ND_PRINT(", Flags [%s]", 197c74ad251Schristos bittok2str_nosep(geneve_flag_values, "none", flags)); 198c74ad251Schristos ND_PRINT(", vni 0x%x", vni); 1993d25ea14Schristos 2003d25ea14Schristos if (reserved) 201c74ad251Schristos ND_PRINT(", rsvd 0x%x", reserved); 2023d25ea14Schristos 2033d25ea14Schristos if (ndo->ndo_eflag) 204c74ad251Schristos ND_PRINT(", proto %s (0x%04x)", 205c74ad251Schristos tok2str(ethertype_values, "unknown", prot), prot); 2063d25ea14Schristos 2073d25ea14Schristos opts_len = (ver_opt & HDR_OPTS_LEN_MASK) * 4; 2083d25ea14Schristos 2093d25ea14Schristos if (len < opts_len) { 210c74ad251Schristos ND_PRINT(" truncated-geneve - %u bytes missing", 211c74ad251Schristos opts_len - len); 2123d25ea14Schristos return; 2133d25ea14Schristos } 2143d25ea14Schristos 215c74ad251Schristos ND_TCHECK_LEN(bp, opts_len); 2163d25ea14Schristos 2173d25ea14Schristos if (opts_len > 0) { 218c74ad251Schristos ND_PRINT(", options ["); 2193d25ea14Schristos 2203d25ea14Schristos if (ndo->ndo_vflag) 2213d25ea14Schristos geneve_opts_print(ndo, bp, opts_len); 2223d25ea14Schristos else 223c74ad251Schristos ND_PRINT("%u bytes", opts_len); 2243d25ea14Schristos 225c74ad251Schristos ND_PRINT("]"); 2263d25ea14Schristos } 2273d25ea14Schristos 2283d25ea14Schristos bp += opts_len; 2293d25ea14Schristos len -= opts_len; 2303d25ea14Schristos 2313d25ea14Schristos if (ndo->ndo_vflag < 1) 232c74ad251Schristos ND_PRINT(": "); 2333d25ea14Schristos else 234c74ad251Schristos ND_PRINT("\n\t"); 2353d25ea14Schristos 236c74ad251Schristos if (ethertype_print(ndo, prot, bp, len, ND_BYTES_AVAILABLE_AFTER(bp), NULL, NULL) == 0) { 2373d25ea14Schristos if (prot == ETHERTYPE_TEB) 238c74ad251Schristos ether_print(ndo, bp, len, ND_BYTES_AVAILABLE_AFTER(bp), NULL, NULL); 2393d25ea14Schristos else 240c74ad251Schristos ND_PRINT("geneve-proto-0x%x", prot); 2413d25ea14Schristos } 2423d25ea14Schristos 2433d25ea14Schristos return; 2443d25ea14Schristos 2453d25ea14Schristos trunc: 246c74ad251Schristos nd_print_trunc(ndo); 2473d25ea14Schristos } 248