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 /* \summary: ATA over Ethernet (AoE) protocol printer */ 29 30 /* specification: 31 * https://web.archive.org/web/20161025044402/http://brantleycoilecompany.com/AoEr11.pdf 32 */ 33 34 #include <config.h> 35 36 #include "netdissect-stdinc.h" 37 38 #define ND_LONGJMP_FROM_TCHECK 39 #include "netdissect.h" 40 #include "extract.h" 41 #include "addrtoname.h" 42 43 44 #define AOE_V1 1 45 #define ATA_SECTOR_SIZE 512 46 47 #define AOEV1_CMD_ISSUE_ATA_COMMAND 0 48 #define AOEV1_CMD_QUERY_CONFIG_INFORMATION 1 49 #define AOEV1_CMD_MAC_MASK_LIST 2 50 #define AOEV1_CMD_RESERVE_RELEASE 3 51 52 static const struct tok cmdcode_str[] = { 53 { AOEV1_CMD_ISSUE_ATA_COMMAND, "Issue ATA Command" }, 54 { AOEV1_CMD_QUERY_CONFIG_INFORMATION, "Query Config Information" }, 55 { AOEV1_CMD_MAC_MASK_LIST, "MAC Mask List" }, 56 { AOEV1_CMD_RESERVE_RELEASE, "Reserve/Release" }, 57 { 0, NULL } 58 }; 59 60 #define AOEV1_COMMON_HDR_LEN 10U /* up to but w/o Arg */ 61 #define AOEV1_ISSUE_ARG_LEN 12U /* up to but w/o Data */ 62 #define AOEV1_QUERY_ARG_LEN 8U /* up to but w/o Config String */ 63 #define AOEV1_MAC_ARG_LEN 4U /* up to but w/o Directive 0 */ 64 #define AOEV1_RESERVE_ARG_LEN 2U /* up to but w/o Ethernet address 0 */ 65 #define AOEV1_MAX_CONFSTR_LEN 1024U 66 67 #define AOEV1_FLAG_R 0x08 68 #define AOEV1_FLAG_E 0x04 69 70 static const struct tok aoev1_flag_str[] = { 71 { AOEV1_FLAG_R, "Response" }, 72 { AOEV1_FLAG_E, "Error" }, 73 { 0x02, "MBZ-1" }, 74 { 0x01, "MBZ-0" }, 75 { 0, NULL } 76 }; 77 78 static const struct tok aoev1_errcode_str[] = { 79 { 1, "Unrecognized command code" }, 80 { 2, "Bad argument parameter" }, 81 { 3, "Device unavailable" }, 82 { 4, "Config string present" }, 83 { 5, "Unsupported version" }, 84 { 6, "Target is reserved" }, 85 { 0, NULL } 86 }; 87 88 #define AOEV1_AFLAG_E 0x40 89 #define AOEV1_AFLAG_D 0x10 90 #define AOEV1_AFLAG_A 0x02 91 #define AOEV1_AFLAG_W 0x01 92 93 static const struct tok aoev1_aflag_bitmap_str[] = { 94 { 0x80, "MBZ-7" }, 95 { AOEV1_AFLAG_E, "Ext48" }, 96 { 0x20, "MBZ-5" }, 97 { AOEV1_AFLAG_D, "Device" }, 98 { 0x08, "MBZ-3" }, 99 { 0x04, "MBZ-2" }, 100 { AOEV1_AFLAG_A, "Async" }, 101 { AOEV1_AFLAG_W, "Write" }, 102 { 0, NULL } 103 }; 104 105 static const struct tok aoev1_ccmd_str[] = { 106 { 0, "read config string" }, 107 { 1, "test config string" }, 108 { 2, "test config string prefix" }, 109 { 3, "set config string" }, 110 { 4, "force set config string" }, 111 { 0, NULL } 112 }; 113 114 static const struct tok aoev1_mcmd_str[] = { 115 { 0, "Read Mac Mask List" }, 116 { 1, "Edit Mac Mask List" }, 117 { 0, NULL } 118 }; 119 120 static const struct tok aoev1_merror_str[] = { 121 { 1, "Unspecified Error" }, 122 { 2, "Bad DCmd directive" }, 123 { 3, "Mask list full" }, 124 { 0, NULL } 125 }; 126 127 static const struct tok aoev1_dcmd_str[] = { 128 { 0, "No Directive" }, 129 { 1, "Add mac address to mask list" }, 130 { 2, "Delete mac address from mask list" }, 131 { 0, NULL } 132 }; 133 134 static const struct tok aoev1_rcmd_str[] = { 135 { 0, "Read reserve list" }, 136 { 1, "Set reserve list" }, 137 { 2, "Force set reserve list" }, 138 { 0, NULL } 139 }; 140 141 static void 142 aoev1_issue_print(netdissect_options *ndo, 143 const u_char *cp, u_int len) 144 { 145 if (len < AOEV1_ISSUE_ARG_LEN) 146 goto invalid; 147 /* AFlags */ 148 ND_PRINT("\n\tAFlags: [%s]", 149 bittok2str(aoev1_aflag_bitmap_str, "none", GET_U_1(cp))); 150 cp += 1; 151 len -= 1; 152 /* Err/Feature */ 153 ND_PRINT(", Err/Feature: %u", GET_U_1(cp)); 154 cp += 1; 155 len -= 1; 156 /* Sector Count (not correlated with the length) */ 157 ND_PRINT(", Sector Count: %u", GET_U_1(cp)); 158 cp += 1; 159 len -= 1; 160 /* Cmd/Status */ 161 ND_PRINT(", Cmd/Status: %u", GET_U_1(cp)); 162 cp += 1; 163 len -= 1; 164 /* lba0 */ 165 ND_PRINT("\n\tlba0: %u", GET_U_1(cp)); 166 cp += 1; 167 len -= 1; 168 /* lba1 */ 169 ND_PRINT(", lba1: %u", GET_U_1(cp)); 170 cp += 1; 171 len -= 1; 172 /* lba2 */ 173 ND_PRINT(", lba2: %u", GET_U_1(cp)); 174 cp += 1; 175 len -= 1; 176 /* lba3 */ 177 ND_PRINT(", lba3: %u", GET_U_1(cp)); 178 cp += 1; 179 len -= 1; 180 /* lba4 */ 181 ND_PRINT(", lba4: %u", GET_U_1(cp)); 182 cp += 1; 183 len -= 1; 184 /* lba5 */ 185 ND_PRINT(", lba5: %u", GET_U_1(cp)); 186 cp += 1; 187 len -= 1; 188 /* Reserved */ 189 ND_TCHECK_2(cp); 190 cp += 2; 191 len -= 2; 192 /* Data */ 193 if (len) 194 ND_PRINT("\n\tData: %u bytes", len); 195 return; 196 197 invalid: 198 nd_print_invalid(ndo); 199 ND_TCHECK_LEN(cp, len); 200 } 201 202 static void 203 aoev1_query_print(netdissect_options *ndo, 204 const u_char *cp, u_int len) 205 { 206 uint16_t cslen; 207 208 if (len < AOEV1_QUERY_ARG_LEN) 209 goto invalid; 210 /* Buffer Count */ 211 ND_PRINT("\n\tBuffer Count: %u", GET_BE_U_2(cp)); 212 cp += 2; 213 len -= 2; 214 /* Firmware Version */ 215 ND_PRINT(", Firmware Version: %u", GET_BE_U_2(cp)); 216 cp += 2; 217 len -= 2; 218 /* Sector Count */ 219 ND_PRINT(", Sector Count: %u", GET_U_1(cp)); 220 cp += 1; 221 len -= 1; 222 /* AoE/CCmd */ 223 ND_PRINT(", AoE: %u, CCmd: %s", (GET_U_1(cp) & 0xF0) >> 4, 224 tok2str(aoev1_ccmd_str, "Unknown (0x02x)", GET_U_1(cp) & 0x0F)); 225 cp += 1; 226 len -= 1; 227 /* Config String Length */ 228 cslen = GET_BE_U_2(cp); 229 cp += 2; 230 len -= 2; 231 if (cslen > AOEV1_MAX_CONFSTR_LEN || cslen > len) 232 goto invalid; 233 /* Config String */ 234 if (cslen) { 235 ND_PRINT("\n\tConfig String (length %u): ", cslen); 236 (void)nd_printn(ndo, cp, cslen, NULL); 237 } 238 return; 239 240 invalid: 241 nd_print_invalid(ndo); 242 ND_TCHECK_LEN(cp, len); 243 } 244 245 static void 246 aoev1_mac_print(netdissect_options *ndo, 247 const u_char *cp, u_int len) 248 { 249 uint8_t dircount, i; 250 251 if (len < AOEV1_MAC_ARG_LEN) 252 goto invalid; 253 /* Reserved */ 254 cp += 1; 255 len -= 1; 256 /* MCmd */ 257 ND_PRINT("\n\tMCmd: %s", 258 tok2str(aoev1_mcmd_str, "Unknown (0x%02x)", GET_U_1(cp))); 259 cp += 1; 260 len -= 1; 261 /* MError */ 262 ND_PRINT(", MError: %s", 263 tok2str(aoev1_merror_str, "Unknown (0x%02x)", GET_U_1(cp))); 264 cp += 1; 265 len -= 1; 266 /* Dir Count */ 267 dircount = GET_U_1(cp); 268 cp += 1; 269 len -= 1; 270 ND_PRINT(", Dir Count: %u", dircount); 271 if (dircount * 8U > len) 272 goto invalid; 273 /* directives */ 274 for (i = 0; i < dircount; i++) { 275 /* Reserved */ 276 cp += 1; 277 len -= 1; 278 /* DCmd */ 279 ND_PRINT("\n\t DCmd: %s", 280 tok2str(aoev1_dcmd_str, "Unknown (0x%02x)", GET_U_1(cp))); 281 cp += 1; 282 len -= 1; 283 /* Ethernet Address */ 284 ND_PRINT(", Ethernet Address: %s", GET_ETHERADDR_STRING(cp)); 285 cp += MAC_ADDR_LEN; 286 len -= MAC_ADDR_LEN; 287 } 288 return; 289 290 invalid: 291 nd_print_invalid(ndo); 292 ND_TCHECK_LEN(cp, len); 293 } 294 295 static void 296 aoev1_reserve_print(netdissect_options *ndo, 297 const u_char *cp, u_int len) 298 { 299 uint8_t nmacs, i; 300 301 if (len < AOEV1_RESERVE_ARG_LEN || (len - AOEV1_RESERVE_ARG_LEN) % MAC_ADDR_LEN) 302 goto invalid; 303 /* RCmd */ 304 ND_PRINT("\n\tRCmd: %s", 305 tok2str(aoev1_rcmd_str, "Unknown (0x%02x)", GET_U_1(cp))); 306 cp += 1; 307 len -= 1; 308 /* NMacs (correlated with the length) */ 309 nmacs = GET_U_1(cp); 310 cp += 1; 311 len -= 1; 312 ND_PRINT(", NMacs: %u", nmacs); 313 if (nmacs * MAC_ADDR_LEN != len) 314 goto invalid; 315 /* addresses */ 316 for (i = 0; i < nmacs; i++) { 317 ND_PRINT("\n\tEthernet Address %u: %s", i, GET_ETHERADDR_STRING(cp)); 318 cp += MAC_ADDR_LEN; 319 len -= MAC_ADDR_LEN; 320 } 321 return; 322 323 invalid: 324 nd_print_invalid(ndo); 325 ND_TCHECK_LEN(cp, len); 326 } 327 328 /* cp points to the Ver/Flags octet */ 329 static void 330 aoev1_print(netdissect_options *ndo, 331 const u_char *cp, u_int len) 332 { 333 uint8_t flags, command; 334 void (*cmd_decoder)(netdissect_options *, const u_char *, u_int); 335 336 if (len < AOEV1_COMMON_HDR_LEN) 337 goto invalid; 338 /* Flags */ 339 flags = GET_U_1(cp) & 0x0F; 340 ND_PRINT(", Flags: [%s]", bittok2str(aoev1_flag_str, "none", flags)); 341 cp += 1; 342 len -= 1; 343 if (! ndo->ndo_vflag) 344 return; 345 /* Error */ 346 if (flags & AOEV1_FLAG_E) 347 ND_PRINT("\n\tError: %s", 348 tok2str(aoev1_errcode_str, "Invalid (%u)", GET_U_1(cp))); 349 cp += 1; 350 len -= 1; 351 /* Major */ 352 ND_PRINT("\n\tMajor: 0x%04x", GET_BE_U_2(cp)); 353 cp += 2; 354 len -= 2; 355 /* Minor */ 356 ND_PRINT(", Minor: 0x%02x", GET_U_1(cp)); 357 cp += 1; 358 len -= 1; 359 /* Command */ 360 command = GET_U_1(cp); 361 cp += 1; 362 len -= 1; 363 ND_PRINT(", Command: %s", tok2str(cmdcode_str, "Unknown (0x%02x)", command)); 364 /* Tag */ 365 ND_PRINT(", Tag: 0x%08x", GET_BE_U_4(cp)); 366 cp += 4; 367 len -= 4; 368 /* Arg */ 369 cmd_decoder = 370 command == AOEV1_CMD_ISSUE_ATA_COMMAND ? aoev1_issue_print : 371 command == AOEV1_CMD_QUERY_CONFIG_INFORMATION ? aoev1_query_print : 372 command == AOEV1_CMD_MAC_MASK_LIST ? aoev1_mac_print : 373 command == AOEV1_CMD_RESERVE_RELEASE ? aoev1_reserve_print : 374 NULL; 375 if (cmd_decoder != NULL) 376 cmd_decoder(ndo, cp, len); 377 return; 378 379 invalid: 380 nd_print_invalid(ndo); 381 ND_TCHECK_LEN(cp, len); 382 } 383 384 void 385 aoe_print(netdissect_options *ndo, 386 const u_char *cp, const u_int len) 387 { 388 uint8_t ver; 389 390 ndo->ndo_protocol = "aoe"; 391 ND_PRINT("AoE length %u", len); 392 393 if (len < 1) 394 goto invalid; 395 /* Ver/Flags */ 396 ver = (GET_U_1(cp) & 0xF0) >> 4; 397 /* Don't advance cp yet: low order 4 bits are version-specific. */ 398 ND_PRINT(", Ver %u", ver); 399 400 switch (ver) { 401 case AOE_V1: 402 aoev1_print(ndo, cp, len); 403 break; 404 } 405 return; 406 407 invalid: 408 nd_print_invalid(ndo); 409 ND_TCHECK_LEN(cp, len); 410 } 411 412