xref: /netbsd-src/external/bsd/libpcap/dist/pcap-snf.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /*	$NetBSD: pcap-snf.c,v 1.1.1.3 2013/12/31 16:57:23 christos Exp $	*/
2 
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
6 
7 #include <sys/param.h>
8 
9 #include <stdlib.h>
10 #include <string.h>
11 #include <errno.h>
12 
13 #include <ctype.h>
14 #include <netinet/in.h>
15 #include <sys/mman.h>
16 #include <sys/socket.h>
17 #include <sys/types.h>
18 #include <unistd.h>
19 
20 #include <snf.h>
21 
22 #include "pcap-int.h"
23 #include "pcap-snf.h"
24 
25 /*
26  * Private data for capturing on SNF devices.
27  */
28 struct pcap_snf {
29 	snf_handle_t snf_handle; /* opaque device handle */
30 	snf_ring_t   snf_ring;   /* opaque device ring handle */
31         int          snf_timeout;
32         int          snf_boardnum;
33 };
34 
35 static int
36 snf_set_datalink(pcap_t *p, int dlt)
37 {
38 	p->linktype = dlt;
39 	return (0);
40 }
41 
42 static int
43 snf_pcap_stats(pcap_t *p, struct pcap_stat *ps)
44 {
45 	struct snf_ring_stats stats;
46 	int rc;
47 
48 	if ((rc = snf_ring_getstats(ps->snf_ring, &stats))) {
49 		snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "snf_get_stats: %s",
50 			 pcap_strerror(rc));
51 		return -1;
52 	}
53 	ps->ps_recv = stats.ring_pkt_recv + stats.ring_pkt_overflow;
54 	ps->ps_drop = stats.ring_pkt_overflow;
55 	ps->ps_ifdrop = stats.nic_pkt_overflow + stats.nic_pkt_bad;
56 	return 0;
57 }
58 
59 static void
60 snf_platform_cleanup(pcap_t *p)
61 {
62 	struct pcap_snf *ps = p->priv;
63 
64 	if (p == NULL)
65 		return;
66 
67 	snf_ring_close(ps->snf_ring);
68 	snf_close(ps->snf_handle);
69 	pcap_cleanup_live_common(p);
70 }
71 
72 static int
73 snf_getnonblock(pcap_t *p, char *errbuf)
74 {
75 	struct pcap_snf *ps = p->priv;
76 
77 	return (ps->snf_timeout == 0);
78 }
79 
80 static int
81 snf_setnonblock(pcap_t *p, int nonblock, char *errbuf)
82 {
83 	struct pcap_snf *ps = p->priv;
84 
85 	if (nonblock)
86 		ps->snf_timeout = 0;
87 	else {
88 		if (p->opt.timeout <= 0)
89 			ps->snf_timeout = -1; /* forever */
90 		else
91 			ps->snf_timeout = p->opt.timeout;
92 	}
93 	return (0);
94 }
95 
96 #define _NSEC_PER_SEC 1000000000
97 
98 static inline
99 struct timeval
100 snf_timestamp_to_timeval(const int64_t ts_nanosec)
101 {
102 	struct timeval tv;
103 	int32_t rem;
104 	if (ts_nanosec == 0)
105 		return (struct timeval) { 0, 0 };
106 	tv.tv_sec = ts_nanosec / _NSEC_PER_SEC;
107 	tv.tv_usec = (ts_nanosec % _NSEC_PER_SEC) / 1000;
108 	return tv;
109 }
110 
111 static int
112 snf_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
113 {
114 	struct pcap_snf *ps = p->priv;
115 	struct pcap_pkthdr hdr;
116 	int i, flags, err, caplen, n;
117 	struct snf_recv_req req;
118 
119 	if (!p || cnt == 0)
120 		return -1;
121 
122 	n = 0;
123 	while (n < cnt || cnt < 0) {
124 		/*
125 		 * Has "pcap_breakloop()" been called?
126 		 */
127 		if (p->break_loop) {
128 			if (n == 0) {
129 				p->break_loop = 0;
130 				return (-2);
131 			} else {
132 				return (n);
133 			}
134 		}
135 
136 		err = snf_ring_recv(ps->snf_ring, ps->snf_timeout, &req);
137 
138 		if (err) {
139 			if (err == EBUSY || err == EAGAIN)
140 				return (0);
141 			if (err == EINTR)
142 				continue;
143 			if (err != 0) {
144 				snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "snf_read: %s",
145 				 	 pcap_strerror(err));
146 				return -1;
147 			}
148 		}
149 
150 		caplen = req.length;
151 		if (caplen > p->snapshot)
152 			caplen = p->snapshot;
153 
154 		if ((p->fcode.bf_insns == NULL) ||
155 		     bpf_filter(p->fcode.bf_insns, req.pkt_addr, req.length, caplen)) {
156 			hdr.ts = snf_timestamp_to_timeval(req.timestamp);
157 			hdr.caplen = caplen;
158 			hdr.len = req.length;
159 			callback(user, &hdr, req.pkt_addr);
160 		}
161 		n++;
162 	}
163 	return (n);
164 }
165 
166 static int
167 snf_setfilter(pcap_t *p, struct bpf_program *fp)
168 {
169 	if (!p)
170 		return -1;
171 	if (!fp) {
172 		strncpy(p->errbuf, "setfilter: No filter specified",
173 			sizeof(p->errbuf));
174 		return -1;
175 	}
176 
177 	/* Make our private copy of the filter */
178 
179 	if (install_bpf_program(p, fp) < 0)
180 		return -1;
181 
182 	return (0);
183 }
184 
185 static int
186 snf_inject(pcap_t *p, const void *buf _U_, size_t size _U_)
187 {
188 	strlcpy(p->errbuf, "Sending packets isn't supported with snf",
189 	    PCAP_ERRBUF_SIZE);
190 	return (-1);
191 }
192 
193 static int
194 snf_activate(pcap_t* p)
195 {
196 	struct pcap_snf *ps = p->priv;
197 	char *device = p->opt.source;
198 	const char *nr = NULL;
199 	int err;
200 	int flags = 0;
201 
202 	if (device == NULL) {
203 		snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
204 			 "device is NULL: %s", pcap_strerror(errno));
205 		return -1;
206 	}
207 
208 	/* In Libpcap, we set pshared by default if NUM_RINGS is set to > 1.
209 	 * Since libpcap isn't thread-safe */
210 	if ((nr = getenv("SNF_NUM_RINGS")) && *nr && atoi(nr) > 1)
211 		flags |= SNF_F_PSHARED;
212 	else
213 		nr = NULL;
214 
215 	err = snf_open(ps->snf_boardnum,
216 			0, /* let SNF API parse SNF_NUM_RINGS, if set */
217 			NULL, /* default RSS, or use SNF_RSS_FLAGS env */
218 			0, /* default to SNF_DATARING_SIZE from env */
219 			flags, /* may want pshared */
220 			&ps->snf_handle);
221 	if (err != 0) {
222 		snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
223 			 "snf_open failed: %s", pcap_strerror(err));
224 		return -1;
225 	}
226 
227 	err = snf_ring_open(ps->snf_handle, &ps->snf_ring);
228 	if (err != 0) {
229 		snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
230 			 "snf_ring_open failed: %s", pcap_strerror(err));
231 		return -1;
232 	}
233 
234 	if (p->opt.timeout <= 0)
235 		ps->snf_timeout = -1;
236 	else
237 		ps->snf_timeout = p->opt.timeout;
238 
239 	err = snf_start(ps->snf_handle);
240 	if (err != 0) {
241 		snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
242 			 "snf_start failed: %s", pcap_strerror(err));
243 		return -1;
244 	}
245 
246 	/*
247 	 * "select()" and "poll()" don't work on snf descriptors.
248 	 */
249 	p->selectable_fd = -1;
250 	p->linktype = DLT_EN10MB;
251 	p->read_op = snf_read;
252 	p->inject_op = snf_inject;
253 	p->setfilter_op = snf_setfilter;
254 	p->setdirection_op = NULL; /* Not implemented.*/
255 	p->set_datalink_op = snf_set_datalink;
256 	p->getnonblock_op = snf_getnonblock;
257 	p->setnonblock_op = snf_setnonblock;
258 	p->stats_op = snf_pcap_stats;
259 	p->cleanup_op = snf_platform_cleanup;
260 	return 0;
261 }
262 
263 int
264 snf_findalldevs(pcap_if_t **devlistp, char *errbuf)
265 {
266 	/*
267 	 * There are no platform-specific devices since each device
268 	 * exists as a regular Ethernet device.
269 	 */
270 	return 0;
271 }
272 
273 pcap_t *
274 snf_create(const char *device, char *ebuf, int *is_ours)
275 {
276 	pcap_t *p;
277 	int boardnum = -1;
278 	struct snf_ifaddrs *ifaddrs, *ifa;
279 	size_t devlen;
280 	struct pcap_snf *ps;
281 
282 	if (snf_init(SNF_VERSION_API)) {
283 		/* Can't initialize the API, so no SNF devices */
284 		*is_ours = 0;
285 		return NULL;
286 	}
287 
288 	/*
289 	 * Match a given interface name to our list of interface names, from
290 	 * which we can obtain the intended board number
291 	 */
292 	if (snf_getifaddrs(&ifaddrs) || ifaddrs == NULL) {
293 		/* Can't get SNF addresses */
294 		*is_ours = 0;
295 		return NULL;
296 	}
297 	devlen = strlen(device) + 1;
298 	ifa = ifaddrs;
299 	while (ifa) {
300 		if (!strncmp(device, ifa->snf_ifa_name, devlen)) {
301 			boardnum = ifa->snf_ifa_boardnum;
302 			break;
303 		}
304 		ifa = ifa->snf_ifa_next;
305 	}
306 	snf_freeifaddrs(ifaddrs);
307 
308 	if (ifa == NULL) {
309 		/*
310 		 * If we can't find the device by name, support the name "snfX"
311 		 * and "snf10gX" where X is the board number.
312 		 */
313 		if (sscanf(device, "snf10g%d", &boardnum) != 1 &&
314 		    sscanf(device, "snf%d", &boardnum) != 1) {
315 			/* Nope, not a supported name */
316 			*is_ours = 0;
317 			return NULL;
318 		    }
319 	}
320 
321 	/* OK, it's probably ours. */
322 	*is_ours = 1;
323 
324 	p = pcap_create_common(device, ebuf, sizeof (struct pcap_snf));
325 	if (p == NULL)
326 		return NULL;
327 	ps = p->priv;
328 
329 	p->activate_op = snf_activate;
330 	ps->snf_boardnum = boardnum;
331 	return p;
332 }
333