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
aoev1_issue_print(netdissect_options * ndo,const u_char * cp,const u_int len)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
aoev1_query_print(netdissect_options * ndo,const u_char * cp,const u_int len)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
aoev1_mac_print(netdissect_options * ndo,const u_char * cp,const u_int len)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
aoev1_reserve_print(netdissect_options * ndo,const u_char * cp,const u_int len)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
aoev1_print(netdissect_options * ndo,const u_char * cp,const u_int len)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
aoe_print(netdissect_options * ndo,const u_char * cp,const u_int len)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