1214455Srpaulo#ifdef HAVE_CONFIG_H
2214455Srpaulo#include "config.h"
3214455Srpaulo#endif
4214455Srpaulo
5214455Srpaulo#include <sys/param.h>
6214455Srpaulo
7214455Srpaulo#include <stdlib.h>
8214455Srpaulo#include <string.h>
9214455Srpaulo#include <errno.h>
10214455Srpaulo
11214455Srpaulo#include <ctype.h>
12214455Srpaulo#include <netinet/in.h>
13214455Srpaulo#include <sys/mman.h>
14214455Srpaulo#include <sys/socket.h>
15214455Srpaulo#include <sys/types.h>
16214455Srpaulo#include <unistd.h>
17214455Srpaulo
18214455Srpaulo#include "snf.h"
19214455Srpaulo#include "pcap-int.h"
20214455Srpaulo
21214455Srpaulostatic int
22214455Srpaulosnf_set_datalink(pcap_t *p, int dlt)
23214455Srpaulo{
24214455Srpaulo	p->linktype = dlt;
25214455Srpaulo	return (0);
26214455Srpaulo}
27214455Srpaulo
28214455Srpaulostatic int
29214455Srpaulosnf_pcap_stats(pcap_t *p, struct pcap_stat *ps)
30214455Srpaulo{
31214455Srpaulo	struct snf_ring_stats stats;
32214455Srpaulo	int rc;
33214455Srpaulo
34214455Srpaulo	if ((rc = snf_ring_getstats(p->md.snf_ring, &stats))) {
35214455Srpaulo		snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "snf_get_stats: %s",
36214455Srpaulo			 pcap_strerror(rc));
37214455Srpaulo		return -1;
38214455Srpaulo	}
39214455Srpaulo	ps->ps_recv = stats.ring_pkt_recv + stats.ring_pkt_overflow;
40214455Srpaulo	ps->ps_drop = stats.ring_pkt_overflow;
41214455Srpaulo	ps->ps_ifdrop = stats.nic_pkt_overflow + stats.nic_pkt_bad;
42214455Srpaulo	return 0;
43214455Srpaulo}
44214455Srpaulo
45214455Srpaulostatic void
46214455Srpaulosnf_platform_cleanup(pcap_t *p)
47214455Srpaulo{
48214455Srpaulo	if (p == NULL)
49214455Srpaulo		return;
50214455Srpaulo
51214455Srpaulo	snf_ring_close(p->md.snf_ring);
52214455Srpaulo	snf_close(p->md.snf_handle);
53214455Srpaulo	pcap_cleanup_live_common(p);
54214455Srpaulo}
55214455Srpaulo
56214455Srpaulostatic int
57214455Srpaulosnf_getnonblock(pcap_t *p, char *errbuf)
58214455Srpaulo{
59214455Srpaulo	return (p->md.snf_timeout == 0);
60214455Srpaulo}
61214455Srpaulo
62214455Srpaulostatic int
63214455Srpaulosnf_setnonblock(pcap_t *p, int nonblock, char *errbuf)
64214455Srpaulo{
65214455Srpaulo	if (nonblock)
66214455Srpaulo		p->md.snf_timeout = 0;
67214455Srpaulo	else {
68214455Srpaulo		if (p->md.timeout <= 0)
69214455Srpaulo			p->md.snf_timeout = -1; /* forever */
70214455Srpaulo		else
71214455Srpaulo			p->md.snf_timeout = p->md.timeout;
72214455Srpaulo	}
73214455Srpaulo	return (0);
74214455Srpaulo}
75214455Srpaulo
76214455Srpaulo#define _NSEC_PER_SEC 1000000000
77214455Srpaulo
78214455Srpaulostatic inline
79214455Srpaulostruct timeval
80214455Srpaulosnf_timestamp_to_timeval(const int64_t ts_nanosec)
81214455Srpaulo{
82214455Srpaulo	struct timeval tv;
83214455Srpaulo	int32_t rem;
84214455Srpaulo	if (ts_nanosec == 0)
85214455Srpaulo		return (struct timeval) { 0, 0 };
86214455Srpaulo	tv.tv_sec = ts_nanosec / _NSEC_PER_SEC;
87214455Srpaulo	tv.tv_usec = (ts_nanosec % _NSEC_PER_SEC) / 1000;
88214455Srpaulo	return tv;
89214455Srpaulo}
90214455Srpaulo
91214455Srpaulostatic int
92214455Srpaulosnf_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
93214455Srpaulo{
94214455Srpaulo	struct pcap_pkthdr hdr;
95214455Srpaulo	int i, flags, err, caplen, n;
96214455Srpaulo	struct snf_recv_req req;
97214455Srpaulo
98214455Srpaulo	if (!p || cnt == 0)
99214455Srpaulo		return -1;
100214455Srpaulo
101214455Srpaulo	n = 0;
102214455Srpaulo	while (n < cnt || cnt < 0) {
103214455Srpaulo		/*
104214455Srpaulo		 * Has "pcap_breakloop()" been called?
105214455Srpaulo		 */
106214455Srpaulo		if (p->break_loop) {
107214455Srpaulo			if (n == 0) {
108214455Srpaulo				p->break_loop = 0;
109214455Srpaulo				return (-2);
110214455Srpaulo			} else {
111214455Srpaulo				return (n);
112214455Srpaulo			}
113214455Srpaulo		}
114214455Srpaulo
115214455Srpaulo		err = snf_ring_recv(p->md.snf_ring, p->md.snf_timeout, &req);
116214455Srpaulo
117214455Srpaulo		if (err) {
118214455Srpaulo			if (err == EBUSY || err == EAGAIN)
119214455Srpaulo				return (0);
120214455Srpaulo			if (err == EINTR)
121214455Srpaulo				continue;
122214455Srpaulo			if (err != 0) {
123214455Srpaulo				snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "snf_read: %s",
124214455Srpaulo				 	 pcap_strerror(err));
125214455Srpaulo				return -1;
126214455Srpaulo			}
127214455Srpaulo		}
128214455Srpaulo
129214455Srpaulo		caplen = req.length;
130214455Srpaulo		if (caplen > p->snapshot)
131214455Srpaulo			caplen = p->snapshot;
132214455Srpaulo
133214455Srpaulo		if ((p->fcode.bf_insns == NULL) ||
134214455Srpaulo		     bpf_filter(p->fcode.bf_insns, req.pkt_addr, req.length, caplen)) {
135214455Srpaulo			hdr.ts = snf_timestamp_to_timeval(req.timestamp);
136214455Srpaulo			hdr.caplen = caplen;
137214455Srpaulo			hdr.len = req.length;
138214455Srpaulo			callback(user, &hdr, req.pkt_addr);
139214455Srpaulo		}
140214455Srpaulo		n++;
141214455Srpaulo	}
142214455Srpaulo	return (n);
143214455Srpaulo}
144214455Srpaulo
145214455Srpaulostatic int
146214455Srpaulosnf_setfilter(pcap_t *p, struct bpf_program *fp)
147214455Srpaulo{
148214455Srpaulo	if (!p)
149214455Srpaulo		return -1;
150214455Srpaulo	if (!fp) {
151214455Srpaulo		strncpy(p->errbuf, "setfilter: No filter specified",
152214455Srpaulo			sizeof(p->errbuf));
153214455Srpaulo		return -1;
154214455Srpaulo	}
155214455Srpaulo
156214455Srpaulo	/* Make our private copy of the filter */
157214455Srpaulo
158214455Srpaulo	if (install_bpf_program(p, fp) < 0)
159214455Srpaulo		return -1;
160214455Srpaulo
161214455Srpaulo	p->md.use_bpf = 0;
162214455Srpaulo
163214455Srpaulo	return (0);
164214455Srpaulo}
165214455Srpaulo
166214455Srpaulostatic int
167214455Srpaulosnf_inject(pcap_t *p, const void *buf _U_, size_t size _U_)
168214455Srpaulo{
169214455Srpaulo	strlcpy(p->errbuf, "Sending packets isn't supported with snf",
170214455Srpaulo	    PCAP_ERRBUF_SIZE);
171214455Srpaulo	return (-1);
172214455Srpaulo}
173214455Srpaulo
174214455Srpaulostatic int
175214455Srpaulosnf_activate(pcap_t* p)
176214455Srpaulo{
177214455Srpaulo	char *device = p->opt.source;
178214455Srpaulo	const char *nr = NULL;
179214455Srpaulo	int err;
180214455Srpaulo	int flags = 0;
181214455Srpaulo
182214455Srpaulo	if (device == NULL) {
183214455Srpaulo		snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
184214455Srpaulo			 "device is NULL: %s", pcap_strerror(errno));
185214455Srpaulo		return -1;
186214455Srpaulo	}
187214455Srpaulo
188214455Srpaulo	/* In Libpcap, we set pshared by default if NUM_RINGS is set to > 1.
189214455Srpaulo	 * Since libpcap isn't thread-safe */
190214455Srpaulo	if ((nr = getenv("SNF_NUM_RINGS")) && *nr && atoi(nr) > 1)
191214455Srpaulo		flags |= SNF_F_PSHARED;
192214455Srpaulo	else
193214455Srpaulo		nr = NULL;
194214455Srpaulo
195214455Srpaulo	err = snf_open(p->md.snf_boardnum,
196214455Srpaulo			0, /* let SNF API parse SNF_NUM_RINGS, if set */
197214455Srpaulo			NULL, /* default RSS, or use SNF_RSS_FLAGS env */
198214455Srpaulo			0, /* default to SNF_DATARING_SIZE from env */
199214455Srpaulo			flags, /* may want pshared */
200214455Srpaulo			&p->md.snf_handle);
201214455Srpaulo	if (err != 0) {
202214455Srpaulo		snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
203214455Srpaulo			 "snf_open failed: %s", pcap_strerror(err));
204214455Srpaulo		return -1;
205214455Srpaulo	}
206214455Srpaulo
207214455Srpaulo	err = snf_ring_open(p->md.snf_handle, &p->md.snf_ring);
208214455Srpaulo	if (err != 0) {
209214455Srpaulo		snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
210214455Srpaulo			 "snf_ring_open failed: %s", pcap_strerror(err));
211214455Srpaulo		return -1;
212214455Srpaulo	}
213214455Srpaulo
214214455Srpaulo	if (p->md.timeout <= 0)
215214455Srpaulo		p->md.snf_timeout = -1;
216214455Srpaulo	else
217214455Srpaulo		p->md.snf_timeout = p->md.timeout;
218214455Srpaulo
219214455Srpaulo	err = snf_start(p->md.snf_handle);
220214455Srpaulo	if (err != 0) {
221214455Srpaulo		snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
222214455Srpaulo			 "snf_start failed: %s", pcap_strerror(err));
223214455Srpaulo		return -1;
224214455Srpaulo	}
225214455Srpaulo
226214455Srpaulo	/*
227214455Srpaulo	 * "select()" and "poll()" don't work on snf descriptors.
228214455Srpaulo	 */
229214455Srpaulo	p->selectable_fd = -1;
230214455Srpaulo	p->linktype = DLT_EN10MB;
231214455Srpaulo	p->read_op = snf_read;
232214455Srpaulo	p->inject_op = snf_inject;
233214455Srpaulo	p->setfilter_op = snf_setfilter;
234214455Srpaulo	p->setdirection_op = NULL; /* Not implemented.*/
235214455Srpaulo	p->set_datalink_op = snf_set_datalink;
236214455Srpaulo	p->getnonblock_op = snf_getnonblock;
237214455Srpaulo	p->setnonblock_op = snf_setnonblock;
238214455Srpaulo	p->stats_op = snf_pcap_stats;
239214455Srpaulo	p->cleanup_op = snf_platform_cleanup;
240214455Srpaulo	p->md.stat.ps_recv = 0;
241214455Srpaulo	p->md.stat.ps_drop = 0;
242214455Srpaulo	p->md.stat.ps_ifdrop = 0;
243214455Srpaulo	return 0;
244214455Srpaulo}
245214455Srpaulo
246214455Srpauloint
247251129Sdelphijsnf_findalldevs(pcap_if_t **devlistp, char *errbuf)
248214455Srpaulo{
249214455Srpaulo	/*
250214455Srpaulo	 * There are no platform-specific devices since each device
251214455Srpaulo	 * exists as a regular Ethernet device.
252214455Srpaulo	 */
253214455Srpaulo	return 0;
254214455Srpaulo}
255214455Srpaulo
256214455Srpaulopcap_t *
257251129Sdelphijsnf_create(const char *device, char *ebuf, int *is_ours)
258214455Srpaulo{
259214455Srpaulo	pcap_t *p;
260214455Srpaulo	int boardnum = -1;
261214455Srpaulo	struct snf_ifaddrs *ifaddrs, *ifa;
262214455Srpaulo	size_t devlen;
263214455Srpaulo
264251129Sdelphij	if (snf_init(SNF_VERSION_API)) {
265251129Sdelphij		/* Can't initialize the API, so no SNF devices */
266251129Sdelphij		*is_ours = 0;
267214455Srpaulo		return NULL;
268251129Sdelphij	}
269214455Srpaulo
270214455Srpaulo	/*
271214455Srpaulo	 * Match a given interface name to our list of interface names, from
272214455Srpaulo	 * which we can obtain the intended board number
273214455Srpaulo	 */
274251129Sdelphij	if (snf_getifaddrs(&ifaddrs) || ifaddrs == NULL) {
275251129Sdelphij		/* Can't get SNF addresses */
276251129Sdelphij		*is_ours = 0;
277214455Srpaulo		return NULL;
278251129Sdelphij	}
279214455Srpaulo	devlen = strlen(device) + 1;
280214455Srpaulo	ifa = ifaddrs;
281214455Srpaulo	while (ifa) {
282214455Srpaulo		if (!strncmp(device, ifa->snf_ifa_name, devlen)) {
283214455Srpaulo			boardnum = ifa->snf_ifa_boardnum;
284214455Srpaulo			break;
285214455Srpaulo		}
286214455Srpaulo		ifa = ifa->snf_ifa_next;
287214455Srpaulo	}
288214455Srpaulo	snf_freeifaddrs(ifaddrs);
289214455Srpaulo
290214455Srpaulo	if (ifa == NULL) {
291214455Srpaulo		/*
292214455Srpaulo		 * If we can't find the device by name, support the name "snfX"
293214455Srpaulo		 * and "snf10gX" where X is the board number.
294214455Srpaulo		 */
295214455Srpaulo		if (sscanf(device, "snf10g%d", &boardnum) != 1 &&
296251129Sdelphij		    sscanf(device, "snf%d", &boardnum) != 1) {
297251129Sdelphij			/* Nope, not a supported name */
298251129Sdelphij			*is_ours = 0;
299214455Srpaulo			return NULL;
300251129Sdelphij		    }
301214455Srpaulo	}
302214455Srpaulo
303251129Sdelphij	/* OK, it's probably ours. */
304251129Sdelphij	*is_ours = 1;
305251129Sdelphij
306214455Srpaulo	p = pcap_create_common(device, ebuf);
307214455Srpaulo	if (p == NULL)
308214455Srpaulo		return NULL;
309214455Srpaulo
310214455Srpaulo	p->activate_op = snf_activate;
311214455Srpaulo	p->md.snf_boardnum = boardnum;
312214455Srpaulo	return p;
313214455Srpaulo}
314