2010-02-15 15:58:13 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2010 IBM Corporation
|
|
|
|
*
|
|
|
|
* 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:
|
|
|
|
* Stefan Berger <stefanb@us.ibm.com>
|
|
|
|
*
|
|
|
|
* Notes:
|
|
|
|
* netlink: http://lovezutto.googlepages.com/netlink.pdf
|
|
|
|
* iproute2 package
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#if WITH_MACVTAP
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
|
|
|
|
#include <linux/if.h>
|
|
|
|
#include <linux/netlink.h>
|
|
|
|
#include <linux/rtnetlink.h>
|
|
|
|
#include <linux/if_tun.h>
|
|
|
|
|
|
|
|
#include "c-ctype.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "memory.h"
|
|
|
|
#include "macvtap.h"
|
|
|
|
#include "conf/domain_conf.h"
|
|
|
|
#include "virterror_internal.h"
|
|
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_NET
|
|
|
|
|
|
|
|
#define ReportError(conn, code, fmt...) \
|
|
|
|
virReportErrorHelper(conn, VIR_FROM_NET, code, __FILE__, \
|
|
|
|
__FUNCTION__, __LINE__, fmt)
|
|
|
|
|
|
|
|
#define MACVTAP_NAME_PREFIX "macvtap"
|
|
|
|
#define MACVTAP_NAME_PATTERN "macvtap%d"
|
|
|
|
|
|
|
|
static int nlOpen(void)
|
|
|
|
{
|
|
|
|
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
|
|
|
if (fd < 0)
|
|
|
|
virReportSystemError(errno,
|
|
|
|
"%s",_("cannot open netlink socket"));
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void nlClose(int fd)
|
|
|
|
{
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nlComm:
|
|
|
|
* @nlmsg: pointer to netlink message
|
|
|
|
* @respbuf: pointer to pointer where response buffer will be allocated
|
|
|
|
* @respbuflen: pointer to integer holding the size of the response buffer
|
|
|
|
* on return of the function.
|
|
|
|
*
|
|
|
|
* Send the given message to the netlink layer and receive response.
|
|
|
|
* Returns 0 on success, -1 on error. In case of error, no response
|
|
|
|
* buffer will be returned.
|
|
|
|
*/
|
|
|
|
static
|
|
|
|
int nlComm(struct nlmsghdr *nlmsg,
|
|
|
|
char **respbuf, int *respbuflen)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
struct sockaddr_nl nladdr = {
|
|
|
|
.nl_family = AF_NETLINK,
|
|
|
|
.nl_pid = 0,
|
|
|
|
.nl_groups = 0,
|
|
|
|
};
|
|
|
|
int rcvChunkSize = 1024; // expecting less than that
|
|
|
|
int rcvoffset = 0;
|
|
|
|
ssize_t nbytes;
|
|
|
|
int fd = nlOpen();
|
|
|
|
|
|
|
|
if (fd < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
nlmsg->nlmsg_flags |= NLM_F_ACK;
|
|
|
|
|
|
|
|
nbytes = sendto(fd, (void *)nlmsg, nlmsg->nlmsg_len, 0,
|
|
|
|
(struct sockaddr *)&nladdr, sizeof(nladdr));
|
|
|
|
if (nbytes < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
"%s", _("cannot send to netlink socket"));
|
|
|
|
rc = -1;
|
|
|
|
goto err_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
if (VIR_REALLOC_N(*respbuf, rcvoffset+rcvChunkSize) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
rc = -1;
|
|
|
|
goto err_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
socklen_t addrlen = sizeof(nladdr);
|
|
|
|
nbytes = recvfrom(fd, &((*respbuf)[rcvoffset]), rcvChunkSize, 0,
|
|
|
|
(struct sockaddr *)&nladdr, &addrlen);
|
|
|
|
if (nbytes < 0) {
|
|
|
|
if (errno == EAGAIN || errno == EINTR)
|
|
|
|
continue;
|
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
_("error receiving from netlink socket"));
|
|
|
|
rc = -1;
|
|
|
|
goto err_exit;
|
|
|
|
}
|
|
|
|
rcvoffset += nbytes;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
*respbuflen = rcvoffset;
|
|
|
|
|
|
|
|
err_exit:
|
|
|
|
if (rc == -1) {
|
|
|
|
VIR_FREE(*respbuf);
|
|
|
|
*respbuf = NULL;
|
|
|
|
*respbuflen = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
nlClose(fd);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static struct rtattr *
|
|
|
|
rtattrCreate(char *buffer, int bufsize, int type,
|
|
|
|
const void *data, int datalen)
|
|
|
|
{
|
|
|
|
struct rtattr *r = (struct rtattr *)buffer;
|
|
|
|
r->rta_type = type;
|
|
|
|
r->rta_len = RTA_LENGTH(datalen);
|
|
|
|
if (r->rta_len > bufsize)
|
|
|
|
return NULL;
|
|
|
|
memcpy(RTA_DATA(r), data, datalen);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
nlInit(struct nlmsghdr *nlm, int flags, int type)
|
|
|
|
{
|
|
|
|
nlm->nlmsg_len = NLMSG_LENGTH(0);
|
|
|
|
nlm->nlmsg_flags = flags;
|
|
|
|
nlm->nlmsg_type = type;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
nlAlign(struct nlmsghdr *nlm)
|
|
|
|
{
|
|
|
|
nlm->nlmsg_len = NLMSG_ALIGN(nlm->nlmsg_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void *
|
|
|
|
nlAppend(struct nlmsghdr *nlm, int totlen, const void *data, int datalen)
|
|
|
|
{
|
|
|
|
char *pos;
|
|
|
|
nlAlign(nlm);
|
|
|
|
if (nlm->nlmsg_len + NLMSG_ALIGN(datalen) > totlen)
|
|
|
|
return NULL;
|
|
|
|
pos = (char *)nlm + nlm->nlmsg_len;
|
|
|
|
memcpy(pos, data, datalen);
|
|
|
|
nlm->nlmsg_len += datalen;
|
|
|
|
nlAlign(nlm);
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
getIfIndex(virConnectPtr conn,
|
|
|
|
const char *ifname,
|
|
|
|
int *idx)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
struct ifreq ifreq;
|
|
|
|
int fd = socket(PF_PACKET, SOCK_DGRAM, 0);
|
|
|
|
|
|
|
|
if (fd < 0)
|
|
|
|
return errno;
|
|
|
|
|
|
|
|
if (virStrncpy(ifreq.ifr_name, ifname, strlen(ifname),
|
|
|
|
sizeof(ifreq.ifr_name)) == NULL) {
|
|
|
|
if (conn)
|
|
|
|
ReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("invalid interface name %s"),
|
|
|
|
ifname);
|
|
|
|
rc = EINVAL;
|
|
|
|
goto err_exit;
|
|
|
|
}
|
|
|
|
if (ioctl(fd, SIOCGIFINDEX, &ifreq) >= 0)
|
|
|
|
*idx = ifreq.ifr_ifindex;
|
|
|
|
else {
|
|
|
|
if (conn)
|
|
|
|
ReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("interface %s does not exist"),
|
|
|
|
ifname);
|
|
|
|
rc = ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
err_exit:
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* chgIfFlags: Change flags on an interface
|
|
|
|
* @ifname : name of the interface
|
|
|
|
* @flagclear : the flags to clear
|
|
|
|
* @flagset : the flags to set
|
|
|
|
*
|
|
|
|
* The new flags of the interface will be calculated as
|
|
|
|
* flagmask = (~0 ^ flagclear)
|
|
|
|
* newflags = (curflags & flagmask) | flagset;
|
|
|
|
*
|
|
|
|
* Returns 0 on success, errno on failure.
|
|
|
|
*/
|
|
|
|
static int chgIfFlags(const char *ifname, short flagclear, short flagset) {
|
|
|
|
struct ifreq ifr;
|
|
|
|
int rc = 0;
|
|
|
|
int flags;
|
|
|
|
short flagmask = (~0 ^ flagclear);
|
|
|
|
int fd = socket(PF_PACKET, SOCK_DGRAM, 0);
|
|
|
|
|
|
|
|
if (fd < 0)
|
|
|
|
return errno;
|
|
|
|
|
|
|
|
if (virStrncpy(ifr.ifr_name,
|
|
|
|
ifname, strlen(ifname), sizeof(ifr.ifr_name)) == NULL) {
|
|
|
|
rc = ENODEV;
|
|
|
|
goto err_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
|
|
|
|
rc = errno;
|
|
|
|
goto err_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
flags = (ifr.ifr_flags & flagmask) | flagset;
|
|
|
|
|
|
|
|
if (ifr.ifr_flags != flags) {
|
|
|
|
ifr.ifr_flags = flags;
|
|
|
|
|
|
|
|
if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0)
|
|
|
|
rc = errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
err_exit:
|
|
|
|
close(fd);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ifUp
|
|
|
|
* @name: name of the interface
|
|
|
|
* @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.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
ifUp(const char *name, int up)
|
|
|
|
{
|
|
|
|
return chgIfFlags(name,
|
|
|
|
(up) ? 0 : IFF_UP,
|
|
|
|
(up) ? IFF_UP : 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
link_add(virConnectPtr conn,
|
|
|
|
const char *type,
|
|
|
|
const unsigned char *macaddress, int macaddrsize,
|
|
|
|
const char *ifname,
|
|
|
|
const char *srcdev,
|
|
|
|
uint32_t macvlan_mode,
|
|
|
|
int *retry)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
char nlmsgbuf[256];
|
|
|
|
struct nlmsghdr *nlm = (struct nlmsghdr *)nlmsgbuf, *resp;
|
|
|
|
struct nlmsgerr *err;
|
|
|
|
char rtattbuf[64];
|
|
|
|
struct rtattr *rta, *rta1, *li;
|
|
|
|
struct ifinfomsg i = { .ifi_family = AF_UNSPEC };
|
|
|
|
int ifindex;
|
|
|
|
char *recvbuf = NULL;
|
|
|
|
int recvbuflen;
|
|
|
|
|
|
|
|
if (getIfIndex(conn, srcdev, &ifindex) != 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
*retry = 0;
|
|
|
|
|
|
|
|
memset(&nlmsgbuf, 0, sizeof(nlmsgbuf));
|
|
|
|
|
|
|
|
nlInit(nlm, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL, RTM_NEWLINK);
|
|
|
|
|
|
|
|
if (!nlAppend(nlm, sizeof(nlmsgbuf), &i, sizeof(i)))
|
|
|
|
goto buffer_too_small;
|
|
|
|
|
|
|
|
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_LINK,
|
|
|
|
&ifindex, sizeof(ifindex));
|
|
|
|
if (!rta)
|
|
|
|
goto buffer_too_small;
|
|
|
|
|
|
|
|
if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
|
|
|
|
goto buffer_too_small;
|
|
|
|
|
|
|
|
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_ADDRESS,
|
|
|
|
macaddress, macaddrsize);
|
|
|
|
if (!rta)
|
|
|
|
goto buffer_too_small;
|
|
|
|
|
|
|
|
if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
|
|
|
|
goto buffer_too_small;
|
|
|
|
|
|
|
|
if (ifname) {
|
|
|
|
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_IFNAME,
|
|
|
|
ifname, strlen(ifname) + 1);
|
|
|
|
if (!rta)
|
|
|
|
goto buffer_too_small;
|
|
|
|
|
|
|
|
if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
|
|
|
|
goto buffer_too_small;
|
|
|
|
}
|
|
|
|
|
|
|
|
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_LINKINFO, NULL, 0);
|
|
|
|
if (!rta)
|
|
|
|
goto buffer_too_small;
|
|
|
|
|
|
|
|
if (!(li = nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len)))
|
|
|
|
goto buffer_too_small;
|
|
|
|
|
|
|
|
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_INFO_KIND,
|
|
|
|
type, strlen(type));
|
|
|
|
if (!rta)
|
|
|
|
goto buffer_too_small;
|
|
|
|
|
|
|
|
if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
|
|
|
|
goto buffer_too_small;
|
|
|
|
|
|
|
|
if (macvlan_mode > 0) {
|
|
|
|
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_INFO_DATA,
|
|
|
|
NULL, 0);
|
|
|
|
if (!rta)
|
|
|
|
goto buffer_too_small;
|
|
|
|
|
|
|
|
if (!(rta1 = nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len)))
|
|
|
|
goto buffer_too_small;
|
|
|
|
|
|
|
|
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_MACVLAN_MODE,
|
|
|
|
&macvlan_mode, sizeof(macvlan_mode));
|
|
|
|
if (!rta)
|
|
|
|
goto buffer_too_small;
|
|
|
|
|
|
|
|
if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
|
|
|
|
goto buffer_too_small;
|
|
|
|
|
|
|
|
rta1->rta_len = (char *)nlm + nlm->nlmsg_len - (char *)rta1;
|
|
|
|
}
|
|
|
|
|
|
|
|
li->rta_len = (char *)nlm + nlm->nlmsg_len - (char *)li;
|
|
|
|
|
|
|
|
if (nlComm(nlm, &recvbuf, &recvbuflen) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (recvbuflen < NLMSG_LENGTH(0) || recvbuf == NULL)
|
|
|
|
goto malformed_resp;
|
|
|
|
|
|
|
|
resp = (struct nlmsghdr *)recvbuf;
|
|
|
|
|
|
|
|
switch (resp->nlmsg_type) {
|
|
|
|
case NLMSG_ERROR:
|
|
|
|
err = (struct nlmsgerr *)NLMSG_DATA(resp);
|
|
|
|
if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
|
|
|
|
goto malformed_resp;
|
|
|
|
|
|
|
|
switch (-err->error) {
|
|
|
|
|
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EEXIST:
|
|
|
|
*retry = 1;
|
|
|
|
rc = -1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
virReportSystemError(-err->error,
|
|
|
|
_("error creating %s type of interface"),
|
|
|
|
type);
|
|
|
|
rc = -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NLMSG_DONE:
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
goto malformed_resp;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FREE(recvbuf);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
malformed_resp:
|
|
|
|
ReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("malformed netlink response message"));
|
|
|
|
VIR_FREE(recvbuf);
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
buffer_too_small:
|
|
|
|
ReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("internal buffer is too small"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
link_del(const char *type,
|
|
|
|
const char *name)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
char nlmsgbuf[256];
|
|
|
|
struct nlmsghdr *nlm = (struct nlmsghdr *)nlmsgbuf, *resp;
|
|
|
|
struct nlmsgerr *err;
|
|
|
|
char rtattbuf[64];
|
|
|
|
struct rtattr *rta, *rta1;
|
|
|
|
struct ifinfomsg ifinfo = { .ifi_family = AF_UNSPEC };
|
|
|
|
char *recvbuf = NULL;
|
|
|
|
int recvbuflen;
|
|
|
|
|
|
|
|
memset(&nlmsgbuf, 0, sizeof(nlmsgbuf));
|
|
|
|
|
|
|
|
nlInit(nlm, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL, RTM_DELLINK);
|
|
|
|
|
|
|
|
if (!nlAppend(nlm, sizeof(nlmsgbuf), &ifinfo, sizeof(ifinfo)))
|
|
|
|
goto buffer_too_small;
|
|
|
|
|
|
|
|
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_LINKINFO, NULL, 0);
|
|
|
|
if (!rta)
|
|
|
|
goto buffer_too_small;
|
|
|
|
|
|
|
|
if (!(rta1 = nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len)))
|
|
|
|
goto buffer_too_small;
|
|
|
|
|
|
|
|
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_INFO_KIND,
|
|
|
|
type, strlen(type));
|
|
|
|
if (!rta)
|
|
|
|
goto buffer_too_small;
|
|
|
|
|
|
|
|
if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
|
|
|
|
goto buffer_too_small;
|
|
|
|
|
|
|
|
rta1->rta_len = (char *)nlm + nlm->nlmsg_len - (char *)rta1;
|
|
|
|
|
|
|
|
rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_IFNAME,
|
|
|
|
name, strlen(name)+1);
|
|
|
|
if (!rta)
|
|
|
|
goto buffer_too_small;
|
|
|
|
|
|
|
|
if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
|
|
|
|
goto buffer_too_small;
|
|
|
|
|
|
|
|
if (nlComm(nlm, &recvbuf, &recvbuflen) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (recvbuflen < NLMSG_LENGTH(0) || recvbuf == NULL)
|
|
|
|
goto malformed_resp;
|
|
|
|
|
|
|
|
resp = (struct nlmsghdr *)recvbuf;
|
|
|
|
|
|
|
|
switch (resp->nlmsg_type) {
|
|
|
|
case NLMSG_ERROR:
|
|
|
|
err = (struct nlmsgerr *)NLMSG_DATA(resp);
|
|
|
|
if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
|
|
|
|
goto malformed_resp;
|
|
|
|
|
|
|
|
switch (-err->error) {
|
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
virReportSystemError(-err->error,
|
|
|
|
_("error destroying %s interface"),
|
|
|
|
name);
|
|
|
|
rc = -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NLMSG_DONE:
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
goto malformed_resp;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FREE(recvbuf);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
malformed_resp:
|
|
|
|
ReportError(NULL, VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("malformed netlink response message"));
|
|
|
|
VIR_FREE(recvbuf);
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
buffer_too_small:
|
|
|
|
ReportError(NULL, VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("internal buffer is too small"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Open the macvtap's tap device.
|
|
|
|
* @conn: Pointer to virConnect object
|
|
|
|
* @name: Name of the macvtap interface
|
|
|
|
* @retries : Number of retries in case udev for example may need to be
|
|
|
|
* waited for to create the tap chardev
|
|
|
|
* Returns negative value in case of error, the file descriptor otherwise.
|
|
|
|
*/
|
|
|
|
static
|
|
|
|
int openTap(const char *ifname,
|
|
|
|
int retries)
|
|
|
|
{
|
|
|
|
FILE *file;
|
|
|
|
char path[64];
|
|
|
|
int ifindex;
|
|
|
|
char tapname[50];
|
|
|
|
int tapfd;
|
|
|
|
|
|
|
|
if (snprintf(path, sizeof(path),
|
|
|
|
"/sys/class/net/%s/ifindex", ifname) >= sizeof(path)) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
"%s",
|
|
|
|
_("buffer for ifindex path is too small"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
file = fopen(path, "r");
|
|
|
|
|
|
|
|
if (!file) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("cannot open macvtap file %s to determine "
|
|
|
|
"interface index"), path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fscanf(file, "%d", &ifindex) != 1) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
"%s",_("cannot determine macvtap's tap device "
|
|
|
|
"interface index"));
|
|
|
|
fclose(file);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(file);
|
|
|
|
|
|
|
|
if (snprintf(tapname, sizeof(tapname),
|
|
|
|
"/dev/tap%d", ifindex) >= sizeof(tapname)) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
"%s",
|
|
|
|
_("internal buffer for tap device is too small"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
// may need to wait for udev to be done
|
|
|
|
tapfd = open(tapname, O_RDWR);
|
|
|
|
if (tapfd < 0 && retries > 0) {
|
|
|
|
retries--;
|
|
|
|
usleep(20000);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tapfd < 0)
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("cannot open macvtap tap device %s"),
|
|
|
|
tapname);
|
|
|
|
|
|
|
|
return tapfd;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static uint32_t
|
|
|
|
macvtapModeFromInt(enum virDomainNetdevMacvtapType mode)
|
|
|
|
{
|
|
|
|
switch (mode) {
|
|
|
|
case VIR_DOMAIN_NETDEV_MACVTAP_MODE_PRIVATE:
|
|
|
|
return MACVLAN_MODE_PRIVATE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_DOMAIN_NETDEV_MACVTAP_MODE_BRIDGE:
|
|
|
|
return MACVLAN_MODE_BRIDGE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_DOMAIN_NETDEV_MACVTAP_MODE_VEPA:
|
|
|
|
default:
|
|
|
|
return MACVLAN_MODE_VEPA;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* openMacvtapTap:
|
|
|
|
* Create an instance of a macvtap device and open its tap character
|
|
|
|
* device.
|
|
|
|
* @conn: Pointer to virConnect object
|
|
|
|
* @tgifname: Interface name that the macvtap is supposed to have. May
|
|
|
|
* be NULL if this function is supposed to choose a name
|
|
|
|
* @macaddress: The MAC address for the macvtap device
|
|
|
|
* @linkdev: The interface name of the NIC to connect to the external bridge
|
|
|
|
* @mode_str: String describing the mode. Valid are 'bridge', 'vepa' and
|
|
|
|
* 'private'.
|
|
|
|
* @res_ifname: Pointer to a string pointer where the actual name of the
|
|
|
|
* interface will be stored into if everything succeeded. It is up
|
|
|
|
* to the caller to free the string.
|
|
|
|
*
|
|
|
|
* Returns file descriptor of the tap device in case of success,
|
|
|
|
* negative value otherwise with error message attached to the 'conn'
|
|
|
|
* object.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
openMacvtapTap(virConnectPtr conn,
|
|
|
|
const char *tgifname,
|
|
|
|
const unsigned char *macaddress,
|
|
|
|
const char *linkdev,
|
|
|
|
int mode,
|
|
|
|
char **res_ifname)
|
|
|
|
{
|
|
|
|
const char *type = "macvtap";
|
|
|
|
int c, rc;
|
|
|
|
char ifname[IFNAMSIZ];
|
|
|
|
int retries, do_retry = 0;
|
|
|
|
uint32_t macvtapMode = macvtapModeFromInt(mode);
|
|
|
|
const char *cr_ifname;
|
|
|
|
int ifindex;
|
|
|
|
|
|
|
|
*res_ifname = NULL;
|
|
|
|
|
|
|
|
if (tgifname) {
|
|
|
|
if(getIfIndex(NULL, tgifname, &ifindex) == 0) {
|
|
|
|
if (STRPREFIX(tgifname,
|
|
|
|
MACVTAP_NAME_PREFIX)) {
|
|
|
|
goto create_name;
|
|
|
|
}
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Interface %s already exists"), tgifname);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
cr_ifname = tgifname;
|
|
|
|
rc = link_add(conn, type, macaddress, 6, tgifname, linkdev,
|
|
|
|
macvtapMode, &do_retry);
|
|
|
|
if (rc)
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
create_name:
|
|
|
|
retries = 5;
|
|
|
|
for (c = 0; c < 8192; c++) {
|
|
|
|
snprintf(ifname, sizeof(ifname), MACVTAP_NAME_PATTERN, c);
|
|
|
|
if (getIfIndex(NULL, ifname, &ifindex) == ENODEV) {
|
|
|
|
rc = link_add(conn, type, macaddress, 6, ifname, linkdev,
|
|
|
|
macvtapMode, &do_retry);
|
|
|
|
if (rc == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (do_retry && --retries)
|
|
|
|
continue;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cr_ifname = ifname;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = ifUp(cr_ifname, 1);
|
|
|
|
if (rc != 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("cannot 'up' interface %s"), cr_ifname);
|
|
|
|
rc = -1;
|
|
|
|
goto link_del_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = openTap(cr_ifname, 10);
|
|
|
|
|
|
|
|
if (rc > 0)
|
|
|
|
*res_ifname = strdup(cr_ifname);
|
|
|
|
else
|
|
|
|
goto link_del_exit;
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
link_del_exit:
|
|
|
|
link_del(type, ifname);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Delete a macvtap type of interface given the MAC address. This
|
|
|
|
* function will delete all macvtap type of interfaces that have the
|
|
|
|
* given MAC address.
|
|
|
|
* @macaddress : Pointer to 6 bytes holding the MAC address of the
|
|
|
|
* macvtap device(s) to destroy
|
|
|
|
*
|
|
|
|
* Returns 0 in case of success, negative value in case of error.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
delMacvtapByMACAddress(const unsigned char *macaddress,
|
|
|
|
int *hasBusyDev)
|
|
|
|
{
|
|
|
|
struct ifreq ifr;
|
|
|
|
FILE *file;
|
|
|
|
char *ifname, *pos;
|
|
|
|
char buffer[1024];
|
|
|
|
off_t oldpos = 0;
|
|
|
|
int tapfd;
|
|
|
|
|
|
|
|
*hasBusyDev = 0;
|
|
|
|
|
|
|
|
file = fopen("/proc/net/dev", "r");
|
|
|
|
|
|
|
|
if (!file) {
|
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
_("cannot open file to read network interfaces "
|
|
|
|
"from"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int sock = socket(AF_INET, SOCK_DGRAM, 0);
|
|
|
|
if (sock < 0) {
|
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
_("cannot open socket"));
|
|
|
|
goto sock_err;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (NULL != (ifname = fgets(buffer, sizeof(buffer), file))) {
|
|
|
|
if (c_isspace(ifname[0]))
|
|
|
|
ifname++;
|
|
|
|
if ((pos = strchr(ifname, ':')) != NULL) {
|
|
|
|
pos[0] = 0;
|
|
|
|
if (virStrncpy(ifr.ifr_name, ifname, strlen(ifname),
|
|
|
|
sizeof(ifr.ifr_name)) == NULL)
|
|
|
|
continue;
|
|
|
|
if (ioctl(sock, SIOCGIFHWADDR, (char *)&ifr) >= 0) {
|
|
|
|
if (memcmp(&ifr.ifr_hwaddr.sa_data[0], macaddress, 6) == 0) {
|
|
|
|
tapfd = openTap(ifname, 0);
|
|
|
|
if (tapfd > 0) {
|
|
|
|
close(tapfd);
|
|
|
|
ifUp(ifname, 0);
|
|
|
|
if (link_del("macvtap", ifname) == 0)
|
|
|
|
fseeko(file, oldpos, SEEK_SET);
|
|
|
|
} else {
|
|
|
|
*hasBusyDev = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
oldpos = ftello(file);
|
|
|
|
}
|
|
|
|
|
|
|
|
close(sock);
|
|
|
|
sock_err:
|
|
|
|
fclose(file);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|