[Top][All Lists]
[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"
- [Qemu-devel] [PATCH] Add pcap-based host network bridge,
Jan Kiszka <=