]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libpcap/pcap-netmap.c
MFC r313695, r313760, r314769, r314863, r314865, r316125
[FreeBSD/FreeBSD.git] / lib / libpcap / pcap-netmap.c
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  * $FreeBSD$
27  */
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include <poll.h>
34 #include <ctype.h>
35 #include <errno.h>
36 #include <netdb.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41
42 #define NETMAP_WITH_LIBS
43 #include <net/netmap_user.h>
44
45 #include "pcap-int.h"
46
47 #define NM_PRIV(p)      ((struct pcap_netmap *)(p->priv))
48
49 #if defined (linux)
50 /* On FreeBSD we use IFF_PPROMISC which is in ifr_flagshigh.
51  * remap to IFF_PROMISC on linux
52  */
53 #define IFF_PPROMISC    IFF_PROMISC
54 #endif /* linux */
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 = NM_PRIV(p);
69
70         ps->ps_recv = 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 = NM_PRIV(p);
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 = NM_PRIV(p);
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 nm_desc *d = NM_PRIV(p)->d;
123
124         return nm_inject(d, buf, size);
125 }
126
127
128 static int
129 pcap_netmap_ioctl(pcap_t *p, u_long what, uint32_t *if_flags)
130 {
131         struct pcap_netmap *pn = NM_PRIV(p);
132         struct nm_desc *d = pn->d;
133         struct ifreq ifr;
134         int error, fd = d->fd;
135
136 #ifdef linux
137         fd = socket(AF_INET, SOCK_DGRAM, 0);
138         if (fd < 0) {
139                 fprintf(stderr, "Error: cannot get device control socket.\n");
140                 return -1;
141         }
142 #endif /* linux */
143         bzero(&ifr, sizeof(ifr));
144         strncpy(ifr.ifr_name, d->req.nr_name, sizeof(ifr.ifr_name));
145         switch (what) {
146         case SIOCSIFFLAGS:
147                 ifr.ifr_flags = *if_flags;
148 #ifdef __FreeBSD__
149                 ifr.ifr_flagshigh = *if_flags >> 16;
150 #endif /* __FreeBSD__ */
151                 break;
152         }
153         error = ioctl(fd, what, &ifr);
154         if (!error) {
155                 switch (what) {
156                 case SIOCGIFFLAGS:
157                         *if_flags = ifr.ifr_flags;
158 #ifdef __FreeBSD__
159                         *if_flags |= (ifr.ifr_flagshigh << 16);
160 #endif /* __FreeBSD__ */
161                 }
162         }
163 #ifdef linux
164         close(fd);
165 #endif /* linux */
166         return error ? -1 : 0;
167 }
168
169
170 static void
171 pcap_netmap_close(pcap_t *p)
172 {
173         struct pcap_netmap *pn = NM_PRIV(p);
174         struct nm_desc *d = pn->d;
175         uint32_t if_flags = 0;
176
177         if (pn->must_clear_promisc) {
178                 pcap_netmap_ioctl(p, SIOCGIFFLAGS, &if_flags); /* fetch flags */
179                 if (if_flags & IFF_PPROMISC) {
180                         if_flags &= ~IFF_PPROMISC;
181                         pcap_netmap_ioctl(p, SIOCSIFFLAGS, &if_flags);
182                 }
183         }
184         nm_close(d);
185         pcap_cleanup_live_common(p);
186 }
187
188
189 static int
190 pcap_netmap_activate(pcap_t *p)
191 {
192         struct pcap_netmap *pn = NM_PRIV(p);
193         struct nm_desc *d = nm_open(p->opt.device, NULL, 0, NULL);
194         uint32_t if_flags = 0;
195
196         if (d == NULL) {
197                 snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
198                         "netmap open: cannot access %s: %s\n",
199                         p->opt.device, pcap_strerror(errno));
200                 pcap_cleanup_live_common(p);
201                 return (PCAP_ERROR);
202         }
203         if (0)
204             fprintf(stderr, "%s device %s priv %p fd %d ports %d..%d\n",
205                 __FUNCTION__, p->opt.device, d, d->fd,
206                 d->first_rx_ring, d->last_rx_ring);
207         pn->d = d;
208         p->fd = d->fd;
209         if (p->opt.promisc && !(d->req.nr_ringid & NETMAP_SW_RING)) {
210                 pcap_netmap_ioctl(p, SIOCGIFFLAGS, &if_flags); /* fetch flags */
211                 if (!(if_flags & IFF_PPROMISC)) {
212                         pn->must_clear_promisc = 1;
213                         if_flags |= IFF_PPROMISC;
214                         pcap_netmap_ioctl(p, SIOCSIFFLAGS, &if_flags);
215                 }
216         }
217         p->linktype = DLT_EN10MB;
218         p->selectable_fd = p->fd;
219         p->read_op = pcap_netmap_dispatch;
220         p->inject_op = pcap_netmap_inject;
221         p->setfilter_op = install_bpf_program;
222         p->setdirection_op = NULL;
223         p->set_datalink_op = NULL;
224         p->getnonblock_op = pcap_getnonblock_fd;
225         p->setnonblock_op = pcap_setnonblock_fd;
226         p->stats_op = pcap_netmap_stats;
227         p->cleanup_op = pcap_netmap_close;
228
229         return (0);
230 }
231
232
233 pcap_t *
234 pcap_netmap_create(const char *device, char *ebuf, int *is_ours)
235 {
236         pcap_t *p;
237
238         *is_ours = (!strncmp(device, "netmap:", 7) || !strncmp(device, "vale", 4));
239         if (! *is_ours)
240                 return NULL;
241         p = pcap_create_common(ebuf, sizeof (struct pcap_netmap));
242         if (p == NULL)
243                 return (NULL);
244         p->activate_op = pcap_netmap_activate;
245         return (p);
246 }