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