1/*
2 * Stanford Enetfilter subroutines for tcpdump
3 *
4 * Based on the MERIT NNstat etherifrt.c and the Ultrix pcap-pf.c
5 * subroutines.
6 *
7 * Rayan Zachariassen, CA*Net
8 */
9#ifndef lint
10static const char rcsid[] _U_ =
11    "@(#) $Header: /tcpdump/master/libpcap/pcap-enet.c,v 1.9 2006-10-04 18:09:22 guy Exp $";
12#endif
13
14#ifdef HAVE_CONFIG_H
15#include "config.h"
16#endif
17
18#include <sys/types.h>
19#include <sys/time.h>
20#include <sys/file.h>
21#include <sys/ioctl.h>
22#include <sys/socket.h>
23
24#include <net/if.h>
25#include <pcap/bpf.h>
26#include <net/enet.h>
27
28#include <netinet/in.h>
29#include <netinet/if_ether.h>
30
31#include <stdio.h>
32#include <errno.h>
33
34#include "interface.h"
35
36struct packet_header {
37#ifdef	IBMRTPC
38	struct LengthWords	length;
39	struct tap_header	tap;
40#endif	/* IBMRTPC */
41	u_char			packet[8]
42};
43
44extern int errno;
45
46#define BUFSPACE (4*1024)
47
48/* Forwards */
49static void efReadError(int, char *);
50
51void
52readloop(int cnt, int if_fd, struct bpf_program *fp, printfunc printit)
53{
54#ifdef	IBMRTPC
55	register struct packet_header *ph;
56	register u_char *bp;
57	register int inc;
58#else	/* !IBMRTPC */
59	static struct timeval tv = { 0 };
60#endif	/* IBMRTPC */
61	register int cc, caplen;
62	register struct bpf_insn *fcode = fp->bf_insns;
63	union {
64		struct packet_header hdr;
65		u_char	p[BUFSPACE];
66		u_short	s;
67	} buf;
68
69	while (1) {
70		if ((cc = read(if_fd, (char *)buf.p, sizeof(buf))) < 0)
71			efReadError(if_fd, "reader");
72
73#ifdef	IBMRTPC
74		/*
75		 * Loop through each packet.
76		 */
77		bp = buf.p;
78		while (cc > 0) {
79			ph = (struct packet_header *)bp;
80			caplen = ph->tap.th_wirelen > snaplen ? snaplen : ph->tap
81.th_wirelen ;
82			if (bpf_filter(fcode, (char *)ph->packet,
83						ph->tap.th_wirelen, caplen)) {
84				if (cnt >= 0 && --cnt < 0)
85					goto out;
86				(*printit)((char *)ph->packet,
87					(struct timeval *)ph->tap.th_timestamp,
88					ph->tap.th_wirelen, caplen);
89			}
90			inc = ph->length.PacketOffset;
91			cc -= inc;
92			bp += inc;
93		}
94#else	/* !IBMRTPC */
95		caplen = cc > snaplen ? snaplen : cc ;
96		if (bpf_filter(fcode, buf.hdr.packet, cc, caplen)) {
97			if (cnt >= 0 && --cnt < 0)
98				goto out;
99			(*printit)(buf.hdr.packet, &tv, cc, caplen);
100		}
101#endif	/* IBMRTPC */
102	}
103 out:
104	wrapup(if_fd);
105}
106
107/* Call ONLY if read() has returned an error on packet filter */
108static void
109efReadError(int fid, char *msg)
110{
111	if (errno == EINVAL) {	/* read MAXINT bytes already! */
112		if (lseek(fid, 0, 0) < 0) {
113			perror("tcpdump: efReadError/lseek");
114			exit(-1);
115		}
116		else
117			return;
118	}
119	else {
120		(void) fprintf(stderr, "tcpdump: ");
121		perror(msg);
122		exit(-1);
123	}
124}
125
126void
127wrapup(int fd)
128{
129#ifdef	IBMRTPC
130	struct enstats es;
131
132	if (ioctl(fd, EIOSTATS, &es) == -1) {
133		perror("tcpdump: enet ioctl EIOSTATS error");
134		exit(-1);
135	}
136
137	fprintf(stderr, "%d packets queued", es.enStat_Rcnt);
138	if (es.enStat_Rdrops > 0)
139		fprintf(stderr, ", %d dropped", es.enStat_Rdrops);
140	if (es.enStat_Reads > 0)
141		fprintf(stderr, ", %d tcpdump %s", es.enStat_Reads,
142				es.enStat_Reads > 1 ? "reads" : "read");
143	if (es.enStat_MaxRead > 1)
144		fprintf(stderr, ", %d packets in largest read",
145			es.enStat_MaxRead);
146	putc('\n', stderr);
147#endif	/* IBMRTPC */
148	close(fd);
149}
150
151int
152initdevice(char *device, int pflag, int *linktype)
153{
154	struct eniocb ctl;
155	struct enfilter filter;
156	u_int maxwaiting;
157	int if_fd;
158
159#ifdef	IBMRTPC
160	GETENETDEVICE(0, O_RDONLY, &if_fd);
161#else	/* !IBMRTPC */
162	if_fd = open("/dev/enet", O_RDONLY, 0);
163#endif	/* IBMRTPC */
164
165	if (if_fd == -1) {
166		perror("tcpdump: enet open error");
167		error(
168"your system may not be properly configured; see \"man enet(4)\"");
169		exit(-1);
170	}
171
172	/*  Get operating parameters. */
173
174	if (ioctl(if_fd, EIOCGETP, (char *)&ctl) == -1) {
175		perror("tcpdump: enet ioctl EIOCGETP error");
176		exit(-1);
177	}
178
179	/*  Set operating parameters. */
180
181#ifdef	IBMRTPC
182	ctl.en_rtout = 1 * ctl.en_hz;
183	ctl.en_tr_etherhead = 1;
184	ctl.en_tap_network = 1;
185	ctl.en_multi_packet = 1;
186	ctl.en_maxlen = BUFSPACE;
187#else	/* !IBMRTPC */
188	ctl.en_rtout = 64;	/* randomly picked value for HZ */
189#endif	/* IBMRTPC */
190	if (ioctl(if_fd, EIOCSETP, &ctl) == -1) {
191		perror("tcpdump: enet ioctl EIOCSETP error");
192		exit(-1);
193	}
194
195	/*  Flush the receive queue, since we've changed
196	    the operating parameters and we otherwise might
197	    receive data without headers. */
198
199	if (ioctl(if_fd, EIOCFLUSH) == -1) {
200		perror("tcpdump: enet ioctl EIOCFLUSH error");
201		exit(-1);
202	}
203
204	/*  Set the receive queue depth to its maximum. */
205
206	maxwaiting = ctl.en_maxwaiting;
207	if (ioctl(if_fd, EIOCSETW, &maxwaiting) == -1) {
208		perror("tcpdump: enet ioctl EIOCSETW error");
209		exit(-1);
210	}
211
212#ifdef	IBMRTPC
213	/*  Clear statistics. */
214
215	if (ioctl(if_fd, EIOCLRSTAT, 0) == -1) {
216		perror("tcpdump: enet ioctl EIOCLRSTAT error");
217		exit(-1);
218	}
219#endif	/* IBMRTPC */
220
221	/*  Set the filter (accept all packets). */
222
223	filter.enf_Priority = 3;
224	filter.enf_FilterLen = 0;
225	if (ioctl(if_fd, EIOCSETF, &filter) == -1) {
226		perror("tcpdump: enet ioctl EIOCSETF error");
227		exit(-1);
228	}
229	/*
230	 * "enetfilter" supports only ethernets.
231	 */
232	*linktype = DLT_EN10MB;
233
234	return(if_fd);
235}
236