1276761Sdelphij/* 2276761Sdelphij * Copyright (c) 2014 The TCPDUMP project 3276761Sdelphij * All rights reserved. 4276761Sdelphij * 5276761Sdelphij * Redistribution and use in source and binary forms, with or without 6276761Sdelphij * modification, are permitted provided that the following conditions 7276761Sdelphij * are met: 8276761Sdelphij * 1. Redistributions of source code must retain the above copyright 9276761Sdelphij * notice, this list of conditions and the following disclaimer. 10276761Sdelphij * 2. Redistributions in binary form must reproduce the above copyright 11276761Sdelphij * notice, this list of conditions and the following disclaimer in the 12276761Sdelphij * documentation and/or other materials provided with the distribution. 13276761Sdelphij * 14276761Sdelphij * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 15276761Sdelphij * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 16276761Sdelphij * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 17276761Sdelphij * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 18276761Sdelphij * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 19276761Sdelphij * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20276761Sdelphij * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21276761Sdelphij * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22276761Sdelphij * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23276761Sdelphij * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 24276761Sdelphij * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25276761Sdelphij * POSSIBILITY OF SUCH DAMAGE. 26276761Sdelphij */ 27276761Sdelphij 28313537Sglebius/* \summary: ATA over Ethernet (AoE) protocol printer */ 29313537Sglebius 30313537Sglebius/* specification: http://brantleycoilecompany.com/AoEr11.pdf */ 31313537Sglebius 32276761Sdelphij#ifdef HAVE_CONFIG_H 33276761Sdelphij#include "config.h" 34276761Sdelphij#endif 35276761Sdelphij 36313537Sglebius#include <netdissect-stdinc.h> 37276761Sdelphij 38313537Sglebius#include "netdissect.h" 39276761Sdelphij#include "extract.h" 40276761Sdelphij#include "addrtoname.h" 41276761Sdelphij#include "ether.h" 42276761Sdelphij 43276761Sdelphijstatic const char tstr[] = " [|aoe]"; 44276761Sdelphij 45276761Sdelphij#define AOE_V1 1 46276761Sdelphij#define ATA_SECTOR_SIZE 512 47276761Sdelphij 48276761Sdelphij#define AOEV1_CMD_ISSUE_ATA_COMMAND 0 49276761Sdelphij#define AOEV1_CMD_QUERY_CONFIG_INFORMATION 1 50276761Sdelphij#define AOEV1_CMD_MAC_MASK_LIST 2 51276761Sdelphij#define AOEV1_CMD_RESERVE_RELEASE 3 52276761Sdelphij 53276761Sdelphijstatic const struct tok cmdcode_str[] = { 54276761Sdelphij { AOEV1_CMD_ISSUE_ATA_COMMAND, "Issue ATA Command" }, 55276761Sdelphij { AOEV1_CMD_QUERY_CONFIG_INFORMATION, "Query Config Information" }, 56276761Sdelphij { AOEV1_CMD_MAC_MASK_LIST, "MAC Mask List" }, 57276761Sdelphij { AOEV1_CMD_RESERVE_RELEASE, "Reserve/Release" }, 58276761Sdelphij { 0, NULL } 59276761Sdelphij}; 60276761Sdelphij 61276761Sdelphij#define AOEV1_COMMON_HDR_LEN 10U /* up to but w/o Arg */ 62276761Sdelphij#define AOEV1_ISSUE_ARG_LEN 12U /* up to but w/o Data */ 63276761Sdelphij#define AOEV1_QUERY_ARG_LEN 8U /* up to but w/o Config String */ 64276761Sdelphij#define AOEV1_MAC_ARG_LEN 4U /* up to but w/o Directive 0 */ 65276761Sdelphij#define AOEV1_RESERVE_ARG_LEN 2U /* up to but w/o Ethernet address 0 */ 66276761Sdelphij#define AOEV1_MAX_CONFSTR_LEN 1024U 67276761Sdelphij 68276761Sdelphij#define AOEV1_FLAG_R 0x08 69276761Sdelphij#define AOEV1_FLAG_E 0x04 70276761Sdelphij 71276761Sdelphijstatic const struct tok aoev1_flag_str[] = { 72276761Sdelphij { AOEV1_FLAG_R, "Response" }, 73276761Sdelphij { AOEV1_FLAG_E, "Error" }, 74276761Sdelphij { 0x02, "MBZ-0x02" }, 75276761Sdelphij { 0x01, "MBZ-0x01" }, 76276761Sdelphij { 0, NULL } 77276761Sdelphij}; 78276761Sdelphij 79276761Sdelphijstatic const struct tok aoev1_errcode_str[] = { 80276761Sdelphij { 1, "Unrecognized command code" }, 81276761Sdelphij { 2, "Bad argument parameter" }, 82276761Sdelphij { 3, "Device unavailable" }, 83276761Sdelphij { 4, "Config string present" }, 84276761Sdelphij { 5, "Unsupported version" }, 85276761Sdelphij { 6, "Target is reserved" }, 86276761Sdelphij { 0, NULL } 87276761Sdelphij}; 88276761Sdelphij 89276761Sdelphij#define AOEV1_AFLAG_E 0x40 90276761Sdelphij#define AOEV1_AFLAG_D 0x10 91276761Sdelphij#define AOEV1_AFLAG_A 0x02 92276761Sdelphij#define AOEV1_AFLAG_W 0x01 93276761Sdelphij 94276761Sdelphijstatic const struct tok aoev1_aflag_str[] = { 95276761Sdelphij { 0x08, "MBZ-0x08" }, 96276761Sdelphij { AOEV1_AFLAG_E, "Ext48" }, 97276761Sdelphij { 0x06, "MBZ-0x06" }, 98276761Sdelphij { AOEV1_AFLAG_D, "Device" }, 99276761Sdelphij { 0x04, "MBZ-0x04" }, 100276761Sdelphij { 0x03, "MBZ-0x03" }, 101276761Sdelphij { AOEV1_AFLAG_A, "Async" }, 102276761Sdelphij { AOEV1_AFLAG_W, "Write" }, 103276761Sdelphij { 0, NULL } 104276761Sdelphij}; 105276761Sdelphij 106276761Sdelphijstatic const struct tok aoev1_ccmd_str[] = { 107276761Sdelphij { 0, "read config string" }, 108276761Sdelphij { 1, "test config string" }, 109276761Sdelphij { 2, "test config string prefix" }, 110276761Sdelphij { 3, "set config string" }, 111276761Sdelphij { 4, "force set config string" }, 112276761Sdelphij { 0, NULL } 113276761Sdelphij}; 114276761Sdelphij 115276761Sdelphijstatic const struct tok aoev1_mcmd_str[] = { 116276761Sdelphij { 0, "Read Mac Mask List" }, 117276761Sdelphij { 1, "Edit Mac Mask List" }, 118276761Sdelphij { 0, NULL } 119276761Sdelphij}; 120276761Sdelphij 121276761Sdelphijstatic const struct tok aoev1_merror_str[] = { 122276761Sdelphij { 1, "Unspecified Error" }, 123276761Sdelphij { 2, "Bad DCmd directive" }, 124276761Sdelphij { 3, "Mask list full" }, 125276761Sdelphij { 0, NULL } 126276761Sdelphij}; 127276761Sdelphij 128276761Sdelphijstatic const struct tok aoev1_dcmd_str[] = { 129276761Sdelphij { 0, "No Directive" }, 130276761Sdelphij { 1, "Add mac address to mask list" }, 131276761Sdelphij { 2, "Delete mac address from mask list" }, 132276761Sdelphij { 0, NULL } 133276761Sdelphij}; 134276761Sdelphij 135276761Sdelphijstatic const struct tok aoev1_rcmd_str[] = { 136276761Sdelphij { 0, "Read reserve list" }, 137276761Sdelphij { 1, "Set reserve list" }, 138276761Sdelphij { 2, "Force set reserve list" }, 139276761Sdelphij { 0, NULL } 140276761Sdelphij}; 141276761Sdelphij 142276761Sdelphijstatic void 143276761Sdelphijaoev1_issue_print(netdissect_options *ndo, 144276761Sdelphij const u_char *cp, const u_int len) 145276761Sdelphij{ 146276761Sdelphij const u_char *ep = cp + len; 147276761Sdelphij 148276761Sdelphij if (len < AOEV1_ISSUE_ARG_LEN) 149313537Sglebius goto invalid; 150276761Sdelphij /* AFlags */ 151276761Sdelphij ND_TCHECK2(*cp, 1); 152276761Sdelphij ND_PRINT((ndo, "\n\tAFlags: [%s]", bittok2str(aoev1_aflag_str, "none", *cp))); 153276761Sdelphij cp += 1; 154276761Sdelphij /* Err/Feature */ 155276761Sdelphij ND_TCHECK2(*cp, 1); 156276761Sdelphij ND_PRINT((ndo, ", Err/Feature: %u", *cp)); 157276761Sdelphij cp += 1; 158276761Sdelphij /* Sector Count (not correlated with the length) */ 159276761Sdelphij ND_TCHECK2(*cp, 1); 160276761Sdelphij ND_PRINT((ndo, ", Sector Count: %u", *cp)); 161276761Sdelphij cp += 1; 162276761Sdelphij /* Cmd/Status */ 163276761Sdelphij ND_TCHECK2(*cp, 1); 164276761Sdelphij ND_PRINT((ndo, ", Cmd/Status: %u", *cp)); 165276761Sdelphij cp += 1; 166276761Sdelphij /* lba0 */ 167276761Sdelphij ND_TCHECK2(*cp, 1); 168276761Sdelphij ND_PRINT((ndo, "\n\tlba0: %u", *cp)); 169276761Sdelphij cp += 1; 170276761Sdelphij /* lba1 */ 171276761Sdelphij ND_TCHECK2(*cp, 1); 172276761Sdelphij ND_PRINT((ndo, ", lba1: %u", *cp)); 173276761Sdelphij cp += 1; 174276761Sdelphij /* lba2 */ 175276761Sdelphij ND_TCHECK2(*cp, 1); 176276761Sdelphij ND_PRINT((ndo, ", lba2: %u", *cp)); 177276761Sdelphij cp += 1; 178276761Sdelphij /* lba3 */ 179276761Sdelphij ND_TCHECK2(*cp, 1); 180276761Sdelphij ND_PRINT((ndo, ", lba3: %u", *cp)); 181276761Sdelphij cp += 1; 182276761Sdelphij /* lba4 */ 183276761Sdelphij ND_TCHECK2(*cp, 1); 184276761Sdelphij ND_PRINT((ndo, ", lba4: %u", *cp)); 185276761Sdelphij cp += 1; 186276761Sdelphij /* lba5 */ 187276761Sdelphij ND_TCHECK2(*cp, 1); 188276761Sdelphij ND_PRINT((ndo, ", lba5: %u", *cp)); 189276761Sdelphij cp += 1; 190276761Sdelphij /* Reserved */ 191276761Sdelphij ND_TCHECK2(*cp, 2); 192276761Sdelphij cp += 2; 193276761Sdelphij /* Data */ 194276761Sdelphij if (len > AOEV1_ISSUE_ARG_LEN) 195276761Sdelphij ND_PRINT((ndo, "\n\tData: %u bytes", len - AOEV1_ISSUE_ARG_LEN)); 196276761Sdelphij return; 197276761Sdelphij 198313537Sglebiusinvalid: 199313537Sglebius ND_PRINT((ndo, "%s", istr)); 200276761Sdelphij ND_TCHECK2(*cp, ep - cp); 201276761Sdelphij return; 202276761Sdelphijtrunc: 203276761Sdelphij ND_PRINT((ndo, "%s", tstr)); 204276761Sdelphij} 205276761Sdelphij 206276761Sdelphijstatic void 207276761Sdelphijaoev1_query_print(netdissect_options *ndo, 208276761Sdelphij const u_char *cp, const u_int len) 209276761Sdelphij{ 210276761Sdelphij const u_char *ep = cp + len; 211276761Sdelphij uint16_t cslen; 212276761Sdelphij 213276761Sdelphij if (len < AOEV1_QUERY_ARG_LEN) 214313537Sglebius goto invalid; 215276761Sdelphij /* Buffer Count */ 216276761Sdelphij ND_TCHECK2(*cp, 2); 217276761Sdelphij ND_PRINT((ndo, "\n\tBuffer Count: %u", EXTRACT_16BITS(cp))); 218276761Sdelphij cp += 2; 219276761Sdelphij /* Firmware Version */ 220276761Sdelphij ND_TCHECK2(*cp, 2); 221276761Sdelphij ND_PRINT((ndo, ", Firmware Version: %u", EXTRACT_16BITS(cp))); 222276761Sdelphij cp += 2; 223276761Sdelphij /* Sector Count */ 224276761Sdelphij ND_TCHECK2(*cp, 1); 225276761Sdelphij ND_PRINT((ndo, ", Sector Count: %u", *cp)); 226276761Sdelphij cp += 1; 227276761Sdelphij /* AoE/CCmd */ 228276761Sdelphij ND_TCHECK2(*cp, 1); 229276761Sdelphij ND_PRINT((ndo, ", AoE: %u, CCmd: %s", (*cp & 0xF0) >> 4, 230276761Sdelphij tok2str(aoev1_ccmd_str, "Unknown (0x02x)", *cp & 0x0F))); 231276761Sdelphij cp += 1; 232276761Sdelphij /* Config String Length */ 233276761Sdelphij ND_TCHECK2(*cp, 2); 234276761Sdelphij cslen = EXTRACT_16BITS(cp); 235276761Sdelphij cp += 2; 236276761Sdelphij if (cslen > AOEV1_MAX_CONFSTR_LEN || AOEV1_QUERY_ARG_LEN + cslen > len) 237313537Sglebius goto invalid; 238276761Sdelphij /* Config String */ 239276761Sdelphij ND_TCHECK2(*cp, cslen); 240276761Sdelphij if (cslen) { 241276761Sdelphij ND_PRINT((ndo, "\n\tConfig String (length %u): ", cslen)); 242276761Sdelphij if (fn_printn(ndo, cp, cslen, ndo->ndo_snapend)) 243276761Sdelphij goto trunc; 244276761Sdelphij } 245276761Sdelphij return; 246276761Sdelphij 247313537Sglebiusinvalid: 248313537Sglebius ND_PRINT((ndo, "%s", istr)); 249276761Sdelphij ND_TCHECK2(*cp, ep - cp); 250276761Sdelphij return; 251276761Sdelphijtrunc: 252276761Sdelphij ND_PRINT((ndo, "%s", tstr)); 253276761Sdelphij} 254276761Sdelphij 255276761Sdelphijstatic void 256276761Sdelphijaoev1_mac_print(netdissect_options *ndo, 257276761Sdelphij const u_char *cp, const u_int len) 258276761Sdelphij{ 259276761Sdelphij const u_char *ep = cp + len; 260276761Sdelphij uint8_t dircount, i; 261276761Sdelphij 262276761Sdelphij if (len < AOEV1_MAC_ARG_LEN) 263313537Sglebius goto invalid; 264276761Sdelphij /* Reserved */ 265276761Sdelphij ND_TCHECK2(*cp, 1); 266276761Sdelphij cp += 1; 267276761Sdelphij /* MCmd */ 268276761Sdelphij ND_TCHECK2(*cp, 1); 269276761Sdelphij ND_PRINT((ndo, "\n\tMCmd: %s", tok2str(aoev1_mcmd_str, "Unknown (0x%02x)", *cp))); 270276761Sdelphij cp += 1; 271276761Sdelphij /* MError */ 272276761Sdelphij ND_TCHECK2(*cp, 1); 273276761Sdelphij ND_PRINT((ndo, ", MError: %s", tok2str(aoev1_merror_str, "Unknown (0x%02x)", *cp))); 274276761Sdelphij cp += 1; 275276761Sdelphij /* Dir Count */ 276276761Sdelphij ND_TCHECK2(*cp, 1); 277276761Sdelphij dircount = *cp; 278276761Sdelphij cp += 1; 279276761Sdelphij ND_PRINT((ndo, ", Dir Count: %u", dircount)); 280276761Sdelphij if (AOEV1_MAC_ARG_LEN + dircount * 8 > len) 281313537Sglebius goto invalid; 282276761Sdelphij /* directives */ 283276761Sdelphij for (i = 0; i < dircount; i++) { 284276761Sdelphij /* Reserved */ 285276761Sdelphij ND_TCHECK2(*cp, 1); 286276761Sdelphij cp += 1; 287276761Sdelphij /* DCmd */ 288276761Sdelphij ND_TCHECK2(*cp, 1); 289276761Sdelphij ND_PRINT((ndo, "\n\t DCmd: %s", tok2str(aoev1_dcmd_str, "Unknown (0x%02x)", *cp))); 290276761Sdelphij cp += 1; 291276761Sdelphij /* Ethernet Address */ 292276761Sdelphij ND_TCHECK2(*cp, ETHER_ADDR_LEN); 293276761Sdelphij ND_PRINT((ndo, ", Ethernet Address: %s", etheraddr_string(ndo, cp))); 294276761Sdelphij cp += ETHER_ADDR_LEN; 295276761Sdelphij } 296276761Sdelphij return; 297276761Sdelphij 298313537Sglebiusinvalid: 299313537Sglebius ND_PRINT((ndo, "%s", istr)); 300276761Sdelphij ND_TCHECK2(*cp, ep - cp); 301276761Sdelphij return; 302276761Sdelphijtrunc: 303276761Sdelphij ND_PRINT((ndo, "%s", tstr)); 304276761Sdelphij} 305276761Sdelphij 306276761Sdelphijstatic void 307276761Sdelphijaoev1_reserve_print(netdissect_options *ndo, 308276761Sdelphij const u_char *cp, const u_int len) 309276761Sdelphij{ 310276761Sdelphij const u_char *ep = cp + len; 311276761Sdelphij uint8_t nmacs, i; 312276761Sdelphij 313276761Sdelphij if (len < AOEV1_RESERVE_ARG_LEN || (len - AOEV1_RESERVE_ARG_LEN) % ETHER_ADDR_LEN) 314313537Sglebius goto invalid; 315276761Sdelphij /* RCmd */ 316276761Sdelphij ND_TCHECK2(*cp, 1); 317276761Sdelphij ND_PRINT((ndo, "\n\tRCmd: %s", tok2str(aoev1_rcmd_str, "Unknown (0x%02x)", *cp))); 318276761Sdelphij cp += 1; 319276761Sdelphij /* NMacs (correlated with the length) */ 320276761Sdelphij ND_TCHECK2(*cp, 1); 321276761Sdelphij nmacs = *cp; 322276761Sdelphij cp += 1; 323276761Sdelphij ND_PRINT((ndo, ", NMacs: %u", nmacs)); 324276761Sdelphij if (AOEV1_RESERVE_ARG_LEN + nmacs * ETHER_ADDR_LEN != len) 325313537Sglebius goto invalid; 326276761Sdelphij /* addresses */ 327276761Sdelphij for (i = 0; i < nmacs; i++) { 328356341Scy ND_TCHECK2(*cp, ETHER_ADDR_LEN); 329276761Sdelphij ND_PRINT((ndo, "\n\tEthernet Address %u: %s", i, etheraddr_string(ndo, cp))); 330276761Sdelphij cp += ETHER_ADDR_LEN; 331276761Sdelphij } 332276761Sdelphij return; 333276761Sdelphij 334313537Sglebiusinvalid: 335313537Sglebius ND_PRINT((ndo, "%s", istr)); 336276761Sdelphij ND_TCHECK2(*cp, ep - cp); 337276761Sdelphij return; 338276761Sdelphijtrunc: 339276761Sdelphij ND_PRINT((ndo, "%s", tstr)); 340276761Sdelphij} 341276761Sdelphij 342276761Sdelphij/* cp points to the Ver/Flags octet */ 343276761Sdelphijstatic void 344276761Sdelphijaoev1_print(netdissect_options *ndo, 345276761Sdelphij const u_char *cp, const u_int len) 346276761Sdelphij{ 347276761Sdelphij const u_char *ep = cp + len; 348276761Sdelphij uint8_t flags, command; 349276761Sdelphij void (*cmd_decoder)(netdissect_options *, const u_char *, const u_int); 350276761Sdelphij 351276761Sdelphij if (len < AOEV1_COMMON_HDR_LEN) 352313537Sglebius goto invalid; 353276761Sdelphij /* Flags */ 354356341Scy ND_TCHECK2(*cp, 1); 355276761Sdelphij flags = *cp & 0x0F; 356276761Sdelphij ND_PRINT((ndo, ", Flags: [%s]", bittok2str(aoev1_flag_str, "none", flags))); 357276761Sdelphij cp += 1; 358276761Sdelphij if (! ndo->ndo_vflag) 359276761Sdelphij return; 360276761Sdelphij /* Error */ 361276761Sdelphij ND_TCHECK2(*cp, 1); 362276761Sdelphij if (flags & AOEV1_FLAG_E) 363276761Sdelphij ND_PRINT((ndo, "\n\tError: %s", tok2str(aoev1_errcode_str, "Invalid (%u)", *cp))); 364276761Sdelphij cp += 1; 365276761Sdelphij /* Major */ 366276761Sdelphij ND_TCHECK2(*cp, 2); 367276761Sdelphij ND_PRINT((ndo, "\n\tMajor: 0x%04x", EXTRACT_16BITS(cp))); 368276761Sdelphij cp += 2; 369276761Sdelphij /* Minor */ 370276761Sdelphij ND_TCHECK2(*cp, 1); 371276761Sdelphij ND_PRINT((ndo, ", Minor: 0x%02x", *cp)); 372276761Sdelphij cp += 1; 373276761Sdelphij /* Command */ 374276761Sdelphij ND_TCHECK2(*cp, 1); 375276761Sdelphij command = *cp; 376276761Sdelphij cp += 1; 377276761Sdelphij ND_PRINT((ndo, ", Command: %s", tok2str(cmdcode_str, "Unknown (0x%02x)", command))); 378276761Sdelphij /* Tag */ 379276761Sdelphij ND_TCHECK2(*cp, 4); 380276761Sdelphij ND_PRINT((ndo, ", Tag: 0x%08x", EXTRACT_32BITS(cp))); 381276761Sdelphij cp += 4; 382276761Sdelphij /* Arg */ 383276761Sdelphij cmd_decoder = 384276761Sdelphij command == AOEV1_CMD_ISSUE_ATA_COMMAND ? aoev1_issue_print : 385276761Sdelphij command == AOEV1_CMD_QUERY_CONFIG_INFORMATION ? aoev1_query_print : 386276761Sdelphij command == AOEV1_CMD_MAC_MASK_LIST ? aoev1_mac_print : 387276761Sdelphij command == AOEV1_CMD_RESERVE_RELEASE ? aoev1_reserve_print : 388276761Sdelphij NULL; 389276761Sdelphij if (cmd_decoder != NULL) 390276761Sdelphij cmd_decoder(ndo, cp, len - AOEV1_COMMON_HDR_LEN); 391276761Sdelphij return; 392276761Sdelphij 393313537Sglebiusinvalid: 394313537Sglebius ND_PRINT((ndo, "%s", istr)); 395276761Sdelphij ND_TCHECK2(*cp, ep - cp); 396276761Sdelphij return; 397276761Sdelphijtrunc: 398276761Sdelphij ND_PRINT((ndo, "%s", tstr)); 399276761Sdelphij} 400276761Sdelphij 401276761Sdelphijvoid 402276761Sdelphijaoe_print(netdissect_options *ndo, 403276761Sdelphij const u_char *cp, const u_int len) 404276761Sdelphij{ 405276761Sdelphij const u_char *ep = cp + len; 406276761Sdelphij uint8_t ver; 407276761Sdelphij 408276761Sdelphij ND_PRINT((ndo, "AoE length %u", len)); 409276761Sdelphij 410276761Sdelphij if (len < 1) 411313537Sglebius goto invalid; 412276761Sdelphij /* Ver/Flags */ 413276761Sdelphij ND_TCHECK2(*cp, 1); 414276761Sdelphij ver = (*cp & 0xF0) >> 4; 415276761Sdelphij /* Don't advance cp yet: low order 4 bits are version-specific. */ 416276761Sdelphij ND_PRINT((ndo, ", Ver %u", ver)); 417276761Sdelphij 418276761Sdelphij switch (ver) { 419276761Sdelphij case AOE_V1: 420276761Sdelphij aoev1_print(ndo, cp, len); 421276761Sdelphij break; 422276761Sdelphij } 423276761Sdelphij return; 424276761Sdelphij 425313537Sglebiusinvalid: 426313537Sglebius ND_PRINT((ndo, "%s", istr)); 427276761Sdelphij ND_TCHECK2(*cp, ep - cp); 428276761Sdelphij return; 429276761Sdelphijtrunc: 430276761Sdelphij ND_PRINT((ndo, "%s", tstr)); 431276761Sdelphij} 432276761Sdelphij 433