<commit_before>/*
 * Copyright (C) 2010-2013 Red Hat, Inc.
 * Copyright IBM Corp. 2008
 *
 * 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, see
 * <http://www.gnu.org/licenses/>.
 *
 * Authors:
 *     <NAME> <dlesko at linux.vnet.ibm.com>
 *     <NAME> <<EMAIL>>
 */

#include <config.h>

#include <sys/wait.h>

#include "virnetdevveth.h"
#include "viralloc.h"
#include "virlog.h"
#include "vircommand.h"
#include "virerror.h"
#include "virfile.h"
#include "virstring.h"
#include "virutil.h"

#define VIR_FROM_THIS VIR_FROM_NONE

/* Functions */
/**
 * virNetDevVethGetFreeName:
 * @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 non-negative device number on success or -1 in case of error
 */
static int virNetDevVethGetFreeName(char **veth, int startDev)
{
    int devNum = startDev-1;
    char *path = NULL;

    VIR_DEBUG("Find free from veth%d", startDev);
    do {
        VIR_FREE(path);
        ++devNum;
        if (virAsprintf(&path, "/sys/class/net/veth%d/", devNum) < 0)
            return -1;
        VIR_DEBUG("Probe %s", path);
    } while (virFileExists(path));
    VIR_FREE(path);

    if (virAsprintf(veth, "veth%d", devNum) < 0)
        return -1;

    return devNum;
}

/**
 * virNetDevVethCreate:
 * @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
 *          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 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 virNetDevVethCreate(char** veth1, char** veth2)
{
    int rc = -1;
    const char *argv[] = {
        "ip", "link", "add", NULL, "type", "veth", "peer", "name", NULL, NULL
    };
    int vethDev = 0;
    bool veth1_alloc = false;
    bool veth2_alloc = false;

    VIR_DEBUG("Host: %s guest: %s", NULLSTR(*veth1), NULLSTR(*veth2));

    if (*veth1 == NULL) {
        if ((vethDev = virNetDevVethGetFreeName(veth1, vethDev)) < 0)
            goto cleanup;
        VIR_DEBUG("Assigned host: %s", *veth1);
        veth1_alloc = true;
        vethDev++;
    }
    argv[3] = *veth1;

    while (*veth2 == NULL) {
        if ((vethDev = virNetDevVethGetFreeName(veth2, vethDev)) < 0) {
            if (veth1_alloc)
                VIR_FREE(*veth1);
            goto cleanup;
        }

        /* Just make sure they didn't accidentally get same name */
        if (STREQ(*veth1, *veth2)) {
            vethDev++;
            VIR_FREE(*veth2);
            continue;
        }

        VIR_DEBUG("Assigned guest: %s", *veth2);
        veth2_alloc = true;
    }
    argv[8] = *veth2;

    VIR_DEBUG("Create Host: %s guest: %s", *veth1, *veth2);
    if (virRun(argv, NULL) < 0) {
        if (veth1_alloc)
            VIR_FREE(*veth1);
        if (veth2_alloc)
            VIR_FREE(*veth2);
        goto cleanup;
    }

    rc = 0;

cleanup:
    return rc;
}

/**
 * virNetDevVethDelete:
 * @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<commit_msg>Retry veth device creation on failure<commit_after>/*
 * Copyright (C) 2010-2013 Red Hat, Inc.
 * Copyright IBM Corp. 2008
 *
 * 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, see
 * <http://www.gnu.org/licenses/>.
 *
 * Authors:
 *     <NAME> <dlesko at linux.vnet.ibm.com>
 *     <NAME> <<EMAIL>>
 */

#include <config.h>

#include <sys/wait.h>

#include "virnetdevveth.h"
#include "viralloc.h"
#include "virlog.h"
#include "vircommand.h"
#include "virerror.h"
#include "virfile.h"
#include "virstring.h"
#include "virutil.h"
#include "virnetdev.h"

