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