diff --git a/configure.ac b/configure.ac index 40f6654334..3b7535eb2b 100644 --- a/configure.ac +++ b/configure.ac @@ -150,7 +150,8 @@ LIBS=$old_libs dnl Availability of various common headers (non-fatal if missing). AC_CHECK_HEADERS([pwd.h paths.h regex.h sys/un.h \ sys/poll.h syslog.h mntent.h net/ethernet.h linux/magic.h \ - sys/un.h sys/syscall.h netinet/tcp.h ifaddrs.h libtasn1.h]) + sys/un.h sys/syscall.h netinet/tcp.h ifaddrs.h libtasn1.h \ + net/if.h]) dnl Our only use of libtasn1.h is in the testsuite, and can be skipped dnl if the header is not present. Assume -ltasn1 is present if the diff --git a/po/POTFILES.in b/po/POTFILES.in index bd1d7bd0c6..a3685e813b 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -102,7 +102,6 @@ src/test/test_driver.c src/uml/uml_conf.c src/uml/uml_driver.c src/util/authhelper.c -src/util/bridge.c src/util/cgroup.c src/util/command.c src/util/conf.c @@ -127,6 +126,9 @@ src/util/sysinfo.c src/util/util.c src/util/viraudit.c src/util/virfile.c +src/util/virnetdev.c +src/util/virnetdevbridge.c +src/util/virnetdevtap.c src/util/virpidfile.c src/util/virterror.c src/util/xml.c diff --git a/src/Makefile.am b/src/Makefile.am index c419c9420e..e931d41592 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -54,7 +54,6 @@ augeastest_DATA = UTIL_SOURCES = \ util/authhelper.c util/authhelper.h \ util/bitmap.c util/bitmap.h \ - util/bridge.c util/bridge.h \ util/buf.c util/buf.h \ util/command.c util/command.h \ util/conf.c util/conf.h \ @@ -93,7 +92,10 @@ UTIL_SOURCES = \ util/xml.c util/xml.h \ util/virterror.c util/virterror_internal.h \ util/virkeycode.c util/virkeycode.h \ - util/virkeymaps.h + util/virkeymaps.h \ + util/virnetdev.h util/virnetdev.c \ + util/virnetdevbridge.h util/virnetdevbridge.c \ + util/virnetdevtap.h util/virnetdevtap.c EXTRA_DIST += $(srcdir)/util/virkeymaps.h $(srcdir)/util/keymaps.csv \ $(srcdir)/util/virkeycode-mapgen.py diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 9d9e4159d2..97a8a213ad 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -43,7 +43,7 @@ #include "lxc_driver.h" #include "memory.h" #include "util.h" -#include "bridge.h" +#include "virnetdevbridge.h" #include "veth.h" #include "nodeinfo.h" #include "uuid.h" diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index db65ffeeec..a4e7d8aa47 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -42,6 +42,7 @@ #include #include #include +#include #include "virterror_internal.h" #include "datatypes.h" @@ -55,13 +56,15 @@ #include "memory.h" #include "uuid.h" #include "iptables.h" -#include "bridge.h" #include "interface.h" #include "logging.h" #include "dnsmasq.h" #include "util/network.h" #include "configmake.h" #include "ignore-value.h" +#include "virnetdev.h" +#include "virnetdevbridge.h" +#include "virnetdevtap.h" #define NETWORK_PID_DIR LOCALSTATEDIR "/run/libvirt/network" #define NETWORK_STATE_DIR LOCALSTATEDIR "/lib/libvirt/network" diff --git a/src/openvz/openvz_driver.c b/src/openvz/openvz_driver.c index 69ff44436e..12867d3fac 100644 --- a/src/openvz/openvz_driver.c +++ b/src/openvz/openvz_driver.c @@ -54,7 +54,6 @@ #include "openvz_conf.h" #include "nodeinfo.h" #include "memory.h" -#include "bridge.h" #include "virfile.h" #include "logging.h" #include "command.h" diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 5680636564..7c9e60b400 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -38,6 +38,7 @@ #include "domain_audit.h" #include "domain_conf.h" #include "network/bridge_driver.h" +#include "virnetdevtap.h" #include #include diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 1ba200225c..bbe7e74eec 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -28,7 +28,6 @@ # include "ebtables.h" # include "internal.h" -# include "bridge.h" # include "capabilities.h" # include "network_conf.h" # include "domain_conf.h" diff --git a/src/uml/uml_conf.c b/src/uml/uml_conf.c index 9b6abe7a3f..3089abb9d8 100644 --- a/src/uml/uml_conf.c +++ b/src/uml/uml_conf.c @@ -43,11 +43,11 @@ #include "util.h" #include "memory.h" #include "nodeinfo.h" -#include "bridge.h" #include "logging.h" #include "domain_nwfilter.h" #include "virfile.h" #include "command.h" +#include "virnetdevtap.h" #define VIR_FROM_THIS VIR_FROM_UML diff --git a/src/uml/uml_conf.h b/src/uml/uml_conf.h index 01695c700a..383ae6600d 100644 --- a/src/uml/uml_conf.h +++ b/src/uml/uml_conf.h @@ -25,7 +25,6 @@ # define __UML_CONF_H # include "internal.h" -# include "bridge.h" # include "capabilities.h" # include "network_conf.h" # include "domain_conf.h" diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c index 4a417ab2ba..c5587d07a5 100644 --- a/src/uml/uml_driver.c +++ b/src/uml/uml_driver.c @@ -62,6 +62,7 @@ #include "virfile.h" #include "fdstream.h" #include "configmake.h" +#include "virnetdevtap.h" #define VIR_FROM_THIS VIR_FROM_UML diff --git a/src/util/bridge.c b/src/util/bridge.c deleted file mode 100644 index 37c717bff1..0000000000 --- a/src/util/bridge.c +++ /dev/null @@ -1,1115 +0,0 @@ -/* - * Copyright (C) 2007, 2009, 2011 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 - -#if defined(WITH_BRIDGE) - -# include "bridge.h" -# include "virfile.h" - -# include -# include -# 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 /* ARPHRD_ETHER */ - -# include "internal.h" -# include "command.h" -# include "memory.h" -# include "util.h" -# include "logging.h" -# include "network.h" -# include "virterror_internal.h" -# include "intprops.h" - -# define JIFFIES_TO_MS(j) (((j)*1000)/HZ) -# define MS_TO_JIFFIES(ms) (((ms)*HZ)/1000) - -# define VIR_FROM_THIS VIR_FROM_NONE - -static int virNetDevSetupControlFull(const char *ifname, - struct ifreq *ifr, - int domain, - int type) -{ - int fd; - - if (ifname && ifr) { - memset(ifr, 0, sizeof(*ifr)); - - if (virStrcpyStatic(ifr->ifr_name, ifname) == NULL) { - virReportSystemError(ERANGE, - _("Network interface name '%s' is too long"), - ifname); - return -1; - } - } - - if ((fd = socket(domain, type, 0)) < 0) { - virReportSystemError(errno, "%s", - _("Cannot open network interface control socket")); - return -1; - } - - if (virSetInherit(fd, false) < 0) { - virReportSystemError(errno, "%s", - _("Cannot set close-on-exec flag for socket")); - VIR_FORCE_CLOSE(fd); - return -1; - } - - return fd; -} - - -static int virNetDevSetupControl(const char *ifname, - struct ifreq *ifr) -{ - return virNetDevSetupControlFull(ifname, ifr, AF_PACKET, SOCK_DGRAM); -} - -# define SYSFS_NET_DIR "/sys/class/net" -/* - * Bridge parameters can be set via sysfs on newish kernels, - * or by ioctl on older kernels. Perhaps we could just use - * ioctl for every kernel, but its not clear what the long - * term lifespan of the ioctl interface is... - */ -static int virNetDevBridgeSet(const char *brname, - const char *paramname, /* sysfs param name */ - unsigned long value, /* new value */ - int fd, /* control socket */ - struct ifreq *ifr) /* pre-filled bridge name */ -{ - char *path = NULL; - int ret = -1; - - if (virAsprintf(&path, "%s/%s/bridge/%s", SYSFS_NET_DIR, brname, paramname) < 0) { - virReportOOMError(); - return -1; - } - - if (virFileExists(path)) { - char valuestr[INT_BUFSIZE_BOUND(value)]; - snprintf(valuestr, sizeof(valuestr), "%lu", value); - if (virFileWriteStr(path, valuestr, 0) < 0) { - virReportSystemError(errno, - _("Unable to set bridge %s %s"), brname, paramname); - goto cleanup; - } - } else { - unsigned long paramid; - if (STREQ(paramname, "stp_state")) { - paramid = BRCTL_SET_BRIDGE_STP_STATE; - } else if (STREQ(paramname, "forward_delay")) { - paramid = BRCTL_SET_BRIDGE_FORWARD_DELAY; - } else { - virReportSystemError(EINVAL, - _("Unable to set bridge %s %s"), brname, paramname); - goto cleanup; - } - unsigned long args[] = { paramid, value, 0, 0 }; - ifr->ifr_data = (char*)&args; - if (ioctl(fd, SIOCDEVPRIVATE, ifr) < 0) { - virReportSystemError(errno, - _("Unable to set bridge %s %s"), brname, paramname); - goto cleanup; - } - } - - ret = 0; -cleanup: - VIR_FREE(path); - return ret; -} - - -static int virNetDevBridgeGet(const char *brname, - const char *paramname, /* sysfs param name */ - unsigned long *value, /* current value */ - int fd, /* control socket */ - struct ifreq *ifr) /* pre-filled bridge name */ -{ - char *path = NULL; - int ret = -1; - - if (virAsprintf(&path, "%s/%s/bridge/%s", SYSFS_NET_DIR, brname, paramname) < 0) { - virReportOOMError(); - return -1; - } - - if (virFileExists(path)) { - char *valuestr; - if (virFileReadAll(path, INT_BUFSIZE_BOUND(unsigned long), &valuestr) < 0) - goto cleanup; - - if (virStrToLong_ul(valuestr, NULL, 10, value) < 0) { - virReportSystemError(EINVAL, - _("Unable to get bridge %s %s"), brname, paramname); - } - } else { - struct __bridge_info info; - unsigned long args[] = { BRCTL_GET_BRIDGE_INFO, (unsigned long)&info, 0, 0 }; - ifr->ifr_data = (char*)&args; - if (ioctl(fd, SIOCDEVPRIVATE, ifr) < 0) { - virReportSystemError(errno, - _("Unable to get bridge %s %s"), brname, paramname); - goto cleanup; - } - - if (STREQ(paramname, "stp_state")) { - *value = info.stp_enabled; - } else if (STREQ(paramname, "forward_delay")) { - *value = info.forward_delay; - } else { - virReportSystemError(EINVAL, - _("Unable to get bridge %s %s"), brname, paramname); - goto cleanup; - } - } - - ret = 0; -cleanup: - VIR_FREE(path); - return ret; -} - - -/** - * virNetDevBridgeCreate: - * @brname: the bridge name - * - * This function register a new bridge - * - * Returns 0 in case of success or -1 on failure - */ -# ifdef SIOCBRADDBR -int virNetDevBridgeCreate(const char *brname) -{ - int fd = -1; - int ret = -1; - - if ((fd = virNetDevSetupControl(NULL, NULL)) < 0) - return -1; - - if (ioctl(fd, SIOCBRADDBR, brname) < 0) { - virReportSystemError(errno, - _("Unable to create bridge %s"), brname); - goto cleanup; - } - - ret = 0; - -cleanup: - VIR_FORCE_CLOSE(fd); - return ret; -} -# else -int virNetDevBridgeCreate(const char *brname) -{ - virReportSystemError(ENOSYS, - _("Unable to create bridge %s"), brname); - return -1; -} -# endif - -# ifdef SIOCBRDELBR -/** - * virNetDevExists: - * @ifname - * - * Check if the network device @ifname exists - * - * Returns 1 if it exists, 0 if it does not, -1 on error - */ -int virNetDevExists(const char *ifname) -{ - int fd = -1; - int ret = -1; - struct ifreq ifr; - - if ((fd = virNetDevSetupControl(ifname, &ifr)) < 0) - return -1; - - if (ioctl(fd, SIOCGIFFLAGS, &ifr)) { - if (errno == ENODEV) - ret = 0; - else - virReportSystemError(errno, - _("Unable to check interface flags for %s"), ifname); - goto cleanup; - } - - ret = 1; - -cleanup: - VIR_FORCE_CLOSE(fd); - return ret; -} -# else -int virNetDevExists(const char *ifname) -{ - virReportSystemError(ENOSYS, - _("Unable to check interface %s"), ifname); - return -1; -} -# endif - -/** - * virNetDevBridgeDelete: - * @brname: the bridge name - * - * Remove a bridge from the layer. - * - * Returns 0 in case of success or an errno code in case of failure. - */ -# ifdef SIOCBRDELBR -int virNetDevBridgeDelete(const char *brname) -{ - int fd = -1; - int ret = -1; - - if ((fd = virNetDevSetupControl(NULL, NULL)) < 0) - return -1; - - if (ioctl(fd, SIOCBRDELBR, brname) < 0) { - virReportSystemError(errno, - _("Unable to delete bridge %s"), brname); - goto cleanup; - } - - ret = 0; - -cleanup: - VIR_FORCE_CLOSE(fd); - return ret; -} -# else -int virNetDevBridgeDelete(const char *brname ATTRIBUTE_UNUSED) -{ - virReportSystemError(ENOSYS, - _("Unable to delete bridge %s"), brname); - return -1; -} -# endif - -/** - * virNetDevBridgeAddPort: - * @brname: the bridge name - * @ifname: the network interface name - * - * Adds an interface to a bridge - * - * Returns 0 in case of success or an errno code in case of failure. - */ -# ifdef SIOCBRADDIF -int virNetDevBridgeAddPort(const char *brname, - const char *ifname) -{ - int fd = -1; - int ret = -1; - struct ifreq ifr; - - if ((fd = virNetDevSetupControl(brname, &ifr)) < 0) - return -1; - - if (!(ifr.ifr_ifindex = if_nametoindex(ifname))) { - virReportSystemError(ENODEV, - _("Unable to get interface index for %s"), ifname); - goto cleanup; - } - - if (ioctl(fd, SIOCBRADDIF, &ifr) < 0) { - virReportSystemError(errno, - _("Unable to add bridge %s port %s"), brname, ifname); - goto cleanup; - } - - ret = 0; -cleanup: - VIR_FORCE_CLOSE(fd); - return ret; -} -# else -int virNetDevBridgeAddPort(const char *brname, - const char *ifname) -{ - virReportSystemError(ENOSYS, - _("Unable to add bridge %s port %s"), brname, ifname); - return -1; -} -# endif - -/** - * virNetDevBridgeRemovePort: - * @brname: the bridge name - * @ifname: the network interface name - * - * Removes an interface from a bridge - * - * Returns 0 in case of success or an errno code in case of failure. - */ -# ifdef SIOCBRDELIF -int virNetDevBridgeRemovePort(const char *brname, - const char *ifname) -{ - int fd = -1; - int ret = -1; - struct ifreq ifr; - - if ((fd = virNetDevSetupControl(brname, &ifr)) < 0) - return -1; - - if (!(ifr.ifr_ifindex = if_nametoindex(ifname))) { - virReportSystemError(ENODEV, - _("Unable to get interface index for %s"), ifname); - - goto cleanup; - } - - if (ioctl(fd, SIOCBRDELIF, &ifr) < 0) { - virReportSystemError(errno, - _("Unable to remove bridge %s port %s"), brname, ifname); - goto cleanup; - } - - ret = 0; -cleanup: - VIR_FORCE_CLOSE(fd); - return ret; -} -# else -int virNetDevBridgeRemovePort(const char *brname, - const char *ifname) -{ - virReportSystemError(errno, - _("Unable to remove bridge %s port %s"), brname, ifname); - return -1; -} -# endif - -/** - * virNetDevSetMAC: - * @ifname: interface name to set MTU for - * @macaddr: MAC address (VIR_MAC_BUFLEN in size) - * - * This function sets the @macaddr for a given interface @ifname. This - * gets rid of the kernel's automatically assigned random MAC. - * - * Returns 0 in case of success or -1 on failure - */ -int virNetDevSetMAC(const char *ifname, - const unsigned char *macaddr) -{ - int fd = -1; - int ret = -1; - struct ifreq ifr; - - if ((fd = virNetDevSetupControl(ifname, &ifr)) < 0) - return -1; - - /* To fill ifr.ifr_hdaddr.sa_family field */ - if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { - virReportSystemError(errno, - _("Cannot get interface MAC on '%s'"), - ifname); - goto cleanup; - } - - memcpy(ifr.ifr_hwaddr.sa_data, macaddr, VIR_MAC_BUFLEN); - - if (ioctl(fd, SIOCSIFHWADDR, &ifr) < 0) { - virReportSystemError(errno, - _("Cannot set interface MAC on '%s'"), - ifname); - goto cleanup; - } - - ret = 0; - -cleanup: - VIR_FORCE_CLOSE(fd); - return ret; -} - -/** - * virNetDevGetMAC: - * @ifname: interface name to set MTU for - * @macaddr: MAC address (VIR_MAC_BUFLEN in size) - * - * This function gets the @macaddr for a given interface @ifname. - * - * Returns 0 in case of success or -1 on failure - */ -int virNetDevGetMAC(const char *ifname, - unsigned char *macaddr) -{ - int fd = -1; - int ret = -1; - struct ifreq ifr; - - if ((fd = virNetDevSetupControl(ifname, &ifr)) < 0) - return -1; - - if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { - virReportSystemError(errno, - _("Cannot get interface MAC on '%s'"), - ifname); - goto cleanup; - } - - memcpy(macaddr, ifr.ifr_hwaddr.sa_data, VIR_MAC_BUFLEN); - - ret = 0; - -cleanup: - VIR_FORCE_CLOSE(fd); - return ret; -} - -/** - * virNetDevGetMTU: - * @ifname: interface name get MTU for - * - * This function gets the @mtu value set for a given interface @ifname. - * - * Returns the MTU value in case of success, or -1 on failure. - */ -int virNetDevGetMTU(const char *ifname) -{ - int fd = -1; - int ret = -1; - struct ifreq ifr; - - if ((fd = virNetDevSetupControl(ifname, &ifr)) < 0) - return -1; - - if (ioctl(fd, SIOCGIFMTU, &ifr) < 0) { - virReportSystemError(errno, - _("Cannot get interface MTU on '%s'"), - ifname); - goto cleanup; - } - - ret = ifr.ifr_mtu; - -cleanup: - VIR_FORCE_CLOSE(fd); - return ret; -} - -/** - * virNetDevSetMTU: - * @ifname: interface name to set MTU for - * @mtu: MTU value - * - * This function sets the @mtu for a given interface @ifname. Typically - * used on a tap device to set up for Jumbo Frames. - * - * Returns 0 in case of success, or -1 on failure - */ -int virNetDevSetMTU(const char *ifname, int mtu) -{ - int fd = -1; - int ret = -1; - struct ifreq ifr; - - if ((fd = virNetDevSetupControl(ifname, &ifr)) < 0) - return -1; - - ifr.ifr_mtu = mtu; - - if (ioctl(fd, SIOCSIFMTU, &ifr) < 0) { - virReportSystemError(errno, - _("Cannot set interface MTU on '%s'"), - ifname); - goto cleanup; - } - - ret = 0; - -cleanup: - VIR_FORCE_CLOSE(fd); - return ret; -} - -/** - * virNetDevSetMTUFromDevice: - * @ifname: name of the interface whose MTU we want to set - * @otherifname: name of the interface whose MTU we want to copy - * - * Sets the interface mtu to the same MTU as another interface - * - * Returns 0 in case of success, or -1 on failure - */ -int virNetDevSetMTUFromDevice(const char *ifname, - const char *otherifname) -{ - int mtu = virNetDevGetMTU(otherifname); - - if (mtu < 0) - return -1; - - return virNetDevSetMTU(ifname, mtu); -} - -/** - * virNetDevProbeVnetHdr: - * @tapfd: a tun/tap file descriptor - * - * Check whether it is safe to enable the IFF_VNET_HDR flag on the - * tap interface. - * - * Setting IFF_VNET_HDR enables QEMU's virtio_net driver to allow - * guests to pass larger (GSO) packets, with partial checksums, to - * the host. This greatly increases the achievable throughput. - * - * It is only useful to enable this when we're setting up a virtio - * interface. And it is only *safe* to enable it when we know for - * sure that a) qemu has support for IFF_VNET_HDR and b) the running - * kernel implements the TUNGETIFF ioctl(), which qemu needs to query - * the supplied tapfd. - * - * Returns 1 if VnetHdr is supported, 0 if not supported - */ -# ifdef IFF_VNET_HDR -static int -virNetDevProbeVnetHdr(int tapfd) -{ -# if defined(IFF_VNET_HDR) && defined(TUNGETFEATURES) && defined(TUNGETIFF) - unsigned int features; - struct ifreq dummy; - - if (ioctl(tapfd, TUNGETFEATURES, &features) != 0) { - VIR_INFO("Not enabling IFF_VNET_HDR; " - "TUNGETFEATURES ioctl() not implemented"); - return 0; - } - - if (!(features & IFF_VNET_HDR)) { - VIR_INFO("Not enabling IFF_VNET_HDR; " - "TUNGETFEATURES ioctl() reports no IFF_VNET_HDR"); - return 0; - } - - /* The kernel will always return -1 at this point. - * If TUNGETIFF is not implemented then errno == EBADFD. - */ - if (ioctl(tapfd, TUNGETIFF, &dummy) != -1 || errno != EBADFD) { - VIR_INFO("Not enabling IFF_VNET_HDR; " - "TUNGETIFF ioctl() not implemented"); - return 0; - } - - VIR_INFO("Enabling IFF_VNET_HDR"); - - return 1; -# else - (void) tapfd; - VIR_INFO("Not enabling IFF_VNET_HDR; disabled at build time"); - return 0; -# endif -} -# endif - -/** - * brAddTap: - * @brname: the bridge name - * @ifname: the interface name (or name template) - * @macaddr: desired MAC address (VIR_MAC_BUFLEN long) - * @vnet_hdr: whether to try enabling IFF_VNET_HDR - * @tapfd: file descriptor return value for the new tap device - * - * This function creates a new tap device on a bridge. @ifname can be either - * a fixed name or a name template with '%d' for dynamic name allocation. - * in either case the final name for the bridge will be stored in @ifname. - * If the @tapfd parameter is supplied, the open tap device file - * descriptor will be returned, otherwise the TAP device will be made - * persistent and closed. The caller must use brDeleteTap to remove - * a persistent TAP devices when it is no longer needed. - * - * Returns 0 in case of success or -1 on failure - */ -int virNetDevTapCreateInBridgePort(const char *brname, - char **ifname, - const unsigned char *macaddr, - int vnet_hdr, - bool up, - int *tapfd) -{ - if (virNetDevTapCreate(ifname, vnet_hdr, tapfd) < 0) - return -1; - - /* We need to set the interface MAC before adding it - * to the bridge, because the bridge assumes the lowest - * MAC of all enslaved interfaces & we don't want it - * seeing the kernel allocate random MAC for the TAP - * device before we set our static MAC. - */ - if (virNetDevSetMAC(*ifname, macaddr) < 0) - goto error; - - /* We need to set the interface MTU before adding it - * to the bridge, because the bridge will have its - * MTU adjusted automatically when we add the new interface. - */ - if (virNetDevSetMTUFromDevice(*ifname, brname) < 0) - goto error; - - if (virNetDevBridgeAddPort(brname, *ifname) < 0) - goto error; - - if (virNetDevSetOnline(*ifname, up) < 0) - goto error; - - return 0; - -error: - if (tapfd) - VIR_FORCE_CLOSE(*tapfd); - return -1; -} - -int virNetDevTapDelete(const char *ifname) -{ - struct ifreq try; - int fd; - int ret = -1; - - if ((fd = open("/dev/net/tun", O_RDWR)) < 0) { - virReportSystemError(errno, "%s", - _("Unable to open /dev/net/tun, is tun module loaded?")); - return -1; - } - - memset(&try, 0, sizeof(struct ifreq)); - try.ifr_flags = IFF_TAP|IFF_NO_PI; - - if (virStrcpyStatic(try.ifr_name, ifname) == NULL) { - virReportSystemError(ERANGE, - _("Network interface name '%s' is too long"), - ifname); - goto cleanup; - } - - if (ioctl(fd, TUNSETIFF, &try) < 0) { - virReportSystemError(errno, "%s", - _("Unable to associate TAP device")); - goto cleanup; - } - - if (ioctl(fd, TUNSETPERSIST, 0) < 0) { - virReportSystemError(errno, "%s", - _("Unable to make TAP device non-persistent")); - goto cleanup; - } - - ret = 0; - -cleanup: - VIR_FORCE_CLOSE(fd); - return ret; -} - - -/** - * virNetDevSetOnline: - * @ifname: the interface name - * @online: true for up, false for down - * - * Function to control if an interface is activated (up, true) or not (down, false) - * - * Returns 0 in case of success or -1 on error. - */ -int virNetDevSetOnline(const char *ifname, - bool online) -{ - int fd = -1; - int ret = -1; - struct ifreq ifr; - int ifflags; - - if ((fd = virNetDevSetupControl(ifname, &ifr)) < 0) - return -1; - - if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { - virReportSystemError(errno, - _("Cannot get interface flags on '%s'"), - ifname); - goto cleanup; - } - - if (online) - ifflags = ifr.ifr_flags | IFF_UP; - else - ifflags = ifr.ifr_flags & ~IFF_UP; - - if (ifr.ifr_flags != ifflags) { - ifr.ifr_flags = ifflags; - if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) { - virReportSystemError(errno, - _("Cannot set interface flags on '%s'"), - ifname); - goto cleanup; - } - } - - ret = 0; - -cleanup: - VIR_FORCE_CLOSE(fd); - return ret; -} - -/** - * virNetDevIsOnline: - * @ifname: the interface name - * @online: where to store the status - * - * Function to query if an interface is activated (true) or not (false) - * - * Returns 0 in case of success or an errno code in case of failure. - */ -int virNetDevIsOnline(const char *ifname, - bool *online) -{ - int fd = -1; - int ret = -1; - struct ifreq ifr; - - if ((fd = virNetDevSetupControl(ifname, &ifr)) < 0) - return -1; - - if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { - virReportSystemError(errno, - _("Cannot get interface flags on '%s'"), - ifname); - goto cleanup; - } - - *online = (ifr.ifr_flags & IFF_UP) ? true : false; - ret = 0; - -cleanup: - VIR_FORCE_CLOSE(fd); - return ret; -} - -/** - * virNetDevSetIPv4Address: - * @ifname: the interface name - * @addr: the IP address (IPv4 or IPv6) - * @prefix: number of 1 bits in the netmask - * - * Add an IP address to an interface. This function *does not* remove - * any previously added IP addresses - that must be done separately with - * brDelInetAddress. - * - * Returns 0 in case of success or -1 in case of error. - */ - -int virNetDevSetIPv4Address(const char *ifname, - virSocketAddr *addr, - unsigned int prefix) -{ - virCommandPtr cmd = NULL; - char *addrstr = NULL, *bcaststr = NULL; - virSocketAddr broadcast; - int ret = -1; - - if (!(addrstr = virSocketFormatAddr(addr))) - goto cleanup; - /* format up a broadcast address if this is IPv4 */ - if ((VIR_SOCKET_IS_FAMILY(addr, AF_INET)) && - ((virSocketAddrBroadcastByPrefix(addr, prefix, &broadcast) < 0) || - !(bcaststr = virSocketFormatAddr(&broadcast)))) { - goto cleanup; - } - cmd = virCommandNew(IP_PATH); - virCommandAddArgList(cmd, "addr", "add", NULL); - virCommandAddArgFormat(cmd, "%s/%u", addrstr, prefix); - if (bcaststr) - virCommandAddArgList(cmd, "broadcast", bcaststr, NULL); - virCommandAddArgList(cmd, "dev", ifname, NULL); - - if (virCommandRun(cmd, NULL) < 0) - goto cleanup; - - ret = 0; -cleanup: - VIR_FREE(addrstr); - VIR_FREE(bcaststr); - virCommandFree(cmd); - return ret; -} - -/** - * virNetDevClearIPv4Address: - * @ifname: the interface name - * @addr: the IP address (IPv4 or IPv6) - * @prefix: number of 1 bits in the netmask - * - * Delete an IP address from an interface. - * - * Returns 0 in case of success or -1 in case of error. - */ - -int virNetDevClearIPv4Address(const char *ifname, - virSocketAddr *addr, - unsigned int prefix) -{ - virCommandPtr cmd = NULL; - char *addrstr; - int ret = -1; - - if (!(addrstr = virSocketFormatAddr(addr))) - goto cleanup; - cmd = virCommandNew(IP_PATH); - virCommandAddArgList(cmd, "addr", "del", NULL); - virCommandAddArgFormat(cmd, "%s/%u", addrstr, prefix); - virCommandAddArgList(cmd, "dev", ifname, NULL); - - if (virCommandRun(cmd, NULL) < 0) - goto cleanup; - - ret = 0; -cleanup: - VIR_FREE(addrstr); - virCommandFree(cmd); - return ret; -} - -/** - * virNetDevBridgeSetSTPDelay: - * @brname: the bridge name - * @delay: delay in seconds - * - * Set the bridge forward delay - * - * Returns 0 in case of success or -1 on failure - */ - -int virNetDevBridgeSetSTPDelay(const char *brname, - int delay) -{ - int fd = -1; - int ret = -1; - struct ifreq ifr; - - if ((fd = virNetDevSetupControl(brname, &ifr)) < 0) - goto cleanup; - - ret = virNetDevBridgeSet(brname, "stp_state", MS_TO_JIFFIES(delay), - fd, &ifr); - -cleanup: - VIR_FORCE_CLOSE(fd); - return ret; -} - - -/** - * virNetDevBridgeGetSTPDelay: - * @brname: the bridge device name - * @delayms: the forward delay in milliseconds - * - * Retrives the forward delay for the bridge device @brname - * storing it in @delayms. The forward delay is only meaningful - * if STP is enabled - * - * Returns 0 on success, -1 on error+ - */ -int virNetDevBridgeGetSTPDelay(const char *brname, - int *delayms) -{ - int fd = -1; - int ret = -1; - struct ifreq ifr; - unsigned long i; - - if ((fd = virNetDevSetupControl(brname, &ifr)) < 0) - goto cleanup; - - ret = virNetDevBridgeGet(brname, "stp_state", &i, - fd, &ifr); - *delayms = JIFFIES_TO_MS(i); - -cleanup: - VIR_FORCE_CLOSE(fd); - return ret; -} - - -/** - * virNetDevBridgeSetSTP: - * @brname: the bridge name - * @enable: 1 to enable, 0 to disable - * - * Control whether the bridge participates in the spanning tree protocol, - * in general don't disable it without good reasons. - * - * Returns 0 in case of success or -1 on failure - */ -int virNetDevBridgeSetSTP(const char *brname, - bool enable) -{ - int fd = -1; - int ret = -1; - struct ifreq ifr; - - if ((fd = virNetDevSetupControl(brname, &ifr)) < 0) - goto cleanup; - - ret = virNetDevBridgeSet(brname, "stp_state", enable ? 1 : 0, - fd, &ifr); - -cleanup: - VIR_FORCE_CLOSE(fd); - return ret; -} - - -/** - * virNetDevBridgeGetSTP: - * @brname: the bridge device name - * @enabled: returns the STP state - * - * Determine the state of the spanning tree protocol on - * the device @brname, returning the state in @enabled - * - * Returns 0 on success, -1 on error - */ -int virNetDevBridgeGetSTP(const char *brname, - bool *enabled) -{ - int fd = -1; - int ret = -1; - struct ifreq ifr; - unsigned long i; - - if ((fd = virNetDevSetupControl(brname, &ifr)) < 0) - goto cleanup; - - ret = virNetDevBridgeGet(brname, "stp_state", &i, - fd, &ifr); - *enabled = i ? true : false; - -cleanup: - VIR_FORCE_CLOSE(fd); - return ret; -} - - -/** - * brCreateTap: - * @ifname: the interface name - * @vnet_hr: whether to try enabling IFF_VNET_HDR - * @tapfd: file descriptor return value for the new tap device - * - * Creates a tap interface. - * If the @tapfd parameter is supplied, the open tap device file - * descriptor will be returned, otherwise the TAP device will be made - * persistent and closed. The caller must use brDeleteTap to remove - * a persistent TAP devices when it is no longer needed. - * - * Returns 0 in case of success or an errno code in case of failure. - */ - -int virNetDevTapCreate(char **ifname, - int vnet_hdr ATTRIBUTE_UNUSED, - int *tapfd) -{ - int fd; - struct ifreq ifr; - int ret = -1; - - if ((fd = open("/dev/net/tun", O_RDWR)) < 0) { - virReportSystemError(errno, "%s", - _("Unable to open /dev/net/tun, is tun module loaded?")); - return -1; - } - - memset(&ifr, 0, sizeof(ifr)); - - ifr.ifr_flags = IFF_TAP|IFF_NO_PI; - -# ifdef IFF_VNET_HDR - if (vnet_hdr && virNetDevProbeVnetHdr(fd)) - ifr.ifr_flags |= IFF_VNET_HDR; -# endif - - if (virStrcpyStatic(ifr.ifr_name, *ifname) == NULL) { - virReportSystemError(ERANGE, - _("Network interface name '%s' is too long"), - *ifname); - goto cleanup; - - } - - if (ioctl(fd, TUNSETIFF, &ifr) < 0) { - virReportSystemError(errno, - _("Unable to create tap device %s"), - NULLSTR(*ifname)); - goto cleanup; - } - - if (!tapfd && - (errno = ioctl(fd, TUNSETPERSIST, 1))) { - virReportSystemError(errno, - _("Unable to set tap device %s to persistent"), - NULLSTR(*ifname)); - goto cleanup; - } - - VIR_FREE(*ifname); - if (!(*ifname = strdup(ifr.ifr_name))) { - virReportOOMError(); - goto cleanup; - } - if (tapfd) - *tapfd = fd; - else - VIR_FORCE_CLOSE(fd); - - ret = 0; - -cleanup: - if (ret < 0) - VIR_FORCE_CLOSE(fd); - - return ret; -} - -#endif /* WITH_BRIDGE */ diff --git a/src/util/bridge.h b/src/util/bridge.h deleted file mode 100644 index ba1a108774..0000000000 --- a/src/util/bridge.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * 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 - -# if defined(WITH_BRIDGE) - -# include -# include -# include "network.h" - -/** - * BR_IFNAME_MAXLEN: - * maximum size in byte of the name for an interface - */ -# define BR_IFNAME_MAXLEN IF_NAMESIZE - -/** - * BR_INET_ADDR_MAXLEN: - * maximum size in bytes for an inet addess name - */ -# define BR_INET_ADDR_MAXLEN INET_ADDRSTRLEN - -int virNetDevBridgeCreate(const char *brname) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; -int virNetDevBridgeDelete(const char *brname) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; -int virNetDevExists(const char *brname) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; - -int virNetDevBridgeAddPort(const char *brname, - const char *ifname) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; - -int virNetDevBridgeRemovePort(const char *brname, - const char *ifname) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; - -enum { - BR_TAP_VNET_HDR = (1 << 0), - BR_TAP_PERSIST = (1 << 1), -}; - -int virNetDevTapCreateInBridgePort(const char *brname, - char **ifname, - const unsigned char *macaddr, - int vnet_hdr, - bool up, - int *tapfd) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3) - ATTRIBUTE_RETURN_CHECK; - - -int virNetDevTapDelete(const char *ifname) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; - -int virNetDevSetOnline(const char *ifname, - bool online) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; -int virNetDevIsOnline(const char *ifname, - bool *online) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; - -int virNetDevSetIPv4Address(const char *ifname, - virSocketAddr *addr, - unsigned int prefix) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; -int virNetDevClearIPv4Address(const char *ifname, - virSocketAddr *addr, - unsigned int prefix) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; - -int virNetDevBridgeSetSTPDelay(const char *brname, - int delay) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; -int virNetDevBridgeGetSTPDelay(const char *brname, - int *delay) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; -int virNetDevBridgeSetSTP(const char *brname, - bool enable) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; -int virNetDevBridgeGetSTP(const char *brname, - bool *enable) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; - -int virNetDevTapCreate(char **ifname, - int vnet_hdr, - int *tapfd) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; - -int virNetDevSetMAC(const char *ifname, - const unsigned char *macaddr) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; -int virNetDevGetMAC(const char *ifname, - unsigned char *macaddr) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; -int virNetDevSetMTU(const char *ifname, - int mtu) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; -int virNetDevSetMTUFromDevice(const char *ifname, - const char *otherifname) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; -int virNetDevGetMTU(const char *ifname) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; - -# endif /* WITH_BRIDGE */ - -#endif /* __QEMUD_BRIDGE_H__ */ diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c new file mode 100644 index 0000000000..d8a9dd8759 --- /dev/null +++ b/src/util/virnetdev.c @@ -0,0 +1,524 @@ +/* + * Copyright (C) 2007-2011 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 + * Daniel P. Berrange + */ + +#include + +#include "virnetdev.h" +#include "virfile.h" +#include "virterror_internal.h" +#include "command.h" +#include "memory.h" + +#include +#ifdef HAVE_NET_IF_H +# include +#endif + +#define VIR_FROM_THIS VIR_FROM_NONE + +#ifdef HAVE_NET_IF_H +static int virNetDevSetupControlFull(const char *ifname, + struct ifreq *ifr, + int domain, + int type) +{ + int fd; + + memset(ifr, 0, sizeof(*ifr)); + + if (virStrcpyStatic(ifr->ifr_name, ifname) == NULL) { + virReportSystemError(ERANGE, + _("Network interface name '%s' is too long"), + ifname); + return -1; + } + + if ((fd = socket(domain, type, 0)) < 0) { + virReportSystemError(errno, "%s", + _("Cannot open network interface control socket")); + return -1; + } + + if (virSetInherit(fd, false) < 0) { + virReportSystemError(errno, "%s", + _("Cannot set close-on-exec flag for socket")); + VIR_FORCE_CLOSE(fd); + return -1; + } + + return fd; +} + + +static int virNetDevSetupControl(const char *ifname, + struct ifreq *ifr) +{ + return virNetDevSetupControlFull(ifname, ifr, AF_PACKET, SOCK_DGRAM); +} +#endif + + +#ifdef SIOCGIFFLAGS +/** + * virNetDevExists: + * @ifname + * + * Check if the network device @ifname exists + * + * Returns 1 if it exists, 0 if it does not, -1 on error + */ +int virNetDevExists(const char *ifname) +{ + int fd = -1; + int ret = -1; + struct ifreq ifr; + + if ((fd = virNetDevSetupControl(ifname, &ifr)) < 0) + return -1; + + if (ioctl(fd, SIOCGIFFLAGS, &ifr)) { + if (errno == ENODEV) + ret = 0; + else + virReportSystemError(errno, + _("Unable to check interface flags for %s"), ifname); + goto cleanup; + } + + ret = 1; + +cleanup: + VIR_FORCE_CLOSE(fd); + return ret; +} +#else +int virNetDevExists(const char *ifname) +{ + virReportSystemError(ENOSYS, + _("Unable to check interface %s"), ifname); + return -1; +} +#endif + + +#ifdef SIOCGIFHWADDR +/** + * virNetDevSetMAC: + * @ifname: interface name to set MTU for + * @macaddr: MAC address (VIR_MAC_BUFLEN in size) + * + * This function sets the @macaddr for a given interface @ifname. This + * gets rid of the kernel's automatically assigned random MAC. + * + * Returns 0 in case of success or -1 on failure + */ +int virNetDevSetMAC(const char *ifname, + const unsigned char *macaddr) +{ + int fd = -1; + int ret = -1; + struct ifreq ifr; + + if ((fd = virNetDevSetupControl(ifname, &ifr)) < 0) + return -1; + + /* To fill ifr.ifr_hdaddr.sa_family field */ + if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { + virReportSystemError(errno, + _("Cannot get interface MAC on '%s'"), + ifname); + goto cleanup; + } + + memcpy(ifr.ifr_hwaddr.sa_data, macaddr, VIR_MAC_BUFLEN); + + if (ioctl(fd, SIOCSIFHWADDR, &ifr) < 0) { + virReportSystemError(errno, + _("Cannot set interface MAC on '%s'"), + ifname); + goto cleanup; + } + + ret = 0; + +cleanup: + VIR_FORCE_CLOSE(fd); + return ret; +} +#else +int virNetDevSetMAC(const char *ifname, + const unsigned char *macaddr ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENOSYS, + _("Cannot set interface MAC on '%s'"), + ifname); + return -1; +} +#endif + + +#ifdef SIOCGIFHWADDR +/** + * virNetDevGetMAC: + * @ifname: interface name to set MTU for + * @macaddr: MAC address (VIR_MAC_BUFLEN in size) + * + * This function gets the @macaddr for a given interface @ifname. + * + * Returns 0 in case of success or -1 on failure + */ +int virNetDevGetMAC(const char *ifname, + unsigned char *macaddr) +{ + int fd = -1; + int ret = -1; + struct ifreq ifr; + + if ((fd = virNetDevSetupControl(ifname, &ifr)) < 0) + return -1; + + if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { + virReportSystemError(errno, + _("Cannot get interface MAC on '%s'"), + ifname); + goto cleanup; + } + + memcpy(macaddr, ifr.ifr_hwaddr.sa_data, VIR_MAC_BUFLEN); + + ret = 0; + +cleanup: + VIR_FORCE_CLOSE(fd); + return ret; +} +#else +int virNetDevGetMAC(const char *ifname, + unsigned char *macaddr ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENOSYS, + _("Cannot get interface MAC on '%s'"), + ifname); + return -1; +} +#endif + + +#ifdef SIOCGIFMTU +/** + * virNetDevGetMTU: + * @ifname: interface name get MTU for + * + * This function gets the @mtu value set for a given interface @ifname. + * + * Returns the MTU value in case of success, or -1 on failure. + */ +int virNetDevGetMTU(const char *ifname) +{ + int fd = -1; + int ret = -1; + struct ifreq ifr; + + if ((fd = virNetDevSetupControl(ifname, &ifr)) < 0) + return -1; + + if (ioctl(fd, SIOCGIFMTU, &ifr) < 0) { + virReportSystemError(errno, + _("Cannot get interface MTU on '%s'"), + ifname); + goto cleanup; + } + + ret = ifr.ifr_mtu; + +cleanup: + VIR_FORCE_CLOSE(fd); + return ret; +} +#else +int virNetDevGetMTU(const char *ifname) +{ + virReportSystemError(ENOSYS, + _("Cannot get interface MTU on '%s'"), + ifname); + return -1; +} +#endif + + +#ifdef SIOCSIFMTU +/** + * virNetDevSetMTU: + * @ifname: interface name to set MTU for + * @mtu: MTU value + * + * This function sets the @mtu for a given interface @ifname. Typically + * used on a tap device to set up for Jumbo Frames. + * + * Returns 0 in case of success, or -1 on failure + */ +int virNetDevSetMTU(const char *ifname, int mtu) +{ + int fd = -1; + int ret = -1; + struct ifreq ifr; + + if ((fd = virNetDevSetupControl(ifname, &ifr)) < 0) + return -1; + + ifr.ifr_mtu = mtu; + + if (ioctl(fd, SIOCSIFMTU, &ifr) < 0) { + virReportSystemError(errno, + _("Cannot set interface MTU on '%s'"), + ifname); + goto cleanup; + } + + ret = 0; + +cleanup: + VIR_FORCE_CLOSE(fd); + return ret; +} +#else +int virNetDevSetMTU(const char *ifname, int mtu ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENOSYS, + _("Cannot set interface MTU on '%s'"), + ifname); + return -1; +} +#endif + + +/** + * virNetDevSetMTUFromDevice: + * @ifname: name of the interface whose MTU we want to set + * @otherifname: name of the interface whose MTU we want to copy + * + * Sets the interface mtu to the same MTU as another interface + * + * Returns 0 in case of success, or -1 on failure + */ +int virNetDevSetMTUFromDevice(const char *ifname, + const char *otherifname) +{ + int mtu = virNetDevGetMTU(otherifname); + + if (mtu < 0) + return -1; + + return virNetDevSetMTU(ifname, mtu); +} + + +#ifdef SIOCSIFFLAGS +/** + * virNetDevSetOnline: + * @ifname: the interface name + * @online: true for up, false for down + * + * Function to control if an interface is activated (up, true) or not (down, false) + * + * Returns 0 in case of success or -1 on error. + */ +int virNetDevSetOnline(const char *ifname, + bool online) +{ + int fd = -1; + int ret = -1; + struct ifreq ifr; + int ifflags; + + if ((fd = virNetDevSetupControl(ifname, &ifr)) < 0) + return -1; + + if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { + virReportSystemError(errno, + _("Cannot get interface flags on '%s'"), + ifname); + goto cleanup; + } + + if (online) + ifflags = ifr.ifr_flags | IFF_UP; + else + ifflags = ifr.ifr_flags & ~IFF_UP; + + if (ifr.ifr_flags != ifflags) { + ifr.ifr_flags = ifflags; + if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) { + virReportSystemError(errno, + _("Cannot set interface flags on '%s'"), + ifname); + goto cleanup; + } + } + + ret = 0; + +cleanup: + VIR_FORCE_CLOSE(fd); + return ret; +} +#else +int virNetDevSetOnline(const char *ifname, + bool online ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENOSYS, + _("Cannot set interface flags on '%s'"), + ifname); + return -1; +} +#endif + + +#ifdef SIOCGIFFLAGS +/** + * virNetDevIsOnline: + * @ifname: the interface name + * @online: where to store the status + * + * Function to query if an interface is activated (true) or not (false) + * + * Returns 0 in case of success or an errno code in case of failure. + */ +int virNetDevIsOnline(const char *ifname, + bool *online) +{ + int fd = -1; + int ret = -1; + struct ifreq ifr; + + if ((fd = virNetDevSetupControl(ifname, &ifr)) < 0) + return -1; + + if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { + virReportSystemError(errno, + _("Cannot get interface flags on '%s'"), + ifname); + goto cleanup; + } + + *online = (ifr.ifr_flags & IFF_UP) ? true : false; + ret = 0; + +cleanup: + VIR_FORCE_CLOSE(fd); + return ret; +} +#else +int virNetDevIsOnline(const char *ifname, + bool *online ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENOSYS, + _("Cannot get interface flags on '%s'"), + ifname); + return -1; +} +#endif + + +/** + * virNetDevSetIPv4Address: + * @ifname: the interface name + * @addr: the IP address (IPv4 or IPv6) + * @prefix: number of 1 bits in the netmask + * + * Add an IP address to an interface. This function *does not* remove + * any previously added IP addresses - that must be done separately with + * brDelInetAddress. + * + * Returns 0 in case of success or -1 in case of error. + */ + +int virNetDevSetIPv4Address(const char *ifname, + virSocketAddr *addr, + unsigned int prefix) +{ + virCommandPtr cmd = NULL; + char *addrstr = NULL, *bcaststr = NULL; + virSocketAddr broadcast; + int ret = -1; + + if (!(addrstr = virSocketFormatAddr(addr))) + goto cleanup; + /* format up a broadcast address if this is IPv4 */ + if ((VIR_SOCKET_IS_FAMILY(addr, AF_INET)) && + ((virSocketAddrBroadcastByPrefix(addr, prefix, &broadcast) < 0) || + !(bcaststr = virSocketFormatAddr(&broadcast)))) { + goto cleanup; + } + cmd = virCommandNew(IP_PATH); + virCommandAddArgList(cmd, "addr", "add", NULL); + virCommandAddArgFormat(cmd, "%s/%u", addrstr, prefix); + if (bcaststr) + virCommandAddArgList(cmd, "broadcast", bcaststr, NULL); + virCommandAddArgList(cmd, "dev", ifname, NULL); + + if (virCommandRun(cmd, NULL) < 0) + goto cleanup; + + ret = 0; +cleanup: + VIR_FREE(addrstr); + VIR_FREE(bcaststr); + virCommandFree(cmd); + return ret; +} + +/** + * virNetDevClearIPv4Address: + * @ifname: the interface name + * @addr: the IP address (IPv4 or IPv6) + * @prefix: number of 1 bits in the netmask + * + * Delete an IP address from an interface. + * + * Returns 0 in case of success or -1 in case of error. + */ + +int virNetDevClearIPv4Address(const char *ifname, + virSocketAddr *addr, + unsigned int prefix) +{ + virCommandPtr cmd = NULL; + char *addrstr; + int ret = -1; + + if (!(addrstr = virSocketFormatAddr(addr))) + goto cleanup; + cmd = virCommandNew(IP_PATH); + virCommandAddArgList(cmd, "addr", "del", NULL); + virCommandAddArgFormat(cmd, "%s/%u", addrstr, prefix); + virCommandAddArgList(cmd, "dev", ifname, NULL); + + if (virCommandRun(cmd, NULL) < 0) + goto cleanup; + + ret = 0; +cleanup: + VIR_FREE(addrstr); + virCommandFree(cmd); + return ret; +} diff --git a/src/util/virnetdev.h b/src/util/virnetdev.h new file mode 100644 index 0000000000..a7d4a77bcd --- /dev/null +++ b/src/util/virnetdev.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2007-2011 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 + * Daniel P. Berrange + */ + +#ifndef __VIR_NETDEV_H__ +# define __VIR_NETDEV_H__ + +# include "network.h" + +int virNetDevExists(const char *brname) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; + +int virNetDevSetOnline(const char *ifname, + bool online) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; +int virNetDevIsOnline(const char *ifname, + bool *online) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; + +int virNetDevSetIPv4Address(const char *ifname, + virSocketAddr *addr, + unsigned int prefix) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; +int virNetDevClearIPv4Address(const char *ifname, + virSocketAddr *addr, + unsigned int prefix) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; + + +int virNetDevSetMAC(const char *ifname, + const unsigned char *macaddr) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; +int virNetDevGetMAC(const char *ifname, + unsigned char *macaddr) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; +int virNetDevSetMTU(const char *ifname, + int mtu) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; +int virNetDevSetMTUFromDevice(const char *ifname, + const char *otherifname) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; +int virNetDevGetMTU(const char *ifname) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; + +#endif /* __VIR_NETDEV_H__ */ diff --git a/src/util/virnetdevbridge.c b/src/util/virnetdevbridge.c new file mode 100644 index 0000000000..060445db85 --- /dev/null +++ b/src/util/virnetdevbridge.c @@ -0,0 +1,527 @@ +/* + * Copyright (C) 2007-2011 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 + * Daniel P. Berrange + */ + +#include + +#include "virnetdevbridge.h" +#include "virterror_internal.h" +#include "util.h" +#include "virfile.h" +#include "memory.h" +#include "intprops.h" + +#include +#ifdef HAVE_NET_IF_H +# include +#endif +#ifdef __linux__ +# include +# include /* HZ */ +# include /* SYSFS_BRIDGE_ATTR */ + +# define JIFFIES_TO_MS(j) (((j)*1000)/HZ) +# define MS_TO_JIFFIES(ms) (((ms)*HZ)/1000) +#endif + +#define VIR_FROM_THIS VIR_FROM_NONE + + +#ifdef HAVE_NET_IF_H +static int virNetDevSetupControlFull(const char *ifname, + struct ifreq *ifr, + int domain, + int type) +{ + int fd; + + if (ifname && ifr) { + memset(ifr, 0, sizeof(*ifr)); + + if (virStrcpyStatic(ifr->ifr_name, ifname) == NULL) { + virReportSystemError(ERANGE, + _("Network interface name '%s' is too long"), + ifname); + return -1; + } + } + + if ((fd = socket(domain, type, 0)) < 0) { + virReportSystemError(errno, "%s", + _("Cannot open network interface control socket")); + return -1; + } + + if (virSetInherit(fd, false) < 0) { + virReportSystemError(errno, "%s", + _("Cannot set close-on-exec flag for socket")); + VIR_FORCE_CLOSE(fd); + return -1; + } + + return fd; +} + + +static int virNetDevSetupControl(const char *ifname, + struct ifreq *ifr) +{ + return virNetDevSetupControlFull(ifname, ifr, AF_PACKET, SOCK_DGRAM); +} +#endif + +#ifdef __linux__ +# define SYSFS_NET_DIR "/sys/class/net" +/* + * Bridge parameters can be set via sysfs on newish kernels, + * or by ioctl on older kernels. Perhaps we could just use + * ioctl for every kernel, but its not clear what the long + * term lifespan of the ioctl interface is... + */ +static int virNetDevBridgeSet(const char *brname, + const char *paramname, /* sysfs param name */ + unsigned long value, /* new value */ + int fd, /* control socket */ + struct ifreq *ifr) /* pre-filled bridge name */ +{ + char *path = NULL; + int ret = -1; + + if (virAsprintf(&path, "%s/%s/bridge/%s", SYSFS_NET_DIR, brname, paramname) < 0) { + virReportOOMError(); + return -1; + } + + if (virFileExists(path)) { + char valuestr[INT_BUFSIZE_BOUND(value)]; + snprintf(valuestr, sizeof(valuestr), "%lu", value); + if (virFileWriteStr(path, valuestr, 0) < 0) { + virReportSystemError(errno, + _("Unable to set bridge %s %s"), brname, paramname); + goto cleanup; + } + } else { + unsigned long paramid; + if (STREQ(paramname, "stp_state")) { + paramid = BRCTL_SET_BRIDGE_STP_STATE; + } else if (STREQ(paramname, "forward_delay")) { + paramid = BRCTL_SET_BRIDGE_FORWARD_DELAY; + } else { + virReportSystemError(EINVAL, + _("Unable to set bridge %s %s"), brname, paramname); + goto cleanup; + } + unsigned long args[] = { paramid, value, 0, 0 }; + ifr->ifr_data = (char*)&args; + if (ioctl(fd, SIOCDEVPRIVATE, ifr) < 0) { + virReportSystemError(errno, + _("Unable to set bridge %s %s"), brname, paramname); + goto cleanup; + } + } + + ret = 0; +cleanup: + VIR_FREE(path); + return ret; +} + + +static int virNetDevBridgeGet(const char *brname, + const char *paramname, /* sysfs param name */ + unsigned long *value, /* current value */ + int fd, /* control socket */ + struct ifreq *ifr) /* pre-filled bridge name */ +{ + char *path = NULL; + int ret = -1; + + if (virAsprintf(&path, "%s/%s/bridge/%s", SYSFS_NET_DIR, brname, paramname) < 0) { + virReportOOMError(); + return -1; + } + + if (virFileExists(path)) { + char *valuestr; + if (virFileReadAll(path, INT_BUFSIZE_BOUND(unsigned long), &valuestr) < 0) + goto cleanup; + + if (virStrToLong_ul(valuestr, NULL, 10, value) < 0) { + virReportSystemError(EINVAL, + _("Unable to get bridge %s %s"), brname, paramname); + } + } else { + struct __bridge_info info; + unsigned long args[] = { BRCTL_GET_BRIDGE_INFO, (unsigned long)&info, 0, 0 }; + ifr->ifr_data = (char*)&args; + if (ioctl(fd, SIOCDEVPRIVATE, ifr) < 0) { + virReportSystemError(errno, + _("Unable to get bridge %s %s"), brname, paramname); + goto cleanup; + } + + if (STREQ(paramname, "stp_state")) { + *value = info.stp_enabled; + } else if (STREQ(paramname, "forward_delay")) { + *value = info.forward_delay; + } else { + virReportSystemError(EINVAL, + _("Unable to get bridge %s %s"), brname, paramname); + goto cleanup; + } + } + + ret = 0; +cleanup: + VIR_FREE(path); + return ret; +} +#endif /* __linux__ */ + + +/** + * virNetDevBridgeCreate: + * @brname: the bridge name + * + * This function register a new bridge + * + * Returns 0 in case of success or -1 on failure + */ +#ifdef SIOCBRADDBR +int virNetDevBridgeCreate(const char *brname) +{ + int fd = -1; + int ret = -1; + + if ((fd = virNetDevSetupControl(NULL, NULL)) < 0) + return -1; + + if (ioctl(fd, SIOCBRADDBR, brname) < 0) { + virReportSystemError(errno, + _("Unable to create bridge %s"), brname); + goto cleanup; + } + + ret = 0; + +cleanup: + VIR_FORCE_CLOSE(fd); + return ret; +} +#else +int virNetDevBridgeCreate(const char *brname) +{ + virReportSystemError(ENOSYS, + _("Unable to create bridge %s"), brname); + return -1; +} +#endif + +/** + * virNetDevBridgeDelete: + * @brname: the bridge name + * + * Remove a bridge from the layer. + * + * Returns 0 in case of success or an errno code in case of failure. + */ +#ifdef SIOCBRDELBR +int virNetDevBridgeDelete(const char *brname) +{ + int fd = -1; + int ret = -1; + + if ((fd = virNetDevSetupControl(NULL, NULL)) < 0) + return -1; + + if (ioctl(fd, SIOCBRDELBR, brname) < 0) { + virReportSystemError(errno, + _("Unable to delete bridge %s"), brname); + goto cleanup; + } + + ret = 0; + +cleanup: + VIR_FORCE_CLOSE(fd); + return ret; +} +#else +int virNetDevBridgeDelete(const char *brname ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENOSYS, + _("Unable to delete bridge %s"), brname); + return EINVAL; +} +#endif + +/** + * virNetDevBridgeAddPort: + * @brname: the bridge name + * @ifname: the network interface name + * + * Adds an interface to a bridge + * + * Returns 0 in case of success or an errno code in case of failure. + */ +#ifdef SIOCBRADDIF +int virNetDevBridgeAddPort(const char *brname, + const char *ifname) +{ + int fd = -1; + int ret = -1; + struct ifreq ifr; + + if ((fd = virNetDevSetupControl(brname, &ifr)) < 0) + return -1; + + if (!(ifr.ifr_ifindex = if_nametoindex(ifname))) { + virReportSystemError(ENODEV, + _("Unable to get interface index for %s"), ifname); + goto cleanup; + } + + if (ioctl(fd, SIOCBRADDIF, &ifr) < 0) { + virReportSystemError(errno, + _("Unable to add bridge %s port %s"), brname, ifname); + goto cleanup; + } + + ret = 0; +cleanup: + VIR_FORCE_CLOSE(fd); + return ret; +} +#else +int virNetDevBridgeAddPort(const char *brname, + const char *ifname) +{ + virReportSystemError(ENOSYS, + _("Unable to add bridge %s port %s"), brname, ifname); + return -1; +} +#endif + +/** + * virNetDevBridgeRemovePort: + * @brname: the bridge name + * @ifname: the network interface name + * + * Removes an interface from a bridge + * + * Returns 0 in case of success or an errno code in case of failure. + */ +#ifdef SIOCBRDELIF +int virNetDevBridgeRemovePort(const char *brname, + const char *ifname) +{ + int fd = -1; + int ret = -1; + struct ifreq ifr; + + if ((fd = virNetDevSetupControl(brname, &ifr)) < 0) + return -1; + + if (!(ifr.ifr_ifindex = if_nametoindex(ifname))) { + virReportSystemError(ENODEV, + _("Unable to get interface index for %s"), ifname); + + goto cleanup; + } + + if (ioctl(fd, SIOCBRDELIF, &ifr) < 0) { + virReportSystemError(errno, + _("Unable to remove bridge %s port %s"), brname, ifname); + goto cleanup; + } + + ret = 0; +cleanup: + VIR_FORCE_CLOSE(fd); + return ret; +} +#else +int virNetDevBridgeRemovePort(const char *brname, + const char *ifname) +{ + virReportSystemError(ENOSYS, + _("Unable to remove bridge %s port %s"), brname, ifname); + return -1; +} +#endif + + +#ifdef __linux__ +/** + * virNetDevBridgeSetSTPDelay: + * @brname: the bridge name + * @delay: delay in seconds + * + * Set the bridge forward delay + * + * Returns 0 in case of success or -1 on failure + */ + +int virNetDevBridgeSetSTPDelay(const char *brname, + int delay) +{ + int fd = -1; + int ret = -1; + struct ifreq ifr; + + if ((fd = virNetDevSetupControl(brname, &ifr)) < 0) + goto cleanup; + + ret = virNetDevBridgeSet(brname, "stp_state", MS_TO_JIFFIES(delay), + fd, &ifr); + +cleanup: + VIR_FORCE_CLOSE(fd); + return ret; +} + + +/** + * virNetDevBridgeGetSTPDelay: + * @brname: the bridge device name + * @delayms: the forward delay in milliseconds + * + * Retrives the forward delay for the bridge device @brname + * storing it in @delayms. The forward delay is only meaningful + * if STP is enabled + * + * Returns 0 on success, -1 on error+ + */ +int virNetDevBridgeGetSTPDelay(const char *brname, + int *delayms) +{ + int fd = -1; + int ret = -1; + struct ifreq ifr; + unsigned long i; + + if ((fd = virNetDevSetupControl(brname, &ifr)) < 0) + goto cleanup; + + ret = virNetDevBridgeGet(brname, "stp_state", &i, + fd, &ifr); + *delayms = JIFFIES_TO_MS(i); + +cleanup: + VIR_FORCE_CLOSE(fd); + return ret; +} + + +/** + * virNetDevBridgeSetSTP: + * @brname: the bridge name + * @enable: 1 to enable, 0 to disable + * + * Control whether the bridge participates in the spanning tree protocol, + * in general don't disable it without good reasons. + * + * Returns 0 in case of success or -1 on failure + */ +int virNetDevBridgeSetSTP(const char *brname, + bool enable) +{ + int fd = -1; + int ret = -1; + struct ifreq ifr; + + if ((fd = virNetDevSetupControl(brname, &ifr)) < 0) + goto cleanup; + + ret = virNetDevBridgeSet(brname, "stp_state", enable ? 1 : 0, + fd, &ifr); + +cleanup: + VIR_FORCE_CLOSE(fd); + return ret; +} + + +/** + * virNetDevBridgeGetSTP: + * @brname: the bridge device name + * @enabled: returns the STP state + * + * Determine the state of the spanning tree protocol on + * the device @brname, returning the state in @enabled + * + * Returns 0 on success, -1 on error + */ +int virNetDevBridgeGetSTP(const char *brname, + bool *enabled) +{ + int fd = -1; + int ret = -1; + struct ifreq ifr; + unsigned long i; + + if ((fd = virNetDevSetupControl(brname, &ifr)) < 0) + goto cleanup; + + ret = virNetDevBridgeGet(brname, "stp_state", &i, + fd, &ifr); + *enabled = i ? true : false; + +cleanup: + VIR_FORCE_CLOSE(fd); + return ret; +} +#else /* !__linux__ */ +int virNetDevBridgeSetSTPDelay(const char *brname, + int delay ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENOSYS, + _("Unable to set STP delay on %s on this platform"), + brname); + return -1; +} +int virNetDevBridgeGetSTPDelay(const char *brname, + int *delay ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENOSYS, + _("Unable to get STP delay on %s on this platform"), + brname); + return -1; +} + +int virNetDevBridgeSetSTP(const char *brname, + bool enable ATTRIBUTE_UNUSED) + +{ + virReportSystemError(ENOSYS, + _("Unable to set STP on %s on this platform"), + brname); + return -1; +} +int virNetDevBridgeGetSTP(const char *brname, + bool *enable ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENOSYS, + _("Unable to get STP on %s on this platform"), + brname); + return -1; +} +#endif /* __linux__ */ diff --git a/src/util/virnetdevbridge.h b/src/util/virnetdevbridge.h new file mode 100644 index 0000000000..98fc1d2cf3 --- /dev/null +++ b/src/util/virnetdevbridge.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2007-2011 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 + * Daniel P. Berrange + */ + +#ifndef __VIR_NETDEV_BRIDGE_H__ +# define __VIR_NETDEV_BRIDGE_H__ + +# include "internal.h" + +int virNetDevBridgeCreate(const char *brname) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; +int virNetDevBridgeDelete(const char *brname) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; + +int virNetDevBridgeAddPort(const char *brname, + const char *ifname) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; + +int virNetDevBridgeRemovePort(const char *brname, + const char *ifname) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; + +int virNetDevBridgeSetSTPDelay(const char *brname, + int delay) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; +int virNetDevBridgeGetSTPDelay(const char *brname, + int *delay) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; +int virNetDevBridgeSetSTP(const char *brname, + bool enable) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; +int virNetDevBridgeGetSTP(const char *brname, + bool *enable) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; + +#endif /* __VIR_NETDEV_BRIDGE_H__ */ diff --git a/src/util/virnetdevtap.c b/src/util/virnetdevtap.c new file mode 100644 index 0000000000..5c60925e4b --- /dev/null +++ b/src/util/virnetdevtap.c @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2007-2011 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 + * Daniel P. Berrange + */ + +#include + +#include "virnetdevtap.h" +#include "virnetdev.h" +#include "virnetdevbridge.h" +#include "virterror_internal.h" +#include "virfile.h" +#include "virterror_internal.h" +#include "memory.h" +#include "logging.h" + +#include +#ifdef HAVE_NET_IF_H +# include +#endif +#include +#ifdef __linux__ +# include /* IFF_TUN, IFF_NO_PI */ +#endif + +#define VIR_FROM_THIS VIR_FROM_NONE + +/** + * virNetDevProbeVnetHdr: + * @tapfd: a tun/tap file descriptor + * + * Check whether it is safe to enable the IFF_VNET_HDR flag on the + * tap interface. + * + * Setting IFF_VNET_HDR enables QEMU's virtio_net driver to allow + * guests to pass larger (GSO) packets, with partial checksums, to + * the host. This greatly increases the achievable throughput. + * + * It is only useful to enable this when we're setting up a virtio + * interface. And it is only *safe* to enable it when we know for + * sure that a) qemu has support for IFF_VNET_HDR and b) the running + * kernel implements the TUNGETIFF ioctl(), which qemu needs to query + * the supplied tapfd. + * + * Returns 1 if VnetHdr is supported, 0 if not supported + */ +#ifdef IFF_VNET_HDR +static int +virNetDevProbeVnetHdr(int tapfd) +{ +# if defined(IFF_VNET_HDR) && defined(TUNGETFEATURES) && defined(TUNGETIFF) + unsigned int features; + struct ifreq dummy; + + if (ioctl(tapfd, TUNGETFEATURES, &features) != 0) { + VIR_INFO("Not enabling IFF_VNET_HDR; " + "TUNGETFEATURES ioctl() not implemented"); + return 0; + } + + if (!(features & IFF_VNET_HDR)) { + VIR_INFO("Not enabling IFF_VNET_HDR; " + "TUNGETFEATURES ioctl() reports no IFF_VNET_HDR"); + return 0; + } + + /* The kernel will always return -1 at this point. + * If TUNGETIFF is not implemented then errno == EBADFD. + */ + if (ioctl(tapfd, TUNGETIFF, &dummy) != -1 || errno != EBADFD) { + VIR_INFO("Not enabling IFF_VNET_HDR; " + "TUNGETIFF ioctl() not implemented"); + return 0; + } + + VIR_INFO("Enabling IFF_VNET_HDR"); + + return 1; +# else + (void) tapfd; + VIR_INFO("Not enabling IFF_VNET_HDR; disabled at build time"); + return 0; +# endif +} +#endif + + +#ifdef TUNSETIFF +/** + * brCreateTap: + * @ifname: the interface name + * @vnet_hr: whether to try enabling IFF_VNET_HDR + * @tapfd: file descriptor return value for the new tap device + * + * Creates a tap interface. + * If the @tapfd parameter is supplied, the open tap device file + * descriptor will be returned, otherwise the TAP device will be made + * persistent and closed. The caller must use brDeleteTap to remove + * a persistent TAP devices when it is no longer needed. + * + * Returns 0 in case of success or an errno code in case of failure. + */ +int virNetDevTapCreate(char **ifname, + int vnet_hdr ATTRIBUTE_UNUSED, + int *tapfd) +{ + int fd; + struct ifreq ifr; + int ret = -1; + + if ((fd = open("/dev/net/tun", O_RDWR)) < 0) { + virReportSystemError(errno, "%s", + _("Unable to open /dev/net/tun, is tun module loaded?")); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + + ifr.ifr_flags = IFF_TAP|IFF_NO_PI; + +# ifdef IFF_VNET_HDR + if (vnet_hdr && virNetDevProbeVnetHdr(fd)) + ifr.ifr_flags |= IFF_VNET_HDR; +# endif + + if (virStrcpyStatic(ifr.ifr_name, *ifname) == NULL) { + virReportSystemError(ERANGE, + _("Network interface name '%s' is too long"), + *ifname); + goto cleanup; + + } + + if (ioctl(fd, TUNSETIFF, &ifr) < 0) { + virReportSystemError(errno, + _("Unable to create tap device %s"), + NULLSTR(*ifname)); + goto cleanup; + } + + if (!tapfd && + (errno = ioctl(fd, TUNSETPERSIST, 1))) { + virReportSystemError(errno, + _("Unable to set tap device %s to persistent"), + NULLSTR(*ifname)); + goto cleanup; + } + + VIR_FREE(*ifname); + if (!(*ifname = strdup(ifr.ifr_name))) { + virReportOOMError(); + goto cleanup; + } + if (tapfd) + *tapfd = fd; + else + VIR_FORCE_CLOSE(fd); + + ret = 0; + +cleanup: + if (ret < 0) + VIR_FORCE_CLOSE(fd); + + return ret; +} + + +int virNetDevTapDelete(const char *ifname) +{ + struct ifreq try; + int fd; + int ret = -1; + + if ((fd = open("/dev/net/tun", O_RDWR)) < 0) { + virReportSystemError(errno, "%s", + _("Unable to open /dev/net/tun, is tun module loaded?")); + return -1; + } + + memset(&try, 0, sizeof(struct ifreq)); + try.ifr_flags = IFF_TAP|IFF_NO_PI; + + if (virStrcpyStatic(try.ifr_name, ifname) == NULL) { + virReportSystemError(ERANGE, + _("Network interface name '%s' is too long"), + ifname); + goto cleanup; + } + + if (ioctl(fd, TUNSETIFF, &try) < 0) { + virReportSystemError(errno, "%s", + _("Unable to associate TAP device")); + goto cleanup; + } + + if (ioctl(fd, TUNSETPERSIST, 0) < 0) { + virReportSystemError(errno, "%s", + _("Unable to make TAP device non-persistent")); + goto cleanup; + } + + ret = 0; + +cleanup: + VIR_FORCE_CLOSE(fd); + return ret; +} +#else /* ! TUNSETIFF */ +int virNetDevTapCreate(char **ifname ATTRIBUTE_UNUSED, + int vnet_hdr ATTRIBUTE_UNUSED, + int *tapfd ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENOSYS, "%s", + _("Unable to create TAP devices on this platform")); + return -1; +} +int virNetDevTapDelete(const char *ifname ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENOSYS, "%s", + _("Unable to delete TAP devices on this platform")); + return -1; +} +#endif /* ! TUNSETIFF */ + + +/** + * virNetDevTapCreateInBridgePort: + * @brname: the bridge name + * @ifname: the interface name (or name template) + * @macaddr: desired MAC address (VIR_MAC_BUFLEN long) + * @vnet_hdr: whether to try enabling IFF_VNET_HDR + * @tapfd: file descriptor return value for the new tap device + * + * This function creates a new tap device on a bridge. @ifname can be either + * a fixed name or a name template with '%d' for dynamic name allocation. + * in either case the final name for the bridge will be stored in @ifname. + * If the @tapfd parameter is supplied, the open tap device file + * descriptor will be returned, otherwise the TAP device will be made + * persistent and closed. The caller must use brDeleteTap to remove + * a persistent TAP devices when it is no longer needed. + * + * Returns 0 in case of success or -1 on failure + */ +int virNetDevTapCreateInBridgePort(const char *brname, + char **ifname, + const unsigned char *macaddr, + int vnet_hdr, + bool up, + int *tapfd) +{ + if (virNetDevTapCreate(ifname, vnet_hdr, tapfd) < 0) + return -1; + + /* We need to set the interface MAC before adding it + * to the bridge, because the bridge assumes the lowest + * MAC of all enslaved interfaces & we don't want it + * seeing the kernel allocate random MAC for the TAP + * device before we set our static MAC. + */ + if (virNetDevSetMAC(*ifname, macaddr) < 0) + goto error; + + /* We need to set the interface MTU before adding it + * to the bridge, because the bridge will have its + * MTU adjusted automatically when we add the new interface. + */ + if (virNetDevSetMTUFromDevice(*ifname, brname) < 0) + goto error; + + if (virNetDevBridgeAddPort(brname, *ifname) < 0) + goto error; + + if (virNetDevSetOnline(*ifname, up) < 0) + goto error; + + return 0; + + error: + VIR_FORCE_CLOSE(*tapfd); + + return errno; +} diff --git a/src/util/virnetdevtap.h b/src/util/virnetdevtap.h new file mode 100644 index 0000000000..fb35ac5379 --- /dev/null +++ b/src/util/virnetdevtap.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2007-2011 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 + * Daniel P. Berrange + */ + +#ifndef __VIR_NETDEV_TAP_H__ +# define __VIR_NETDEV_TAP_H__ + +# include "internal.h" + +int virNetDevTapCreate(char **ifname, + int vnet_hdr, + int *tapfd) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; + +int virNetDevTapDelete(const char *ifname) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; + +int virNetDevTapCreateInBridgePort(const char *brname, + char **ifname, + const unsigned char *macaddr, + int vnet_hdr, + bool up, + int *tapfd) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3) + ATTRIBUTE_RETURN_CHECK; + +#endif /* __VIR_NETDEV_TAP_H__ */