#define VIR_FROM_THIS VIR_FROM_NONE

/* Functions */

static int virNetDevVethExists(int devNum)
{
    int ret;
    char *path = NULL;
    if (virAsprintf(&path, "/sys/class/net/veth%d/", devNum) < 0)
        return -1;
    ret = virFileExists(path) ? 1 : 0;
    VIR_DEBUG("Checked dev veth%d usage: %d", devNum, ret);
    VIR_FREE(path);
    return ret;
}

/**
 * virNetDevVethGetFreeNum:
 * @startDev: device number to start at (x in vethx)
 *
 * Looks in /sys/class/net/ to find the first available veth device
 * name.
 *
 * Returns non-negative device number on success or -1 in case of error
 */
static int virNetDevVethGetFreeNum(int startDev)
{
    int devNum;

#define MAX_DEV_NUM 65536

    for (devNum = startDev; devNum < MAX_DEV_NUM; devNum++) {
        int ret = virNetDevVethExists(devNum);
        if (ret < 0)
            return -1;
        if (ret == 0)
            return devNum;
    }

    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                   _("No free veth devices available"));
    return -1;
}

/**
 * virNetDevVethCreate:
 * @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
 *          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 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 virNetDevVethCreate(char** veth1, char** veth2)
{
    int ret = -1;
    char *veth1auto = NULL;
    char *veth2auto = NULL;
    int vethNum = 0;
    size_t i;

    /*
     * We might race with other containers, but this is reasonably
     * unlikely, so don't do too many retries for device creation
     */
#define MAX_VETH_RETRIES 10

    for (i = 0; i < MAX_VETH_RETRIES; i++) {
        int status;
        if (!*veth1) {
            int veth1num;
            if ((veth1num = virNetDevVethGetFreeNum(vethNum)) < 0)
                goto cleanup;

            if (virAsprintf(&veth1auto, "veth%d", veth1num) < 0)
                goto cleanup;
            vethNum = veth1num + 1;
        }
        if (!*veth2) {
            int veth2num;
            if ((veth2num = virNetDevVethGetFreeNum(vethNum)) < 0)
                goto cleanup;

            if (virAsprintf(&veth2auto, "veth%d", veth2num) < 0)
                goto cleanup;
            vethNum = veth2num + 1;
        }

        virCommandPtr cmd = virCommandNew("ip");
        virCommandAddArgList(cmd, "link", "add",
                             *veth1 ? *veth1 : veth1auto,
                             "type", "veth", "peer", "name",
                             *veth2 ? *veth2 : veth2auto,
                             NULL);

        if (virCommandRun(cmd, &status) < 0)
            goto cleanup;

        if (status == 0) {
            if (veth1auto) {
                *veth1 = veth1auto;
                veth1auto = NULL;
            }
            if (veth2auto) {
                *veth2 = veth2auto;
                veth2auto = NULL;
            }
            VIR_DEBUG("Create Host: %s guest: %s", *veth1, *veth2);
            ret = 0;
            goto cleanup;
        }

        VIR_DEBUG("Failed to create veth host: %s guest: %s: %d",
                  *veth1 ? *veth1 : veth1auto,
                  *veth1 ? *veth1 : veth1auto,
                  status);
        VIR_FREE(veth1auto);
        VIR_FREE(veth2auto);
    }

    virReportError(VIR_ERR_INTERNAL_ERROR,
                   _("Failed to allocate free veth pair after %d attempts"),
                   MAX_VETH_RETRIES);

cleanup:
    VIR_FREE(veth1auto);
    VIR_FREE(veth2auto);
    return ret;
}

/**
 * virNetDevVethDelete:
 * @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
{'fraction_non_alphanumeric': 0.09734347132263212, 'fraction_numerical': 0.015300854219468694, 'mean_word_length': 2.3419071518193224, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7626183', 'n_tokens_mistral': 3449, 'n_tokens_neox': 3354, 'n_words': 1238}