qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH] Add pcap-based host network bridge


From: Jan Kiszka
Subject: [Qemu-devel] [PATCH] Add pcap-based host network bridge
Date: Wed, 18 Mar 2009 10:33:01 +0100
User-agent: Mozilla/5.0 (X11; U; Linux i686 (x86_64); de; rv:1.8.1.12) Gecko/20080226 SUSE/2.0.0.12-1.1 Thunderbird/2.0.0.12 Mnenhy/0.7.5.666

This introduces bridged networking via pcap. Both Unix and Windows
platforms are supported.

While tap-based bridging provides basically the same support pcap does,
the latter is often more handy to set up. Under Linux, it doesn't
require to configure a bridge and it is able to bypass the ebtables if
this is desired. Also under Windows, the bridge setup can be lengthy
procedure compared to using the pcap interface.

Signed-off-by: Klaus Wenninger <address@hidden>
Signed-off-by: Jan Kiszka <address@hidden>
---

 Makefile.target |    2 
 configure       |   35 ++++++++
 net.c           |  252 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 qemu-doc.texi   |   14 +++
 vl.c            |    4 +
 5 files changed, 305 insertions(+), 2 deletions(-)

diff --git a/Makefile.target b/Makefile.target
index 47a4ada..395fb15 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -729,7 +729,7 @@ LDFLAGS+=-p
 main.o: CFLAGS+=-p
 endif
 
-$(QEMU_PROG): LIBS += $(SDL_LIBS) $(COCOA_LIBS) $(CURSES_LIBS) $(BRLAPI_LIBS) 
$(VDE_LIBS)
+$(QEMU_PROG): LIBS += $(SDL_LIBS) $(COCOA_LIBS) $(CURSES_LIBS) $(BRLAPI_LIBS) 
$(VDE_LIBS) $(PCAP_LIBS)
 
 $(QEMU_PROG): $(OBJS) ../libqemu_common.a libqemu.a
        $(LINK)
diff --git a/configure b/configure
index 8899030..7d77f32 100755
--- a/configure
+++ b/configure
@@ -160,6 +160,7 @@ EXESUF=""
 gdbstub="yes"
 slirp="yes"
 vde="yes"
+pcap="yes"
 fmod_lib=""
 fmod_inc=""
 oss_lib=""
@@ -404,6 +405,8 @@ for opt do
   ;;
   --disable-vde) vde="no"
   ;;
+  --disable-pcap) pcap="no"
+  ;;
   --disable-kqemu) kqemu="no"
   ;;
   --disable-brlapi) brlapi="no"
@@ -576,6 +579,7 @@ echo "  --oss-lib                path to OSS library"
 echo "  --enable-uname-release=R Return R for uname -r in usermode emulation"
 echo "  --sparc_cpu=V            Build qemu for Sparc architecture v7, v8, 
v8plus, v8plusa, v9"
 echo "  --disable-vde            disable support for vde network"
+echo "  --disable-pcap           disable support for pcap-based network"
 echo "  --disable-aio            disable AIO support"
 echo "  --disable-blobs          disable installing provided firmware blobs"
 echo "  --kerneldir=PATH         look for kernel includes in PATH"
@@ -906,6 +910,31 @@ EOF
 fi
 
 ##########################################
+# pcap libraries probe
+if test "$pcap" = "yes" ; then
+  cat > $TMPC << EOF
+#include <pcap.h>
+int main(void)
+{
+    char errbuf[PCAP_ERRBUF_SIZE];
+
+    pcap_lookupdev(errbuf);
+    return 0;
+}
+EOF
+    if test "$mingw32" = "yes" ; then
+        PCAP_LIBS=-lwpcap
+    else
+        PCAP_LIBS=-lpcap
+    fi
+    if $cc $ARCH_CFLAGS -o $TMPE $TMPC $PCAP_LIBS > /dev/null 2> /dev/null ; 
then
+        :
+    else
+        pcap="no"
+    fi
+fi
+
+##########################################
 # Sound support libraries probe
 
 audio_drv_probe()
