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