nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 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 | #if SNF_VERSION_API >= 0x0003 |
||
20 | #define SNF_HAVE_INJECT_API |
||
21 | #endif |
||
22 | |||
23 | #include "pcap-int.h" |
||
24 | #include "pcap-snf.h" |
||
25 | |||
26 | /* |
||
27 | * Private data for capturing on SNF devices. |
||
28 | */ |
||
29 | struct pcap_snf { |
||
30 | snf_handle_t snf_handle; /* opaque device handle */ |
||
31 | snf_ring_t snf_ring; /* opaque device ring handle */ |
||
32 | #ifdef SNF_HAVE_INJECT_API |
||
33 | snf_inject_t snf_inj; /* inject handle, if inject is used */ |
||
34 | #endif |
||
35 | int snf_timeout; |
||
36 | int snf_boardnum; |
||
37 | }; |
||
38 | |||
39 | static int |
||
40 | snf_set_datalink(pcap_t *p, int dlt) |
||
41 | { |
||
42 | p->linktype = dlt; |
||
43 | return (0); |
||
44 | } |
||
45 | |||
46 | static int |
||
47 | snf_pcap_stats(pcap_t *p, struct pcap_stat *ps) |
||
48 | { |
||
49 | struct snf_ring_stats stats; |
||
50 | struct pcap_snf *snfps = p->priv; |
||
51 | int rc; |
||
52 | |||
53 | if ((rc = snf_ring_getstats(snfps->snf_ring, &stats))) { |
||
54 | snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "snf_get_stats: %s", |
||
55 | pcap_strerror(rc)); |
||
56 | return -1; |
||
57 | } |
||
58 | ps->ps_recv = stats.ring_pkt_recv + stats.ring_pkt_overflow; |
||
59 | ps->ps_drop = stats.ring_pkt_overflow; |
||
60 | ps->ps_ifdrop = stats.nic_pkt_overflow + stats.nic_pkt_bad; |
||
61 | return 0; |
||
62 | } |
||
63 | |||
64 | static void |
||
65 | snf_platform_cleanup(pcap_t *p) |
||
66 | { |
||
67 | struct pcap_snf *ps = p->priv; |
||
68 | |||
69 | if (p == NULL) |
||
70 | return; |
||
71 | |||
72 | #ifdef SNF_HAVE_INJECT_API |
||
73 | if (ps->snf_inj) |
||
74 | snf_inject_close(ps->snf_inj); |
||
75 | #endif |
||
76 | snf_ring_close(ps->snf_ring); |
||
77 | snf_close(ps->snf_handle); |
||
78 | pcap_cleanup_live_common(p); |
||
79 | } |
||
80 | |||
81 | static int |
||
82 | snf_getnonblock(pcap_t *p, char *errbuf) |
||
83 | { |
||
84 | struct pcap_snf *ps = p->priv; |
||
85 | |||
86 | return (ps->snf_timeout == 0); |
||
87 | } |
||
88 | |||
89 | static int |
||
90 | snf_setnonblock(pcap_t *p, int nonblock, char *errbuf) |
||
91 | { |
||
92 | struct pcap_snf *ps = p->priv; |
||
93 | |||
94 | if (nonblock) |
||
95 | ps->snf_timeout = 0; |
||
96 | else { |
||
97 | if (p->opt.timeout <= 0) |
||
98 | ps->snf_timeout = -1; /* forever */ |
||
99 | else |
||
100 | ps->snf_timeout = p->opt.timeout; |
||
101 | } |
||
102 | return (0); |
||
103 | } |
||
104 | |||
105 | #define _NSEC_PER_SEC 1000000000 |
||
106 | |||
107 | static inline |
||
108 | struct timeval |
||
109 | snf_timestamp_to_timeval(const int64_t ts_nanosec, const int tstamp_precision) |
||
110 | { |
||
111 | struct timeval tv; |
||
112 | long tv_nsec; |
||
113 | |||
114 | if (ts_nanosec == 0) |
||
115 | return (struct timeval) { 0, 0 }; |
||
116 | |||
117 | tv.tv_sec = ts_nanosec / _NSEC_PER_SEC; |
||
118 | tv_nsec = (ts_nanosec % _NSEC_PER_SEC); |
||
119 | |||
120 | /* libpcap expects tv_usec to be nanos if using nanosecond precision. */ |
||
121 | if (tstamp_precision == PCAP_TSTAMP_PRECISION_NANO) |
||
122 | tv.tv_usec = tv_nsec; |
||
123 | else |
||
124 | tv.tv_usec = tv_nsec / 1000; |
||
125 | |||
126 | return tv; |
||
127 | } |
||
128 | |||
129 | static int |
||
130 | snf_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) |
||
131 | { |
||
132 | struct pcap_snf *ps = p->priv; |
||
133 | struct pcap_pkthdr hdr; |
||
134 | int i, flags, err, caplen, n; |
||
135 | struct snf_recv_req req; |
||
136 | int nonblock, timeout; |
||
137 | |||
138 | if (!p) |
||
139 | return -1; |
||
140 | |||
141 | n = 0; |
||
142 | timeout = ps->snf_timeout; |
||
143 | while (n < cnt || PACKET_COUNT_IS_UNLIMITED(cnt)) { |
||
144 | /* |
||
145 | * Has "pcap_breakloop()" been called? |
||
146 | */ |
||
147 | if (p->break_loop) { |
||
148 | if (n == 0) { |
||
149 | p->break_loop = 0; |
||
150 | return (-2); |
||
151 | } else { |
||
152 | return (n); |
||
153 | } |
||
154 | } |
||
155 | |||
156 | err = snf_ring_recv(ps->snf_ring, timeout, &req); |
||
157 | |||
158 | if (err) { |
||
159 | if (err == EBUSY || err == EAGAIN) { |
||
160 | return (n); |
||
161 | } |
||
162 | else if (err == EINTR) { |
||
163 | timeout = 0; |
||
164 | continue; |
||
165 | } |
||
166 | else { |
||
167 | snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "snf_read: %s", |
||
168 | pcap_strerror(err)); |
||
169 | return -1; |
||
170 | } |
||
171 | } |
||
172 | |||
173 | caplen = req.length; |
||
174 | if (caplen > p->snapshot) |
||
175 | caplen = p->snapshot; |
||
176 | |||
177 | if ((p->fcode.bf_insns == NULL) || |
||
178 | bpf_filter(p->fcode.bf_insns, req.pkt_addr, req.length, caplen)) { |
||
179 | hdr.ts = snf_timestamp_to_timeval(req.timestamp, p->opt.tstamp_precision); |
||
180 | hdr.caplen = caplen; |
||
181 | hdr.len = req.length; |
||
182 | callback(user, &hdr, req.pkt_addr); |
||
183 | } |
||
184 | n++; |
||
185 | |||
186 | /* After one successful packet is received, we won't block |
||
187 | * again for that timeout. */ |
||
188 | if (timeout != 0) |
||
189 | timeout = 0; |
||
190 | } |
||
191 | return (n); |
||
192 | } |
||
193 | |||
194 | static int |
||
195 | snf_setfilter(pcap_t *p, struct bpf_program *fp) |
||
196 | { |
||
197 | if (!p) |
||
198 | return -1; |
||
199 | if (!fp) { |
||
200 | strncpy(p->errbuf, "setfilter: No filter specified", |
||
201 | sizeof(p->errbuf)); |
||
202 | return -1; |
||
203 | } |
||
204 | |||
205 | /* Make our private copy of the filter */ |
||
206 | |||
207 | if (install_bpf_program(p, fp) < 0) |
||
208 | return -1; |
||
209 | |||
210 | return (0); |
||
211 | } |
||
212 | |||
213 | static int |
||
214 | snf_inject(pcap_t *p, const void *buf _U_, size_t size _U_) |
||
215 | { |
||
216 | #ifdef SNF_HAVE_INJECT_API |
||
217 | struct pcap_snf *ps = p->priv; |
||
218 | int rc; |
||
219 | if (ps->snf_inj == NULL) { |
||
220 | rc = snf_inject_open(ps->snf_boardnum, 0, &ps->snf_inj); |
||
221 | if (rc) { |
||
222 | snprintf(p->errbuf, PCAP_ERRBUF_SIZE, |
||
223 | "snf_inject_open: %s", pcap_strerror(rc)); |
||
224 | return (-1); |
||
225 | } |
||
226 | } |
||
227 | |||
228 | rc = snf_inject_send(ps->snf_inj, -1, 0, buf, size); |
||
229 | if (!rc) { |
||
230 | return (size); |
||
231 | } |
||
232 | else { |
||
233 | snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "snf_inject_send: %s", |
||
234 | pcap_strerror(rc)); |
||
235 | return (-1); |
||
236 | } |
||
237 | #else |
||
238 | strlcpy(p->errbuf, "Sending packets isn't supported with this snf version", |
||
239 | PCAP_ERRBUF_SIZE); |
||
240 | return (-1); |
||
241 | #endif |
||
242 | } |
||
243 | |||
244 | static int |
||
245 | snf_activate(pcap_t* p) |
||
246 | { |
||
247 | struct pcap_snf *ps = p->priv; |
||
248 | char *device = p->opt.source; |
||
249 | const char *nr = NULL; |
||
250 | int err; |
||
251 | int flags = -1, ring_id = -1; |
||
252 | |||
253 | if (device == NULL) { |
||
254 | snprintf(p->errbuf, PCAP_ERRBUF_SIZE, |
||
255 | "device is NULL: %s", pcap_strerror(errno)); |
||
256 | return -1; |
||
257 | } |
||
258 | |||
259 | /* In Libpcap, we set pshared by default if NUM_RINGS is set to > 1. |
||
260 | * Since libpcap isn't thread-safe */ |
||
261 | if ((nr = getenv("SNF_FLAGS")) && *nr) |
||
262 | flags = strtol(nr, NULL, 0); |
||
263 | else if ((nr = getenv("SNF_NUM_RINGS")) && *nr && atoi(nr) > 1) |
||
264 | flags = SNF_F_PSHARED; |
||
265 | else |
||
266 | nr = NULL; |
||
267 | |||
268 | err = snf_open(ps->snf_boardnum, |
||
269 | 0, /* let SNF API parse SNF_NUM_RINGS, if set */ |
||
270 | NULL, /* default RSS, or use SNF_RSS_FLAGS env */ |
||
271 | 0, /* default to SNF_DATARING_SIZE from env */ |
||
272 | flags, /* may want pshared */ |
||
273 | &ps->snf_handle); |
||
274 | if (err != 0) { |
||
275 | snprintf(p->errbuf, PCAP_ERRBUF_SIZE, |
||
276 | "snf_open failed: %s", pcap_strerror(err)); |
||
277 | return -1; |
||
278 | } |
||
279 | |||
280 | if ((nr = getenv("SNF_PCAP_RING_ID")) && *nr) { |
||
281 | ring_id = (int) strtol(nr, NULL, 0); |
||
282 | } |
||
283 | err = snf_ring_open_id(ps->snf_handle, ring_id, &ps->snf_ring); |
||
284 | if (err != 0) { |
||
285 | snprintf(p->errbuf, PCAP_ERRBUF_SIZE, |
||
286 | "snf_ring_open_id(ring=%d) failed: %s", |
||
287 | ring_id, pcap_strerror(err)); |
||
288 | return -1; |
||
289 | } |
||
290 | |||
291 | if (p->opt.timeout <= 0) |
||
292 | ps->snf_timeout = -1; |
||
293 | else |
||
294 | ps->snf_timeout = p->opt.timeout; |
||
295 | |||
296 | err = snf_start(ps->snf_handle); |
||
297 | if (err != 0) { |
||
298 | snprintf(p->errbuf, PCAP_ERRBUF_SIZE, |
||
299 | "snf_start failed: %s", pcap_strerror(err)); |
||
300 | return -1; |
||
301 | } |
||
302 | |||
303 | /* |
||
304 | * "select()" and "poll()" don't work on snf descriptors. |
||
305 | */ |
||
306 | p->selectable_fd = -1; |
||
307 | p->linktype = DLT_EN10MB; |
||
308 | p->read_op = snf_read; |
||
309 | p->inject_op = snf_inject; |
||
310 | p->setfilter_op = snf_setfilter; |
||
311 | p->setdirection_op = NULL; /* Not implemented.*/ |
||
312 | p->set_datalink_op = snf_set_datalink; |
||
313 | p->getnonblock_op = snf_getnonblock; |
||
314 | p->setnonblock_op = snf_setnonblock; |
||
315 | p->stats_op = snf_pcap_stats; |
||
316 | p->cleanup_op = snf_platform_cleanup; |
||
317 | #ifdef SNF_HAVE_INJECT_API |
||
318 | ps->snf_inj = NULL; |
||
319 | #endif |
||
320 | return 0; |
||
321 | } |
||
322 | |||
323 | #define MAX_DESC_LENGTH 128 |
||
324 | int |
||
325 | snf_findalldevs(pcap_if_t **devlistp, char *errbuf) |
||
326 | { |
||
327 | pcap_if_t *devlist = NULL,*curdev,*prevdev; |
||
328 | pcap_addr_t *curaddr; |
||
329 | struct snf_ifaddrs *ifaddrs, *ifa; |
||
330 | char desc[MAX_DESC_LENGTH]; |
||
331 | int ret; |
||
332 | |||
333 | if (snf_init(SNF_VERSION_API)) |
||
334 | return (-1); |
||
335 | |||
336 | if (snf_getifaddrs(&ifaddrs) || ifaddrs == NULL) |
||
337 | { |
||
338 | (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, |
||
339 | "snf_getifaddrs: %s", pcap_strerror(errno)); |
||
340 | return (-1); |
||
341 | } |
||
342 | ifa = ifaddrs; |
||
343 | while (ifa) |
||
344 | { |
||
345 | /* |
||
346 | * Allocate a new entry |
||
347 | */ |
||
348 | curdev = (pcap_if_t *)malloc(sizeof(pcap_if_t)); |
||
349 | if (curdev == NULL) { |
||
350 | (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, |
||
351 | "snf_findalldevs malloc: %s", pcap_strerror(errno)); |
||
352 | return (-1); |
||
353 | } |
||
354 | if (devlist == NULL) /* save first entry */ |
||
355 | devlist = curdev; |
||
356 | else |
||
357 | prevdev->next = curdev; |
||
358 | /* |
||
359 | * Fill in the entry. |
||
360 | */ |
||
361 | curdev->next = NULL; |
||
362 | curdev->name = strdup(ifa->snf_ifa_name); |
||
363 | if (curdev->name == NULL) { |
||
364 | (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, |
||
365 | "snf_findalldevs strdup: %s", pcap_strerror(errno)); |
||
366 | free(curdev); |
||
367 | return (-1); |
||
368 | } |
||
369 | (void)snprintf(desc,MAX_DESC_LENGTH,"Myricom snf%d", |
||
370 | ifa->snf_ifa_portnum); |
||
371 | curdev->description = strdup(desc); |
||
372 | if (curdev->description == NULL) { |
||
373 | (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, |
||
374 | "snf_findalldevs strdup1: %s", pcap_strerror(errno)); |
||
375 | free(curdev->name); |
||
376 | free(curdev); |
||
377 | return (-1); |
||
378 | } |
||
379 | curdev->addresses = NULL; |
||
380 | curdev->flags = 0; |
||
381 | |||
382 | curaddr = (pcap_addr_t *)malloc(sizeof(pcap_addr_t)); |
||
383 | if (curaddr == NULL) { |
||
384 | (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, |
||
385 | "snf_findalldevs malloc1: %s", pcap_strerror(errno)); |
||
386 | free(curdev->description); |
||
387 | free(curdev->name); |
||
388 | free(curdev); |
||
389 | return (-1); |
||
390 | } |
||
391 | curdev->addresses = curaddr; |
||
392 | curaddr->next = NULL; |
||
393 | curaddr->addr = (struct sockaddr*)malloc(sizeof(struct sockaddr_storage)); |
||
394 | if (curaddr->addr == NULL) { |
||
395 | (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, |
||
396 | "malloc2: %s", pcap_strerror(errno)); |
||
397 | free(curdev->description); |
||
398 | free(curdev->name); |
||
399 | free(curaddr); |
||
400 | free(curdev); |
||
401 | return (-1); |
||
402 | } |
||
403 | curaddr->addr->sa_family = AF_INET; |
||
404 | curaddr->netmask = NULL; |
||
405 | curaddr->broadaddr = NULL; |
||
406 | curaddr->dstaddr = NULL; |
||
407 | curaddr->next = NULL; |
||
408 | |||
409 | prevdev = curdev; |
||
410 | ifa = ifa->snf_ifa_next; |
||
411 | } |
||
412 | snf_freeifaddrs(ifaddrs); |
||
413 | *devlistp = devlist; |
||
414 | |||
415 | /* |
||
416 | * There are no platform-specific devices since each device |
||
417 | * exists as a regular Ethernet device. |
||
418 | */ |
||
419 | return 0; |
||
420 | } |
||
421 | |||
422 | pcap_t * |
||
423 | snf_create(const char *device, char *ebuf, int *is_ours) |
||
424 | { |
||
425 | pcap_t *p; |
||
426 | int boardnum = -1; |
||
427 | struct snf_ifaddrs *ifaddrs, *ifa; |
||
428 | size_t devlen; |
||
429 | struct pcap_snf *ps; |
||
430 | |||
431 | if (snf_init(SNF_VERSION_API)) { |
||
432 | /* Can't initialize the API, so no SNF devices */ |
||
433 | *is_ours = 0; |
||
434 | return NULL; |
||
435 | } |
||
436 | |||
437 | /* |
||
438 | * Match a given interface name to our list of interface names, from |
||
439 | * which we can obtain the intended board number |
||
440 | */ |
||
441 | if (snf_getifaddrs(&ifaddrs) || ifaddrs == NULL) { |
||
442 | /* Can't get SNF addresses */ |
||
443 | *is_ours = 0; |
||
444 | return NULL; |
||
445 | } |
||
446 | devlen = strlen(device) + 1; |
||
447 | ifa = ifaddrs; |
||
448 | while (ifa) { |
||
449 | if (!strncmp(device, ifa->snf_ifa_name, devlen)) { |
||
450 | boardnum = ifa->snf_ifa_boardnum; |
||
451 | break; |
||
452 | } |
||
453 | ifa = ifa->snf_ifa_next; |
||
454 | } |
||
455 | snf_freeifaddrs(ifaddrs); |
||
456 | |||
457 | if (ifa == NULL) { |
||
458 | /* |
||
459 | * If we can't find the device by name, support the name "snfX" |
||
460 | * and "snf10gX" where X is the board number. |
||
461 | */ |
||
462 | if (sscanf(device, "snf10g%d", &boardnum) != 1 && |
||
463 | sscanf(device, "snf%d", &boardnum) != 1) { |
||
464 | /* Nope, not a supported name */ |
||
465 | *is_ours = 0; |
||
466 | return NULL; |
||
467 | } |
||
468 | } |
||
469 | |||
470 | /* OK, it's probably ours. */ |
||
471 | *is_ours = 1; |
||
472 | |||
473 | p = pcap_create_common(device, ebuf, sizeof (struct pcap_snf)); |
||
474 | if (p == NULL) |
||
475 | return NULL; |
||
476 | ps = p->priv; |
||
477 | |||
478 | /* |
||
479 | * We support microsecond and nanosecond time stamps. |
||
480 | */ |
||
481 | p->tstamp_precision_count = 2; |
||
482 | p->tstamp_precision_list = malloc(2 * sizeof(u_int)); |
||
483 | if (p->tstamp_precision_list == NULL) { |
||
484 | snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", |
||
485 | pcap_strerror(errno)); |
||
486 | if (p->tstamp_type_list != NULL) |
||
487 | free(p->tstamp_type_list); |
||
488 | free(p); |
||
489 | return NULL; |
||
490 | } |
||
491 | p->tstamp_precision_list[0] = PCAP_TSTAMP_PRECISION_MICRO; |
||
492 | p->tstamp_precision_list[1] = PCAP_TSTAMP_PRECISION_NANO; |
||
493 | |||
494 | p->activate_op = snf_activate; |
||
495 | ps->snf_boardnum = boardnum; |
||
496 | return p; |
||
497 | } |