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