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