Home | History | Annotate | Line # | Download | only in dist
pcap-netmap.c revision 1.1.1.2
      1 /*
      2  * Copyright (C) 2014 Luigi Rizzo. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  *
      8  *   1. Redistributions of source code must retain the above copyright
      9  *      notice, this list of conditions and the following disclaimer.
     10  *   2. Redistributions in binary form must reproduce the above copyright
     11  *      notice, this list of conditions and the following disclaimer in the
     12  *      documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''AND
     15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     17  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     24  * SUCH DAMAGE.
     25  */
     26 
     27 #ifdef HAVE_CONFIG_H
     28 #include <config.h>
     29 #endif
     30 
     31 #include <poll.h>
     32 #include <ctype.h>
     33 #include <errno.h>
     34 #include <netdb.h>
     35 #include <stdio.h>
     36 #include <stdlib.h>
     37 #include <string.h>
     38 #include <unistd.h>
     39 
     40 #define NETMAP_WITH_LIBS
     41 #include <net/netmap_user.h>
     42 
     43 #include "pcap-int.h"
     44 #include "pcap-netmap.h"
     45 
     46 #ifndef __FreeBSD__
     47   /*
     48    * On FreeBSD we use IFF_PPROMISC which is in ifr_flagshigh.
     49    * Remap to IFF_PROMISC on other platforms.
     50    *
     51    * XXX - DragonFly BSD?
     52    */
     53   #define IFF_PPROMISC	IFF_PROMISC
     54 #endif /* __FreeBSD__ */
     55 
     56 struct pcap_netmap {
     57 	struct nm_desc *d;	/* pointer returned by nm_open() */
     58 	pcap_handler cb;	/* callback and argument */
     59 	u_char *cb_arg;
     60 	int must_clear_promisc;	/* flag */
     61 	uint64_t rx_pkts;	/* # of pkts received before the filter */
     62 };
     63 
     64 
     65 static int
     66 pcap_netmap_stats(pcap_t *p, struct pcap_stat *ps)
     67 {
     68 	struct pcap_netmap *pn = p->priv;
     69 
     70 	ps->ps_recv = (u_int)pn->rx_pkts;
     71 	ps->ps_drop = 0;
     72 	ps->ps_ifdrop = 0;
     73 	return 0;
     74 }
     75 
     76 
     77 static void
     78 pcap_netmap_filter(u_char *arg, struct pcap_pkthdr *h, const u_char *buf)
     79 {
     80 	pcap_t *p = (pcap_t *)arg;
     81 	struct pcap_netmap *pn = p->priv;
     82 	const struct bpf_insn *pc = p->fcode.bf_insns;
     83 
     84 	++pn->rx_pkts;
     85 	if (pc == NULL || bpf_filter(pc, buf, h->len, h->caplen))
     86 		pn->cb(pn->cb_arg, h, buf);
     87 }
     88 
     89 
     90 static int
     91 pcap_netmap_dispatch(pcap_t *p, int cnt, pcap_handler cb, u_char *user)
     92 {
     93 	int ret;
     94 	struct pcap_netmap *pn = p->priv;
     95 	struct nm_desc *d = pn->d;
     96 	struct pollfd pfd = { .fd = p->fd, .events = POLLIN, .revents = 0 };
     97 
     98 	pn->cb = cb;
     99 	pn->cb_arg = user;
    100 
    101 	for (;;) {
    102 		if (p->break_loop) {
    103 			p->break_loop = 0;
    104 			return PCAP_ERROR_BREAK;
    105 		}
    106 		/* nm_dispatch won't run forever */
    107 
    108 		ret = nm_dispatch((void *)d, cnt, (void *)pcap_netmap_filter, (void *)p);
    109 		if (ret != 0)
    110 			break;
    111 		errno = 0;
    112 		ret = poll(&pfd, 1, p->opt.timeout);
    113 	}
    114 	return ret;
    115 }
    116 
    117 
    118 /* XXX need to check the NIOCTXSYNC/poll */
    119 static int
    120 pcap_netmap_inject(pcap_t *p, const void *buf, size_t size)
    121 {
    122 	struct pcap_netmap *pn = p->priv;
    123 	struct nm_desc *d = pn->d;
    124 
    125 	return nm_inject(d, buf, size);
    126 }
    127 
    128 
    129 static int
    130 pcap_netmap_ioctl(pcap_t *p, u_long what, uint32_t *if_flags)
    131 {
    132 	struct pcap_netmap *pn = p->priv;
    133 	struct nm_desc *d = pn->d;
    134 	struct ifreq ifr;
    135 	int error, fd = d->fd;
    136 
    137 #ifdef linux
    138 	fd = socket(AF_INET, SOCK_DGRAM, 0);
    139 	if (fd < 0) {
    140 		fprintf(stderr, "Error: cannot get device control socket.\n");
    141 		return -1;
    142 	}
    143 #endif /* linux */
    144 	bzero(&ifr, sizeof(ifr));
    145 	strncpy(ifr.ifr_name, d->req.nr_name, sizeof(ifr.ifr_name));
    146 	switch (what) {
    147 	case SIOCSIFFLAGS:
    148 		/*
    149 		 * The flags we pass in are 32-bit and unsigned.
    150 		 *
    151 		 * On most if not all UN*Xes, ifr_flags is 16-bit and
    152 		 * signed, and the result of assigning a longer
    153 		 * unsigned value to a shorter signed value is
    154 		 * implementation-defined (even if, in practice, it'll
    155 		 * do what's intended on all platforms we support
    156 		 * result of assigning a 32-bit unsigned value).
    157 		 * So we mask out the upper 16 bits.
    158 		 */
    159 		ifr.ifr_flags = *if_flags & 0xffff;
    160 #ifdef __FreeBSD__
    161 		/*
    162 		 * In FreeBSD, we need to set the high-order flags,
    163 		 * as we're using IFF_PPROMISC, which is in those bits.
    164 		 *
    165 		 * XXX - DragonFly BSD?
    166 		 */
    167 		ifr.ifr_flagshigh = *if_flags >> 16;
    168 #endif /* __FreeBSD__ */
    169 		break;
    170 	}
    171 	error = ioctl(fd, what, &ifr);
    172 	if (!error) {
    173 		switch (what) {
    174 		case SIOCGIFFLAGS:
    175 			/*
    176 			 * The flags we return are 32-bit.
    177 			 *
    178 			 * On most if not all UN*Xes, ifr_flags is
    179 			 * 16-bit and signed, and will get sign-
    180 			 * extended, so that the upper 16 bits of
    181 			 * those flags will be forced on.  So we
    182 			 * mask out the upper 16 bits of the
    183 			 * sign-extended value.
    184 			 */
    185 			*if_flags = ifr.ifr_flags & 0xffff;
    186 #ifdef __FreeBSD__
    187 			/*
    188 			 * In FreeBSD, we need to return the
    189 			 * high-order flags, as we're using
    190 			 * IFF_PPROMISC, which is in those bits.
    191 			 *
    192 			 * XXX - DragonFly BSD?
    193 			 */
    194 			*if_flags |= (ifr.ifr_flagshigh << 16);
    195 #endif /* __FreeBSD__ */
    196 		}
    197 	}
    198 #ifdef linux
    199 	close(fd);
    200 #endif /* linux */
    201 	return error ? -1 : 0;
    202 }
    203 
    204 
    205 static void
    206 pcap_netmap_close(pcap_t *p)
    207 {
    208 	struct pcap_netmap *pn = p->priv;
    209 	struct nm_desc *d = pn->d;
    210 	uint32_t if_flags = 0;
    211 
    212 	if (pn->must_clear_promisc) {
    213 		pcap_netmap_ioctl(p, SIOCGIFFLAGS, &if_flags); /* fetch flags */
    214 		if (if_flags & IFF_PPROMISC) {
    215 			if_flags &= ~IFF_PPROMISC;
    216 			pcap_netmap_ioctl(p, SIOCSIFFLAGS, &if_flags);
    217 		}
    218 	}
    219 	nm_close(d);
    220 	pcap_cleanup_live_common(p);
    221 }
    222 
    223 
    224 static int
    225 pcap_netmap_activate(pcap_t *p)
    226 {
    227 	struct pcap_netmap *pn = p->priv;
    228 	struct nm_desc *d;
    229 	uint32_t if_flags = 0;
    230 
    231 	d = nm_open(p->opt.device, NULL, 0, NULL);
    232 	if (d == NULL) {
    233 		pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
    234 		    errno, "netmap open: cannot access %s",
    235 		    p->opt.device);
    236 		pcap_cleanup_live_common(p);
    237 		return (PCAP_ERROR);
    238 	}
    239 #if 0
    240 	fprintf(stderr, "%s device %s priv %p fd %d ports %d..%d\n",
    241 	    __FUNCTION__, p->opt.device, d, d->fd,
    242 	    d->first_rx_ring, d->last_rx_ring);
    243 #endif
    244 	pn->d = d;
    245 	p->fd = d->fd;
    246 
    247 	/*
    248 	 * Turn a negative snapshot value (invalid), a snapshot value of
    249 	 * 0 (unspecified), or a value bigger than the normal maximum
    250 	 * value, into the maximum allowed value.
    251 	 *
    252 	 * If some application really *needs* a bigger snapshot
    253 	 * length, we should just increase MAXIMUM_SNAPLEN.
    254 	 */
    255 	if (p->snapshot <= 0 || p->snapshot > MAXIMUM_SNAPLEN)
    256 		p->snapshot = MAXIMUM_SNAPLEN;
    257 
    258 	if (p->opt.promisc && !(d->req.nr_ringid & NETMAP_SW_RING)) {
    259 		pcap_netmap_ioctl(p, SIOCGIFFLAGS, &if_flags); /* fetch flags */
    260 		if (!(if_flags & IFF_PPROMISC)) {
    261 			pn->must_clear_promisc = 1;
    262 			if_flags |= IFF_PPROMISC;
    263 			pcap_netmap_ioctl(p, SIOCSIFFLAGS, &if_flags);
    264 		}
    265 	}
    266 	p->linktype = DLT_EN10MB;
    267 	p->selectable_fd = p->fd;
    268 	p->read_op = pcap_netmap_dispatch;
    269 	p->inject_op = pcap_netmap_inject;
    270 	p->setfilter_op = install_bpf_program;
    271 	p->setdirection_op = NULL;
    272 	p->set_datalink_op = NULL;
    273 	p->getnonblock_op = pcap_getnonblock_fd;
    274 	p->setnonblock_op = pcap_setnonblock_fd;
    275 	p->stats_op = pcap_netmap_stats;
    276 	p->cleanup_op = pcap_netmap_close;
    277 
    278 	return (0);
    279 }
    280 
    281 
    282 pcap_t *
    283 pcap_netmap_create(const char *device, char *ebuf, int *is_ours)
    284 {
    285 	pcap_t *p;
    286 
    287 	*is_ours = (!strncmp(device, "netmap:", 7) || !strncmp(device, "vale", 4));
    288 	if (! *is_ours)
    289 		return NULL;
    290 	p = pcap_create_common(ebuf, sizeof (struct pcap_netmap));
    291 	if (p == NULL)
    292 		return (NULL);
    293 	p->activate_op = pcap_netmap_activate;
    294 	return (p);
    295 }
    296 
    297 /*
    298  * The "device name" for netmap devices isn't a name for a device, it's
    299  * an expression that indicates how the device should be set up, so
    300  * there's no way to enumerate them.
    301  */
    302 int
    303 pcap_netmap_findalldevs(pcap_if_list_t *devlistp _U_, char *err_str _U_)
    304 {
    305 	return 0;
    306 }
    307