blob: ee6ffa4dcc6ec225bcb205d17bc52cc71809a2de [file] [log] [blame]
JP Abgrall511eca32014-02-12 13:46:45 -08001#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 */
26struct 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
33static int
34snf_set_datalink(pcap_t *p, int dlt)
35{
36 p->linktype = dlt;
37 return (0);
38}
39
40static int
41snf_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
57static void
58snf_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
70static int
71snf_getnonblock(pcap_t *p, char *errbuf)
72{
73 struct pcap_snf *ps = p->priv;
74
75 return (ps->snf_timeout == 0);
76}
77
78static int
79snf_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
96static inline
97struct timeval
98snf_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
109static int
110snf_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
164static int
165snf_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
183static int
184snf_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
191static int
192snf_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
261int
262snf_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
271pcap_t *
272snf_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}