configure --- configure 2015-12-17 04:04:48.000000000 +0600 +++ configure 2015-12-25 01:32:09.000000000 +0600 @@ -342,6 +342,10 @@ tpm="yes" libssh2="" vhdx="" +quorum="no" +pcap="no" +pcap_create="no" +bpf="no" numa="" tcmalloc="no" jemalloc="no" @@ -602,7 +606,7 @@ audio_drv_list="oss" audio_possible_drivers="oss sdl pa" # needed for kinfo_getvmmap(3) in libutil.h - LIBS="-lutil $LIBS" + LIBS="-lprocstat -lkvm -lelf -lutil $LIBS" netmap="" # enable netmap autodetect HOST_VARIANT_DIR="freebsd" ;; @@ -905,6 +909,10 @@ ;; --enable-vnc-png) vnc_png="yes" ;; + --enable-pcap) pcap="yes" + ;; + --disable-pcap) pcap="no" + ;; --disable-slirp) slirp="no" ;; --disable-uuid) uuid="no" @@ -2427,6 +2435,14 @@ ########################################## +# getifaddrs (for tests/test-io-channel-socket ) + +have_ifaddrs_h=yes +if ! check_include "ifaddrs.h" ; then + have_ifaddrs_h=no +fi + +########################################## # VTE probe if test "$vte" != "no"; then @@ -2569,6 +2585,50 @@ fi fi +########################################## +# pcap probe + +if test "$pcap" = "yes" -a "$pcap" != "no"; then + cat > $TMPC << EOF +#include +int main(void) { return (pcap_lib_version() == (char *)0 ? 1 : 0); } +EOF + if test "$mingw32" = "no" ; then + libpcap=-lpcap + else + libpcap=-lwpcap + fi + if compile_prog "" "$libpcap" ; then + : + else + echo + echo "Error: Could not find pcap" + echo "Make sure to have the pcap libs and headers installed." + echo + exit 1 + fi + cat > $TMPC << EOF +#include +int main(void) +{ + char errbuf[PCAP_ERRBUF_SIZE]; + return (pcap_create("foo", errbuf) == (pcap_t *)0 ? 1 : 0); +} +EOF + if compile_prog "" "$libpcap" ; then + pcap_create="yes" + fi + cat > $TMPC << EOF +#define PCAP_DONT_INCLUDE_PCAP_BPF_H +#include +#include +int main(void) { return (BPF_MAJOR_VERSION); } +EOF + if compile_prog ; then + bpf="yes" + fi + libs_softmmu="$libpcap $libs_softmmu" +fi # test "$pcap" ########################################## # VNC SASL detection @@ -4758,7 +4833,11 @@ echo "GNUTLS support $gnutls" echo "GNUTLS hash $gnutls_hash" echo "libgcrypt $gcrypt" -echo "nettle $nettle ${nettle+($nettle_version)}" +if test "$nettle" = "yes"; then + echo "nettle $nettle ($nettle_version)" +else + echo "nettle $nettle" +fi echo "libtasn1 $tasn1" echo "VTE support $vte" echo "curses support $curses" @@ -4769,6 +4848,7 @@ echo "Block whitelist (rw) $block_drv_rw_whitelist" echo "Block whitelist (ro) $block_drv_ro_whitelist" echo "VirtFS support $virtfs" +echo "pcap support $pcap" echo "VNC support $vnc" if test "$vnc" = "yes" ; then echo "VNC SASL support $vnc_sasl" @@ -4947,6 +5027,15 @@ if test "$profiler" = "yes" ; then echo "CONFIG_PROFILER=y" >> $config_host_mak fi +if test "$pcap" = "yes" ; then + echo "CONFIG_PCAP=y" >> $config_host_mak + if test "$pcap_create" = "yes" ; then + echo "CONFIG_PCAP_CREATE=y" >> $config_host_mak + fi + if test "$bpf" = "yes" ; then + echo "CONFIG_BPF=y" >> $config_host_mak + fi +fi if test "$slirp" = "yes" ; then echo "CONFIG_SLIRP=y" >> $config_host_mak echo "CONFIG_SMBD_COMMAND=\"$smbd\"" >> $config_host_mak @@ -5137,6 +5226,9 @@ if test "$tasn1" = "yes" ; then echo "CONFIG_TASN1=y" >> $config_host_mak fi +if test "$have_ifaddrs_h" = "yes" ; then + echo "HAVE_IFADDRS_H=y" >> $config_host_mak +fi if test "$vte" = "yes" ; then echo "CONFIG_VTE=y" >> $config_host_mak echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak diff -ruN net/clients.h net/clients.h --- net/clients.h 2015-12-17 04:04:50.000000000 +0600 +++ net/clients.h 2015-12-25 01:32:09.000000000 +0600 @@ -47,6 +47,11 @@ int net_init_bridge(const NetClientOptions *opts, const char *name, NetClientState *peer, Error **errp); +#ifdef CONFIG_PCAP +int net_init_pcap(const NetClientOptions *opts, const char *name, + NetClientState *peer, Error **errp); +#endif + int net_init_l2tpv3(const NetClientOptions *opts, const char *name, NetClientState *peer, Error **errp); #ifdef CONFIG_VDE diff -ruN net/hub.c net/hub.c --- net/hub.c 2015-12-17 04:04:50.000000000 +0600 +++ net/hub.c 2015-12-25 01:32:09.000000000 +0600 @@ -322,6 +322,7 @@ case NET_CLIENT_OPTIONS_KIND_SOCKET: case NET_CLIENT_OPTIONS_KIND_VDE: case NET_CLIENT_OPTIONS_KIND_VHOST_USER: + case NET_CLIENT_OPTIONS_KIND_PCAP: has_host_dev = 1; break; default: diff -ruN net/net.c net/net.c --- net/net.c 2015-12-17 04:04:50.000000000 +0600 +++ net/net.c 2015-12-25 01:32:09.000000000 +0600 @@ -46,6 +46,11 @@ #include "sysemu/sysemu.h" #include "net/filter.h" +#include +#ifdef __FreeBSD__ +#include +#endif + /* Net bridge is currently not supported for W32. */ #if !defined(_WIN32) # define CONFIG_NET_BRIDGE @@ -942,8 +947,224 @@ return idx; } +#if defined(CONFIG_PCAP) +#if defined(CONFIG_BPF) +#define PCAP_DONT_INCLUDE_PCAP_BPF_H +#include +#endif +#include + +struct PCAPState { + NetClientState nc; + pcap_t *handle; + int max_eth_frame_size; +}; + +static ssize_t pcap_receive(NetClientState *nc, const uint8_t *buf, size_t size) +{ + struct PCAPState *s = DO_UPCAST(struct PCAPState, nc, nc); + + return pcap_inject(s->handle, (u_char*)buf, size); +} + +static void pcap_callback(u_char *user, struct pcap_pkthdr *phdr, u_char *pdata + ) +{ + NetClientState *nc = (NetClientState *)user; + + int len = phdr->len; +#ifdef __FreeBSD__ + struct PCAPState *s = DO_UPCAST(struct PCAPState, nc, nc); + int max_eth_frame_size = s->max_eth_frame_size; + + if (len > max_eth_frame_size) { + fprintf(stderr, + "pcap_send: packet size > %d (%d), truncating\n", + max_eth_frame_size, len); + len = max_eth_frame_size; + } +#endif + qemu_send_packet(nc, pdata, len); +} + +static void pcap_send(void *opaque) +{ + struct PCAPState *s = (struct PCAPState *)opaque; + + for (;;) { + if (pcap_dispatch(s->handle, 0, (pcap_handler)&pcap_callback, (u_char *)&s->nc) >= 0) + break; + } +} + +static void pcap_cleanup(NetClientState *nc) +{ + struct PCAPState *s = DO_UPCAST(struct PCAPState, nc, nc); + + qemu_purge_queued_packets(nc); + pcap_close(s->handle); +} + +static NetClientInfo net_pcap_info = { + .type = NET_CLIENT_OPTIONS_KIND_PCAP, + .size = sizeof(struct PCAPState), + .receive = pcap_receive, +// .receive_raw = pcap_receive_raw, +// .receive_iov = pcap_receive_iov, +// .poll = pcap_poll, + .cleanup = pcap_cleanup, +}; +/* + * ... -net pcap,ifname="..." + */ + +int net_init_pcap(const NetClientOptions *opts, + const char *name, NetClientState *peer, Error **errp) +{ + const NetdevPcapOptions *pcap_opts = opts->u.pcap; + NetClientState *nc; + struct PCAPState *s; + const char *ifname; + char errbuf[PCAP_ERRBUF_SIZE]; +#if defined(_WIN32) + HANDLE h; +#endif + int i; + + if (!pcap_opts->has_ifname) + return -1; + + ifname = pcap_opts->ifname; + + /* create the object */ + nc = qemu_new_net_client(&net_pcap_info, peer, "pcap", ifname); + s = DO_UPCAST(struct PCAPState, nc, nc); + + if (ifname == NULL && (ifname = pcap_lookupdev(errbuf)) == NULL) { + fprintf(stderr, "qemu: pcap_create: %s\n", errbuf); + goto fail; + } + +#ifdef __FreeBSD__ + /* + * We want to avoid passing oversize packets to the guest, which + * at least on FreeBSD can happen if the host interface uses tso + * (seen with an em(4) in this case) - so find out the host + * interface's mtu and assume the guest is configured the same. + */ + s->max_eth_frame_size = 1514; + i = socket(AF_INET, SOCK_DGRAM, 0); + if (i >= 0) { + struct ifreq ifr; + + (void) memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + if (ioctl(i, SIOCGIFMTU, &ifr) != -1) + s->max_eth_frame_size = ifr.ifr_mtu + 14; + close(i); + } +#endif + +#if defined(CONFIG_PCAP_CREATE) || defined(_WIN32) + /* + * Create pcap handle for the device, set promiscuous mode and activate. + */ + s->handle = (void *)pcap_create(ifname, errbuf); + if (!s->handle) { + fprintf(stderr, "qemu: pcap_create: %s\n", errbuf); + goto fail; + } + if (pcap_set_promisc(s->handle, 1) != 0) { + pcap_perror(s->handle, (char *)"qemu: pcap_set_promisc:"); + goto fail; + } + if (pcap_activate(s->handle) != 0) { + pcap_perror(s->handle, (char *)"qemu: pcap_activate:"); + goto fail; + } +#else + /* Attempt to connect device. */ + s->handle = (void *)pcap_open_live(ifname, 65535, 1, 0, errbuf); + if (!s->handle) { + fprintf(stderr, "qemu: pcap_open_live: %s\n", errbuf); + goto fail; + } +#endif + + /* Set non-blocking mode. */ + if (pcap_setnonblock(s->handle, 1, errbuf) < 0) { + fprintf(stderr, "qemu: pcap_setnonblock: %s\n", errbuf); + goto fail; + } + +#if defined(_WIN32) + /* + * Tell the kernel that the packet has to be seen immediately. + */ + if (pcap_setmintocopy(s->handle, 0) < 0) { + fprintf(stderr, "qemu: pcap failed to set immediate mode\n"); + goto fail; + } +#else /* !_WIN32 */ +#if defined(CONFIG_BPF) +#if defined(BIOCIMMEDIATE) + /* + * Tell the kernel that the packet has to be seen immediately. + */ + { + unsigned int one = 1; + if (ioctl(pcap_fileno(s->handle), BIOCIMMEDIATE, &one) < 0) { + fprintf(stderr, "qemu: pcap failed to set immediate mode\n"); + goto fail; + } + } +#endif /* BIOCIMMEDIATE */ +#if defined(BIOCFEEDBACK) + /* + * Tell the kernel that the sent packet has to be fed back. + * This is necessary to connect host and guest. + */ + { + unsigned int one = 1; + if (ioctl(pcap_fileno(s->handle), BIOCFEEDBACK, &one) < 0) { + fprintf(stderr, "qemu: pcap failed to set feedback mode\n"); + goto fail; + } + } +#endif /* BIOCFEEDBACK */ +#endif /* CONFIG_BPF */ +#endif /* _WIN32 */ + + snprintf(s->nc.info_str, sizeof(s->nc.info_str), "pcap redirector"); + +#if defined(_WIN32) + if ((h = pcap_getevent(s->handle)) == NULL) { + fprintf(stderr, "qemu: pcap_getevent failed\n"); + goto fail; + } + qemu_add_wait_object(h, pcap_send, s); +#else /* !_WIN32 */ + if ((i = pcap_get_selectable_fd(s->handle)) < 0) { + fprintf(stderr, "qemu: pcap_get_selectable_fd failed\n"); + goto fail; + } + qemu_set_fd_handler(i, pcap_send, NULL, s); +#endif /* _WIN32 */ + + return 0; + +fail: + if (s) { + if (s->handle) + pcap_close(s->handle); + } + + return -1; +} + +#endif -static int (* const net_client_init_fun[NET_CLIENT_OPTIONS_KIND_MAX])( +static int (* const net_client_init_fun[NET_CLIENT_OPTIONS_KIND_MAX])( const NetClientOptions *opts, const char *name, NetClientState *peer, Error **errp) = { @@ -963,6 +1184,9 @@ #ifdef CONFIG_NET_BRIDGE [NET_CLIENT_OPTIONS_KIND_BRIDGE] = net_init_bridge, #endif +#ifdef CONFIG_PCAP + [NET_CLIENT_OPTIONS_KIND_PCAP] = net_init_pcap, +#endif [NET_CLIENT_OPTIONS_KIND_HUBPORT] = net_init_hubport, #ifdef CONFIG_VHOST_NET_USED [NET_CLIENT_OPTIONS_KIND_VHOST_USER] = net_init_vhost_user, diff -ruN qapi-schema.json qapi-schema.json --- qapi-schema.json 2015-12-17 04:04:50.000000000 +0600 +++ qapi-schema.json 2015-12-25 01:32:09.000000000 +0600 @@ -2538,6 +2538,10 @@ '*br': 'str', '*helper': 'str' } } +{ 'struct': 'NetdevPcapOptions', + 'data': { + '*ifname': 'str' } } + ## # @NetdevHubPortOptions # @@ -2608,6 +2612,7 @@ 'nic': 'NetLegacyNicOptions', 'user': 'NetdevUserOptions', 'tap': 'NetdevTapOptions', + 'pcap': 'NetdevPcapOptions', 'l2tpv3': 'NetdevL2TPv3Options', 'socket': 'NetdevSocketOptions', 'vde': 'NetdevVdeOptions',