@@ -1209,6 +1238,7 @@ echo "Documentation     $build_docs"
 echo "uname -r          $uname_release"
 echo "NPTL support      $nptl"
 echo "vde support       $vde"
+echo "pcap net support  $pcap"
 echo "AIO support       $aio"
 echo "Install blobs     $blobs"
 echo "KVM support       $kvm"
@@ -1412,6 +1442,11 @@ if test "$vde" = "yes" ; then
   echo "#define CONFIG_VDE 1" >> $config_h
   echo "VDE_LIBS=-lvdeplug" >> $config_mak
 fi
+if test "$pcap" = "yes" ; then
+  echo "CONFIG_PCAP=yes" >> $config_mak
+  echo "#define CONFIG_PCAP 1" >> $config_h
+  echo "PCAP_LIBS="$PCAP_LIBS >> $config_mak
+fi
 for card in $audio_card_list; do
     def=CONFIG_`echo $card | tr '[:lower:]' '[:upper:]'`
     echo "$def=yes" >> $config_mak
diff --git a/net.c b/net.c
index c853daf..3cf372d 100644
--- a/net.c
+++ b/net.c
@@ -93,6 +93,13 @@
 #endif
 #endif
 
+#if defined(CONFIG_PCAP)
+#include <pcap.h>
+#ifdef _WIN32
+#include <remote-ext.h>
+#endif /* _WIN32 */
+#endif /* CONFIG_PCAP */
+
 #if defined(__OpenBSD__)
 #include <util.h>
 #endif
