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