1/*
2 * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that: (1) source code distributions
7 * retain the above copyright notice and this paragraph in its entirety, (2)
8 * distributions including binary code include the above copyright notice and
9 * this paragraph in its entirety in the documentation or other materials
10 * provided with the distribution, and (3) all advertising materials mentioning
11 * features or use of this software display the following acknowledgement:
12 * ``This product includes software developed by the University of California,
13 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
14 * the University nor the names of its contributors may be used to endorse
15 * or promote products derived from this software without specific prior
16 * written permission.
17 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20 *
21 * From: NetBSD: print-arcnet.c,v 1.2 2000/04/24 13:02:28 itojun Exp
22 */
23
24#define NETDISSECT_REWORKED
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif
28
29#include <tcpdump-stdinc.h>
30
31#include "interface.h"
32#include "extract.h"
33
34/*
35 * from: NetBSD: if_arc.h,v 1.13 1999/11/19 20:41:19 thorpej Exp
36 */
37
38/*
39 * Structure of a 2.5MB/s Arcnet header on the BSDs,
40 * as given to interface code.
41 */
42struct	arc_header {
43	uint8_t  arc_shost;
44	uint8_t  arc_dhost;
45	uint8_t  arc_type;
46	/*
47	 * only present for newstyle encoding with LL fragmentation.
48	 * Don't use sizeof(anything), use ARC_HDR{,NEW}LEN instead.
49	 */
50	uint8_t  arc_flag;
51	uint16_t arc_seqid;
52
53	/*
54	 * only present in exception packets (arc_flag == 0xff)
55	 */
56	uint8_t  arc_type2;	/* same as arc_type */
57	uint8_t  arc_flag2;	/* real flag value */
58	uint16_t arc_seqid2;	/* real seqid value */
59};
60
61#define	ARC_HDRLEN		3
62#define	ARC_HDRNEWLEN		6
63#define	ARC_HDRNEWLEN_EXC	10
64
65/* RFC 1051 */
66#define	ARCTYPE_IP_OLD		240	/* IP protocol */
67#define	ARCTYPE_ARP_OLD		241	/* address resolution protocol */
68
69/* RFC 1201 */
70#define	ARCTYPE_IP		212	/* IP protocol */
71#define	ARCTYPE_ARP		213	/* address resolution protocol */
72#define	ARCTYPE_REVARP		214	/* reverse addr resolution protocol */
73
74#define	ARCTYPE_ATALK		221	/* Appletalk */
75#define	ARCTYPE_BANIAN		247	/* Banyan Vines */
76#define	ARCTYPE_IPX		250	/* Novell IPX */
77
78#define ARCTYPE_INET6		0xc4	/* IPng */
79#define ARCTYPE_DIAGNOSE	0x80	/* as per ANSI/ATA 878.1 */
80
81/*
82 * Structure of a 2.5MB/s Arcnet header on Linux.  Linux has
83 * an extra "offset" field when given to interface code, and
84 * never presents packets that look like exception frames.
85 */
86struct	arc_linux_header {
87	uint8_t  arc_shost;
88	uint8_t  arc_dhost;
89	uint16_t arc_offset;
90	uint8_t  arc_type;
91	/*
92	 * only present for newstyle encoding with LL fragmentation.
93	 * Don't use sizeof(anything), use ARC_LINUX_HDR{,NEW}LEN
94	 * instead.
95	 */
96	uint8_t  arc_flag;
97	uint16_t arc_seqid;
98};
99
100#define	ARC_LINUX_HDRLEN	5
101#define	ARC_LINUX_HDRNEWLEN	8
102
103static int arcnet_encap_print(netdissect_options *, u_char arctype, const u_char *p,
104    u_int length, u_int caplen);
105
106static const struct tok arctypemap[] = {
107	{ ARCTYPE_IP_OLD,	"oldip" },
108	{ ARCTYPE_ARP_OLD,	"oldarp" },
109	{ ARCTYPE_IP,		"ip" },
110	{ ARCTYPE_ARP,		"arp" },
111	{ ARCTYPE_REVARP,	"rarp" },
112	{ ARCTYPE_ATALK,	"atalk" },
113	{ ARCTYPE_BANIAN,	"banyan" },
114	{ ARCTYPE_IPX,		"ipx" },
115	{ ARCTYPE_INET6,	"ipv6" },
116	{ ARCTYPE_DIAGNOSE,	"diag" },
117	{ 0, 0 }
118};
119
120static inline void
121arcnet_print(netdissect_options *ndo, const u_char *bp, u_int length, int phds,
122             int flag, u_int seqid)
123{
124	const struct arc_header *ap;
125	const char *arctypename;
126
127
128	ap = (const struct arc_header *)bp;
129
130
131	if (ndo->ndo_qflag) {
132		ND_PRINT((ndo, "%02x %02x %d: ",
133			     ap->arc_shost,
134			     ap->arc_dhost,
135			     length));
136		return;
137	}
138
139	arctypename = tok2str(arctypemap, "%02x", ap->arc_type);
140
141	if (!phds) {
142		ND_PRINT((ndo, "%02x %02x %s %d: ",
143			     ap->arc_shost, ap->arc_dhost, arctypename,
144			     length));
145			     return;
146	}
147
148	if (flag == 0) {
149		ND_PRINT((ndo, "%02x %02x %s seqid %04x %d: ",
150			ap->arc_shost, ap->arc_dhost, arctypename, seqid,
151			length));
152			return;
153	}
154
155	if (flag & 1)
156		ND_PRINT((ndo, "%02x %02x %s seqid %04x "
157			"(first of %d fragments) %d: ",
158			ap->arc_shost, ap->arc_dhost, arctypename, seqid,
159			(flag + 3) / 2, length));
160	else
161		ND_PRINT((ndo, "%02x %02x %s seqid %04x "
162			"(fragment %d) %d: ",
163			ap->arc_shost, ap->arc_dhost, arctypename, seqid,
164			flag/2 + 1, length));
165}
166
167/*
168 * This is the top level routine of the printer.  'p' points
169 * to the ARCNET header of the packet, 'h->ts' is the timestamp,
170 * 'h->len' is the length of the packet off the wire, and 'h->caplen'
171 * is the number of bytes actually captured.
172 */
173u_int
174arcnet_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h, const u_char *p)
175{
176	u_int caplen = h->caplen;
177	u_int length = h->len;
178	const struct arc_header *ap;
179
180	int phds, flag = 0, archdrlen = 0;
181	u_int seqid = 0;
182	u_char arc_type;
183
184	if (caplen < ARC_HDRLEN || length < ARC_HDRLEN) {
185		ND_PRINT((ndo, "[|arcnet]"));
186		return (caplen);
187	}
188
189	ap = (const struct arc_header *)p;
190	arc_type = ap->arc_type;
191
192	switch (arc_type) {
193	default:
194		phds = 1;
195		break;
196	case ARCTYPE_IP_OLD:
197	case ARCTYPE_ARP_OLD:
198	case ARCTYPE_DIAGNOSE:
199		phds = 0;
200		archdrlen = ARC_HDRLEN;
201		break;
202	}
203
204	if (phds) {
205		if (caplen < ARC_HDRNEWLEN || length < ARC_HDRNEWLEN) {
206			arcnet_print(ndo, p, length, 0, 0, 0);
207			ND_PRINT((ndo, "[|phds]"));
208			return (caplen);
209		}
210
211		if (ap->arc_flag == 0xff) {
212			if (caplen < ARC_HDRNEWLEN_EXC || length < ARC_HDRNEWLEN_EXC) {
213				arcnet_print(ndo, p, length, 0, 0, 0);
214				ND_PRINT((ndo, "[|phds extended]"));
215				return (caplen);
216			}
217			flag = ap->arc_flag2;
218			seqid = EXTRACT_16BITS(&ap->arc_seqid2);
219			archdrlen = ARC_HDRNEWLEN_EXC;
220		} else {
221			flag = ap->arc_flag;
222			seqid = EXTRACT_16BITS(&ap->arc_seqid);
223			archdrlen = ARC_HDRNEWLEN;
224		}
225	}
226
227
228	if (ndo->ndo_eflag)
229		arcnet_print(ndo, p, length, phds, flag, seqid);
230
231	/*
232	 * Go past the ARCNET header.
233	 */
234	length -= archdrlen;
235	caplen -= archdrlen;
236	p += archdrlen;
237
238	if (phds && flag && (flag & 1) == 0) {
239		/*
240		 * This is a middle fragment.
241		 */
242		return (archdrlen);
243	}
244
245	if (!arcnet_encap_print(ndo, arc_type, p, length, caplen))
246		ND_DEFAULTPRINT(p, caplen);
247
248	return (archdrlen);
249}
250
251/*
252 * This is the top level routine of the printer.  'p' points
253 * to the ARCNET header of the packet, 'h->ts' is the timestamp,
254 * 'h->len' is the length of the packet off the wire, and 'h->caplen'
255 * is the number of bytes actually captured.  It is quite similar
256 * to the non-Linux style printer except that Linux doesn't ever
257 * supply packets that look like exception frames, it always supplies
258 * reassembled packets rather than raw frames, and headers have an
259 * extra "offset" field between the src/dest and packet type.
260 */
261u_int
262arcnet_linux_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h, const u_char *p)
263{
264	u_int caplen = h->caplen;
265	u_int length = h->len;
266	const struct arc_linux_header *ap;
267
268	int archdrlen = 0;
269	u_char arc_type;
270
271	if (caplen < ARC_LINUX_HDRLEN || length < ARC_LINUX_HDRLEN) {
272		ND_PRINT((ndo, "[|arcnet]"));
273		return (caplen);
274	}
275
276	ap = (const struct arc_linux_header *)p;
277	arc_type = ap->arc_type;
278
279	switch (arc_type) {
280	default:
281		archdrlen = ARC_LINUX_HDRNEWLEN;
282		if (caplen < ARC_LINUX_HDRNEWLEN || length < ARC_LINUX_HDRNEWLEN) {
283			ND_PRINT((ndo, "[|arcnet]"));
284			return (caplen);
285		}
286		break;
287	case ARCTYPE_IP_OLD:
288	case ARCTYPE_ARP_OLD:
289	case ARCTYPE_DIAGNOSE:
290		archdrlen = ARC_LINUX_HDRLEN;
291		break;
292	}
293
294	if (ndo->ndo_eflag)
295		arcnet_print(ndo, p, length, 0, 0, 0);
296
297	/*
298	 * Go past the ARCNET header.
299	 */
300	length -= archdrlen;
301	caplen -= archdrlen;
302	p += archdrlen;
303
304	if (!arcnet_encap_print(ndo, arc_type, p, length, caplen))
305		ND_DEFAULTPRINT(p, caplen);
306
307	return (archdrlen);
308}
309
310/*
311 * Prints the packet encapsulated in an ARCnet data field,
312 * given the ARCnet system code.
313 *
314 * Returns non-zero if it can do so, zero if the system code is unknown.
315 */
316
317
318static int
319arcnet_encap_print(netdissect_options *ndo, u_char arctype, const u_char *p,
320    u_int length, u_int caplen)
321{
322	switch (arctype) {
323
324	case ARCTYPE_IP_OLD:
325	case ARCTYPE_IP:
326	        ip_print(ndo, p, length);
327		return (1);
328
329	case ARCTYPE_INET6:
330		ip6_print(ndo, p, length);
331		return (1);
332
333	case ARCTYPE_ARP_OLD:
334	case ARCTYPE_ARP:
335	case ARCTYPE_REVARP:
336		arp_print(ndo, p, length, caplen);
337		return (1);
338
339	case ARCTYPE_ATALK:	/* XXX was this ever used? */
340		if (ndo->ndo_vflag)
341			ND_PRINT((ndo, "et1 "));
342		atalk_print(ndo, p, length);
343		return (1);
344
345	case ARCTYPE_IPX:
346		ipx_print(ndo, p, length);
347		return (1);
348
349	default:
350		return (0);
351	}
352}
353
354/*
355 * Local Variables:
356 * c-style: bsd
357 * End:
358 */
359
360