xref: /minix/external/bsd/tcpdump/dist/print-aoe.c (revision bb9622b5)
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