[libvirt] [PATCH 1/2] Modify generic ethernet interface so it will work when sVirt is enabled with qemu

Tyler Coumbes coumbes at gmail.com
Tue Sep 20 23:17:46 UTC 2011


Add a generic utility library for working with the TUN driver (/dev/net/tun).

---

diff --git a/src/Makefile.am b/src/Makefile.am
index 738ee91..ddd1b77 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -88,7 +88,8 @@ 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/tunctl.c util/tunctl.h

 EXTRA_DIST += $(srcdir)/util/virkeymaps.h $(srcdir)/util/keymaps.csv \
 		$(srcdir)/util/virkeycode-mapgen.py
@@ -1182,6 +1183,8 @@ if WITH_NETWORK
 USED_SYM_FILES += libvirt_network.syms
 endif

+USED_SYM_FILES += libvirt_tunctl.syms
+
 EXTRA_DIST += \
   libvirt_public.syms		\
   libvirt_private.syms		\
diff --git a/src/libvirt_tunctl.syms b/src/libvirt_tunctl.syms
new file mode 100644
index 0000000..d1e00bb
--- /dev/null
+++ b/src/libvirt_tunctl.syms
@@ -0,0 +1,5 @@
+#tunctl.h
+createTap;
+delTap;
+tapSetInterfaceUp;
+tapSetInterfaceMac;
diff --git a/src/util/tunctl.c b/src/util/tunctl.c
new file mode 100644
index 0000000..e758e6d
--- /dev/null
+++ b/src/util/tunctl.c
@@ -0,0 +1,295 @@
+/*
+ * 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:
+ */
+
+# include <config.h>
+# include "tunctl.h"
+# include "virfile.h"
+
+# include <stdlib.h>
+# include <stdio.h>
+# include <string.h>
+# include <unistd.h>
+# include <fcntl.h>
+# include <errno.h>
+# include <arpa/inet.h>
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <sys/ioctl.h>
+# include <paths.h>
+# include <sys/wait.h>
+
+# include <linux/param.h>     /* HZ                 */
+# include <linux/sockios.h>   /* SIOCBRADDBR etc.   */
+# include <linux/if_bridge.h> /* SYSFS_BRIDGE_ATTR  */
+# include <linux/if_tun.h>    /* IFF_TUN, IFF_NO_PI */
+# include <net/if_arp.h>    /* ARPHRD_ETHER */
+
+# include "internal.h"
+# include "command.h"
+# include "memory.h"
+# include "util.h"
+# include "logging.h"
+# include "network.h"
+
+/**
+ * tapProbeVnetHdr:
+ * @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 0 in case of success or an errno code in case of failure.
+ */
+# ifdef IFF_VNET_HDR
+static int tapProbeVnetHdr(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
+
+/**
+ * createTap:
+ * @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 delTap 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 createTap(char **ifname,
+              int vnet_hdr,
+              int *tapfd)
+{
+
+    int fd;
+    struct ifreq ifr;
+
+    if (!ifname)
+        return EINVAL;
+
+    if ((fd = open("/dev/net/tun", O_RDWR)) < 0)
+      return errno;
+
+    memset(&ifr, 0, sizeof(ifr));
+
+    ifr.ifr_flags = IFF_TAP|IFF_NO_PI;
+
+# ifdef IFF_VNET_HDR
+    if (vnet_hdr && tapProbeVnetHdr(fd))
+        ifr.ifr_flags |= IFF_VNET_HDR;
+# else
+    (void) vnet_hdr;
+# endif
+
+    if (virStrcpyStatic(ifr.ifr_name, *ifname) == NULL) {
+        errno = EINVAL;
+        goto error;
+    }
+
+    if (ioctl(fd, TUNSETIFF, &ifr) < 0)
+        goto error;
+
+    if (!tapfd &&
+        (errno = ioctl(fd, TUNSETPERSIST, 1)))
+        goto error;
+    VIR_FREE(*ifname);
+    if (!(*ifname = strdup(ifr.ifr_name)))
+        goto error;
+    if(tapfd)
+        *tapfd = fd;
+    else
+        VIR_FORCE_CLOSE(fd);
+    return 0;
+
+ error:
+    VIR_FORCE_CLOSE(fd);
+
+    return errno;
+}
+
+/**
+ * delTap:
+ * @ifname the interface name
+ *
+ * Deletes the tap interface.
+ *
+ * Returns 0 in case of success or an errno code in case of failure.
+ */
+
+int delTap(const char *ifname) {
+    struct ifreq try;
+    int fd;
+
+    if (!ifname)
+        return EINVAL;
+
+    if ((fd = open("/dev/net/tun", O_RDWR)) < 0)
+        return errno;
+
+    memset(&try, 0, sizeof(struct ifreq));
+    try.ifr_flags = IFF_TAP|IFF_NO_PI;
+
+    if (virStrcpyStatic(try.ifr_name, ifname) == NULL) {
+        errno = EINVAL;
+        goto error;
+    }
+
+    if (ioctl(fd, TUNSETIFF, &try) == 0) {
+        if ((errno = ioctl(fd, TUNSETPERSIST, 0)))
+            goto error;
+    }
+
+ error:
+    VIR_FORCE_CLOSE(fd);
+
+    return errno;
+}
+
+/**
+ * tapSetInterfaceUp:
+ * @ifname: the interface name
+ * @up: 1 for up, 0 for down
+ *
+ * Function to control if an interface is activated (up, 1) or not (down, 0)
+ *
+ * Returns 0 in case of success or an errno code in case of failure.
+ */
+
+int tapSetInterfaceUp(const char *ifname,
+                      int up)
+{
+    struct ifreq ifr;
+    int ifflags;
+    int ctrl_fd;
+
+    if (!ifname)
+        return EINVAL;
+
+    ctrl_fd = socket(AF_INET, SOCK_STREAM, 0);
+    if (ctrl_fd < 0)
+        return errno;
+
+    memset(&ifr, 0, sizeof(struct ifreq));
+
+    if (virStrcpyStatic(ifr.ifr_name, ifname) == NULL)
+        return EINVAL;
+
+    if (ioctl(ctrl_fd, SIOCGIFFLAGS, &ifr) < 0)
+        return errno;
+
+    ifflags = up ? (ifr.ifr_flags | IFF_UP) : (ifr.ifr_flags & ~IFF_UP);
+
+    if (ifr.ifr_flags != ifflags) {
+        ifr.ifr_flags = ifflags;
+
+        if (ioctl(ctrl_fd, SIOCSIFFLAGS, &ifr) < 0)
+            return errno;
+    }
+    VIR_FORCE_CLOSE(ctrl_fd);
+    return 0;
+}
+
+/**
+ * tapSetInterfaceMac:
+ * @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 an errno code in case of failure.
+ */
+int tapSetInterfaceMac(const char *ifname,
+                       const unsigned char *macaddr)
+{
+    struct ifreq ifr;
+    int ctrl_fd;
+    int err;
+
+    if (!ifname)
+        return EINVAL;
+
+    ctrl_fd = socket(AF_INET, SOCK_STREAM, 0);
+    if (ctrl_fd < 0)
+        return errno;
+
+    memset(&ifr, 0, sizeof(struct ifreq));
+    if (virStrcpyStatic(ifr.ifr_name, ifname) == NULL)
+        return EINVAL;
+    /* To fill ifr.ifr_hdaddr.sa_family field */
+    if (ioctl(ctrl_fd, SIOCGIFHWADDR, &ifr) != 0)
+        return errno;
+
+    memcpy(ifr.ifr_hwaddr.sa_data, macaddr, VIR_MAC_BUFLEN);
+
+    err =  ioctl(ctrl_fd, SIOCSIFHWADDR, &ifr) == 0 ? 0 : errno;
+
+    VIR_FORCE_CLOSE(ctrl_fd);
+
+    return err;
+}
diff --git a/src/util/tunctl.h b/src/util/tunctl.h
new file mode 100644
index 0000000..73dd4aa
--- /dev/null
+++ b/src/util/tunctl.h
@@ -0,0 +1,38 @@
+/*
+ * 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:
+ */
+
+#ifndef __QEMUD_TAP_H__
+# define __QEMUD_TAP_H__
+
+# include <config.h>
+# include <net/if.h>
+# include <netinet/in.h>
+# include "network.h"
+
+int createTap               (char **ifname,
+                             int vnet_hdr,
+                             int *tapfd);
+int delTap                  (const char *ifname);
+int tapSetInterfaceUp       (const char *ifname,
+                             int up);
+int tapSetInterfaceMac      (const char *ifname,
+                             const unsigned char *macaddr);
+
+#endif /* __QEMUD_TAP_H__ */




More information about the libvir-list mailing list