1 #include <sys/cdefs.h> 2 __RCSID("$NetBSD: pcap-snf.c,v 1.6 2019/10/01 16:02:12 christos Exp $"); 3 4 /* $NetBSD: pcap-snf.c,v 1.6 2019/10/01 16:02:12 christos Exp $ */ 5 6 #ifdef HAVE_CONFIG_H 7 #include <config.h> 8 #endif 9 10 #ifndef _WIN32 11 #include <sys/param.h> 12 #endif /* !_WIN32 */ 13 14 #include <stdlib.h> 15 #include <string.h> 16 #include <errno.h> 17 18 #include <ctype.h> 19 #ifndef _WIN32 20 #include <netinet/in.h> 21 #include <sys/mman.h> 22 #include <sys/socket.h> 23 #include <sys/types.h> 24 #include <unistd.h> 25 #endif /* !_WIN32 */ 26 27 #include <snf.h> 28 #if SNF_VERSION_API >= 0x0003 29 #define SNF_HAVE_INJECT_API 30 #endif 31 32 #include "pcap-int.h" 33 #include "pcap-snf.h" 34 35 /* 36 * Private data for capturing on SNF devices. 37 */ 38 struct pcap_snf { 39 snf_handle_t snf_handle; /* opaque device handle */ 40 snf_ring_t snf_ring; /* opaque device ring handle */ 41 #ifdef SNF_HAVE_INJECT_API 42 snf_inject_t snf_inj; /* inject handle, if inject is used */ 43 #endif 44 int snf_timeout; 45 int snf_boardnum; 46 }; 47 48 static int 49 snf_set_datalink(pcap_t *p, int dlt) 50 { 51 p->linktype = dlt; 52 return (0); 53 } 54 55 static int 56 snf_pcap_stats(pcap_t *p, struct pcap_stat *ps) 57 { 58 struct snf_ring_stats stats; 59 struct pcap_snf *snfps = p->priv; 60 int rc; 61 62 if ((rc = snf_ring_getstats(snfps->snf_ring, &stats))) { 63 pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, 64 rc, "snf_get_stats"); 65 return -1; 66 } 67 ps->ps_recv = stats.ring_pkt_recv + stats.ring_pkt_overflow; 68 ps->ps_drop = stats.ring_pkt_overflow; 69 ps->ps_ifdrop = stats.nic_pkt_overflow + stats.nic_pkt_bad; 70 return 0; 71 } 72 73 static void 74 snf_platform_cleanup(pcap_t *p) 75 { 76 struct pcap_snf *ps = p->priv; 77 78 #ifdef SNF_HAVE_INJECT_API 79 if (ps->snf_inj) 80 snf_inject_close(ps->snf_inj); 81 #endif 82 snf_ring_close(ps->snf_ring); 83 snf_close(ps->snf_handle); 84 pcap_cleanup_live_common(p); 85 } 86 87 static int 88 snf_getnonblock(pcap_t *p) 89 { 90 struct pcap_snf *ps = p->priv; 91 92 return (ps->snf_timeout == 0); 93 } 94 95 static int 96 snf_setnonblock(pcap_t *p, int nonblock) 97 { 98 struct pcap_snf *ps = p->priv; 99 100 if (nonblock) 101 ps->snf_timeout = 0; 102 else { 103 if (p->opt.timeout <= 0) 104 ps->snf_timeout = -1; /* forever */ 105 else 106 ps->snf_timeout = p->opt.timeout; 107 } 108 return (0); 109 } 110 111 #define _NSEC_PER_SEC 1000000000 112 113 static inline 114 struct timeval 115 snf_timestamp_to_timeval(const int64_t ts_nanosec, const int tstamp_precision) 116 { 117 struct timeval tv; 118 long tv_nsec; 119 const static struct timeval zero_timeval; 120 121 if (ts_nanosec == 0) 122 return zero_timeval; 123 124 tv.tv_sec = ts_nanosec / _NSEC_PER_SEC; 125 tv_nsec = (ts_nanosec % _NSEC_PER_SEC); 126 127 /* libpcap expects tv_usec to be nanos if using nanosecond precision. */ 128 if (tstamp_precision == PCAP_TSTAMP_PRECISION_NANO) 129 tv.tv_usec = tv_nsec; 130 else 131 tv.tv_usec = tv_nsec / 1000; 132 133 return tv; 134 } 135 136 static int 137 snf_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) 138 { 139 struct pcap_snf *ps = p->priv; 140 struct pcap_pkthdr hdr; 141 int i, flags, err, caplen, n; 142 struct snf_recv_req req; 143 int nonblock, timeout; 144 145 if (!p) 146 return -1; 147 148 n = 0; 149 timeout = ps->snf_timeout; 150 while (n < cnt || PACKET_COUNT_IS_UNLIMITED(cnt)) { 151 /* 152 * Has "pcap_breakloop()" been called? 153 */ 154 if (p->break_loop) { 155 if (n == 0) { 156 p->break_loop = 0; 157 return (-2); 158 } else { 159 return (n); 160 } 161 } 162 163 err = snf_ring_recv(ps->snf_ring, timeout, &req); 164 165 if (err) { 166 if (err == EBUSY || err == EAGAIN) { 167 return (n); 168 } 169 else if (err == EINTR) { 170 timeout = 0; 171 continue; 172 } 173 else { 174 pcap_fmt_errmsg_for_errno(p->errbuf, 175 PCAP_ERRBUF_SIZE, err, "snf_read"); 176 return -1; 177 } 178 } 179 180 caplen = req.length; 181 if (caplen > p->snapshot) 182 caplen = p->snapshot; 183 184 if ((p->fcode.bf_insns == NULL) || 185 bpf_filter(p->fcode.bf_insns, req.pkt_addr, req.length, caplen)) { 186 hdr.ts = snf_timestamp_to_timeval(req.timestamp, p->opt.tstamp_precision); 187 hdr.caplen = caplen; 188 hdr.len = req.length; 189 callback(user, &hdr, req.pkt_addr); 190 n++; 191 } 192 193 /* After one successful packet is received, we won't block 194 * again for that timeout. */ 195 if (timeout != 0) 196 timeout = 0; 197 } 198 return (n); 199 } 200 201 static int 202 snf_setfilter(pcap_t *p, struct bpf_program *fp) 203 { 204 if (!p) 205 return -1; 206 if (!fp) { 207 strncpy(p->errbuf, "setfilter: No filter specified", 208 sizeof(p->errbuf)); 209 return -1; 210 } 211 212 /* Make our private copy of the filter */ 213 214 if (install_bpf_program(p, fp) < 0) 215 return -1; 216 217 return (0); 218 } 219 220 static int 221 snf_inject(pcap_t *p, const void *buf _U_, size_t size _U_) 222 { 223 #ifdef SNF_HAVE_INJECT_API 224 struct pcap_snf *ps = p->priv; 225 int rc; 226 if (ps->snf_inj == NULL) { 227 rc = snf_inject_open(ps->snf_boardnum, 0, &ps->snf_inj); 228 if (rc) { 229 pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, 230 rc, "snf_inject_open"); 231 return (-1); 232 } 233 } 234 235 rc = snf_inject_send(ps->snf_inj, -1, 0, buf, size); 236 if (!rc) { 237 return (size); 238 } 239 else { 240 pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, 241 rc, "snf_inject_send"); 242 return (-1); 243 } 244 #else 245 pcap_strlcpy(p->errbuf, "Sending packets isn't supported with this snf version", 246 PCAP_ERRBUF_SIZE); 247 return (-1); 248 #endif 249 } 250 251 static int 252 snf_activate(pcap_t* p) 253 { 254 struct pcap_snf *ps = p->priv; 255 char *device = p->opt.device; 256 const char *nr = NULL; 257 int err; 258 int flags = -1, ring_id = -1; 259 260 if (device == NULL) { 261 pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "device is NULL"); 262 return -1; 263 } 264 265 /* In Libpcap, we set pshared by default if NUM_RINGS is set to > 1. 266 * Since libpcap isn't thread-safe */ 267 if ((nr = getenv("SNF_FLAGS")) && *nr) 268 flags = strtol(nr, NULL, 0); 269 else if ((nr = getenv("SNF_NUM_RINGS")) && *nr && atoi(nr) > 1) 270 flags = SNF_F_PSHARED; 271 else 272 nr = NULL; 273 274 275 /* Allow pcap_set_buffer_size() to set dataring_size. 276 * Default is zero which allows setting from env SNF_DATARING_SIZE. 277 * pcap_set_buffer_size() is in bytes while snf_open() accepts values 278 * between 0 and 1048576 in Megabytes. Values in this range are 279 * mapped to 1MB. 280 */ 281 err = snf_open(ps->snf_boardnum, 282 0, /* let SNF API parse SNF_NUM_RINGS, if set */ 283 NULL, /* default RSS, or use SNF_RSS_FLAGS env */ 284 (p->opt.buffer_size > 0 && p->opt.buffer_size < 1048576) ? 1048576 : p->opt.buffer_size, /* default to SNF_DATARING_SIZE from env */ 285 flags, /* may want pshared */ 286 &ps->snf_handle); 287 if (err != 0) { 288 pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, 289 err, "snf_open failed"); 290 return -1; 291 } 292 293 if ((nr = getenv("SNF_PCAP_RING_ID")) && *nr) { 294 ring_id = (int) strtol(nr, NULL, 0); 295 } 296 err = snf_ring_open_id(ps->snf_handle, ring_id, &ps->snf_ring); 297 if (err != 0) { 298 pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, 299 err, "snf_ring_open_id(ring=%d) failed", ring_id); 300 return -1; 301 } 302 303 /* 304 * Turn a negative snapshot value (invalid), a snapshot value of 305 * 0 (unspecified), or a value bigger than the normal maximum 306 * value, into the maximum allowed value. 307 * 308 * If some application really *needs* a bigger snapshot 309 * length, we should just increase MAXIMUM_SNAPLEN. 310 */ 311 if (p->snapshot <= 0 || p->snapshot > MAXIMUM_SNAPLEN) 312 p->snapshot = MAXIMUM_SNAPLEN; 313 314 if (p->opt.timeout <= 0) 315 ps->snf_timeout = -1; 316 else 317 ps->snf_timeout = p->opt.timeout; 318 319 err = snf_start(ps->snf_handle); 320 if (err != 0) { 321 pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, 322 err, "snf_start failed"); 323 return -1; 324 } 325 326 /* 327 * "select()" and "poll()" don't work on snf descriptors. 328 */ 329 #ifndef _WIN32 330 p->selectable_fd = -1; 331 #endif /* !_WIN32 */ 332 p->linktype = DLT_EN10MB; 333 p->read_op = snf_read; 334 p->inject_op = snf_inject; 335 p->setfilter_op = snf_setfilter; 336 p->setdirection_op = NULL; /* Not implemented.*/ 337 p->set_datalink_op = snf_set_datalink; 338 p->getnonblock_op = snf_getnonblock; 339 p->setnonblock_op = snf_setnonblock; 340 p->stats_op = snf_pcap_stats; 341 p->cleanup_op = snf_platform_cleanup; 342 #ifdef SNF_HAVE_INJECT_API 343 ps->snf_inj = NULL; 344 #endif 345 return 0; 346 } 347 348 #define MAX_DESC_LENGTH 128 349 int 350 snf_findalldevs(pcap_if_list_t *devlistp, char *errbuf) 351 { 352 pcap_if_t *dev; 353 #ifdef _WIN32 354 struct sockaddr_in addr; 355 #endif 356 struct snf_ifaddrs *ifaddrs, *ifa; 357 char name[MAX_DESC_LENGTH]; 358 char desc[MAX_DESC_LENGTH]; 359 int ret, allports = 0, merge = 0; 360 const char *nr = NULL; 361 362 if (snf_init(SNF_VERSION_API)) { 363 (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, 364 "snf_getifaddrs: snf_init failed"); 365 return (-1); 366 } 367 368 if (snf_getifaddrs(&ifaddrs) || ifaddrs == NULL) 369 { 370 pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, 371 errno, "snf_getifaddrs"); 372 return (-1); 373 } 374 if ((nr = getenv("SNF_FLAGS")) && *nr) { 375 errno = 0; 376 merge = strtol(nr, NULL, 0); 377 if (errno) { 378 (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, 379 "snf_getifaddrs: SNF_FLAGS is not a valid number"); 380 return (-1); 381 } 382 merge = merge & SNF_F_AGGREGATE_PORTMASK; 383 } 384 385 for (ifa = ifaddrs; ifa != NULL; ifa = ifa->snf_ifa_next) { 386 /* 387 * Myricom SNF adapter ports may appear as regular 388 * network interfaces, which would already have been 389 * added to the list of adapters by pcap_platform_finddevs() 390 * if this isn't an SNF-only version of libpcap. 391 * 392 * Our create routine intercepts pcap_create() calls for 393 * those interfaces and arranges that they will be 394 * opened using the SNF API instead. 395 * 396 * So if we already have an entry for the device, we 397 * don't add an additional entry for it, we just 398 * update the description for it, if any, to indicate 399 * which snfN device it is. Otherwise, we add an entry 400 * for it. 401 * 402 * In either case, if SNF_F_AGGREGATE_PORTMASK is set 403 * in SNF_FLAGS, we add this port to the bitmask 404 * of ports, which we use to generate a device 405 * we can use to capture on all ports. 406 * 407 * Generate the description string. If port aggregation 408 * is set, use 2^{port number} as the unit number, 409 * rather than {port number}. 410 * 411 * XXX - do entries in this list have IP addresses for 412 * the port? If so, should we add them to the 413 * entry for the device, if they're not already in the 414 * list of IP addresses for the device? 415 */ 416 (void)pcap_snprintf(desc,MAX_DESC_LENGTH,"Myricom %ssnf%d", 417 merge ? "Merge Bitmask Port " : "", 418 merge ? 1 << ifa->snf_ifa_portnum : ifa->snf_ifa_portnum); 419 /* 420 * Add the port to the bitmask. 421 */ 422 if (merge) 423 allports |= 1 << ifa->snf_ifa_portnum; 424 /* 425 * See if there's already an entry for the device 426 * with the name ifa->snf_ifa_name. 427 */ 428 dev = find_dev(devlistp, ifa->snf_ifa_name); 429 if (dev != NULL) { 430 /* 431 * Yes. Update its description. 432 */ 433 char *desc_str; 434 435 desc_str = strdup(desc); 436 if (desc_str == NULL) { 437 pcap_fmt_errmsg_for_errno(errbuf, 438 PCAP_ERRBUF_SIZE, errno, 439 "snf_findalldevs strdup"); 440 return -1; 441 } 442 free(dev->description); 443 dev->description = desc_str; 444 } else { 445 /* 446 * No. Add an entry for it. 447 * 448 * XXX - is there a notion of "up" or "running", 449 * and can we determine whether something's 450 * plugged into the adapter and set 451 * PCAP_IF_CONNECTION_STATUS_CONNECTED or 452 * PCAP_IF_CONNECTION_STATUS_DISCONNECTED? 453 */ 454 dev = add_dev(devlistp, ifa->snf_ifa_name, 0, desc, 455 errbuf); 456 if (dev == NULL) 457 return -1; 458 #ifdef _WIN32 459 /* 460 * On Windows, fill in IP# from device name 461 */ 462 ret = inet_pton(AF_INET, dev->name, &addr.sin_addr); 463 if (ret == 1) { 464 /* 465 * Successful conversion of device name 466 * to IPv4 address. 467 */ 468 addr.sin_family = AF_INET; 469 if (add_addr_to_dev(dev, &addr, sizeof(addr), 470 NULL, 0, NULL, 0, NULL, 0, errbuf) == -1) 471 return -1; 472 } else if (ret == -1) { 473 /* 474 * Error. 475 */ 476 pcap_fmt_errmsg_for_errno(errbuf, 477 PCAP_ERRBUF_SIZE, errno, 478 "sinf_findalldevs inet_pton"); 479 return -1; 480 } 481 #endif _WIN32 482 } 483 } 484 snf_freeifaddrs(ifaddrs); 485 /* 486 * Create a snfX entry if port aggregation is enabled 487 */ 488 if (merge) { 489 /* 490 * Add a new entry with all ports bitmask 491 */ 492 (void)pcap_snprintf(name,MAX_DESC_LENGTH,"snf%d",allports); 493 (void)pcap_snprintf(desc,MAX_DESC_LENGTH,"Myricom Merge Bitmask All Ports snf%d", 494 allports); 495 /* 496 * XXX - is there any notion of "up" and "running" that 497 * would apply to this device, given that it handles 498 * multiple ports? 499 * 500 * Presumably, there's no notion of "connected" vs. 501 * "disconnected", as "is this plugged into a network?" 502 * would be a per-port property. 503 */ 504 if (add_dev(devlistp, name, 505 PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE, desc, 506 errbuf) == NULL) 507 return (-1); 508 /* 509 * XXX - should we give it a list of addresses with all 510 * the addresses for all the ports? 511 */ 512 } 513 514 return 0; 515 } 516 517 pcap_t * 518 snf_create(const char *device, char *ebuf, int *is_ours) 519 { 520 pcap_t *p; 521 int boardnum = -1; 522 struct snf_ifaddrs *ifaddrs, *ifa; 523 size_t devlen; 524 struct pcap_snf *ps; 525 526 if (snf_init(SNF_VERSION_API)) { 527 /* Can't initialize the API, so no SNF devices */ 528 *is_ours = 0; 529 return NULL; 530 } 531 532 /* 533 * Match a given interface name to our list of interface names, from 534 * which we can obtain the intended board number 535 */ 536 if (snf_getifaddrs(&ifaddrs) || ifaddrs == NULL) { 537 /* Can't get SNF addresses */ 538 *is_ours = 0; 539 return NULL; 540 } 541 devlen = strlen(device) + 1; 542 ifa = ifaddrs; 543 while (ifa) { 544 if (strncmp(device, ifa->snf_ifa_name, devlen) == 0) { 545 boardnum = ifa->snf_ifa_boardnum; 546 break; 547 } 548 ifa = ifa->snf_ifa_next; 549 } 550 snf_freeifaddrs(ifaddrs); 551 552 if (ifa == NULL) { 553 /* 554 * If we can't find the device by name, support the name "snfX" 555 * and "snf10gX" where X is the board number. 556 */ 557 if (sscanf(device, "snf10g%d", &boardnum) != 1 && 558 sscanf(device, "snf%d", &boardnum) != 1) { 559 /* Nope, not a supported name */ 560 *is_ours = 0; 561 return NULL; 562 } 563 } 564 565 /* OK, it's probably ours. */ 566 *is_ours = 1; 567 568 p = pcap_create_common(ebuf, sizeof (struct pcap_snf)); 569 if (p == NULL) 570 return NULL; 571 ps = p->priv; 572 573 /* 574 * We support microsecond and nanosecond time stamps. 575 */ 576 p->tstamp_precision_count = 2; 577 p->tstamp_precision_list = malloc(2 * sizeof(u_int)); 578 if (p->tstamp_precision_list == NULL) { 579 pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, errno, 580 "malloc"); 581 pcap_close(p); 582 return NULL; 583 } 584 p->tstamp_precision_list[0] = PCAP_TSTAMP_PRECISION_MICRO; 585 p->tstamp_precision_list[1] = PCAP_TSTAMP_PRECISION_NANO; 586 587 p->activate_op = snf_activate; 588 ps->snf_boardnum = boardnum; 589 return p; 590 } 591 592 #ifdef SNF_ONLY 593 /* 594 * This libpcap build supports only SNF cards, not regular network 595 * interfaces.. 596 */ 597 598 /* 599 * There are no regular interfaces, just SNF interfaces. 600 */ 601 int 602 pcap_platform_finddevs(pcap_if_list_t *devlistp, char *errbuf) 603 { 604 return (0); 605 } 606 607 /* 608 * Attempts to open a regular interface fail. 609 */ 610 pcap_t * 611 pcap_create_interface(const char *device, char *errbuf) 612 { 613 pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, 614 "This version of libpcap only supports SNF cards"); 615 return NULL; 616 } 617 618 /* 619 * Libpcap version string. 620 */ 621 const char * 622 pcap_lib_version(void) 623 { 624 return (PCAP_VERSION_STRING " (SNF-only)"); 625 } 626 #endif 627