From 4e6c38e10fa4f4d15380016bfff56deb975e9a21 Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Wed, 14 Feb 2007 16:02:40 +0000 Subject: [PATCH] Tue Feb 14 16:02:23 IST 2007 Mark McLoughlin * configure.in: add --disable-bridge-params, check for libsysfs and various kernel headers * bridge.[ch]: add code for managing bridges * qemud/Makefile.am: add bridge.[ch] and link against libsysfs if enabled. * qemud/conf.c: add support for bridge config. * qemud/internal.h: add various bridging bits * qemud/qemud.c: implement qemudStartNetworkDaemon() and qemudShutdownNetworkDaemon(). --- ChangeLog | 110 ++++++++- configure.in | 22 ++ qemud/Makefile.am | 5 +- qemud/bridge.c | 609 ++++++++++++++++++++++++++++++++++++++++++++++ qemud/bridge.h | 101 ++++++++ qemud/conf.c | 352 +++++++++++++++++++++++++-- qemud/internal.h | 14 ++ qemud/qemud.c | 127 +++++++++- 8 files changed, 1309 insertions(+), 31 deletions(-) create mode 100644 qemud/bridge.c create mode 100644 qemud/bridge.h diff --git a/ChangeLog b/ChangeLog index f33dffe65b..08eddde8bd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,16 +1,108 @@ -Tue Feb 14 14:58:35 EST 2007 Mark McLoughlin + + * configure.in: add --disable-bridge-params, check + for libsysfs and various kernel headers + + * bridge.[ch]: add code for managing bridges + + * qemud/Makefile.am: add bridge.[ch] and link against + libsysfs if enabled. + + * qemud/conf.c: add support for bridge config. + + * qemud/internal.h: add various bridging bits + + * qemud/qemud.c: implement qemudStartNetworkDaemon() + and qemudShutdownNetworkDaemon(). + +Tue Feb 14 15:55:02 IST 2007 Mark McLoughlin + + * qemud/conf.[ch]: implement parsing and saving network + configs. + + * qemud/driver.c: flesh out the stubs + + * qemud/internal.h: add networks list etc. to + struct qemud_server + + * qemud/qemud.c: add qemudStartNetworkDaemon() and + qemudShutdownNetworkDaemon() stubs. + +Tue Feb 14 15:52:34 EST 2007 Mark McLoughlin + + * qemud/protocol.h: add the protocol for virtual networks + + * qemud/dispatch.c: implement the protocol + + * qemud/driver.[ch]: add stubs for the driver + + * qemud/internal.h: add struct qemud_network + + * src/qemu_internal.c: add a virtual networks driver + +Tue Feb 14 15:43:28 IST 2007 Mark McLoughlin + + * src/virsh.c: add the net-* commands. + +Tue Feb 14 15:37:17 IST 2007 Mark McLoughlin + + Note: potential ABI break here, but people should + only really be using virError structs returned from + libvirt itself. + + * include/libvirt/virterror.h: add virNetwork + to virError + + * src/internal.h, src/virterror.c: add network param + to __virRaiseError() + + * src/conf.c, src/hash.c, src/libvirt.c, src/proxy_internal.c, + src/qemu_internal.c, src/sexpr.c, src/test.c, src/xen_internal.c, + src/xend_internal.c, src/xm_internal.c, src/xml.c, src/xmlrpc.c, + src/xs_internal.c: update. + +Tue Feb 14 15:33:05 IST 2007 Mark McLoughlin + + * include/libvirt/libvirt.h.in: add the networks APIs + + * include/libvirt/virterror.h: add some error codes + + * src/driver.h: add network driver vtable + + * src/hash.c: add networks hash + + * src/internal.h: add virNetwork + + * src/libvirt.c: hook up the APIs to the network + driver + + * src/libvirt_sym.version: add the new APIs + + * src/virterror.c: handle the new error codes + +Tue Feb 14 15:07:26 IST 2007 Mark McLoughlin + + * src/conf.h: fix merge error - remove the argc argument + from qemudBuildCommandLine() + +Tue Feb 14 15:03:22 IST 2007 Mark McLoughlin + + * src/virsh.c: Re-name some of the VSH_DOMBYFOO stuff + to VSH_BYFOO in order to re-use it for the network stuff. + +Tue Feb 14 14:58:35 IST 2007 Mark McLoughlin * src/hash.c, src/internal.h: Re-name virConnect->domains_mux to virConnect->hashes_mux since it will also be used to protect the networks hash. -Tue Feb 14 14:57:52 EST 2007 Mark McLoughlin * qemud/conf.c: qemudSaveConfig() will always report a more specific error, so we should avoid overwriting this error. -Tue Feb 14 14:54:25 EST 2007 Mark McLoughlin * qemud/qemud.c: Re-factor out qemudExec() so that it can be used to launch dnsmasq. @@ -18,7 +110,7 @@ Tue Feb 14 14:54:25 EST 2007 Mark McLoughlin * qemud/conf.c: Re-factor bits of conf.c so that: @@ -28,25 +120,25 @@ Tue Feb 14 14:52:12 EST 2007 Mark McLoughlin * qemud/conf.c: handle an unspecified MAC address, fix the argv freeing code in qemudBuildCommandLine() and fix copy and paste error in qemudGenerateXML() -Tue Feb 14 14:42:38 EST 2007 Mark McLoughlin * src/internal.h: add virConnect->qemud_fd so that xen and qemu don't share the handle member. * src/hash.c, src/qemu_internal.c: update -Tue Feb 14 14:40:52 EST 2007 Mark McLoughlin * qemud/conf.c, qemud/dispatch.c, qemud/driver.c, qemud/qemud.c: include autoconf's config.h -Tue Feb 14 14:39:18 EST 2007 Mark McLoughlin * conf.[ch]: rename from config.[ch] so we can use autoconf's config.h @@ -55,7 +147,7 @@ Tue Feb 14 14:39:18 EST 2007 Mark McLoughlin * autogen.sh: run autoheader diff --git a/configure.in b/configure.in index 21168d128f..082576d637 100644 --- a/configure.in +++ b/configure.in @@ -102,6 +102,28 @@ then dnl search for the Xen store library AC_SEARCH_LIBS(xs_read, [xenstore], [], [AC_MSG_ERROR([Xen store library not found])]) +dnl +dnl check for libsyfs (>= 2.0.0); allow disabling bridge parameters support altogether +dnl +AC_ARG_ENABLE(bridge-params, + AC_HELP_STRING([--disable-bridge-params], + [disable support for setting bridge parameters using libsysfs [default=no]]),, + enable_bridge_params=yes) + +if test x"$enable_bridge_params" == "xyes"; then + AC_CHECK_LIB(sysfs, sysfs_open_device, + [AC_CHECK_HEADER(sysfs/libsysfs.h, + AC_DEFINE(ENABLE_BRIDGE_PARAMS, , [enable setting bridge parameters using libsysfs]) + SYSFS_LIBS="-lsysfs" AC_SUBST(SYSFS_LIBS), + AC_MSG_ERROR([You must install libsysfs in order to compile libvirt]))]) +fi + +dnl +dnl check for kernel headers required by qemud/bridge.c +dnl +AC_CHECK_HEADERS(linux/param.h linux/sockios.h linux/if_bridge.h linux/if_tun.h,, + AC_MSG_ERROR([You must install kernel-headers in order to compile libvirt])) + dnl ========================================================================== dnl find libxml2 library, borrowed from xmlsec dnl ========================================================================== diff --git a/qemud/Makefile.am b/qemud/Makefile.am index e0692ff700..73243c10d0 100644 --- a/qemud/Makefile.am +++ b/qemud/Makefile.am @@ -7,13 +7,14 @@ libexec_PROGRAMS = libvirt_qemud libvirt_qemud_SOURCES = qemud.c internal.h protocol.h \ driver.c driver.h \ dispatch.c dispatch.h \ - conf.c conf.h + conf.c conf.h \ + bridge.c bridge.h #-D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_POSIX_C_SOURCE=199506L libvirt_qemud_CFLAGS = \ -I$(top_srcdir)/include -I$(top_builddir)/include $(LIBXML_CFLAGS) \ -Werror -Wall -Wextra -DLOCAL_STATE_DIR="\"$(localstatedir)\"" \ -DSYSCONF_DIR="\"$(sysconfdir)\"" -libvirt_qemud_LDFLAGS = $(LIBXML_LIBS) +libvirt_qemud_LDFLAGS = $(LIBXML_LIBS) $(SYSFS_LIBS) libvirt_qemud_DEPENDENCIES = libvirt_qemud_LDADD = diff --git a/qemud/bridge.c b/qemud/bridge.c new file mode 100644 index 0000000000..7438241061 --- /dev/null +++ b/qemud/bridge.c @@ -0,0 +1,609 @@ +/* + * Copyright (C) 2007 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Mark McLoughlin + */ + +#include + +#include "bridge.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* HZ */ +#include /* SIOCBRADDBR etc. */ +#include /* SYSFS_BRIDGE_ATTR */ +#include /* IFF_TUN, IFF_NO_PI */ + +#include "internal.h" + +#define MAX_BRIDGE_ID 256 + +#define JIFFIES_TO_MS(j) (((j)*1000)/HZ) +#define MS_TO_JIFFIES(ms) (((ms)*HZ)/1000) + +struct _brControl { + int fd; +}; + +int +brInit(brControl **ctlp) +{ + int fd; + + if (!ctlp || *ctlp) + return EINVAL; + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) + return errno; + + *ctlp = (brControl *)malloc(sizeof(struct _brControl)); + if (!*ctlp) + return ENOMEM; + + (*ctlp)->fd = fd; + + return 0; +} + +void +brShutdown(brControl *ctl) +{ + if (!ctl) + return; + + close(ctl->fd); + ctl->fd = 0; + + free(ctl); +} + +int +brAddBridge(brControl *ctl, + const char *nameOrFmt, + char *name, + int maxlen) +{ + int id, subst; + + if (!ctl || !ctl->fd || !nameOrFmt || !name) + return EINVAL; + + if (maxlen >= BR_IFNAME_MAXLEN) + maxlen = BR_IFNAME_MAXLEN; + + subst = id = 0; + + if (strstr(nameOrFmt, "%d")) + subst = 1; + + do { + char try[BR_IFNAME_MAXLEN]; + int len; + + if (subst) { + len = snprintf(try, maxlen, nameOrFmt, id); + if (len >= maxlen) + return EADDRINUSE; + } else { + len = strlen(nameOrFmt); + if (len >= maxlen - 1) + return EINVAL; + + strncpy(try, nameOrFmt, len); + try[len] = '\0'; + } + + if (ioctl(ctl->fd, SIOCBRADDBR, try) == 0) { + strncpy(name, try, maxlen); + return 0; + } + + id++; + } while (subst && id <= MAX_BRIDGE_ID); + + return errno; +} + +int +brDeleteBridge(brControl *ctl, + const char *name) +{ + if (!ctl || !ctl->fd || !name) + return EINVAL; + + return ioctl(ctl->fd, SIOCBRDELBR, name) == 0 ? 0 : errno; +} + +static int +brAddDelInterface(brControl *ctl, + int cmd, + const char *bridge, + const char *iface) +{ + struct ifreq ifr; + int len; + + if (!ctl || !ctl->fd || !bridge || !iface) + return EINVAL; + + if ((len = strlen(bridge)) >= BR_IFNAME_MAXLEN) + return EINVAL; + + memset(&ifr, 0, sizeof(struct ifreq)); + + strncpy(ifr.ifr_name, bridge, len); + ifr.ifr_name[len] = '\0'; + + if (!(ifr.ifr_ifindex = if_nametoindex(iface))) + return ENODEV; + + return ioctl(ctl->fd, cmd, &ifr) == 0 ? 0 : errno; +} + +int +brAddInterface(brControl *ctl, + const char *bridge, + const char *iface) +{ + return brAddDelInterface(ctl, SIOCBRADDIF, bridge, iface); +} + +int +brDeleteInterface(brControl *ctl, + const char *bridge, + const char *iface) +{ + return brAddDelInterface(ctl, SIOCBRDELIF, bridge, iface); +} + +int +brAddTap(brControl *ctl, + const char *bridge, + const char *ifnameOrFmt, + char *ifname, + int maxlen, + int *tapfd) +{ + int id, subst, fd; + + if (!ctl || !ctl->fd || !bridge || !ifnameOrFmt || !tapfd) + return EINVAL; + + if (!ifname) + maxlen = BR_IFNAME_MAXLEN; + else if (maxlen >= BR_IFNAME_MAXLEN) + maxlen = BR_IFNAME_MAXLEN; + + subst = id = 0; + + if (strstr(ifnameOrFmt, "%d")) + subst = 1; + + if ((fd = open("/dev/net/tun", O_RDWR)) < 0) + return errno; + + do { + struct ifreq try; + int len; + + memset(&try, 0, sizeof(struct ifreq)); + + try.ifr_flags = IFF_TAP|IFF_NO_PI; + + if (subst) { + len = snprintf(try.ifr_name, maxlen, ifnameOrFmt, id); + if (len >= maxlen) { + errno = EADDRINUSE; + goto error; + } + } else { + len = strlen(ifnameOrFmt); + if (len >= maxlen - 1) { + errno = EINVAL; + goto error; + } + + strncpy(try.ifr_name, ifnameOrFmt, len); + try.ifr_name[len] = '\0'; + } + + if (ioctl(fd, TUNSETIFF, &try) == 0) { + if ((errno = brAddInterface(ctl, bridge, try.ifr_name))) + goto error; + if ((errno = brSetInterfaceUp(ctl, try.ifr_name, 1))) + goto error; + if (ifname) + strncpy(ifname, try.ifr_name, maxlen); + *tapfd = fd; + return 0; + } + + id++; + } while (subst && id <= MAX_BRIDGE_ID); + + error: + close(fd); + + return errno; +} + +int +brSetInterfaceUp(brControl *ctl, + const char *ifname, + int up) +{ + struct ifreq ifr; + int len; + int flags; + + if (!ctl || !ifname) + return EINVAL; + + if ((len = strlen(ifname)) >= BR_IFNAME_MAXLEN) + return EINVAL; + + memset(&ifr, 0, sizeof(struct ifreq)); + + strncpy(ifr.ifr_name, ifname, len); + ifr.ifr_name[len] = '\0'; + + if (ioctl(ctl->fd, SIOCGIFFLAGS, &ifr) < 0) + return errno; + + flags = up ? (ifr.ifr_flags | IFF_UP) : (ifr.ifr_flags & ~IFF_UP); + + if (ifr.ifr_flags != flags) { + ifr.ifr_flags = flags; + + if (ioctl(ctl->fd, SIOCSIFFLAGS, &ifr) < 0) + return errno; + } + + return 0; +} + +int +brGetInterfaceUp(brControl *ctl, + const char *ifname, + int *up) +{ + struct ifreq ifr; + int len; + + if (!ctl || !ifname) + return EINVAL; + + if ((len = strlen(ifname)) >= BR_IFNAME_MAXLEN) + return EINVAL; + + memset(&ifr, 0, sizeof(struct ifreq)); + + strncpy(ifr.ifr_name, ifname, len); + ifr.ifr_name[len] = '\0'; + + if (ioctl(ctl->fd, SIOCGIFFLAGS, &ifr) < 0) + return errno; + + *up = (ifr.ifr_flags & IFF_UP) ? 1 : 0; + + return 0; +} + +static int +brSetInetAddr(brControl *ctl, + const char *ifname, + int cmd, + const char *addr) +{ + struct ifreq ifr; + struct in_addr inaddr; + int len, ret; + + if (!ctl || !ctl->fd || !ifname || !addr) + return EINVAL; + + if ((len = strlen(ifname)) >= BR_IFNAME_MAXLEN) + return EINVAL; + + memset(&ifr, 0, sizeof(struct ifreq)); + + strncpy(ifr.ifr_name, ifname, len); + ifr.ifr_name[len] = '\0'; + + if ((ret = inet_pton(AF_INET, addr, &inaddr)) < 0) + return errno; + else if (ret == 0) + return EINVAL; + + ((struct sockaddr_in *)&ifr.ifr_addr)->sin_family = AF_INET; + ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr = inaddr; + + if (ioctl(ctl->fd, cmd, &ifr) < 0) + return errno; + + return 0; +} + +static int +brGetInetAddr(brControl *ctl, + const char *ifname, + int cmd, + char *addr, + int maxlen) +{ + struct ifreq ifr; + struct in_addr *inaddr; + int len; + + if (!ctl || !ctl->fd || !ifname || !addr) + return EINVAL; + + if ((len = strlen(ifname)) >= BR_IFNAME_MAXLEN) + return EINVAL; + + memset(&ifr, 0, sizeof(struct ifreq)); + + strncpy(ifr.ifr_name, ifname, len); + ifr.ifr_name[len] = '\0'; + + if (ioctl(ctl->fd, cmd, &ifr) < 0) + return errno; + + if (maxlen < BR_INET_ADDR_MAXLEN || ifr.ifr_addr.sa_family != AF_INET) + return EFAULT; + + inaddr = &((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr; + + if (!inet_ntop(AF_INET, inaddr, addr, maxlen)) + return errno; + + return 0; +} + +int +brSetInetAddress(brControl *ctl, + const char *ifname, + const char *addr) +{ + return brSetInetAddr(ctl, ifname, SIOCSIFADDR, addr); +} + +int +brGetInetAddress(brControl *ctl, + const char *ifname, + char *addr, + int maxlen) +{ + return brGetInetAddr(ctl, ifname, SIOCGIFADDR, addr, maxlen); +} + +int +brSetInetNetmask(brControl *ctl, + const char *ifname, + const char *addr) +{ + return brSetInetAddr(ctl, ifname, SIOCSIFNETMASK, addr); +} + +int +brGetInetNetmask(brControl *ctl, + const char *ifname, + char *addr, + int maxlen) +{ + return brGetInetAddr(ctl, ifname, SIOCGIFNETMASK, addr, maxlen); +} + +#ifdef ENABLE_BRIDGE_PARAMS + +#include + +static int +brSysfsPrep(struct sysfs_class_device **dev, + struct sysfs_attribute **attr, + const char *bridge, + const char *attrname) +{ + *dev = NULL; + *attr = NULL; + + if (!(*dev = sysfs_open_class_device("net", bridge))) + return errno; + + if (!(*attr = sysfs_get_classdev_attr(*dev, attrname))) { + int err = errno; + + sysfs_close_class_device(*dev); + *dev = NULL; + + return err; + } + + return 0; +} + +static int +brSysfsWriteInt(struct sysfs_attribute *attr, + int value) +{ + char buf[32]; + int len; + + len = snprintf(buf, sizeof(buf), "%d\n", value); + + if (len > (int)sizeof(buf)) + len = sizeof(buf); /* paranoia, shouldn't happen */ + + return sysfs_write_attribute(attr, buf, len) == 0 ? 0 : errno; +} + +int +brSetForwardDelay(brControl *ctl, + const char *bridge, + int delay) +{ + struct sysfs_class_device *dev; + struct sysfs_attribute *attr; + int err = 0; + + if (!ctl || !bridge) + return EINVAL; + + if ((err = brSysfsPrep(&dev, &attr, bridge, SYSFS_BRIDGE_ATTR "/forward_delay"))) + return err; + + err = brSysfsWriteInt(attr, MS_TO_JIFFIES(delay)); + + sysfs_close_class_device(dev); + + return err; +} + +int +brGetForwardDelay(brControl *ctl, + const char *bridge, + int *delayp) +{ + struct sysfs_class_device *dev; + struct sysfs_attribute *attr; + int err = 0; + + if (!ctl || !bridge || !delayp) + return EINVAL; + + if ((err = brSysfsPrep(&dev, &attr, bridge, SYSFS_BRIDGE_ATTR "/forward_delay"))) + return err; + + *delayp = strtoul(attr->value, NULL, 0); + + if (errno != ERANGE) { + *delayp = JIFFIES_TO_MS(*delayp); + } else { + err = errno; + } + + sysfs_close_class_device(dev); + + return err; +} + +int +brSetEnableSTP(brControl *ctl, + const char *bridge, + int enable) +{ + struct sysfs_class_device *dev; + struct sysfs_attribute *attr; + int err = 0; + + if (!ctl || !bridge) + return EINVAL; + + if ((err = brSysfsPrep(&dev, &attr, bridge, SYSFS_BRIDGE_ATTR "/stp_state"))) + return err; + + err = brSysfsWriteInt(attr, (enable == 0) ? 0 : 1); + + sysfs_close_class_device(dev); + + return err; +} + +int +brGetEnableSTP(brControl *ctl, + const char *bridge, + int *enablep) +{ + struct sysfs_class_device *dev; + struct sysfs_attribute *attr; + int err = 0; + + if (!ctl || !bridge || !enablep) + return EINVAL; + + if ((err = brSysfsPrep(&dev, &attr, bridge, SYSFS_BRIDGE_ATTR "/stp_state"))) + return err; + + *enablep = strtoul(attr->value, NULL, 0); + + if (errno != ERANGE) { + *enablep = (*enablep == 0) ? 0 : 1; + } else { + err = errno; + } + + sysfs_close_class_device(dev); + + return err; +} + +#else /* ENABLE_BRIDGE_PARAMS */ + +int +brSetForwardDelay(brControl *ctl ATTRIBUTE_UNUSED, + const char *bridge ATTRIBUTE_UNUSED, + int delay ATTRIBUTE_UNUSED) +{ + return 0; +} + +int +brGetForwardDelay(brControl *ctl ATTRIBUTE_UNUSED, + const char *bridge ATTRIBUTE_UNUSED, + int *delay ATTRIBUTE_UNUSED) +{ + return 0; +} + +int +brSetEnableSTP(brControl *ctl ATTRIBUTE_UNUSED, + const char *bridge ATTRIBUTE_UNUSED, + int enable ATTRIBUTE_UNUSED) +{ + return 0; +} + +int +brGetEnableSTP(brControl *ctl ATTRIBUTE_UNUSED, + const char *bridge ATTRIBUTE_UNUSED, + int *enable ATTRIBUTE_UNUSED) +{ + return 0; +} + +#endif /* ENABLE_BRIDGE_PARAMS */ + +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff --git a/qemud/bridge.h b/qemud/bridge.h new file mode 100644 index 0000000000..8109944375 --- /dev/null +++ b/qemud/bridge.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2007 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Mark McLoughlin + */ + +#ifndef __QEMUD_BRIDGE_H__ +#define __QEMUD_BRIDGE_H__ + +#include +#include + +#define BR_IFNAME_MAXLEN IF_NAMESIZE +#define BR_INET_ADDR_MAXLEN INET_ADDRSTRLEN + +typedef struct _brControl brControl; + +int brInit (brControl **ctl); +void brShutdown (brControl *ctl); + +int brAddBridge (brControl *ctl, + const char *nameOrFmt, + char *name, + int maxlen); +int brDeleteBridge (brControl *ctl, + const char *name); + +int brAddInterface (brControl *ctl, + const char *bridge, + const char *iface); +int brDeleteInterface (brControl *ctl, + const char *bridge, + const char *iface); + +int brAddTap (brControl *ctl, + const char *bridge, + const char *ifnameOrFmt, + char *ifname, + int maxlen, + int *tapfd); + +int brSetInterfaceUp (brControl *ctl, + const char *ifname, + int up); +int brGetInterfaceUp (brControl *ctl, + const char *ifname, + int *up); + +int brSetInetAddress (brControl *ctl, + const char *ifname, + const char *addr); +int brGetInetAddress (brControl *ctl, + const char *ifname, + char *addr, + int maxlen); +int brSetInetNetmask (brControl *ctl, + const char *ifname, + const char *netmask); +int brGetInetNetmask (brControl *ctl, + const char *ifname, + char *netmask, + int maxlen); + +int brSetForwardDelay (brControl *ctl, + const char *bridge, + int delay); +int brGetForwardDelay (brControl *ctl, + const char *bridge, + int *delay); +int brSetEnableSTP (brControl *ctl, + const char *bridge, + int enable); +int brGetEnableSTP (brControl *ctl, + const char *bridge, + int *enable); + +#endif /* __QEMUD_BRIDGE_H__ */ + +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff --git a/qemud/conf.c b/qemud/conf.c index 306abe5b78..da80dfc048 100644 --- a/qemud/conf.c +++ b/qemud/conf.c @@ -1098,17 +1098,263 @@ struct qemud_vm *qemudLoadConfigXML(struct qemud_server *server, } +void qemudFreeNetwork(struct qemud_network *network) { + free(network); +} + + +static int qemudSaveNetworkConfig(struct qemud_server *server, + struct qemud_network *network) { + char *xml; + int fd, ret = -1; + int towrite; + + if (!(xml = qemudGenerateNetworkXML(server, network))) { + return -1; + } + + if (qemudEnsureConfigDir(server, server->networkConfigDir) < 0) { + goto cleanup; + } + + if ((fd = open(network->configFile, + O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR )) < 0) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot create config file %s", network->configFile); + goto cleanup; + } + + towrite = strlen(xml); + if (write(fd, xml, towrite) != towrite) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot write config file %s", network->configFile); + goto cleanup; + } + + if (close(fd) < 0) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot save config file %s", network->configFile); + goto cleanup; + } + + ret = 0; + + cleanup: + + free(xml); + + return ret; +} + + +static int qemudParseBridgeXML(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_network *network, + xmlNodePtr node) { + xmlChar *name, *stp, *delay; + + name = xmlGetProp(node, BAD_CAST "name"); + if (name != NULL) { + strncpy(network->def.bridge, (const char *)name, IF_NAMESIZE-1); + network->def.bridge[IF_NAMESIZE-1] = '\0'; + xmlFree(name); + name = NULL; + } + + stp = xmlGetProp(node, BAD_CAST "stp"); + if (stp != NULL) { + if (xmlStrEqual(stp, BAD_CAST "off")) { + network->def.disableSTP = 1; + } + xmlFree(stp); + stp = NULL; + } + + delay = xmlGetProp(node, BAD_CAST "delay"); + if (delay != NULL) { + network->def.forwardDelay = strtol((const char *)delay, NULL, 10); + xmlFree(delay); + delay = NULL; + } + + return 1; +} + + +static int qemudParseInetXML(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_network *network, + xmlNodePtr node) { + xmlChar *address, *netmask; + + address = xmlGetProp(node, BAD_CAST "address"); + if (address != NULL) { + strncpy(network->def.ipAddress, (const char *)address, BR_INET_ADDR_MAXLEN-1); + network->def.ipAddress[BR_INET_ADDR_MAXLEN-1] = '\0'; + xmlFree(address); + address = NULL; + } + + netmask = xmlGetProp(node, BAD_CAST "netmask"); + if (netmask != NULL) { + strncpy(network->def.netmask, (const char *)netmask, BR_INET_ADDR_MAXLEN-1); + network->def.netmask[BR_INET_ADDR_MAXLEN-1] = '\0'; + xmlFree(netmask); + netmask = NULL; + } + + return 1; +} + + +static int qemudParseNetworkXML(struct qemud_server *server, + xmlDocPtr xml, + struct qemud_network *network) { + xmlNodePtr root = NULL; + xmlXPathContextPtr ctxt = NULL; + xmlXPathObjectPtr obj = NULL; + + /* Prepare parser / xpath context */ + root = xmlDocGetRootElement(xml); + if ((root == NULL) || (!xmlStrEqual(root->name, BAD_CAST "network"))) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "incorrect root element"); + goto error; + } + + ctxt = xmlXPathNewContext(xml); + if (ctxt == NULL) { + qemudReportError(server, VIR_ERR_NO_MEMORY, "xmlXPathContext"); + goto error; + } + + + /* Extract network name */ + obj = xmlXPathEval(BAD_CAST "string(/network/name[1])", ctxt); + if ((obj == NULL) || (obj->type != XPATH_STRING) || + (obj->stringval == NULL) || (obj->stringval[0] == 0)) { + qemudReportError(server, VIR_ERR_NO_NAME, NULL); + goto error; + } + if (strlen((const char *)obj->stringval) >= (QEMUD_MAX_NAME_LEN-1)) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "network name length too long"); + goto error; + } + strcpy(network->def.name, (const char *)obj->stringval); + xmlXPathFreeObject(obj); + + + /* Extract network uuid */ + obj = xmlXPathEval(BAD_CAST "string(/network/uuid[1])", ctxt); + if ((obj == NULL) || (obj->type != XPATH_STRING) || + (obj->stringval == NULL) || (obj->stringval[0] == 0)) { + /* XXX auto-generate a UUID */ + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "missing uuid element"); + goto error; + } + if (qemudParseUUID((const char *)obj->stringval, network->def.uuid) < 0) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "malformed uuid element"); + goto error; + } + xmlXPathFreeObject(obj); + + /* Parse bridge information */ + obj = xmlXPathEval(BAD_CAST "/network/bridge[1]", ctxt); + if ((obj != NULL) && (obj->type == XPATH_NODESET) && + (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr > 0)) { + if (!qemudParseBridgeXML(server, network, obj->nodesetval->nodeTab[0])) { + goto error; + } + } + xmlXPathFreeObject(obj); + + /* Parse IP information */ + obj = xmlXPathEval(BAD_CAST "/network/ip[1]", ctxt); + if ((obj != NULL) && (obj->type == XPATH_NODESET) && + (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr > 0)) { + if (!qemudParseInetXML(server, network, obj->nodesetval->nodeTab[0])) { + goto error; + } + } + xmlXPathFreeObject(obj); + + xmlXPathFreeContext(ctxt); + + return 0; + + error: + /* XXX free all the stuff in the qemud_network struct, or leave it upto + the caller ? */ + if (obj) + xmlXPathFreeObject(obj); + if (ctxt) + xmlXPathFreeContext(ctxt); + return -1; +} + +struct qemud_network *qemudLoadNetworkConfigXML(struct qemud_server *server, + const char *file, + const char *doc, + int save) { + struct qemud_network *network = NULL; + xmlDocPtr xml; + + if (!(xml = xmlReadDoc(BAD_CAST doc, file ? file : "network.xml", NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) { + qemudReportError(server, VIR_ERR_XML_ERROR, NULL); + return NULL; + } + + if (!(network = calloc(1, sizeof(struct qemud_network)))) { + qemudReportError(server, VIR_ERR_NO_MEMORY, "network"); + return NULL; + } + + if (qemudParseNetworkXML(server, xml, network) < 0) { + xmlFreeDoc(xml); + qemudFreeNetwork(network); + return NULL; + } + xmlFreeDoc(xml); + + if (qemudFindNetworkByUUID(server, network->def.uuid) || + qemudFindNetworkByName(server, network->def.name)) { + qemudReportError(server, VIR_ERR_NETWORK_EXIST, network->def.name); + qemudFreeNetwork(network); + return NULL; + } + + if (file) { + strncpy(network->configFile, file, PATH_MAX); + network->configFile[PATH_MAX-1] = '\0'; + } else { + if (save) { + if (qemudMakeConfigPath(server->networkConfigDir, network->def.name, ".xml", network->configFile, PATH_MAX) < 0) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot construct config file path"); + qemudFreeNetwork(network); + return NULL; + } + + if (qemudSaveNetworkConfig(server, network) < 0) { + qemudFreeNetwork(network); + return NULL; + } + } else { + network->configFile[0] = '\0'; + } + } + + return network; +} + + /* Load a guest from its persistent config file */ static void qemudLoadConfig(struct qemud_server *server, - const char *file) { + const char *file, + int isGuest) { FILE *fh; struct stat st; - struct qemud_vm *vm; char xml[QEMUD_MAX_XML_LEN]; int ret; if (!(fh = fopen(file, "r"))) { - qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot open guest config file %s", file); + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot open config file %s", file); return; } @@ -1118,7 +1364,7 @@ static void qemudLoadConfig(struct qemud_server *server, } if (st.st_size >= QEMUD_MAX_XML_LEN) { - qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "guest config too large in file %s", file); + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "config too large in file %s", file); goto cleanup; } @@ -1128,10 +1374,20 @@ static void qemudLoadConfig(struct qemud_server *server, } xml[st.st_size] = '\0'; - if ((vm = qemudLoadConfigXML(server, file, xml, 1))) { - vm->next = server->inactivevms; - server->inactivevms = vm; - server->ninactivevms++; + if (isGuest) { + struct qemud_vm *vm; + if ((vm = qemudLoadConfigXML(server, file, xml, 1))) { + vm->next = server->inactivevms; + server->inactivevms = vm; + server->ninactivevms++; + } + } else { + struct qemud_network *network; + if ((network = qemudLoadNetworkConfigXML(server, file, xml, 1))) { + network->next = server->inactivenetworks; + server->inactivenetworks = network; + server->ninactivenetworks++; + } } cleanup: @@ -1141,7 +1397,8 @@ static void qemudLoadConfig(struct qemud_server *server, static int qemudScanConfigDir(struct qemud_server *server, - const char *configDir) { + const char *configDir, + int isGuest) { DIR *dir; struct dirent *entry; @@ -1159,7 +1416,7 @@ int qemudScanConfigDir(struct qemud_server *server, if (qemudMakeConfigPath(configDir, entry->d_name, NULL, file, PATH_MAX) < 0) continue; - qemudLoadConfig(server, file); + qemudLoadConfig(server, file, isGuest); } closedir(dir); @@ -1169,7 +1426,9 @@ int qemudScanConfigDir(struct qemud_server *server, /* Scan for all guest and network config files */ int qemudScanConfigs(struct qemud_server *server) { - return qemudScanConfigDir(server, server->configDir); + if (qemudScanConfigDir(server, server->configDir, 0) < 0) + return -1; + return qemudScanConfigDir(server, server->networkConfigDir, 1); } /* Simple grow-on-demand string buffer */ @@ -1424,19 +1683,76 @@ char *qemudGenerateXML(struct qemud_server *server, struct qemud_vm *vm) { } -int qemudDeleteConfigXML(struct qemud_server *server, struct qemud_vm *vm) { - if (!vm->configFile[0]) { - qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "no config file for guest %s", vm->def.name); +char *qemudGenerateNetworkXML(struct qemud_server *server, + struct qemud_network *network) { + struct qemudBuffer buf; + unsigned char *uuid; + + buf.len = QEMUD_MAX_XML_LEN; + buf.used = 0; + buf.data = malloc(buf.len); + + if (qemudBufferPrintf(&buf, "\n") < 0) + goto no_memory; + + if (qemudBufferPrintf(&buf, " %s\n", network->def.name) < 0) + goto no_memory; + + uuid = network->def.uuid; + if (qemudBufferPrintf(&buf, " %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", + uuid[0], uuid[1], uuid[2], uuid[3], + uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15]) < 0) + goto no_memory; + + if (qemudBufferPrintf(&buf, " \n", + network->def.bridge, + network->def.disableSTP ? "off" : "on", + network->def.forwardDelay) < 0) + goto no_memory; + + if (network->def.ipAddress[0] || network->def.netmask[0]) { + if (qemudBufferAdd(&buf, " def.ipAddress[0] && + qemudBufferPrintf(&buf, " address='%s'", network->def.ipAddress) < 0) + goto no_memory; + + if (network->def.netmask[0] && + qemudBufferPrintf(&buf, " netmask='%s'", network->def.netmask) < 0) + goto no_memory; + + if (qemudBufferAdd(&buf, "/>\n") < 0) + goto no_memory; + } + + if (qemudBufferAdd(&buf, "\n") < 0) + goto no_memory; + + return buf.data; + + no_memory: + qemudReportError(server, VIR_ERR_NO_MEMORY, "xml"); + free(buf.data); + return NULL; +} + + +int qemudDeleteConfig(struct qemud_server *server, + const char *configFile, + const char *name) { + if (!configFile[0]) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "no config file for %s", name); return -1; } - if (unlink(vm->configFile) < 0) { - qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot remove config for guest %s", vm->def.name); + if (unlink(configFile) < 0) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot remove config for %s", name); return -1; } - vm->configFile[0] = '\0'; - return 0; } diff --git a/qemud/internal.h b/qemud/internal.h index 7a6d86b52a..9552829123 100644 --- a/qemud/internal.h +++ b/qemud/internal.h @@ -30,6 +30,7 @@ #include #include "protocol.h" +#include "bridge.h" #ifdef __GNUC__ #ifdef HAVE_ANSIDECL_H @@ -203,6 +204,13 @@ struct qemud_vm { struct qemud_network_def { unsigned char uuid[QEMUD_UUID_RAW_LEN]; char name[QEMUD_MAX_NAME_LEN]; + + char bridge[BR_IFNAME_MAXLEN]; + int disableSTP; + int forwardDelay; + + char ipAddress[BR_INET_ADDR_MAXLEN]; + char netmask[BR_INET_ADDR_MAXLEN]; }; /* Virtual Network runtime state */ @@ -210,6 +218,11 @@ struct qemud_network { char configFile[PATH_MAX]; struct qemud_network_def def; + + char bridge[BR_IFNAME_MAXLEN]; + + unsigned int active : 1; + struct qemud_network *next; }; @@ -249,6 +262,7 @@ struct qemud_server { struct qemud_network *activenetworks; int ninactivenetworks; struct qemud_network *inactivenetworks; + brControl *brctl; char configDir[PATH_MAX]; char networkConfigDir[PATH_MAX]; char errorMessage[QEMUD_MAX_ERROR_LEN]; diff --git a/qemud/qemud.c b/qemud/qemud.c index a6c99a041a..061eaecd91 100644 --- a/qemud/qemud.c +++ b/qemud/qemud.c @@ -706,14 +706,115 @@ static int qemudDispatchVMFailure(struct qemud_server *server, struct qemud_vm * int qemudStartNetworkDaemon(struct qemud_server *server, struct qemud_network *network) { - server = NULL; network = NULL; + const char *name; + int err; + + if (network->active) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, + "network is already active"); + return -1; + } + + if (!server->brctl && (err = brInit(&server->brctl))) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, + "cannot initialize bridge support: %s", strerror(err)); + return -1; + } + + if (network->def.bridge[0] == '\0' || + strchr(network->def.bridge, '%')) { + name = "vnet%d"; + } else { + name = network->def.bridge; + } + + if ((err = brAddBridge(server->brctl, name, network->bridge, sizeof(network->bridge)))) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, + "cannot create bridge '%s' : %s", name, strerror(err)); + return -1; + } + + if (network->def.ipAddress[0] && + (err = brSetInetAddress(server->brctl, network->bridge, network->def.ipAddress))) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, + "cannot set IP address on bridge '%s' to '%s' : %s\n", + network->bridge, network->def.ipAddress, strerror(err)); + goto err_delbr; + } + + if (network->def.netmask[0] && + (err = brSetInetNetmask(server->brctl, network->bridge, network->def.netmask))) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, + "cannot set netmask on bridge '%s' to '%s' : %s\n", + network->bridge, network->def.netmask, strerror(err)); + goto err_delbr; + } + + if (network->def.ipAddress[0] && + (err = brSetInterfaceUp(server->brctl, network->bridge, 1))) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, + "failed to bring the bridge '%s' up : %s\n", + network->bridge, strerror(err)); + goto err_delbr; + } + + network->active = 1; + return 0; + + err_delbr: + if ((err = brDeleteBridge(server->brctl, network->bridge))) { + printf("Damn! Couldn't delete bridge '%s' : %s\n", + network->bridge, strerror(err)); + } + + return -1; } int qemudShutdownNetworkDaemon(struct qemud_server *server, struct qemud_network *network) { - server = NULL; network = NULL; + struct qemud_network *prev, *curr; + int err; + + if (!network->active) + return 0; + + if (network->def.ipAddress[0] && + (err = brSetInterfaceUp(server->brctl, network->bridge, 0))) { + printf("Damn! Failed to bring down bridge '%s' : %s\n", + network->bridge, strerror(err)); + } + + if ((err = brDeleteBridge(server->brctl, network->bridge))) { + printf("Damn! Failed to delete bridge '%s' : %s\n", + network->bridge, strerror(err)); + } + + /* Move it to inactive networks list */ + prev = NULL; + curr = server->activenetworks; + while (curr) { + if (curr == network) { + if (prev) { + prev->next = curr->next; + } else { + server->activenetworks = curr->next; + } + server->nactivenetworks--; + + curr->next = server->inactivenetworks; + server->inactivenetworks = curr; + server->ninactivenetworks++; + break; + } + prev = curr; + curr = curr->next; + } + + network->bridge[0] = '\0'; + network->active = 0; + return 0; } @@ -723,6 +824,7 @@ static int qemudDispatchPoll(struct qemud_server *server, struct pollfd *fds) { struct qemud_client *client = server->clients; struct qemud_vm *vm = server->activevms; struct qemud_vm *tmp; + struct qemud_network *network, *prevnet; int ret = 0; int fd = 0; @@ -806,6 +908,25 @@ static int qemudDispatchPoll(struct qemud_server *server, struct pollfd *fds) { } } + /* Cleanup any networks too */ + network = server->inactivenetworks; + prevnet = NULL; + while (network) { + if (!network->configFile[0]) { + struct qemud_network *next = network->next; + if (prevnet) { + prevnet->next = next; + } else { + server->inactivenetworks = next; + } + qemudFreeNetwork(network); + network = next; + } else { + prevnet = network; + network = network->next; + } + } + return ret; } @@ -896,6 +1017,8 @@ static void qemudCleanup(struct qemud_server *server) { close(sock->fd); sock = sock->next; } + if (server->brctl) + brShutdown(server->brctl); free(server); }