1276761Sdelphij/*
2276761Sdelphij * Copyright (c) 2014 The TCPDUMP project
3276761Sdelphij * All rights reserved.
4276761Sdelphij *
5276761Sdelphij * Redistribution and use in source and binary forms, with or without
6276761Sdelphij * modification, are permitted provided that the following conditions
7276761Sdelphij * are met:
8276761Sdelphij * 1. Redistributions of source code must retain the above copyright
9276761Sdelphij *    notice, this list of conditions and the following disclaimer.
10276761Sdelphij * 2. Redistributions in binary form must reproduce the above copyright
11276761Sdelphij *    notice, this list of conditions and the following disclaimer in the
12276761Sdelphij *    documentation and/or other materials provided with the distribution.
13276761Sdelphij *
14276761Sdelphij * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15276761Sdelphij * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16276761Sdelphij * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
17276761Sdelphij * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
18276761Sdelphij * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
19276761Sdelphij * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20276761Sdelphij * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21276761Sdelphij * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22276761Sdelphij * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23276761Sdelphij * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
24276761Sdelphij * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25276761Sdelphij * POSSIBILITY OF SUCH DAMAGE.
26276761Sdelphij */
27276761Sdelphij
28313537Sglebius/* \summary: ATA over Ethernet (AoE) protocol printer */
29313537Sglebius
30313537Sglebius/* specification: http://brantleycoilecompany.com/AoEr11.pdf */
31313537Sglebius
32276761Sdelphij#ifdef HAVE_CONFIG_H
33276761Sdelphij#include "config.h"
34276761Sdelphij#endif
35276761Sdelphij
36313537Sglebius#include <netdissect-stdinc.h>
37276761Sdelphij
38313537Sglebius#include "netdissect.h"
39276761Sdelphij#include "extract.h"
40276761Sdelphij#include "addrtoname.h"
41276761Sdelphij#include "ether.h"
42276761Sdelphij
43276761Sdelphijstatic const char tstr[] = " [|aoe]";
44276761Sdelphij
45276761Sdelphij#define AOE_V1 1
46276761Sdelphij#define ATA_SECTOR_SIZE 512
47276761Sdelphij
48276761Sdelphij#define AOEV1_CMD_ISSUE_ATA_COMMAND        0
49276761Sdelphij#define AOEV1_CMD_QUERY_CONFIG_INFORMATION 1
50276761Sdelphij#define AOEV1_CMD_MAC_MASK_LIST            2
51276761Sdelphij#define AOEV1_CMD_RESERVE_RELEASE          3
52276761Sdelphij
53276761Sdelphijstatic const struct tok cmdcode_str[] = {
54276761Sdelphij	{ AOEV1_CMD_ISSUE_ATA_COMMAND,        "Issue ATA Command"        },
55276761Sdelphij	{ AOEV1_CMD_QUERY_CONFIG_INFORMATION, "Query Config Information" },
56276761Sdelphij	{ AOEV1_CMD_MAC_MASK_LIST,            "MAC Mask List"            },
57276761Sdelphij	{ AOEV1_CMD_RESERVE_RELEASE,          "Reserve/Release"          },
58276761Sdelphij	{ 0, NULL }
59276761Sdelphij};
60276761Sdelphij
61276761Sdelphij#define AOEV1_COMMON_HDR_LEN    10U /* up to but w/o Arg                */
62276761Sdelphij#define AOEV1_ISSUE_ARG_LEN     12U /* up to but w/o Data               */
63276761Sdelphij#define AOEV1_QUERY_ARG_LEN      8U /* up to but w/o Config String      */
64276761Sdelphij#define AOEV1_MAC_ARG_LEN        4U /* up to but w/o Directive 0        */
65276761Sdelphij#define AOEV1_RESERVE_ARG_LEN    2U /* up to but w/o Ethernet address 0 */
66276761Sdelphij#define AOEV1_MAX_CONFSTR_LEN 1024U
67276761Sdelphij
68276761Sdelphij#define AOEV1_FLAG_R 0x08
69276761Sdelphij#define AOEV1_FLAG_E 0x04
70276761Sdelphij
71276761Sdelphijstatic const struct tok aoev1_flag_str[] = {
72276761Sdelphij	{ AOEV1_FLAG_R, "Response" },
73276761Sdelphij	{ AOEV1_FLAG_E, "Error"    },
74276761Sdelphij	{ 0x02,         "MBZ-0x02" },
75276761Sdelphij	{ 0x01,         "MBZ-0x01" },
76276761Sdelphij	{ 0, NULL }
77276761Sdelphij};
78276761Sdelphij
79276761Sdelphijstatic const struct tok aoev1_errcode_str[] = {
80276761Sdelphij	{ 1, "Unrecognized command code" },
81276761Sdelphij	{ 2, "Bad argument parameter"    },
82276761Sdelphij	{ 3, "Device unavailable"        },
83276761Sdelphij	{ 4, "Config string present"     },
84276761Sdelphij	{ 5, "Unsupported version"       },
85276761Sdelphij	{ 6, "Target is reserved"        },
86276761Sdelphij	{ 0, NULL }
87276761Sdelphij};
88276761Sdelphij
89276761Sdelphij#define AOEV1_AFLAG_E 0x40
90276761Sdelphij#define AOEV1_AFLAG_D 0x10
91276761Sdelphij#define AOEV1_AFLAG_A 0x02
92276761Sdelphij#define AOEV1_AFLAG_W 0x01
93276761Sdelphij
94276761Sdelphijstatic const struct tok aoev1_aflag_str[] = {
95276761Sdelphij	{ 0x08,          "MBZ-0x08" },
96276761Sdelphij	{ AOEV1_AFLAG_E, "Ext48"    },
97276761Sdelphij	{ 0x06,          "MBZ-0x06" },
98276761Sdelphij	{ AOEV1_AFLAG_D, "Device"   },
99276761Sdelphij	{ 0x04,          "MBZ-0x04" },
100276761Sdelphij	{ 0x03,          "MBZ-0x03" },
101276761Sdelphij	{ AOEV1_AFLAG_A, "Async"    },
102276761Sdelphij	{ AOEV1_AFLAG_W, "Write"    },
103276761Sdelphij	{ 0, NULL }
104276761Sdelphij};
105276761Sdelphij
106276761Sdelphijstatic const struct tok aoev1_ccmd_str[] = {
107276761Sdelphij	{ 0, "read config string"        },
108276761Sdelphij	{ 1, "test config string"        },
109276761Sdelphij	{ 2, "test config string prefix" },
110276761Sdelphij	{ 3, "set config string"         },
111276761Sdelphij	{ 4, "force set config string"   },
112276761Sdelphij	{ 0, NULL }
113276761Sdelphij};
114276761Sdelphij
115276761Sdelphijstatic const struct tok aoev1_mcmd_str[] = {
116276761Sdelphij	{ 0, "Read Mac Mask List" },
117276761Sdelphij	{ 1, "Edit Mac Mask List" },
118276761Sdelphij	{ 0, NULL }
119276761Sdelphij};
120276761Sdelphij
121276761Sdelphijstatic const struct tok aoev1_merror_str[] = {
122276761Sdelphij	{ 1, "Unspecified Error"  },
123276761Sdelphij	{ 2, "Bad DCmd directive" },
124276761Sdelphij	{ 3, "Mask list full"     },
125276761Sdelphij	{ 0, NULL }
126276761Sdelphij};
127276761Sdelphij
128276761Sdelphijstatic const struct tok aoev1_dcmd_str[] = {
129276761Sdelphij	{ 0, "No Directive"                      },
130276761Sdelphij	{ 1, "Add mac address to mask list"      },
131276761Sdelphij	{ 2, "Delete mac address from mask list" },
132276761Sdelphij	{ 0, NULL }
133276761Sdelphij};
134276761Sdelphij
135276761Sdelphijstatic const struct tok aoev1_rcmd_str[] = {
136276761Sdelphij	{ 0, "Read reserve list"      },
137276761Sdelphij	{ 1, "Set reserve list"       },
138276761Sdelphij	{ 2, "Force set reserve list" },
139276761Sdelphij	{ 0, NULL }
140276761Sdelphij};
141276761Sdelphij
142276761Sdelphijstatic void
143276761Sdelphijaoev1_issue_print(netdissect_options *ndo,
144276761Sdelphij                  const u_char *cp, const u_int len)
145276761Sdelphij{
146276761Sdelphij	const u_char *ep = cp + len;
147276761Sdelphij
148276761Sdelphij	if (len < AOEV1_ISSUE_ARG_LEN)
149313537Sglebius		goto invalid;
150276761Sdelphij	/* AFlags */
151276761Sdelphij	ND_TCHECK2(*cp, 1);
152276761Sdelphij	ND_PRINT((ndo, "\n\tAFlags: [%s]", bittok2str(aoev1_aflag_str, "none", *cp)));
153276761Sdelphij	cp += 1;
154276761Sdelphij	/* Err/Feature */
155276761Sdelphij	ND_TCHECK2(*cp, 1);
156276761Sdelphij	ND_PRINT((ndo, ", Err/Feature: %u", *cp));
157276761Sdelphij	cp += 1;
158276761Sdelphij	/* Sector Count (not correlated with the length) */
159276761Sdelphij	ND_TCHECK2(*cp, 1);
160276761Sdelphij	ND_PRINT((ndo, ", Sector Count: %u", *cp));
161276761Sdelphij	cp += 1;
162276761Sdelphij	/* Cmd/Status */
163276761Sdelphij	ND_TCHECK2(*cp, 1);
164276761Sdelphij	ND_PRINT((ndo, ", Cmd/Status: %u", *cp));
165276761Sdelphij	cp += 1;
166276761Sdelphij	/* lba0 */
167276761Sdelphij	ND_TCHECK2(*cp, 1);
168276761Sdelphij	ND_PRINT((ndo, "\n\tlba0: %u", *cp));
169276761Sdelphij	cp += 1;
170276761Sdelphij	/* lba1 */
171276761Sdelphij	ND_TCHECK2(*cp, 1);
172276761Sdelphij	ND_PRINT((ndo, ", lba1: %u", *cp));
173276761Sdelphij	cp += 1;
174276761Sdelphij	/* lba2 */
175276761Sdelphij	ND_TCHECK2(*cp, 1);
176276761Sdelphij	ND_PRINT((ndo, ", lba2: %u", *cp));
177276761Sdelphij	cp += 1;
178276761Sdelphij	/* lba3 */
179276761Sdelphij	ND_TCHECK2(*cp, 1);
180276761Sdelphij	ND_PRINT((ndo, ", lba3: %u", *cp));
181276761Sdelphij	cp += 1;
182276761Sdelphij	/* lba4 */
183276761Sdelphij	ND_TCHECK2(*cp, 1);
184276761Sdelphij	ND_PRINT((ndo, ", lba4: %u", *cp));
185276761Sdelphij	cp += 1;
186276761Sdelphij	/* lba5 */
187276761Sdelphij	ND_TCHECK2(*cp, 1);
188276761Sdelphij	ND_PRINT((ndo, ", lba5: %u", *cp));
189276761Sdelphij	cp += 1;
190276761Sdelphij	/* Reserved */
191276761Sdelphij	ND_TCHECK2(*cp, 2);
192276761Sdelphij	cp += 2;
193276761Sdelphij	/* Data */
194276761Sdelphij	if (len > AOEV1_ISSUE_ARG_LEN)
195276761Sdelphij		ND_PRINT((ndo, "\n\tData: %u bytes", len - AOEV1_ISSUE_ARG_LEN));
196276761Sdelphij	return;
197276761Sdelphij
198313537Sglebiusinvalid:
199313537Sglebius	ND_PRINT((ndo, "%s", istr));
200276761Sdelphij	ND_TCHECK2(*cp, ep - cp);
201276761Sdelphij	return;
202276761Sdelphijtrunc:
203276761Sdelphij	ND_PRINT((ndo, "%s", tstr));
204276761Sdelphij}
205276761Sdelphij
206276761Sdelphijstatic void
207276761Sdelphijaoev1_query_print(netdissect_options *ndo,
208276761Sdelphij                  const u_char *cp, const u_int len)
209276761Sdelphij{
210276761Sdelphij	const u_char *ep = cp + len;
211276761Sdelphij	uint16_t cslen;
212276761Sdelphij
213276761Sdelphij	if (len < AOEV1_QUERY_ARG_LEN)
214313537Sglebius		goto invalid;
215276761Sdelphij	/* Buffer Count */
216276761Sdelphij	ND_TCHECK2(*cp, 2);
217276761Sdelphij	ND_PRINT((ndo, "\n\tBuffer Count: %u", EXTRACT_16BITS(cp)));
218276761Sdelphij	cp += 2;
219276761Sdelphij	/* Firmware Version */
220276761Sdelphij	ND_TCHECK2(*cp, 2);
221276761Sdelphij	ND_PRINT((ndo, ", Firmware Version: %u", EXTRACT_16BITS(cp)));
222276761Sdelphij	cp += 2;
223276761Sdelphij	/* Sector Count */
224276761Sdelphij	ND_TCHECK2(*cp, 1);
225276761Sdelphij	ND_PRINT((ndo, ", Sector Count: %u", *cp));
226276761Sdelphij	cp += 1;
227276761Sdelphij	/* AoE/CCmd */
228276761Sdelphij	ND_TCHECK2(*cp, 1);
229276761Sdelphij	ND_PRINT((ndo, ", AoE: %u, CCmd: %s", (*cp & 0xF0) >> 4,
230276761Sdelphij	          tok2str(aoev1_ccmd_str, "Unknown (0x02x)", *cp & 0x0F)));
231276761Sdelphij	cp += 1;
232276761Sdelphij	/* Config String Length */
233276761Sdelphij	ND_TCHECK2(*cp, 2);
234276761Sdelphij	cslen = EXTRACT_16BITS(cp);
235276761Sdelphij	cp += 2;
236276761Sdelphij	if (cslen > AOEV1_MAX_CONFSTR_LEN || AOEV1_QUERY_ARG_LEN + cslen > len)
237313537Sglebius		goto invalid;
238276761Sdelphij	/* Config String */
239276761Sdelphij	ND_TCHECK2(*cp, cslen);
240276761Sdelphij	if (cslen) {
241276761Sdelphij		ND_PRINT((ndo, "\n\tConfig String (length %u): ", cslen));
242276761Sdelphij		if (fn_printn(ndo, cp, cslen, ndo->ndo_snapend))
243276761Sdelphij			goto trunc;
244276761Sdelphij	}
245276761Sdelphij	return;
246276761Sdelphij
247313537Sglebiusinvalid:
248313537Sglebius	ND_PRINT((ndo, "%s", istr));
249276761Sdelphij	ND_TCHECK2(*cp, ep - cp);
250276761Sdelphij	return;
251276761Sdelphijtrunc:
252276761Sdelphij	ND_PRINT((ndo, "%s", tstr));
253276761Sdelphij}
254276761Sdelphij
255276761Sdelphijstatic void
256276761Sdelphijaoev1_mac_print(netdissect_options *ndo,
257276761Sdelphij                const u_char *cp, const u_int len)
258276761Sdelphij{
259276761Sdelphij	const u_char *ep = cp + len;
260276761Sdelphij	uint8_t dircount, i;
261276761Sdelphij
262276761Sdelphij	if (len < AOEV1_MAC_ARG_LEN)
263313537Sglebius		goto invalid;
264276761Sdelphij	/* Reserved */
265276761Sdelphij	ND_TCHECK2(*cp, 1);
266276761Sdelphij	cp += 1;
267276761Sdelphij	/* MCmd */
268276761Sdelphij	ND_TCHECK2(*cp, 1);
269276761Sdelphij	ND_PRINT((ndo, "\n\tMCmd: %s", tok2str(aoev1_mcmd_str, "Unknown (0x%02x)", *cp)));
270276761Sdelphij	cp += 1;
271276761Sdelphij	/* MError */
272276761Sdelphij	ND_TCHECK2(*cp, 1);
273276761Sdelphij	ND_PRINT((ndo, ", MError: %s", tok2str(aoev1_merror_str, "Unknown (0x%02x)", *cp)));
274276761Sdelphij	cp += 1;
275276761Sdelphij	/* Dir Count */
276276761Sdelphij	ND_TCHECK2(*cp, 1);
277276761Sdelphij	dircount = *cp;
278276761Sdelphij	cp += 1;
279276761Sdelphij	ND_PRINT((ndo, ", Dir Count: %u", dircount));
280276761Sdelphij	if (AOEV1_MAC_ARG_LEN + dircount * 8 > len)
281313537Sglebius		goto invalid;
282276761Sdelphij	/* directives */
283276761Sdelphij	for (i = 0; i < dircount; i++) {
284276761Sdelphij		/* Reserved */
285276761Sdelphij		ND_TCHECK2(*cp, 1);
286276761Sdelphij		cp += 1;
287276761Sdelphij		/* DCmd */
288276761Sdelphij		ND_TCHECK2(*cp, 1);
289276761Sdelphij		ND_PRINT((ndo, "\n\t DCmd: %s", tok2str(aoev1_dcmd_str, "Unknown (0x%02x)", *cp)));
290276761Sdelphij		cp += 1;
291276761Sdelphij		/* Ethernet Address */
292276761Sdelphij		ND_TCHECK2(*cp, ETHER_ADDR_LEN);
293276761Sdelphij		ND_PRINT((ndo, ", Ethernet Address: %s", etheraddr_string(ndo, cp)));
294276761Sdelphij		cp += ETHER_ADDR_LEN;
295276761Sdelphij	}
296276761Sdelphij	return;
297276761Sdelphij
298313537Sglebiusinvalid:
299313537Sglebius	ND_PRINT((ndo, "%s", istr));
300276761Sdelphij	ND_TCHECK2(*cp, ep - cp);
301276761Sdelphij	return;
302276761Sdelphijtrunc:
303276761Sdelphij	ND_PRINT((ndo, "%s", tstr));
304276761Sdelphij}
305276761Sdelphij
306276761Sdelphijstatic void
307276761Sdelphijaoev1_reserve_print(netdissect_options *ndo,
308276761Sdelphij                    const u_char *cp, const u_int len)
309276761Sdelphij{
310276761Sdelphij	const u_char *ep = cp + len;
311276761Sdelphij	uint8_t nmacs, i;
312276761Sdelphij
313276761Sdelphij	if (len < AOEV1_RESERVE_ARG_LEN || (len - AOEV1_RESERVE_ARG_LEN) % ETHER_ADDR_LEN)
314313537Sglebius		goto invalid;
315276761Sdelphij	/* RCmd */
316276761Sdelphij	ND_TCHECK2(*cp, 1);
317276761Sdelphij	ND_PRINT((ndo, "\n\tRCmd: %s", tok2str(aoev1_rcmd_str, "Unknown (0x%02x)", *cp)));
318276761Sdelphij	cp += 1;
319276761Sdelphij	/* NMacs (correlated with the length) */
320276761Sdelphij	ND_TCHECK2(*cp, 1);
321276761Sdelphij	nmacs = *cp;
322276761Sdelphij	cp += 1;
323276761Sdelphij	ND_PRINT((ndo, ", NMacs: %u", nmacs));
324276761Sdelphij	if (AOEV1_RESERVE_ARG_LEN + nmacs * ETHER_ADDR_LEN != len)
325313537Sglebius		goto invalid;
326276761Sdelphij	/* addresses */
327276761Sdelphij	for (i = 0; i < nmacs; i++) {
328356341Scy		ND_TCHECK2(*cp, ETHER_ADDR_LEN);
329276761Sdelphij		ND_PRINT((ndo, "\n\tEthernet Address %u: %s", i, etheraddr_string(ndo, cp)));
330276761Sdelphij		cp += ETHER_ADDR_LEN;
331276761Sdelphij	}
332276761Sdelphij	return;
333276761Sdelphij
334313537Sglebiusinvalid:
335313537Sglebius	ND_PRINT((ndo, "%s", istr));
336276761Sdelphij	ND_TCHECK2(*cp, ep - cp);
337276761Sdelphij	return;
338276761Sdelphijtrunc:
339276761Sdelphij	ND_PRINT((ndo, "%s", tstr));
340276761Sdelphij}
341276761Sdelphij
342276761Sdelphij/* cp points to the Ver/Flags octet */
343276761Sdelphijstatic void
344276761Sdelphijaoev1_print(netdissect_options *ndo,
345276761Sdelphij            const u_char *cp, const u_int len)
346276761Sdelphij{
347276761Sdelphij	const u_char *ep = cp + len;
348276761Sdelphij	uint8_t flags, command;
349276761Sdelphij	void (*cmd_decoder)(netdissect_options *, const u_char *, const u_int);
350276761Sdelphij
351276761Sdelphij	if (len < AOEV1_COMMON_HDR_LEN)
352313537Sglebius		goto invalid;
353276761Sdelphij	/* Flags */
354356341Scy	ND_TCHECK2(*cp, 1);
355276761Sdelphij	flags = *cp & 0x0F;
356276761Sdelphij	ND_PRINT((ndo, ", Flags: [%s]", bittok2str(aoev1_flag_str, "none", flags)));
357276761Sdelphij	cp += 1;
358276761Sdelphij	if (! ndo->ndo_vflag)
359276761Sdelphij		return;
360276761Sdelphij	/* Error */
361276761Sdelphij	ND_TCHECK2(*cp, 1);
362276761Sdelphij	if (flags & AOEV1_FLAG_E)
363276761Sdelphij		ND_PRINT((ndo, "\n\tError: %s", tok2str(aoev1_errcode_str, "Invalid (%u)", *cp)));
364276761Sdelphij	cp += 1;
365276761Sdelphij	/* Major */
366276761Sdelphij	ND_TCHECK2(*cp, 2);
367276761Sdelphij	ND_PRINT((ndo, "\n\tMajor: 0x%04x", EXTRACT_16BITS(cp)));
368276761Sdelphij	cp += 2;
369276761Sdelphij	/* Minor */
370276761Sdelphij	ND_TCHECK2(*cp, 1);
371276761Sdelphij	ND_PRINT((ndo, ", Minor: 0x%02x", *cp));
372276761Sdelphij	cp += 1;
373276761Sdelphij	/* Command */
374276761Sdelphij	ND_TCHECK2(*cp, 1);
375276761Sdelphij	command = *cp;
376276761Sdelphij	cp += 1;
377276761Sdelphij	ND_PRINT((ndo, ", Command: %s", tok2str(cmdcode_str, "Unknown (0x%02x)", command)));
378276761Sdelphij	/* Tag */
379276761Sdelphij	ND_TCHECK2(*cp, 4);
380276761Sdelphij	ND_PRINT((ndo, ", Tag: 0x%08x", EXTRACT_32BITS(cp)));
381276761Sdelphij	cp += 4;
382276761Sdelphij	/* Arg */
383276761Sdelphij	cmd_decoder =
384276761Sdelphij		command == AOEV1_CMD_ISSUE_ATA_COMMAND        ? aoev1_issue_print :
385276761Sdelphij		command == AOEV1_CMD_QUERY_CONFIG_INFORMATION ? aoev1_query_print :
386276761Sdelphij		command == AOEV1_CMD_MAC_MASK_LIST            ? aoev1_mac_print :
387276761Sdelphij		command == AOEV1_CMD_RESERVE_RELEASE          ? aoev1_reserve_print :
388276761Sdelphij		NULL;
389276761Sdelphij	if (cmd_decoder != NULL)
390276761Sdelphij		cmd_decoder(ndo, cp, len - AOEV1_COMMON_HDR_LEN);
391276761Sdelphij	return;
392276761Sdelphij
393313537Sglebiusinvalid:
394313537Sglebius	ND_PRINT((ndo, "%s", istr));
395276761Sdelphij	ND_TCHECK2(*cp, ep - cp);
396276761Sdelphij	return;
397276761Sdelphijtrunc:
398276761Sdelphij	ND_PRINT((ndo, "%s", tstr));
399276761Sdelphij}
400276761Sdelphij
401276761Sdelphijvoid
402276761Sdelphijaoe_print(netdissect_options *ndo,
403276761Sdelphij          const u_char *cp, const u_int len)
404276761Sdelphij{
405276761Sdelphij	const u_char *ep = cp + len;
406276761Sdelphij	uint8_t ver;
407276761Sdelphij
408276761Sdelphij	ND_PRINT((ndo, "AoE length %u", len));
409276761Sdelphij
410276761Sdelphij	if (len < 1)
411313537Sglebius		goto invalid;
412276761Sdelphij	/* Ver/Flags */
413276761Sdelphij	ND_TCHECK2(*cp, 1);
414276761Sdelphij	ver = (*cp & 0xF0) >> 4;
415276761Sdelphij	/* Don't advance cp yet: low order 4 bits are version-specific. */
416276761Sdelphij	ND_PRINT((ndo, ", Ver %u", ver));
417276761Sdelphij
418276761Sdelphij	switch (ver) {
419276761Sdelphij		case AOE_V1:
420276761Sdelphij			aoev1_print(ndo, cp, len);
421276761Sdelphij			break;
422276761Sdelphij	}
423276761Sdelphij	return;
424276761Sdelphij
425313537Sglebiusinvalid:
426313537Sglebius	ND_PRINT((ndo, "%s", istr));
427276761Sdelphij	ND_TCHECK2(*cp, ep - cp);
428276761Sdelphij	return;
429276761Sdelphijtrunc:
430276761Sdelphij	ND_PRINT((ndo, "%s", tstr));
431276761Sdelphij}
432276761Sdelphij
433