[libvirt] [PATCH 1 of 3] [LXC] Add functions to manage veth device pairs

Daniel Lezcano dlezcano at fr.ibm.com
Fri Jun 20 15:37:50 UTC 2008


Dan Smith wrote:
> # HG changeset patch
> # User Dave Leskovec <dlesko at linux.vnet.ibm.com>
> # Date 1213891164 25200
> # Node ID 386c067de8995028dd11f70602081c31682dd293
> # Parent  8d2afc533c91c4796512e1e71c8283e86eafd18a
> [LXC] Add functions to manage veth device pairs
> 
> This gives us the ability to create a veth pair so that we can move one
> into the network namespace of an LXC container.
> 
> diff -r 8d2afc533c91 -r 386c067de899 configure.in
> --- a/configure.in	Tue Jun 17 15:55:03 2008 +0000
> +++ b/configure.in	Thu Jun 19 08:59:24 2008 -0700
> @@ -301,6 +301,20 @@
>  if test "$with_qemu" = "yes" ; then
>    AC_CHECK_HEADERS([linux/param.h linux/sockios.h linux/if_bridge.h linux/if_tun.h],,
>                     AC_MSG_ERROR([You must install kernel-headers in order to compile libvirt]))
> +fi
> +
> +dnl
> +dnl check for patched iproute2 for lxc network support
> +dnl
> +if test "$with_lxc" = "yes" ; then
> +   AC_MSG_CHECKING([for NETNS support])
> +   if ip link help 2>&1 | grep -q netns; then
> +     with_lxc_netns="yes"
> +     AC_DEFINE([HAVE_NETNS], [], [Kernel has NETNS support])
> +   else
> +     with_lxc_netns="no"
> +   fi
> +   AC_MSG_RESULT($with_lxc_netns)
>  fi
> 
>  dnl Need to test if pkg-config exists
> diff -r 8d2afc533c91 -r 386c067de899 src/Makefile.am
> --- a/src/Makefile.am	Tue Jun 17 15:55:03 2008 +0000
> +++ b/src/Makefile.am	Thu Jun 19 08:59:24 2008 -0700
> @@ -64,6 +64,7 @@
>  		lxc_driver.c lxc_driver.h			\
>  		lxc_conf.c lxc_conf.h				\
>  		lxc_container.c lxc_container.h			\
> +		veth.c veth.h			\
>                  nodeinfo.h nodeinfo.c                           \
>  		util.c util.h
> 
> diff -r 8d2afc533c91 -r 386c067de899 src/veth.c
> --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
> +++ b/src/veth.c	Thu Jun 19 08:59:24 2008 -0700
> @@ -0,0 +1,247 @@
> +/*
> + * Copyright IBM Corp. 2008
> + *
> + * veth.c: file description
> + *
> + * Authors:
> + *  David L. Leskovec <dlesko at linux.vnet.ibm.com>
> + *
> + * 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
> + */
> +
> +#include <config.h>
> +
> +#ifdef HAVE_NETNS
> +
> +#include <string.h>
> +
> +#include "veth.h"
> +#include "internal.h"
> +#include "memory.h"
> +#include "util.h"
> +
> +#define DEBUG(fmt,...) VIR_DEBUG(__FILE__, fmt, __VA_ARGS__)
> +#define DEBUG0(msg) VIR_DEBUG(__FILE__, "%s", msg)

Do you know ##__VA_ARGS ?

