qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: "Sebastian Herbszt" <herbszt@gmx.de>
To: Consul <void@aleksoft.net>, qemu-devel@nongnu.org
Subject: [Qemu-devel] Re: [PATCH] Add pcap-based host network bridge
Date: Sat, 6 Jun 2009 19:13:26 +0200	[thread overview]
Message-ID: <C0648AF8B9CD4DE5A37965D11776A215@FSCPC> (raw)
In-Reply-To: <gvn125$k91$1@ger.gmane.org>

[-- Attachment #1: Type: text/plain, Size: 942 bytes --]

Consul wrote:
> Sebastian Herbszt wrote:
>> Consul wrote:
>>> Sebastian Herbszt wrote:
>>>> That's really too bad. pcap-based networking works flawless on windows.
>>>> Guest <-> host and guest <-> world communication works just fine.
>>>> According to the previous threads about pcap-based networking it also
>>>> should work fine on FreeBSD.
>>>>
>>>
>>> Maybe make it compile conditionally for Windows only? Would you 
>>> consider such patch?
>>> It would really be a valuable feature.
>> 
>> I did rebase the patch against git master and it still seems to work. I 
>> can post it if there
>> is any interest.
>> 
>> - Sebastian
>> 
> 
> Yes, please post.

Find jan-pcap-updated-qdev3.diff attached.

> I also have it rebased in my tree and it still works great.

Is your tree available somewhere?

> Easier to configure than TAP for sure. Maybe if we'll bug the list often
> enough and loud enough, it finally gets through.

- Sebastian

[-- Attachment #2: jan-pcap-updated-qdev3.diff --]
[-- Type: application/octet-stream, Size: 13707 bytes --]

diff --git a/Makefile.target b/Makefile.target
index 27de4b9..7eb84ad 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -734,7 +734,7 @@ vl.o: qemu-options.h
 
 monitor.o: qemu-monitor.h
 
-$(QEMU_PROG): LIBS += $(SDL_LIBS) $(COCOA_LIBS) $(CURSES_LIBS) $(BRLAPI_LIBS) $(VDE_LIBS) $(CURL_LIBS)
+$(QEMU_PROG): LIBS += $(SDL_LIBS) $(COCOA_LIBS) $(CURSES_LIBS) $(BRLAPI_LIBS) $(VDE_LIBS) $(CURL_LIBS) $(PCAP_LIBS)
 $(QEMU_PROG): ARLIBS=../libqemu_common.a libqemu.a $(HWLIB)
 $(QEMU_PROG): $(OBJS) ../libqemu_common.a libqemu.a $(HWLIB)
 	$(call LINK,$(OBJS))
diff --git a/configure b/configure
index 42d46f2..169f8bc 100755
--- a/configure
+++ b/configure
@@ -165,6 +165,7 @@ mingw32="no"
 EXESUF=""
 slirp="yes"
 vde="yes"
+pcap="yes"
 fmod_lib=""
 fmod_inc=""
 oss_lib=""
@@ -432,6 +433,8 @@ for opt do
   ;;
   --disable-vde) vde="no"
   ;;
+  --disable-pcap) pcap="no"
+  ;;
   --disable-kqemu) kqemu="no"
   ;;
   --disable-xen) xen="no"
@@ -634,6 +637,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-pthread        disable pthread support"
 echo "  --disable-aio            disable AIO support"
 echo "  --enable-io-thread       enable IO thread"
@@ -981,6 +985,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()
@@ -1374,6 +1403,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 "IO thread         $io_thread"
 echo "Install blobs     $blobs"
@@ -1587,6 +1617,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 2d24a7c..e4b3740 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
@@ -1176,6 +1183,268 @@ 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);
+}
+
+static void pcap_cleanup(VLANClientState *vc)
+{
+    PCAPState *s = vc->opaque;
+
+#ifndef _WIN32
+    int fd;
+
+    if ((fd = pcap_get_selectable_fd(s->handle)) < 0) {
+        fprintf(stderr, "qemu: pcap_get_selectable_fd failed\n");
+	return;
+    }
+    qemu_set_fd_handler(fd, NULL, NULL, NULL);
+#else
+    HANDLE hand;
+
+    hand = pcap_getevent(s->handle);
+    if ((hand = pcap_getevent(s->handle)) == 0) {
+        fprintf(stderr, "qemu: pcap_getevent failed\n");
+        return;
+    }
+    qemu_del_wait_object(hand, NULL, NULL);
+#endif
+
+    pcap_close(s->handle);
+    qemu_free(s);
+}
+
+#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, pcap_cleanup, 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;
@@ -2025,6 +2294,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-options.hx b/qemu-options.hx
index 87af798..9d24473 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -753,6 +753,10 @@ DEF("net", HAS_ARG, QEMU_OPTION_net, \
     "                Use group 'groupname' and mode 'octalmode' to change default\n"
     "                ownership and permissions for communication port.\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 dump[,vlan=n][,file=f][,len=n]\n"
     "                dump traffic on vlan 'n' to file 'f' (max n bytes per packet)\n"
     "-net none       use it alone to have zero network devices; if no -net option\n"
@@ -875,6 +879,18 @@ vde_switch -F -sock /tmp/myswitch
 qemu linux.img -net nic -net vde,sock=/tmp/myswitch
 @end example
 
+@item -net pcap[,vlan=@var{n}][,name=@var{name}][,ifname=@var{name}]
+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:
+@example
+qemu linux.img -net nic -net pcap,ifname=eth0
+@end example
+
 @item -net dump[,vlan=@var{n}][,file=@var{file}][,len=@var{len}]
 Dump network traffic on VLAN @var{n} to file @var{file} (@file{qemu-vlan0.pcap} by default).
 At most @var{len} bytes (64k by default) per packet are stored. The file format is

  reply	other threads:[~2009-06-06 17:14 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-03-18  9:33 [Qemu-devel] [PATCH] Add pcap-based host network bridge Jan Kiszka
2009-03-22 20:38 ` Robert Riebisch
2009-03-22 21:38 ` [Qemu-devel] " Sebastian Herbszt
2009-03-24 17:21   ` Jan Kiszka
2009-03-23  2:23 ` [Qemu-devel] " Anthony Liguori
2009-03-24 17:28   ` [Qemu-devel] " Jan Kiszka
2009-03-28 17:26     ` Anthony Liguori
2009-05-11 21:30       ` [Qemu-devel] " Sebastian Herbszt
2009-05-12  8:23         ` [Qemu-devel] " Jan Kiszka
2009-05-12 15:48           ` Lennart Sorensen
2009-05-12 16:34             ` Jan Kiszka
2009-05-12 17:09               ` Lennart Sorensen
2009-05-12 21:45             ` Paul Brook
2009-05-12 21:17           ` Sebastian Herbszt
2009-05-20 17:43             ` Consul
2009-05-28 20:50               ` Sebastian Herbszt
2009-05-28 21:52                 ` Consul
2009-06-06 17:13                   ` Sebastian Herbszt [this message]
2009-06-06 18:40                     ` Alex
2009-06-08 21:27                       ` Consul
2009-06-09 20:13                         ` Sebastian Herbszt
2009-06-09 21:49                           ` Consul
2009-06-10 21:39                             ` Sebastian Herbszt

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=C0648AF8B9CD4DE5A37965D11776A215@FSCPC \
    --to=herbszt@gmx.de \
    --cc=qemu-devel@nongnu.org \
    --cc=void@aleksoft.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).