2008-06-26 16:07:48 +00:00
|
|
|
/*
|
|
|
|
* veth.c: Tools for managing veth pairs
|
|
|
|
*
|
2011-08-23 20:36:00 +00:00
|
|
|
* Copyright (C) 2010-2011 Red Hat, Inc.
|
2008-06-26 16:07:48 +00:00
|
|
|
* Copyright IBM Corp. 2008
|
|
|
|
*
|
|
|
|
* See COPYING.LIB for the License of this software
|
|
|
|
*
|
|
|
|
* Authors:
|
|
|
|
* David L. Leskovec <dlesko at linux.vnet.ibm.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
2011-08-23 20:36:00 +00:00
|
|
|
#include <linux/sockios.h>
|
|
|
|
#include <net/if.h>
|
2008-06-26 16:07:48 +00:00
|
|
|
#include <string.h>
|
2008-11-04 23:22:06 +00:00
|
|
|
#include <stdio.h>
|
2011-08-23 20:36:00 +00:00
|
|
|
#include <sys/ioctl.h>
|
2010-07-23 17:25:56 +00:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/wait.h>
|
2008-06-26 16:07:48 +00:00
|
|
|
|
|
|
|
#include "veth.h"
|
|
|
|
#include "internal.h"
|
2008-11-06 16:36:07 +00:00
|
|
|
#include "logging.h"
|
2008-06-26 16:07:48 +00:00
|
|
|
#include "memory.h"
|
2011-05-10 18:42:59 +00:00
|
|
|
#include "command.h"
|
2010-07-23 17:25:56 +00:00
|
|
|
#include "virterror_internal.h"
|
2011-08-23 20:36:00 +00:00
|
|
|
#include "virfile.h"
|
2010-07-23 17:25:56 +00:00
|
|
|
|
2011-11-02 15:53:39 +00:00
|
|
|
#define VIR_FROM_THIS VIR_FROM_NONE
|
2010-07-23 17:25:56 +00:00
|
|
|
|
2011-11-02 15:53:39 +00:00
|
|
|
#define virNetDevvError(code, ...) \
|
|
|
|
virReportErrorHelper(VIR_FROM_NONE, code, __FILE__, \
|
2010-07-23 17:25:56 +00:00
|
|
|
__FUNCTION__, __LINE__, __VA_ARGS__)
|
2008-06-26 16:07:48 +00:00
|
|
|
|
|
|
|
/* Functions */
|
|
|
|
/**
|
2011-11-02 15:53:39 +00:00
|
|
|
* virNetDevVethGetFreeName:
|
2010-08-31 22:04:46 +00:00
|
|
|
* @veth: pointer to store returned name for veth device
|
2008-06-26 16:07:48 +00:00
|
|
|
* @startDev: device number to start at (x in vethx)
|
|
|
|
*
|
|
|
|
* Looks in /sys/class/net/ to find the first available veth device
|
|
|
|
* name.
|
|
|
|
*
|
2010-08-31 22:04:46 +00:00
|
|
|
* Returns non-negative device number on success or -1 in case of error
|
2008-06-26 16:07:48 +00:00
|
|
|
*/
|
2011-11-02 15:53:39 +00:00
|
|
|
static int virNetDevVethGetFreeName(char **veth, int startDev)
|
2008-06-26 16:07:48 +00:00
|
|
|
{
|
2009-04-14 17:51:12 +00:00
|
|
|
int devNum = startDev-1;
|
2010-08-31 22:04:46 +00:00
|
|
|
char *path = NULL;
|
2008-06-26 16:07:48 +00:00
|
|
|
|
2011-06-07 13:26:48 +00:00
|
|
|
VIR_DEBUG("Find free from veth%d", startDev);
|
2008-06-26 16:07:48 +00:00
|
|
|
do {
|
2010-08-31 22:04:46 +00:00
|
|
|
VIR_FREE(path);
|
2008-06-26 16:07:48 +00:00
|
|
|
++devNum;
|
2010-08-31 22:04:46 +00:00
|
|
|
if (virAsprintf(&path, "/sys/class/net/veth%d/", devNum) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
return -1;
|
|
|
|
}
|
2011-06-07 13:26:48 +00:00
|
|
|
VIR_DEBUG("Probe %s", path);
|
2008-06-26 16:07:48 +00:00
|
|
|
} while (virFileExists(path));
|
2010-08-31 22:04:46 +00:00
|
|
|
VIR_FREE(path);
|
2008-06-26 16:07:48 +00:00
|
|
|
|
2010-08-31 22:04:46 +00:00
|
|
|
if (virAsprintf(veth, "veth%d", devNum) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
return -1;
|
|
|
|
}
|
2011-06-07 13:26:48 +00:00
|
|
|
|
2010-08-31 22:04:46 +00:00
|
|
|
return devNum;
|
2008-06-26 16:07:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-11-02 15:53:39 +00:00
|
|
|
* virNetDevVethCreate:
|
2010-08-31 22:04:46 +00:00
|
|
|
* @veth1: pointer to name for parent end of veth pair
|
|
|
|
* @veth2: pointer to return name for container end of veth pair
|
2008-06-26 16:07:48 +00:00
|
|
|
*
|
|
|
|
* Creates a veth device pair using the ip command:
|
|
|
|
* ip link add veth1 type veth peer name veth2
|
2010-08-31 22:04:46 +00:00
|
|
|
* If veth1 points to NULL on entry, it will be a valid interface on
|
|
|
|
* return. veth2 should point to NULL on entry.
|
|
|
|
*
|
2008-06-26 16:07:48 +00:00
|
|
|
* 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.
|
2010-08-31 22:04:46 +00:00
|
|
|
* Because of these issues, this function currently allocates names
|
|
|
|
* prior to using the ip command, and returns any allocated names
|
|
|
|
* to the caller.
|
2008-06-26 16:07:48 +00:00
|
|
|
*
|
|
|
|
* Returns 0 on success or -1 in case of error
|
|
|
|
*/
|
2011-11-02 15:53:39 +00:00
|
|
|
int virNetDevVethCreate(char** veth1, char** veth2)
|
2008-06-26 16:07:48 +00:00
|
|
|
{
|
2011-03-17 15:54:24 +00:00
|
|
|
int rc = -1;
|
2008-06-26 16:07:48 +00:00
|
|
|
const char *argv[] = {
|
2010-08-31 22:04:46 +00:00
|
|
|
"ip", "link", "add", NULL, "type", "veth", "peer", "name", NULL, NULL
|
2008-06-26 16:07:48 +00:00
|
|
|
};
|
|
|
|
int vethDev = 0;
|
2010-08-31 22:04:46 +00:00
|
|
|
bool veth1_alloc = false;
|
2011-03-17 15:54:24 +00:00
|
|
|
bool veth2_alloc = false;
|
2008-06-26 16:07:48 +00:00
|
|
|
|
2011-06-07 13:26:48 +00:00
|
|
|
VIR_DEBUG("Host: %s guest: %s", NULLSTR(*veth1), NULLSTR(*veth2));
|
2008-06-26 16:07:48 +00:00
|
|
|
|
2010-08-31 22:04:46 +00:00
|
|
|
if (*veth1 == NULL) {
|
2011-11-02 15:53:39 +00:00
|
|
|
if ((vethDev = virNetDevVethGetFreeName(veth1, vethDev)) < 0)
|
2011-03-17 15:54:24 +00:00
|
|
|
goto cleanup;
|
2011-06-07 13:26:48 +00:00
|
|
|
VIR_DEBUG("Assigned host: %s", *veth1);
|
2010-08-31 22:04:46 +00:00
|
|
|
veth1_alloc = true;
|
2011-06-07 13:26:48 +00:00
|
|
|
vethDev++;
|
2008-06-26 16:07:48 +00:00
|
|
|
}
|
2010-08-31 22:04:46 +00:00
|
|
|
argv[3] = *veth1;
|
|
|
|
|
2011-03-17 15:54:24 +00:00
|
|
|
while (*veth2 == NULL) {
|
2011-11-02 15:53:39 +00:00
|
|
|
if ((vethDev = virNetDevVethGetFreeName(veth2, vethDev)) < 0) {
|
2010-08-31 22:04:46 +00:00
|
|
|
if (veth1_alloc)
|
|
|
|
VIR_FREE(*veth1);
|
2011-03-17 15:54:24 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Just make sure they didn't accidentally get same name */
|
|
|
|
if (STREQ(*veth1, *veth2)) {
|
|
|
|
vethDev++;
|
|
|
|
VIR_FREE(*veth2);
|
|
|
|
continue;
|
2010-08-31 22:04:46 +00:00
|
|
|
}
|
2011-03-17 15:54:24 +00:00
|
|
|
|
2011-06-07 13:26:48 +00:00
|
|
|
VIR_DEBUG("Assigned guest: %s", *veth2);
|
2011-03-17 15:54:24 +00:00
|
|
|
veth2_alloc = true;
|
2008-06-26 16:07:48 +00:00
|
|
|
}
|
2010-08-31 22:04:46 +00:00
|
|
|
argv[8] = *veth2;
|
2008-06-26 16:07:48 +00:00
|
|
|
|
2011-06-07 13:26:48 +00:00
|
|
|
VIR_DEBUG("Create Host: %s guest: %s", *veth1, *veth2);
|
2011-03-08 18:46:29 +00:00
|
|
|
if (virRun(argv, NULL) < 0) {
|
2010-08-31 22:04:46 +00:00
|
|
|
if (veth1_alloc)
|
|
|
|
VIR_FREE(*veth1);
|
2011-03-17 15:54:24 +00:00
|
|
|
if (veth2_alloc)
|
|
|
|
VIR_FREE(*veth2);
|
|
|
|
goto cleanup;
|
2008-06-26 16:07:48 +00:00
|
|
|
}
|
|
|
|
|
2011-03-17 15:54:24 +00:00
|
|
|
rc = 0;
|
|
|
|
|
|
|
|
cleanup:
|
2008-06-26 16:07:48 +00:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-11-02 15:53:39 +00:00
|
|
|
* virNetDevVethDelete:
|
2008-06-26 16:07:48 +00:00
|
|
|
* @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
|
|
|
|
*/
|
2011-11-02 15:53:39 +00:00
|
|
|
int virNetDevVethDelete(const char *veth)
|
2008-06-26 16:07:48 +00:00
|
|
|
{
|
2010-07-23 17:25:56 +00:00
|
|
|
int rc;
|
2008-06-26 16:07:48 +00:00
|
|
|
const char *argv[] = {"ip", "link", "del", veth, NULL};
|
2010-07-23 17:25:56 +00:00
|
|
|
int cmdResult = 0;
|
2008-06-26 16:07:48 +00:00
|
|
|
|
2011-02-16 23:37:57 +00:00
|
|
|
VIR_DEBUG("veth: %s", veth);
|
2008-06-26 16:07:48 +00:00
|
|
|
|
2010-02-04 22:41:52 +00:00
|
|
|
rc = virRun(argv, &cmdResult);
|
2008-06-26 16:07:48 +00:00
|
|
|
|
2010-07-23 17:25:56 +00:00
|
|
|
if (rc != 0 ||
|
|
|
|
(WIFEXITED(cmdResult) && WEXITSTATUS(cmdResult) != 0)) {
|
|
|
|
/*
|
|
|
|
* Prevent overwriting an error log which may be set
|
|
|
|
* where an actual failure occurs.
|
|
|
|
*/
|
2010-07-29 19:27:58 +00:00
|
|
|
VIR_DEBUG("Failed to delete '%s' (%d)",
|
2010-07-23 17:25:56 +00:00
|
|
|
veth, WEXITSTATUS(cmdResult));
|
|
|
|
rc = -1;
|
2008-06-26 16:07:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2011-11-02 15:53:39 +00:00
|
|
|
* virNetDevSetNamespace:
|
|
|
|
* @ifname: name of device
|
2008-06-26 16:07:48 +00:00
|
|
|
* @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:
|
2009-11-05 12:35:13 +00:00
|
|
|
* ip link set @iface netns @pidInNs
|
2008-06-26 16:07:48 +00:00
|
|
|
*
|
|
|
|
* Returns 0 on success or -1 in case of error
|
|
|
|
*/
|
2011-11-02 15:53:39 +00:00
|
|
|
int virNetDevSetNamespace(const char* ifname, int pidInNs)
|
2008-06-26 16:07:48 +00:00
|
|
|
{
|
2010-07-23 17:25:56 +00:00
|
|
|
int rc;
|
2008-06-26 16:07:48 +00:00
|
|
|
char *pid = NULL;
|
|
|
|
const char *argv[] = {
|
2011-11-02 15:53:39 +00:00
|
|
|
"ip", "link", "set", ifname, "netns", NULL, NULL
|
2008-06-26 16:07:48 +00:00
|
|
|
};
|
|
|
|
|
2010-07-23 17:25:56 +00:00
|
|
|
if (virAsprintf(&pid, "%d", pidInNs) == -1) {
|
|
|
|
virReportOOMError();
|
|
|
|
return -1;
|
2008-06-26 16:07:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
argv[5] = pid;
|
2011-03-08 18:46:29 +00:00
|
|
|
rc = virRun(argv, NULL);
|
2008-06-26 16:07:48 +00:00
|
|
|
|
|
|
|
VIR_FREE(pid);
|
|
|
|
return rc;
|
|
|
|
}
|
2009-10-21 10:04:02 +00:00
|
|
|
|
|
|
|
/**
|
2011-11-02 15:53:39 +00:00
|
|
|
* virNetDevSetName:
|
|
|
|
* @ifname: name of device
|
|
|
|
* @new: new name of @ifname
|
2009-11-05 13:11:30 +00:00
|
|
|
*
|
2011-08-23 20:36:00 +00:00
|
|
|
* Changes the name of the given device.
|
2009-11-05 13:11:30 +00:00
|
|
|
*
|
2011-08-23 20:36:00 +00:00
|
|
|
* Returns 0 on success, -1 on failure with errno set.
|
2009-11-05 13:11:30 +00:00
|
|
|
*/
|
2011-11-02 15:53:39 +00:00
|
|
|
int virNetDevSetName(const char* ifname, const char* new)
|
2009-11-05 13:11:30 +00:00
|
|
|
{
|
2011-08-23 20:36:00 +00:00
|
|
|
struct ifreq ifr;
|
|
|
|
int fd = socket(PF_PACKET, SOCK_DGRAM, 0);
|
2009-11-05 13:11:30 +00:00
|
|
|
|
2011-08-23 20:36:00 +00:00
|
|
|
memset(&ifr, 0, sizeof(struct ifreq));
|
|
|
|
|
2011-11-02 15:53:39 +00:00
|
|
|
if (virStrcpyStatic(ifr.ifr_name, ifname) == NULL) {
|
2011-08-23 20:36:00 +00:00
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virStrcpyStatic(ifr.ifr_newname, new) == NULL) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ioctl(fd, SIOCSIFNAME, &ifr))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
2009-11-05 13:11:30 +00:00
|
|
|
}
|