* src/lxc/veth.h (vethCreate): Change prototype. * src/lxc/veth.c (vethCreate): Always malloc veth2, and allocate veth1 if needed. (getFreeVethName): Adjust signature, and use virAsprintf. * src/lxc/lxc_driver.c (lxcSetupInterfaces): Adjust caller. --- This issue crossed file boundaries. It was a bit tricky, since vethCreate was used in two patterns - one where the parent name was already known, and another where the parent name is picked as the first available option. But the end result avoids strdup'ing a fixed-width buffer, and I think I correctly avoided any leaks (in lxcSetupInterfaces, once a string is transferred to *veths, then returning failure will cause that string to be freed in the caller). It also gets rid of a PATH_MAX stack over-allocation. src/lxc/lxc_driver.c | 32 ++++++++----------- src/lxc/veth.c | 84 ++++++++++++++++++++++++++++++------------------- src/lxc/veth.h | 6 ++-- 3 files changed, 67 insertions(+), 55 deletions(-) diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 6fe20b1..326fee6 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -812,14 +812,16 @@ static int lxcSetupInterfaces(virConnectPtr conn, return -1; for (i = 0 ; i < def->nnets ; i++) { - char parentVeth[PATH_MAX] = ""; - char containerVeth[PATH_MAX] = ""; + char *parentVeth; + char *containerVeth = NULL; switch (def->nets[i]->type) { case VIR_DOMAIN_NET_TYPE_NETWORK: { - virNetworkPtr network = virNetworkLookupByName(conn, - def->nets[i]->data.network.name); + virNetworkPtr network; + + network = virNetworkLookupByName(conn, + def->nets[i]->data.network.name); if (!network) { goto error_exit; } @@ -852,31 +854,23 @@ static int lxcSetupInterfaces(virConnectPtr conn, } DEBUG0("calling vethCreate()"); - if (NULL != def->nets[i]->ifname) { - strcpy(parentVeth, def->nets[i]->ifname); - } - DEBUG("parentVeth: %s, containerVeth: %s", parentVeth, containerVeth); - if (vethCreate(parentVeth, PATH_MAX, containerVeth, PATH_MAX) < 0) + parentVeth = def->nets[i]->ifname; + if (vethCreate(&parentVeth, &containerVeth) < 0) goto error_exit; + DEBUG("parentVeth: %s, containerVeth: %s", parentVeth, containerVeth); if (NULL == def->nets[i]->ifname) { - def->nets[i]->ifname = strdup(parentVeth); + def->nets[i]->ifname = parentVeth; } + if (VIR_REALLOC_N(*veths, (*nveths)+1) < 0) { virReportOOMError(); + VIR_FREE(containerVeth); goto error_exit; } - if (((*veths)[(*nveths)] = strdup(containerVeth)) == NULL) { - virReportOOMError(); - goto error_exit; - } + (*veths)[(*nveths)] = containerVeth; (*nveths)++; - if (NULL == def->nets[i]->ifname) { - virReportOOMError(); - goto error_exit; - } - { char macaddr[VIR_MAC_STRING_BUFLEN]; virFormatMacAddr(def->nets[i]->mac, macaddr); diff --git a/src/lxc/veth.c b/src/lxc/veth.c index 5f038d6..14cfaa2 100644 --- a/src/lxc/veth.c +++ b/src/lxc/veth.c @@ -13,6 +13,7 @@ #include <config.h> #include <string.h> +#include <stdbool.h> #include <stdio.h> #include <sys/types.h> #include <sys/wait.h> @@ -33,42 +34,46 @@ /* Functions */ /** * getFreeVethName: - * @veth: name for veth device (NULL to find first open) - * @maxLen: max length of veth name + * @veth: pointer to store returned name for veth device * @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 + * Returns non-negative device number on success or -1 in case of error */ -static int getFreeVethName(char *veth, int maxLen, int startDev) +static int getFreeVethName(char **veth, int startDev) { - int rc = -1; int devNum = startDev-1; - char path[PATH_MAX]; + char *path = NULL; do { + VIR_FREE(path); ++devNum; - snprintf(path, PATH_MAX, "/sys/class/net/veth%d/", devNum); + if (virAsprintf(&path, "/sys/class/net/veth%d/", devNum) < 0) { + virReportOOMError(); + return -1; + } } while (virFileExists(path)); + VIR_FREE(path); - snprintf(veth, maxLen, "veth%d", devNum); - - rc = devNum; - - return rc; + if (virAsprintf(veth, "veth%d", devNum) < 0) { + virReportOOMError(); + return -1; + } + return devNum; } /** * 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 + * @veth1: pointer to name for parent end of veth pair + * @veth2: pointer to return name for container end of veth pair * * Creates a veth device pair using the ip command: * ip link add veth1 type veth peer name veth2 + * If veth1 points to NULL on entry, it will be a valid interface on + * return. veth2 should point to NULL on entry. + * * 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 @@ -78,43 +83,56 @@ static int getFreeVethName(char *veth, int maxLen, int startDev) * 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. + * Because of these issues, this function currently allocates names + * prior to using the ip command, and returns any allocated names + * to the caller. * * Returns 0 on success or -1 in case of error */ -int vethCreate(char* veth1, int veth1MaxLen, - char* veth2, int veth2MaxLen) +int vethCreate(char** veth1, char** veth2) { int rc; const char *argv[] = { - "ip", "link", "add", veth1, "type", "veth", "peer", "name", veth2, NULL + "ip", "link", "add", NULL, "type", "veth", "peer", "name", NULL, NULL }; int cmdResult = 0; int vethDev = 0; + bool veth1_alloc = false; - DEBUG("veth1: %s veth2: %s", veth1, veth2); + DEBUG("veth1: %s veth2: %s", NULLSTR(*veth1), NULLSTR(*veth2)); - while ((1 > strlen(veth1)) || STREQ(veth1, veth2)) { - vethDev = getFreeVethName(veth1, veth1MaxLen, 0); - ++vethDev; - DEBUG("Assigned veth1: %s", veth1); + if (*veth1 == NULL) { + vethDev = getFreeVethName(veth1, vethDev); + if (vethDev < 0) + return vethDev; + DEBUG("Assigned veth1: %s", *veth1); + veth1_alloc = true; } - - while ((1 > strlen(veth2)) || STREQ(veth1, veth2)) { - vethDev = getFreeVethName(veth2, veth2MaxLen, vethDev); - ++vethDev; - DEBUG("Assigned veth2: %s", veth2); + argv[3] = *veth1; + + while (*veth2 == NULL || STREQ(*veth1, *veth2)) { + VIR_FREE(*veth2); + vethDev = getFreeVethName(veth2, vethDev + 1); + if (vethDev < 0) { + if (veth1_alloc) + VIR_FREE(*veth1); + return vethDev; + } + DEBUG("Assigned veth2: %s", *veth2); } + argv[8] = *veth2; - DEBUG("veth1: %s veth2: %s", veth1, veth2); + DEBUG("veth1: %s veth2: %s", *veth1, *veth2); rc = virRun(argv, &cmdResult); if (rc != 0 || (WIFEXITED(cmdResult) && WEXITSTATUS(cmdResult) != 0)) { vethError(VIR_ERR_INTERNAL_ERROR, _("Failed to create veth device pair '%s', '%s': %d"), - veth1, veth2, WEXITSTATUS(cmdResult)); + *veth1, *veth2, WEXITSTATUS(cmdResult)); + if (veth1_alloc) + VIR_FREE(*veth1); + VIR_FREE(*veth2); rc = -1; } diff --git a/src/lxc/veth.h b/src/lxc/veth.h index 1ec1ec8..f50a939 100644 --- a/src/lxc/veth.h +++ b/src/lxc/veth.h @@ -1,6 +1,7 @@ /* * veth.h: Interface to tools for managing veth pairs * + * Copyright (C) 2010 Red Hat, Inc. * Copyright IBM Corp. 2008 * * See COPYING.LIB for the License of this software @@ -16,9 +17,8 @@ # include "internal.h" /* Function declarations */ -int vethCreate(char* veth1, int veth1MaxLen, char* veth2, - int veth2MaxLen) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3); +int vethCreate(char** veth1, char** veth2) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); int vethDelete(const char* veth) ATTRIBUTE_NONNULL(1); int vethInterfaceUpOrDown(const char* veth, int upOrDown) -- 1.7.2.2 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list