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