@@ -1033,6 +1040,241 @@ static int net_tap_init(VLANState *vlan, const char 
*model,
 
 #endif /* !_WIN32 */
 
+#if defined(CONFIG_PCAP)
+
+#ifdef _WIN32
+#define ADAPTER_KEY             "SYSTEM\\CurrentControlSet\\Control\\Class" \
+                                "\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
+#define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network" \
+                                "\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
+#endif
+
+typedef struct PCAPState {
+    VLANClientState *vc;
+    pcap_t *handle;
+} PCAPState;
+
+static void pcap_receive(void *opaque, const uint8_t *buf, int size)
+{
+    PCAPState *s = (PCAPState *)opaque;
+
+    pcap_sendpacket(s->handle, (u_char*)buf, size);
+}
+
+static void pcap_callback(u_char *user, struct pcap_pkthdr *phdr, u_char 
*pdata)
+{
+    VLANClientState *vc = (VLANClientState *)user;
+
+    qemu_send_packet(vc, pdata, phdr->len);
+}
+
+static void pcap_send(void *opaque)
+{
+    PCAPState *s = (PCAPState *)opaque;
+
+    pcap_dispatch(s->handle, 1, (pcap_handler)&pcap_callback, (u_char *)s->vc);
+}
+
+#ifdef _WIN32
+static int get_windows_device_name(char *name, int name_size,
+                                   char *actual_name, int actual_name_size)
+{
+    const char name_string[] = "Name";
+    char connection_string[256];
+    HKEY connection_key;
+    char name_data[256];
+    DWORD name_type;
+    LONG status;
+    DWORD len;
+
+    snprintf(connection_string, sizeof(connection_string),
+             "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, name);
+
+    status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, connection_string, 0, KEY_READ,
+                          &connection_key);
+
+    if (status == ERROR_SUCCESS) {
+        len = sizeof(name_data);
+        status = RegQueryValueEx(connection_key, name_string, NULL,
+                                 &name_type, (PBYTE)name_data, &len);
+        if (status != ERROR_SUCCESS || name_type != REG_SZ) {
+            fprintf(stderr, "qemu: error opening registry key: %s\\%s\\%s",
+                    NETWORK_CONNECTIONS_KEY, connection_string, name_string);
+            return -1;
+        } else {
+            snprintf(actual_name, actual_name_size, "%s", name_data);
+        }
+
+        RegCloseKey(connection_key);
+
+    }
+    return 0;
+}
+#endif
+
+static int net_pcap_init(VLANState *vlan, const char *model, const char *name,
+                         char *ifname)
+{
+    PCAPState *s;
+    char errbuf[PCAP_ERRBUF_SIZE];
+    int fd;
+
+    s = qemu_mallocz(sizeof(PCAPState));
+    if (!s)
+        return -1;
+
+#ifndef _WIN32
+    if (!ifname && !(ifname = pcap_lookupdev(errbuf))) {
+        fprintf(stderr, "qemu: pcap_lookupdev: %s\n", errbuf);
+        goto fail;
+    }
+
+    s->handle = (void*)pcap_open_live(ifname, 65535, 1, 0, errbuf);
+#else
+    {
+        pcap_if_t *found_device = NULL;
+        pcap_if_t *alldevs = NULL;
+        pcap_if_t *device;
+        char errbuf[PCAP_ERRBUF_SIZE];
+        char connection_name_data[256];
+        char name_data[256];
+        char *code_strt;
+
+        /* Retrieve the device list */
+        if (pcap_findalldevs(&alldevs, errbuf) == -1) {
+            fprintf(stderr, "qemu: error in pcap_findalldevs: %s\n", errbuf);
+            goto fail;
+        }
+
+        device = alldevs;
+
+        while (device) {
+            memset(connection_name_data, 0, sizeof(connection_name_data));
+
+            code_strt = strchr(device->name, '{');
+            if (code_strt) {
+                snprintf(name_data, sizeof(name_data), "%s", code_strt);
+
+                get_windows_device_name(name_data, sizeof(name_data),
+                                        connection_name_data,
+                                        sizeof(connection_name_data));
+            }
+
+            if (*connection_name_data != 0) {
+                if (ifname) {
+                    /*
+                     * If an exact match exists, over-ride any found device,
+                     * setting exact match device to it.
+                     */
+                    if (strcmp(connection_name_data, ifname) == 0) {
+                        found_device = device;
+                        break;
+                    }
+
+                    /*
+                     * Do a partial search, if successful, set this device as
+                     * the found one, but continue looping through devices.
+                     */
+                    if (found_device && strstr(connection_name_data, ifname))
+                        found_device = device;
+                } else {
+                    /*
+                     * If no name specified and network has an address,
+                     * autoselect the first device.
+                     */
+                    if (device->addresses) {
+                        found_device = device;
+                        break;
+                    }
+                }
+            }
+
+            device = device->next;
+        }
+
+        if (!found_device)
+            goto fail;
+
+        ifname = found_device->name;
+
+        s->handle = pcap_open(ifname, 65536,
+                              PCAP_OPENFLAG_PROMISCUOUS |
+                              PCAP_OPENFLAG_NOCAPTURE_LOCAL |
+                              PCAP_OPENFLAG_MAX_RESPONSIVENESS,
+                              0, NULL, errbuf);
+    }
+#endif
+    if (!s->handle) {
+        fprintf(stderr, "qemu: pcap_open_live: %s\n", errbuf);
+        goto fail;
+    }
+
+    if (pcap_setnonblock(s->handle, 1, errbuf) < 0) {
+        fprintf(stderr, "qemu: pcap_setnonblock: %s\n", errbuf);
+        goto fail;
+    }
+
+#ifndef _WIN32
+#if defined(BIOCSHDRCMPLT)
+    /*
+     * Tell the kernel that the header is fully-formed when it gets it.
+     * This is required in order to fake the src address.
+     */
+    {
+        unsigned int one = 1;
+        ioctl(pcap_fileno(s->handle), BIOCSHDRCMPLT, &one);
+    }
+#endif /* BIOCSHDRCMPLT */
+
+#if defined(BIOCIMMEDIATE)
+    /*
+     * Tell the kernel that the packet has to be processed immediately.
+     */
+    {
+        unsigned int one = 1;
+        ioctl(pcap_fileno(s->handle), BIOCIMMEDIATE, &one);
+    }
+#endif /* BIOCIMMEDIATE */
+#endif
+
+    s->vc = qemu_new_vlan_client(vlan, model, name, pcap_receive, NULL, s);
+    snprintf(s->vc->info_str, sizeof(s->vc->info_str), "pcap redirector");
+#ifndef _WIN32
+    if ((fd = pcap_get_selectable_fd(s->handle)) < 0) {
+        fprintf(stderr, "qemu: pcap_get_selectable_fd failed\n");
+        goto fail;
+    }
+    qemu_set_fd_handler(fd, pcap_send, NULL, s);
+#else
+    {
+        HANDLE hand;
+
+        hand = pcap_getevent(s->handle);
+        if ((hand = pcap_getevent(s->handle)) == 0) {
+            fprintf(stderr, "qemu: pcap_getevent failed\n");
+            goto fail;
+        }
+        if (qemu_add_wait_object(hand, pcap_send, s) < 0) {
+            fprintf(stderr,
+                    "qemu: qemu_add_wait_object for pcap_send failed\n");
+            goto fail;
+        }
+    }
+#endif
+
+    return 0;
+
+fail:
+    if (s) {
+        if (s->handle)
+            pcap_close(s->handle);
+        qemu_free(s);
+    }
+
+    return -1;
+}
+#endif /* CONFIG_PCAP */
+
 #if defined(CONFIG_VDE)
 typedef struct VDEState {
     VLANClientState *vc;
@@ -1723,6 +1965,16 @@ int net_client_init(const char *device, const char *p)
         }
     } else
 #endif
