From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1Ljs97-0002gV-OB for qemu-devel@nongnu.org; Wed, 18 Mar 2009 05:33:09 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1Ljs93-0002gD-V9 for qemu-devel@nongnu.org; Wed, 18 Mar 2009 05:33:09 -0400 Received: from [199.232.76.173] (port=56595 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Ljs93-0002gA-OY for qemu-devel@nongnu.org; Wed, 18 Mar 2009 05:33:05 -0400 Received: from gecko.sbs.de ([194.138.37.40]:16862) by monty-python.gnu.org with esmtps (TLS-1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1Ljs92-0007qZ-Rx for qemu-devel@nongnu.org; Wed, 18 Mar 2009 05:33:05 -0400 Received: from mail1.sbs.de (localhost [127.0.0.1]) by gecko.sbs.de (8.12.11.20060308/8.12.11) with ESMTP id n2I9X1CF019306 for ; Wed, 18 Mar 2009 10:33:01 +0100 Received: from [139.25.109.167] (mchn012c.ww002.siemens.net [139.25.109.167] (may be forged)) by mail1.sbs.de (8.12.11.20060308/8.12.11) with ESMTP id n2I9X1Z5032044 for ; Wed, 18 Mar 2009 10:33:01 +0100 Message-ID: <49C0BFCD.1040304@siemens.com> Date: Wed, 18 Mar 2009 10:33:01 +0100 From: Jan Kiszka MIME-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-15 Content-Transfer-Encoding: 7bit Subject: [Qemu-devel] [PATCH] Add pcap-based host network bridge Reply-To: qemu-devel@nongnu.org List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel 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 Signed-off-by: Jan Kiszka --- 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 +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 +#ifdef _WIN32 +#include +#endif /* _WIN32 */ +#endif /* CONFIG_PCAP */ + #if defined(__OpenBSD__) #include #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"