Index: configure @@ -257,6 +257,9 @@ pkgversion="" check_utests="no" user_pie="no" zero_malloc="" +pcap="no" +pcap_create="no" +bpf="no" # OS specific if check_define __linux__ ; then @@ -492,6 +495,8 @@ for opt do ;; --enable-vnc-sasl) vnc_sasl="yes" ;; + --enable-pcap) pcap="yes" + ;; --disable-slirp) slirp="no" ;; --disable-uuid) uuid="no" @@ -1041,6 +1046,49 @@ EOF fi ########################################## +# pcap probe + +if test "$pcap" = "yes" ; 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 ! $cc $ARCH_CFLAGS -o $TMPE $libpcap $TMPC 2> /dev/null ; then + 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 $cc $ARCH_CFLAGS -o $TMPE $libpcap $TMPC 2> /dev/null ; 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 $cc $ARCH_CFLAGS -o $TMPE $TMPC 2> /dev/null ; then + bpf="yes" + fi + libs_softmmu="$libpcap $libs_softmmu" +fi # test "$pcap" + +########################################## # VNC TLS detection if test "$vnc_tls" != "no" ; then cat > $TMPC <> $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 QEMU_CFLAGS="-I\$(SRC_PATH)/slirp $QEMU_CFLAGS" Index: net.h @@ -33,7 +33,8 @@ typedef enum { NET_CLIENT_TYPE_TAP, NET_CLIENT_TYPE_SOCKET, NET_CLIENT_TYPE_VDE, - NET_CLIENT_TYPE_DUMP + NET_CLIENT_TYPE_DUMP, + NET_CLIENT_TYPE_PCAP } net_client_type; typedef int (NetCanReceive)(VLANClientState *); Index: net.c @@ -36,6 +36,8 @@ #include "qemu-common.h" #include "qemu_socket.h" +#include + static QTAILQ_HEAD(, VLANState) vlans; static QTAILQ_HEAD(, VLANClientState) non_vlan_clients; @@ -820,6 +822,212 @@ static int net_init_nic(QemuOpts *opts, return idx; } +#if defined(CONFIG_PCAP) +#if defined(CONFIG_BPF) +#define PCAP_DONT_INCLUDE_PCAP_BPF_H +#include +#endif +#include + +typedef struct PCAPState { + VLANClientState nc; + pcap_t *handle; +} PCAPState; + +static ssize_t pcap_receive(VLANClientState *nc, const uint8_t *buf, size_t size) +{ + PCAPState *s = DO_UPCAST(PCAPState, nc, nc); + + return pcap_inject(s->handle, (u_char*)buf, size); +} + +#define MAX_ETH_FRAME_SIZE 1514 + +static void pcap_callback(u_char *user, struct pcap_pkthdr *phdr, u_char *pdata) +{ + VLANClientState *vc = (VLANClientState *)user; + int len = phdr->len; + + 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; + } + qemu_send_packet(vc, pdata, len); +} + +static void pcap_send(void *opaque) +{ + PCAPState *s = (PCAPState *)opaque; + + pcap_dispatch(s->handle, 1, (pcap_handler)&pcap_callback, (u_char *)&s->nc); +} + +static void pcap_cleanup(VLANClientState *nc) +{ + PCAPState *s = DO_UPCAST(PCAPState, nc, nc); + + pcap_close(s->handle); +} + +static NetClientInfo net_pcap_info = { + .type = NET_CLIENT_TYPE_PCAP, + .size = sizeof(PCAPState), + .receive = pcap_receive, +#if 0 + .receive_raw = tap_receive_raw, + .receive_iov = tap_receive_iov, +#endif + .cleanup = pcap_cleanup, +}; + +static int net_pcap_init(VLANState *vlan, const char *model, const char *name, const char *ifname) +{ + VLANClientState *nc; + PCAPState *s = NULL; + char errbuf[PCAP_ERRBUF_SIZE]; +#if defined(_WIN32) + HANDLE h; +#endif + int i; + + s = qemu_mallocz(sizeof(PCAPState)); + nc = qemu_new_net_client(&net_pcap_info, vlan, NULL, model, name); +#if 0 + nc = qemu_new_vlan_client(NET_CLIENT_TYPE_PCAP, + vlan, NULL, model, name, NULL, + pcap_receive, NULL, NULL, + pcap_cleanup, s); +#endif + + s = DO_UPCAST(PCAPState, nc, nc); + if (!s) + return -1; + + if (ifname == NULL && (ifname = pcap_lookupdev(errbuf)) == NULL) { + fprintf(stderr, "qemu: pcap_create: %s\n", errbuf); + goto fail; + } + +#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, "qemu: pcap_set_promisc:"); + goto fail; + } + if (pcap_activate(s->handle) != 0) { + pcap_perror(s->handle, "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); + qemu_free(s); + } + + return -1; +} + +static int net_init_pcap(QemuOpts *opts, + Monitor *mon, + const char *name, + VLANState *vlan) +{ + const char *ifname; + + ifname = qemu_opt_get(opts, "ifname"); + + if (net_pcap_init(vlan, "pcap", name, ifname) == -1) { + return -1; + } + + vlan->nb_host_devs++; + + return 0; +} +#endif /* CONFIG_PCAP */ + #define NET_COMMON_PARAMS_DESC \ { \ .name = "type", \ @@ -980,6 +1188,20 @@ static struct { #endif /* _WIN32 */ { /* end of list */ } }, +#ifdef CONFIG_PCAP + }, { + .type = "pcap", + .init = net_init_pcap, + .desc = { + NET_COMMON_PARAMS_DESC, + { + .name = "ifname", + .type = QEMU_OPT_STRING, + .help = "interface name", + }, + { /* end of list */ } + }, +#endif }, { .type = "socket", .init = net_init_socket, --- qemu-options.hx.orig 2009-08-28 16:46:21.000000000 -0400 +++ qemu-options.hx 2009-09-02 16:20:14.000000000 -0400 @@ -783,6 +783,10 @@ DEF("net", HAS_ARG, QEMU_OPTION_net, "-net nic[,vlan=n][,macaddr=mac][,model=type][,name=str][,addr=str][,vectors=v]\n" " create a new Network Interface Card and connect it to VLAN 'n'\n" +#ifdef CONFIG_PCAP + "-net pcap[,vlan=n][,name=str][,ifname=name]\n" + " connect the host network interface using PCAP to VLAN 'n'\n" +#endif #ifdef CONFIG_SLIRP "-net user[,vlan=n][,name=str][,net=addr[/mask]][,host=addr][,restrict=y|n]\n" " [,hostname=host][,dhcpstart=addr][,dns=addr][,tftp=dir][,bootfile=f]\n"