+#ifdef CONFIG_PCAP
+    if (!strcmp(device, "pcap")) {
+        char ifname[64];
+        vlan->nb_host_devs++;
+        if (get_param_value(ifname, sizeof(ifname), "ifname", p) <= 0) {
+            ifname[0] = '\0';
+        }
+        ret = net_pcap_init(vlan, device, name, ifname);
+    } else
+#endif
     if (!strcmp(device, "socket")) {
         if (get_param_value(buf, sizeof(buf), "fd", p) > 0) {
             int fd;
diff --git a/qemu-doc.texi b/qemu-doc.texi
index 8efc943..faed31a 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -770,6 +770,18 @@ vde_switch -F -sock /tmp/myswitch
 qemu linux.img -net nic -net vde,sock=/tmp/myswitch
 @end example
 
address@hidden -net pcap[,address@hidden,address@hidden,address@hidden
+Connect VLAN @var{n} to the host network interface @var{name} using the pcap
+interface. This will switch the host's interface to promisc mode, enabling the
+guest to receive all packets on this VLAN that the host sees, too. The pcap
+host interface is a handy alternative to a TAP interface, attached via a
+bridge to a host network interface.
+
+Example:
address@hidden
+qemu linux.img -net nic -net pcap,ifname=eth0
address@hidden example
+
 @item -net none
 Indicate that no network devices should be configured. It is used to
 override the default configuration (@option{-net nic -net user}) which
@@ -3114,7 +3126,7 @@ MV88W8xx8 Ethernet controller
 @item
 MV88W8618 audio controller, WM8750 CODEC and mixer
 @item
-128??64 display with brightness control
+128?64 display with brightness control
 @item
 2 buttons, 2 navigation wheels with button function
 @end itemize
diff --git a/vl.c b/vl.c
index 41012a8..4e6e7ee 100644
--- a/vl.c
+++ b/vl.c
@@ -4018,6 +4018,10 @@ static void help(int exitcode)
            "                use '[down]script=no' to disable script 
execution;\n"
            "                use 'fd=h' to connect to an already opened TAP 
interface\n"
 #endif
+#ifdef CONFIG_PCAP
+           "-net pcap[,vlan=n][,name=str][,ifname=name]\n"
+           "                connect the host network interface to VLAN 'n' 
using PCAP\n"
+#endif
            "-net 
socket[,vlan=n][,name=str][,fd=h][,listen=[host]:port][,connect=host:port]\n"
            "                connect the vlan 'n' to another VLAN using a 
socket connection\n"
            "-net socket[,vlan=n][,name=str][,fd=h][,mcast=maddr:port]\n"




reply via email to

[Prev in Thread] Current Thread [Next in Thread]