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__ */ -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list