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