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 45/* 46 * $FreeBSD$ 47 * 48 * This code is meant to build also on other versions of libpcap. 49 * 50 * older libpcap miss p->priv, use p->md.device instead (and allocate). 51 * Also opt.timeout was in md.timeout before. 52 * Use #define PCAP_IF_UP to discriminate 53 */ 54#ifdef PCAP_IF_UP 55#define NM_PRIV(p) ((struct pcap_netmap *)(p->priv)) 56#define the_timeout opt.timeout 57#else 58#define HAVE_NO_PRIV 59#define NM_PRIV(p) ((struct pcap_netmap *)(p->md.device)) 60#define SET_PRIV(p, x) p->md.device = (void *)x 61#define the_timeout md.timeout 62#endif 63 64#if defined (linux) 65/* On FreeBSD we use IFF_PPROMISC which is in ifr_flagshigh. 66 * remap to IFF_PROMISC on linux 67 */ 68#define IFF_PPROMISC IFF_PROMISC 69#endif /* linux */ 70 71struct pcap_netmap { 72 struct nm_desc *d; /* pointer returned by nm_open() */ 73 pcap_handler cb; /* callback and argument */ 74 u_char *cb_arg; 75 int must_clear_promisc; /* flag */ 76 uint64_t rx_pkts; /* # of pkts received before the filter */ 77}; 78 79 80static int 81pcap_netmap_stats(pcap_t *p, struct pcap_stat *ps) 82{ 83 struct pcap_netmap *pn = NM_PRIV(p); 84 85 ps->ps_recv = pn->rx_pkts; 86 ps->ps_drop = 0; 87 ps->ps_ifdrop = 0; 88 return 0; 89} 90 91 92static void 93pcap_netmap_filter(u_char *arg, struct pcap_pkthdr *h, const u_char *buf) 94{ 95 pcap_t *p = (pcap_t *)arg; 96 struct pcap_netmap *pn = NM_PRIV(p); 97 const struct bpf_insn *pc = p->fcode.bf_insns; 98 99 ++pn->rx_pkts; 100 if (pc == NULL || bpf_filter(pc, buf, h->len, h->caplen)) 101 pn->cb(pn->cb_arg, h, buf); 102} 103 104 105static int 106pcap_netmap_dispatch(pcap_t *p, int cnt, pcap_handler cb, u_char *user) 107{ 108 int ret; 109 struct pcap_netmap *pn = NM_PRIV(p); 110 struct nm_desc *d = pn->d; 111 struct pollfd pfd = { .fd = p->fd, .events = POLLIN, .revents = 0 }; 112 113 pn->cb = cb; 114 pn->cb_arg = user; 115 116 for (;;) { 117 if (p->break_loop) { 118 p->break_loop = 0; 119 return PCAP_ERROR_BREAK; 120 } 121 /* nm_dispatch won't run forever */ 122 123 ret = nm_dispatch((void *)d, cnt, (void *)pcap_netmap_filter, (void *)p); 124 if (ret != 0) 125 break; 126 errno = 0; 127 ret = poll(&pfd, 1, p->the_timeout); 128 } 129 return ret; 130} 131 132 133/* XXX need to check the NIOCTXSYNC/poll */ 134static int 135pcap_netmap_inject(pcap_t *p, const void *buf, size_t size) 136{ 137 struct nm_desc *d = NM_PRIV(p)->d; 138 139 return nm_inject(d, buf, size); 140} 141 142 143static int 144pcap_netmap_ioctl(pcap_t *p, u_long what, uint32_t *if_flags) 145{ 146 struct pcap_netmap *pn = NM_PRIV(p); 147 struct nm_desc *d = pn->d; 148 struct ifreq ifr; 149 int error, fd = d->fd; 150 151#ifdef linux 152 fd = socket(AF_INET, SOCK_DGRAM, 0); 153 if (fd < 0) { 154 fprintf(stderr, "Error: cannot get device control socket.\n"); 155 return -1; 156 } 157#endif /* linux */ 158 bzero(&ifr, sizeof(ifr)); 159 strncpy(ifr.ifr_name, d->req.nr_name, sizeof(ifr.ifr_name)); 160 switch (what) { 161 case SIOCSIFFLAGS: 162 ifr.ifr_flags = *if_flags; 163#ifdef __FreeBSD__ 164 ifr.ifr_flagshigh = *if_flags >> 16; 165#endif /* __FreeBSD__ */ 166 break; 167 } 168 error = ioctl(fd, what, &ifr); 169 if (!error) { 170 switch (what) { 171 case SIOCGIFFLAGS: 172 *if_flags = ifr.ifr_flags; 173#ifdef __FreeBSD__ 174 *if_flags |= (ifr.ifr_flagshigh << 16); 175#endif /* __FreeBSD__ */ 176 } 177 } 178#ifdef linux 179 close(fd); 180#endif /* linux */ 181 return error ? -1 : 0; 182} 183 184 185static void 186pcap_netmap_close(pcap_t *p) 187{ 188 struct pcap_netmap *pn = NM_PRIV(p); 189 struct nm_desc *d = pn->d; 190 uint32_t if_flags = 0; 191 192 if (pn->must_clear_promisc) { 193 pcap_netmap_ioctl(p, SIOCGIFFLAGS, &if_flags); /* fetch flags */ 194 if (if_flags & IFF_PPROMISC) { 195 if_flags &= ~IFF_PPROMISC; 196 pcap_netmap_ioctl(p, SIOCSIFFLAGS, &if_flags); 197 } 198 } 199 nm_close(d); 200#ifdef HAVE_NO_PRIV 201 free(pn); 202 SET_PRIV(p, NULL); // unnecessary 203#endif 204 pcap_cleanup_live_common(p); 205} 206 207 208static int 209pcap_netmap_activate(pcap_t *p) 210{ 211 struct pcap_netmap *pn = NM_PRIV(p); 212 struct nm_desc *d = nm_open(p->opt.source, NULL, 0, NULL); 213 uint32_t if_flags = 0; 214 215 if (d == NULL) { 216 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, 217 "netmap open: cannot access %s: %s\n", 218 p->opt.source, pcap_strerror(errno)); 219#ifdef HAVE_NO_PRIV 220 free(pn); 221 SET_PRIV(p, NULL); // unnecessary 222#endif 223 pcap_cleanup_live_common(p); 224 return (PCAP_ERROR); 225 } 226 if (0) 227 fprintf(stderr, "%s device %s priv %p fd %d ports %d..%d\n", 228 __FUNCTION__, p->opt.source, d, d->fd, 229 d->first_rx_ring, d->last_rx_ring); 230 pn->d = d; 231 p->fd = d->fd; 232 if (p->opt.promisc && !(d->req.nr_ringid & NETMAP_SW_RING)) { 233 pcap_netmap_ioctl(p, SIOCGIFFLAGS, &if_flags); /* fetch flags */ 234 if (!(if_flags & IFF_PPROMISC)) { 235 pn->must_clear_promisc = 1; 236 if_flags |= IFF_PPROMISC; 237 pcap_netmap_ioctl(p, SIOCSIFFLAGS, &if_flags); 238 } 239 } 240 p->linktype = DLT_EN10MB; 241 p->selectable_fd = p->fd; 242 p->read_op = pcap_netmap_dispatch; 243 p->inject_op = pcap_netmap_inject; 244 p->setfilter_op = install_bpf_program; 245 p->setdirection_op = NULL; 246 p->set_datalink_op = NULL; 247 p->getnonblock_op = pcap_getnonblock_fd; 248 p->setnonblock_op = pcap_setnonblock_fd; 249 p->stats_op = pcap_netmap_stats; 250 p->cleanup_op = pcap_netmap_close; 251 252 return (0); 253} 254 255 256pcap_t * 257pcap_netmap_create(const char *device, char *ebuf, int *is_ours) 258{ 259 pcap_t *p; 260 261 *is_ours = (!strncmp(device, "netmap:", 7) || !strncmp(device, "vale", 4)); 262 if (! *is_ours) 263 return NULL; 264#ifdef HAVE_NO_PRIV 265 { 266 void *pn = calloc(1, sizeof(struct pcap_netmap)); 267 if (pn == NULL) 268 return NULL; 269 p = pcap_create_common(device, ebuf); 270 if (p == NULL) { 271 free(pn); 272 return NULL; 273 } 274 SET_PRIV(p, pn); 275 } 276#else 277 p = pcap_create_common(device, ebuf, sizeof (struct pcap_netmap)); 278 if (p == NULL) 279 return (NULL); 280#endif 281 p->activate_op = pcap_netmap_activate; 282 return (p); 283} 284