]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/tcpdump/print-aoe.c
MFV r285970:
[FreeBSD/FreeBSD.git] / contrib / tcpdump / print-aoe.c
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