> +/* Functions */
> +/**
> + * getFreeVethName:
> + * @veth: name for veth device (NULL to find first open)
> + * @maxLen: max length of veth name
> + * @startDev: device number to start at (x in vethx)
> + *
> + * Looks in /sys/class/net/ to find the first available veth device
> + * name.
> + *
> + * Returns 0 on success or -1 in case of error
> + */
> +static int getFreeVethName(char *veth, int maxLen, int startDev)
> +{
> +    int rc = -1;
> +    int devNum = startDev;
> +    char path[PATH_MAX];
> +
> +    snprintf(path, PATH_MAX, "/sys/class/net/veth%d/", devNum);

You can perhaps, use do { ... } while () here.

> +    while (virFileExists(path)) {
> +        ++devNum;
> +        sprintf(path, "/sys/class/net/veth%d/", devNum);
> +    }

Is this function safe for concurrent access ? eg. getFreeVethName called 
in parallel by two processes or another process creates a pair device 
just after you exit the loop ?

> +    snprintf(veth, maxLen, "veth%d", devNum);
> +
> +    rc = devNum;
> +
> +    return rc;
> +}
> +
> +/**
> + * vethCreate:
> + * @veth1: name for one end of veth pair
> + * @veth1MaxLen: max length of veth1 name
> + * @veth2: name for one end of veth pair
> + * @veth2MaxLen: max length of veth1 name
> + *
> + * Creates a veth device pair using the ip command:
> + * ip link add veth1 type veth peer name veth2
> + * NOTE: If veth1 and veth2 names are not specified, ip will auto assign
> + *       names.  There seems to be two problems here -
> + *       1) There doesn't seem to be a way to determine the names of the
> + *          devices that it creates.  They show up in ip link show and
> + *          under /sys/class/net/ however there is no guarantee that they
> + *          are the devices that this process just created.
> + *       2) Once one of the veth devices is moved to another namespace, it
> + *          is no longer visible in the parent namespace.  This seems to
> + *          confuse the name assignment causing it to fail with File exists.
> + *       Because of these issues, this function currently forces the caller
> + *       to fully specify the veth device names.
> + *
> + * Returns 0 on success or -1 in case of error
> + */
> +int vethCreate(char* veth1, int veth1MaxLen,
> +               char* veth2, int veth2MaxLen)

No need of the veth1MaxLen parameter, you already have it, it is 
IF_NAMESIZE.

