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