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