> +{
> +    int rc = -1;
> +    const char *argv[] = {
> +        "ip", "link", "add", veth1, "type", "veth", "peer", "name", veth2, NULL
> +    };
> +    int cmdResult;
> +    int vethDev = 0;
> +
> +    if ((NULL == veth1) || (NULL == veth2)) {
> +        goto error_out;
> +    }
> +
> +    DEBUG("veth1: %s veth2: %s", veth1, veth2);
> +
> +    if (1 > strlen(veth1)) {

Why do you check with strlen > 1 ?

> +        vethDev = getFreeVethName(veth1, veth1MaxLen, 0);
> +        ++vethDev;
> +        DEBUG("assigned veth1: %s", veth1);
> +    }
> +
> +    if (1 > strlen(veth2)) {
> +        vethDev = getFreeVethName(veth2, veth2MaxLen, vethDev);
> +        DEBUG("assigned veth2: %s", veth2);
> +    }
> +
> +    rc = virRun(NULL, (char**)argv, &cmdResult);
> +
> +    if (0 == rc) {
> +       rc = cmdResult;
> +    }
> +
> +error_out:
> +    return rc;
> +}
> +
> +/**
> + * vethDelete:
> + * @veth: name for one end of veth pair
> + *
> + * This will delete both veth devices in a pair.  Only one end needs to
> + * be specified.  The ip command will identify and delete the other veth
> + * device as well.
> + * ip link del veth
> + *
> + * Returns 0 on success or -1 in case of error
> + */
> +int vethDelete(const char *veth)
> +{
> +    int rc = -1;
> +    const char *argv[] = {"ip", "link", "del", veth, NULL};
> +    int cmdResult;
> +
> +    if (NULL == veth) {
> +        goto error_out;
> +    }
> +
> +    DEBUG("veth: %s", veth);
> +
> +    rc = virRun(NULL, (char**)argv, &cmdResult);
> +
> +    if (0 == rc) {
> +       rc = cmdResult;
> +    }
> +
> +error_out:
> +    return rc;
> +}
> +
> +/**
> + * vethInterfaceUpOrDown:
> + * @veth: name of veth device
> + * @upOrDown: 0 => down, 1 => up
> + *
> + * Enables a veth device using the ifconfig command.  A NULL inetAddress
> + * will cause it to be left off the command line.
> + *
> + * Returns 0 on success or -1 in case of error
> + */
> +int vethInterfaceUpOrDown(const char* veth, int upOrDown)
> +{
> +    int rc = -1;
> +    char upOrDownString[8];
> +    const char *argv[] = {"ifconfig", veth, upOrDownString, NULL};
> +    int cmdResult;
> +
> +    if (NULL == veth) {
> +        goto error_out;
> +    }
> +
> +    if (0 == upOrDown) {
> +        strcpy(upOrDownString, "down");
> +    } else {
> +        strcpy(upOrDownString, "up");
> +    }

You don't need to copy the string, a const char *upOrDownString and 
upOrDownString = "down" will work.

> +
> +    rc = virRun(NULL, (char**)argv, &cmdResult);
> +
> +    if (0 == rc) {
> +       rc = cmdResult;
> +    }
> +
> +error_out:
> +    return rc;
> +}
> +
> +/**
> + * moveInterfaceToNetNs:
> + * @interface: name of device
> + * @pidInNs: PID of process in target net namespace
> + *
> + * Moves the given device into the target net namespace specified by the given
> + * pid using this command:
> + *     ip link set interface netns pidInNs
> + *
> + * Returns 0 on success or -1 in case of error
> + */
> +int moveInterfaceToNetNs(const char* interface, int pidInNs)
> +{
> +    int rc;
> +    /* offset of the pid field in the following args */
> +    const int pidArgvOffset = 5;
> +    const char *argv[] = {
> +        "ip", "link", "set", interface, "netns", NULL, NULL
> +    };
> +    int cmdResult;
> +    int len;
> +
> +    if (NULL == interface) {
> +        goto error_out;
> +    }
> +
> +    if (0 != VIR_ALLOC_N(argv[pidArgvOffset], (sizeof(int) * 3) + 1)) {
> +        goto error_out;
> +    }
> +    len = snprintf(argv[pidArgvOffset], (sizeof(int) * 3) + 1, "%d", pidInNs);
> +    if (len >= (sizeof(int) * 3) + 1) {
> +        goto cleanup;
> +    }

Why don't you just do:

char pidstr[PIDSTRLEN];
const char *argv[] = { "ip", "link", "set", interface, "netns", pidstr, 
NULL };
snprintf(pidstr, PIDSTRLEN, "%d", pidInNs);

That should work, no ?

> +
> +    rc = virRun(NULL, (char**)argv, &cmdResult);
> +
> +    if (0 == rc) {
> +       rc = cmdResult;
> +    }
> +
> +cleanup:
> +    VIR_FREE(argv[pidArgvOffset]);
> +
> +error_out:
> +    return rc;
> +}
> +
> +#endif /* HAVE_NETNS */
> +
> diff -r 8d2afc533c91 -r 386c067de899 src/veth.h
> --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
> +++ b/src/veth.h	Thu Jun 19 08:59:24 2008 -0700
> @@ -0,0 +1,39 @@
> +/*
> + * Copyright IBM Corp. 2008
> + *
> + * veth.h: file description
> + *
> + * Authors:
> + *  David L. Leskovec <dlesko at linux.vnet.ibm.com>
> + *
> + * 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
> + */
> +
> +#ifndef VETH_H
> +#define VETH_H
> +
> +#include <config.h>
> +
> +#ifdef HAVE_NETNS
> +
> +/* Function declarations */
> +int vethCreate(char* veth1, int veth1MaxLen, char* veth2,
> +               int veth2MaxLen);
> +int vethDelete(const char* veth);
> +int vethInterfaceUpOrDown(const char* veth, int upOrDown);
> +int moveInterfaceToNetNs(const char *interface, int pidInNs);
> +
> +#endif /* HAVE_NETNS */
> +#endif /* VETH_H */




More information about the libvir-list mailing list