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