1 /* 2 * Copyright (c) 2014 The TCPDUMP project 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 15 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 16 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 17 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 18 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 24 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 #ifndef lint 30 __RCSID("$NetBSD: print-aoe.c,v 1.5 2019/10/01 16:06:16 christos Exp $"); 31 #endif 32 33 /* \summary: ATA over Ethernet (AoE) protocol printer */ 34 35 /* specification: http://brantleycoilecompany.com/AoEr11.pdf */ 36 37 #ifdef HAVE_CONFIG_H 38 #include "config.h" 39 #endif 40 41 #include <netdissect-stdinc.h> 42 43 #include "netdissect.h" 44 #include "extract.h" 45 #include "addrtoname.h" 46 #include "ether.h" 47 48 static const char tstr[] = " [|aoe]"; 49 50 #define AOE_V1 1 51 #define ATA_SECTOR_SIZE 512 52 53 #define AOEV1_CMD_ISSUE_ATA_COMMAND 0 54 #define AOEV1_CMD_QUERY_CONFIG_INFORMATION 1 55 #define AOEV1_CMD_MAC_MASK_LIST 2 56 #define AOEV1_CMD_RESERVE_RELEASE 3 57 58 static const struct tok cmdcode_str[] = { 59 { AOEV1_CMD_ISSUE_ATA_COMMAND, "Issue ATA Command" }, 60 { AOEV1_CMD_QUERY_CONFIG_INFORMATION, "Query Config Information" }, 61 { AOEV1_CMD_MAC_MASK_LIST, "MAC Mask List" }, 62 { AOEV1_CMD_RESERVE_RELEASE, "Reserve/Release" }, 63 { 0, NULL } 64 }; 65 66 #define AOEV1_COMMON_HDR_LEN 10U /* up to but w/o Arg */ 67 #define AOEV1_ISSUE_ARG_LEN 12U /* up to but w/o Data */ 68 #define AOEV1_QUERY_ARG_LEN 8U /* up to but w/o Config String */ 69 #define AOEV1_MAC_ARG_LEN 4U /* up to but w/o Directive 0 */ 70 #define AOEV1_RESERVE_ARG_LEN 2U /* up to but w/o Ethernet address 0 */ 71 #define AOEV1_MAX_CONFSTR_LEN 1024U 72 73 #define AOEV1_FLAG_R 0x08 74 #define AOEV1_FLAG_E 0x04 75 76 static const struct tok aoev1_flag_str[] = { 77 { AOEV1_FLAG_R, "Response" }, 78 { AOEV1_FLAG_E, "Error" }, 79 { 0x02, "MBZ-0x02" }, 80 { 0x01, "MBZ-0x01" }, 81 { 0, NULL } 82 }; 83 84 static const struct tok aoev1_errcode_str[] = { 85 { 1, "Unrecognized command code" }, 86 { 2, "Bad argument parameter" }, 87 { 3, "Device unavailable" }, 88 { 4, "Config string present" }, 89 { 5, "Unsupported version" }, 90 { 6, "Target is reserved" }, 91 { 0, NULL } 92 }; 93 94 #define AOEV1_AFLAG_E 0x40 95 #define AOEV1_AFLAG_D 0x10 96 #define AOEV1_AFLAG_A 0x02 97 #define AOEV1_AFLAG_W 0x01 98 99 static const struct tok aoev1_aflag_str[] = { 100 { 0x08, "MBZ-0x08" }, 101 { AOEV1_AFLAG_E, "Ext48" }, 102 { 0x06, "MBZ-0x06" }, 103 { AOEV1_AFLAG_D, "Device" }, 104 { 0x04, "MBZ-0x04" }, 105 { 0x03, "MBZ-0x03" }, 106 { AOEV1_AFLAG_A, "Async" }, 107 { AOEV1_AFLAG_W, "Write" }, 108 { 0, NULL } 109 }; 110 111 static const struct tok aoev1_ccmd_str[] = { 112 { 0, "read config string" }, 113 { 1, "test config string" }, 114 { 2, "test config string prefix" }, 115 { 3, "set config string" }, 116 { 4, "force set config string" }, 117 { 0, NULL } 118 }; 119 120 static const struct tok aoev1_mcmd_str[] = { 121 { 0, "Read Mac Mask List" }, 122 { 1, "Edit Mac Mask List" }, 123 { 0, NULL } 124 }; 125 126 static const struct tok aoev1_merror_str[] = { 127 { 1, "Unspecified Error" }, 128 { 2, "Bad DCmd directive" }, 129 { 3, "Mask list full" }, 130 { 0, NULL } 131 }; 132 133 static const struct tok aoev1_dcmd_str[] = { 134 { 0, "No Directive" }, 135 { 1, "Add mac address to mask list" }, 136 { 2, "Delete mac address from mask list" }, 137 { 0, NULL } 138 }; 139 140 static const struct tok aoev1_rcmd_str[] = { 141 { 0, "Read reserve list" }, 142 { 1, "Set reserve list" }, 143 { 2, "Force set reserve list" }, 144 { 0, NULL } 145 }; 146 147 static void 148 aoev1_issue_print(netdissect_options *ndo, 149 const u_char *cp, const u_int len) 150 { 151 const u_char *ep = cp + len; 152 153 if (len < AOEV1_ISSUE_ARG_LEN) 154 goto invalid; 155 /* AFlags */ 156 ND_TCHECK2(*cp, 1); 157 ND_PRINT((ndo, "\n\tAFlags: [%s]", bittok2str(aoev1_aflag_str, "none", *cp))); 158 cp += 1; 159 /* Err/Feature */ 160 ND_TCHECK2(*cp, 1); 161 ND_PRINT((ndo, ", Err/Feature: %u", *cp)); 162 cp += 1; 163 /* Sector Count (not correlated with the length) */ 164 ND_TCHECK2(*cp, 1); 165 ND_PRINT((ndo, ", Sector Count: %u", *cp)); 166 cp += 1; 167 /* Cmd/Status */ 168 ND_TCHECK2(*cp, 1); 169 ND_PRINT((ndo, ", Cmd/Status: %u", *cp)); 170 cp += 1; 171 /* lba0 */ 172 ND_TCHECK2(*cp, 1); 173 ND_PRINT((ndo, "\n\tlba0: %u", *cp)); 174 cp += 1; 175 /* lba1 */ 176 ND_TCHECK2(*cp, 1); 177 ND_PRINT((ndo, ", lba1: %u", *cp)); 178 cp += 1; 179 /* lba2 */ 180 ND_TCHECK2(*cp, 1); 181 ND_PRINT((ndo, ", lba2: %u", *cp)); 182 cp += 1; 183 /* lba3 */ 184 ND_TCHECK2(*cp, 1); 185 ND_PRINT((ndo, ", lba3: %u", *cp)); 186 cp += 1; 187 /* lba4 */ 188 ND_TCHECK2(*cp, 1); 189 ND_PRINT((ndo, ", lba4: %u", *cp)); 190 cp += 1; 191 /* lba5 */ 192 ND_TCHECK2(*cp, 1); 193 ND_PRINT((ndo, ", lba5: %u", *cp)); 194 cp += 1; 195 /* Reserved */ 196 ND_TCHECK2(*cp, 2); 197 cp += 2; 198 /* Data */ 199 if (len > AOEV1_ISSUE_ARG_LEN) 200 ND_PRINT((ndo, "\n\tData: %u bytes", len - AOEV1_ISSUE_ARG_LEN)); 201 return; 202 203 invalid: 204 ND_PRINT((ndo, "%s", istr)); 205 ND_TCHECK2(*cp, ep - cp); 206 return; 207 trunc: 208 ND_PRINT((ndo, "%s", tstr)); 209 } 210 211 static void 212 aoev1_query_print(netdissect_options *ndo, 213 const u_char *cp, const u_int len) 214 { 215 const u_char *ep = cp + len; 216 uint16_t cslen; 217 218 if (len < AOEV1_QUERY_ARG_LEN) 219 goto invalid; 220 /* Buffer Count */ 221 ND_TCHECK2(*cp, 2); 222 ND_PRINT((ndo, "\n\tBuffer Count: %u", EXTRACT_16BITS(cp))); 223 cp += 2; 224 /* Firmware Version */ 225 ND_TCHECK2(*cp, 2); 226 ND_PRINT((ndo, ", Firmware Version: %u", EXTRACT_16BITS(cp))); 227 cp += 2; 228 /* Sector Count */ 229 ND_TCHECK2(*cp, 1); 230 ND_PRINT((ndo, ", Sector Count: %u", *cp)); 231 cp += 1; 232 /* AoE/CCmd */ 233 ND_TCHECK2(*cp, 1); 234 ND_PRINT((ndo, ", AoE: %u, CCmd: %s", (*cp & 0xF0) >> 4, 235 tok2str(aoev1_ccmd_str, "Unknown (0x02x)", *cp & 0x0F))); 236 cp += 1; 237 /* Config String Length */ 238 ND_TCHECK2(*cp, 2); 239 cslen = EXTRACT_16BITS(cp); 240 cp += 2; 241 if (cslen > AOEV1_MAX_CONFSTR_LEN || AOEV1_QUERY_ARG_LEN + cslen > len) 242 goto invalid; 243 /* Config String */ 244 ND_TCHECK2(*cp, cslen); 245 if (cslen) { 246 ND_PRINT((ndo, "\n\tConfig String (length %u): ", cslen)); 247 if (fn_printn(ndo, cp, cslen, ndo->ndo_snapend)) 248 goto trunc; 249 } 250 return; 251 252 invalid: 253 ND_PRINT((ndo, "%s", istr)); 254 ND_TCHECK2(*cp, ep - cp); 255 return; 256 trunc: 257 ND_PRINT((ndo, "%s", tstr)); 258 } 259 260 static void 261 aoev1_mac_print(netdissect_options *ndo, 262 const u_char *cp, const u_int len) 263 { 264 const u_char *ep = cp + len; 265 uint8_t dircount, i; 266 267 if (len < AOEV1_MAC_ARG_LEN) 268 goto invalid; 269 /* Reserved */ 270 ND_TCHECK2(*cp, 1); 271 cp += 1; 272 /* MCmd */ 273 ND_TCHECK2(*cp, 1); 274 ND_PRINT((ndo, "\n\tMCmd: %s", tok2str(aoev1_mcmd_str, "Unknown (0x%02x)", *cp))); 275 cp += 1; 276 /* MError */ 277 ND_TCHECK2(*cp, 1); 278 ND_PRINT((ndo, ", MError: %s", tok2str(aoev1_merror_str, "Unknown (0x%02x)", *cp))); 279 cp += 1; 280 /* Dir Count */ 281 ND_TCHECK2(*cp, 1); 282 dircount = *cp; 283 cp += 1; 284 ND_PRINT((ndo, ", Dir Count: %u", dircount)); 285 if (AOEV1_MAC_ARG_LEN + dircount * 8 > len) 286 goto invalid; 287 /* directives */ 288 for (i = 0; i < dircount; i++) { 289 /* Reserved */ 290 ND_TCHECK2(*cp, 1); 291 cp += 1; 292 /* DCmd */ 293 ND_TCHECK2(*cp, 1); 294 ND_PRINT((ndo, "\n\t DCmd: %s", tok2str(aoev1_dcmd_str, "Unknown (0x%02x)", *cp))); 295 cp += 1; 296 /* Ethernet Address */ 297 ND_TCHECK2(*cp, ETHER_ADDR_LEN); 298 ND_PRINT((ndo, ", Ethernet Address: %s", etheraddr_string(ndo, cp))); 299 cp += ETHER_ADDR_LEN; 300 } 301 return; 302 303 invalid: 304 ND_PRINT((ndo, "%s", istr)); 305 ND_TCHECK2(*cp, ep - cp); 306 return; 307 trunc: 308 ND_PRINT((ndo, "%s", tstr)); 309 } 310 311 static void 312 aoev1_reserve_print(netdissect_options *ndo, 313 const u_char *cp, const u_int len) 314 { 315 const u_char *ep = cp + len; 316 uint8_t nmacs, i; 317 318 if (len < AOEV1_RESERVE_ARG_LEN || (len - AOEV1_RESERVE_ARG_LEN) % ETHER_ADDR_LEN) 319 goto invalid; 320 /* RCmd */ 321 ND_TCHECK2(*cp, 1); 322 ND_PRINT((ndo, "\n\tRCmd: %s", tok2str(aoev1_rcmd_str, "Unknown (0x%02x)", *cp))); 323 cp += 1; 324 /* NMacs (correlated with the length) */ 325 ND_TCHECK2(*cp, 1); 326 nmacs = *cp; 327 cp += 1; 328 ND_PRINT((ndo, ", NMacs: %u", nmacs)); 329 if (AOEV1_RESERVE_ARG_LEN + nmacs * ETHER_ADDR_LEN != len) 330 goto invalid; 331 /* addresses */ 332 for (i = 0; i < nmacs; i++) { 333 ND_TCHECK2(*cp, ETHER_ADDR_LEN); 334 ND_PRINT((ndo, "\n\tEthernet Address %u: %s", i, etheraddr_string(ndo, cp))); 335 cp += ETHER_ADDR_LEN; 336 } 337 return; 338 339 invalid: 340 ND_PRINT((ndo, "%s", istr)); 341 ND_TCHECK2(*cp, ep - cp); 342 return; 343 trunc: 344 ND_PRINT((ndo, "%s", tstr)); 345 } 346 347 /* cp points to the Ver/Flags octet */ 348 static void 349 aoev1_print(netdissect_options *ndo, 350 const u_char *cp, const u_int len) 351 { 352 const u_char *ep = cp + len; 353 uint8_t flags, command; 354 void (*cmd_decoder)(netdissect_options *, const u_char *, const u_int); 355 356 if (len < AOEV1_COMMON_HDR_LEN) 357 goto invalid; 358 /* Flags */ 359 ND_TCHECK2(*cp, 1); 360 flags = *cp & 0x0F; 361 ND_PRINT((ndo, ", Flags: [%s]", bittok2str(aoev1_flag_str, "none", flags))); 362 cp += 1; 363 if (! ndo->ndo_vflag) 364 return; 365 /* Error */ 366 ND_TCHECK2(*cp, 1); 367 if (flags & AOEV1_FLAG_E) 368 ND_PRINT((ndo, "\n\tError: %s", tok2str(aoev1_errcode_str, "Invalid (%u)", *cp))); 369 cp += 1; 370 /* Major */ 371 ND_TCHECK2(*cp, 2); 372 ND_PRINT((ndo, "\n\tMajor: 0x%04x", EXTRACT_16BITS(cp))); 373 cp += 2; 374 /* Minor */ 375 ND_TCHECK2(*cp, 1); 376 ND_PRINT((ndo, ", Minor: 0x%02x", *cp)); 377 cp += 1; 378 /* Command */ 379 ND_TCHECK2(*cp, 1); 380 command = *cp; 381 cp += 1; 382 ND_PRINT((ndo, ", Command: %s", tok2str(cmdcode_str, "Unknown (0x%02x)", command))); 383 /* Tag */ 384 ND_TCHECK2(*cp, 4); 385 ND_PRINT((ndo, ", Tag: 0x%08x", EXTRACT_32BITS(cp))); 386 cp += 4; 387 /* Arg */ 388 cmd_decoder = 389 command == AOEV1_CMD_ISSUE_ATA_COMMAND ? aoev1_issue_print : 390 command == AOEV1_CMD_QUERY_CONFIG_INFORMATION ? aoev1_query_print : 391 command == AOEV1_CMD_MAC_MASK_LIST ? aoev1_mac_print : 392 command == AOEV1_CMD_RESERVE_RELEASE ? aoev1_reserve_print : 393 NULL; 394 if (cmd_decoder != NULL) 395 cmd_decoder(ndo, cp, len - AOEV1_COMMON_HDR_LEN); 396 return; 397 398 invalid: 399 ND_PRINT((ndo, "%s", istr)); 400 ND_TCHECK2(*cp, ep - cp); 401 return; 402 trunc: 403 ND_PRINT((ndo, "%s", tstr)); 404 } 405 406 void 407 aoe_print(netdissect_options *ndo, 408 const u_char *cp, const u_int len) 409 { 410 const u_char *ep = cp + len; 411 uint8_t ver; 412 413 ND_PRINT((ndo, "AoE length %u", len)); 414 415 if (len < 1) 416 goto invalid; 417 /* Ver/Flags */ 418 ND_TCHECK2(*cp, 1); 419 ver = (*cp & 0xF0) >> 4; 420 /* Don't advance cp yet: low order 4 bits are version-specific. */ 421 ND_PRINT((ndo, ", Ver %u", ver)); 422 423 switch (ver) { 424 case AOE_V1: 425 aoev1_print(ndo, cp, len); 426 break; 427 } 428 return; 429 430 invalid: 431 ND_PRINT((ndo, "%s", istr)); 432 ND_TCHECK2(*cp, ep - cp); 433 return; 434 trunc: 435 ND_PRINT((ndo, "%s", tstr)); 436 } 437 438