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