From: Jan Kiszka <jan.kiszka@siemens.com>
To: qemu-devel <qemu-devel@nongnu.org>
Subject: [Qemu-devel] [PATCH] Add pcap-based host network bridge
Date: Wed, 18 Mar 2009 10:33:01 +0100 [thread overview]
Message-ID: <49C0BFCD.1040304@siemens.com> (raw)
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 <klaus.wenninger@siemens-enterprise.com>
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
---
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
+@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 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"
next reply other threads:[~2009-03-18 9:33 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-03-18 9:33 Jan Kiszka [this message]
2009-03-22 20:38 ` [Qemu-devel] [PATCH] Add pcap-based host network bridge 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
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=49C0BFCD.1040304@siemens.com \
--to=jan.kiszka@siemens.com \
--cc=qemu-devel@nongnu.org \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.