libvirt/src/hypervisor/domain_interface.c
Andrea Bolognani 55c3c09197 qemu: Look for qemu-bridge-helper in more directories
Commit 0caacf47d7b423db9126660fb0382ed56cd077c1 recently
made it so the new path used for qemu-bridge-helper in Debian
would be allowed, but the logic used to actually figure out
the complete path for the helper was not updated accordingly.

https://bugs.debian.org/1082530

Signed-off-by: Andrea Bolognani <abologna@redhat.com>
Reviewed-by: Martin Kletzander <mkletzan@redhat.com>
2024-09-30 10:58:15 +02:00

762 lines
25 KiB
C

/*
* Copyright (C) 2015-2016 Red Hat, Inc.
* Copyright IBM Corp. 2014
*
* domain_interface.c: methods to manage guest/domain interfaces
*
* 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/>.
*/
#include <config.h>
#include "datatypes.h"
#include "domain_audit.h"
#include "domain_conf.h"
#include "domain_driver.h"
#include "domain_interface.h"
#include "domain_nwfilter.h"
#include "netdev_bandwidth_conf.h"
#include "network_conf.h"
#include "viralloc.h"
#include "virconftypes.h"
#include "virebtables.h"
#include "virfile.h"
#include "virlog.h"
#include "virmacaddr.h"
#include "virnetdevbridge.h"
#include "virnetdevmidonet.h"
#include "virnetdevopenvswitch.h"
#include "virnetdevtap.h"
#include "vircommand.h"
#define VIR_FROM_THIS VIR_FROM_DOMAIN
VIR_LOG_INIT("domain.interface");
bool
virDomainInterfaceIsVnetCompatModel(const virDomainNetDef *net)
{
return (virDomainNetIsVirtioModel(net) ||
net->model == VIR_DOMAIN_NET_MODEL_E1000E ||
net->model == VIR_DOMAIN_NET_MODEL_IGB ||
net->model == VIR_DOMAIN_NET_MODEL_VMXNET3);
}
/* virDomainInterfaceEthernetConnect:
* @def: the definition of the VM
* @net: a net definition in the VM
* @ebtables: ebtales context
* @macFilter: whether driver support mac Filtering
* @privileged: whether running as privileged user
* @tapfd: returned array of file descriptors for the domain interface
* @tapfdsize: returned number of file descriptors in @tapfd
*
* Called *only* called if actualType is VIR_DOMAIN_NET_TYPE_ETHERNET
* (i.e. if the connection is made with a tap device)
*/
int
virDomainInterfaceEthernetConnect(virDomainDef *def,
virDomainNetDef *net,
ebtablesContext *ebtables,
bool macFilter,
bool privileged,
int *tapfd,
size_t tapfdSize)
{
virMacAddr tapmac;
int ret = -1;
unsigned int tap_create_flags = VIR_NETDEV_TAP_CREATE_IFUP;
bool template_ifname = false;
const char *tunpath = "/dev/net/tun";
const char *auditdev = tunpath;
if (net->backend.tap) {
tunpath = net->backend.tap;
if (!privileged) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("cannot use custom tap device in session mode"));
goto cleanup;
}
}
if (virDomainInterfaceIsVnetCompatModel(net))
tap_create_flags |= VIR_NETDEV_TAP_CREATE_VNET_HDR;
if (net->managed_tap == VIR_TRISTATE_BOOL_NO) {
if (!net->ifname) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("target dev must be supplied when managed='no'"));
goto cleanup;
}
if (virNetDevExists(net->ifname) != 1) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("target managed='no' but specified dev doesn't exist"));
goto cleanup;
}
tap_create_flags |= VIR_NETDEV_TAP_CREATE_ALLOW_EXISTING;
if (virNetDevMacVLanIsMacvtap(net->ifname)) {
auditdev = net->ifname;
if (virNetDevMacVLanTapOpen(net->ifname, tapfd, tapfdSize) < 0)
goto cleanup;
if (virNetDevMacVLanTapSetup(tapfd, tapfdSize,
virDomainInterfaceIsVnetCompatModel(net)) < 0) {
goto cleanup;
}
} else {
if (virNetDevTapCreate(&net->ifname, tunpath, tapfd, tapfdSize,
tap_create_flags) < 0)
goto cleanup;
}
} else {
if (!net->ifname)
template_ifname = true;
if (virNetDevTapCreate(&net->ifname, tunpath, tapfd, tapfdSize,
tap_create_flags) < 0) {
goto cleanup;
}
/* The tap device's MAC address cannot match the MAC address
* used by the guest. This results in "received packet on
* vnetX with own address as source address" error logs from
* the kernel.
*/
virMacAddrSet(&tapmac, &net->mac);
if (tapmac.addr[0] == 0xFE)
tapmac.addr[0] = 0xFA;
else
tapmac.addr[0] = 0xFE;
if (virNetDevSetMAC(net->ifname, &tapmac) < 0)
goto cleanup;
if (virNetDevSetOnline(net->ifname, true) < 0)
goto cleanup;
}
if (net->script &&
virNetDevRunEthernetScript(net->ifname, net->script) < 0)
goto cleanup;
if (macFilter &&
ebtablesAddForwardAllowIn(ebtables,
net->ifname,
&net->mac) < 0)
goto cleanup;
if (net->filter &&
virDomainConfNWFilterInstantiate(def->name, def->uuid, net, false) < 0) {
goto cleanup;
}
virDomainAuditNetDevice(def, net, auditdev, true);
ret = 0;
cleanup:
if (ret < 0) {
size_t i;
virDomainAuditNetDevice(def, net, auditdev, false);
for (i = 0; i < tapfdSize && tapfd[i] >= 0; i++)
VIR_FORCE_CLOSE(tapfd[i]);
if (template_ifname)
VIR_FREE(net->ifname);
}
return ret;
}
/**
* virDomainInterfaceStartDevice:
* @net: net device to start
*
* Based upon the type of device provided, perform the appropriate
* work to completely activate the device and make it reachable from
* the rest of the network.
*/
int
virDomainInterfaceStartDevice(virDomainNetDef *net)
{
virDomainNetType actualType = virDomainNetGetActualType(net);
switch (actualType) {
case VIR_DOMAIN_NET_TYPE_BRIDGE:
case VIR_DOMAIN_NET_TYPE_NETWORK:
if (virDomainNetGetActualBridgeMACTableManager(net) ==
VIR_NETWORK_BRIDGE_MAC_TABLE_MANAGER_LIBVIRT) {
/* libvirt is managing the FDB of the bridge this device
* is attaching to, so we have turned off learning and
* unicast_flood on the device to prevent the kernel from
* adding any FDB entries for it. This means we need to
* add an fdb entry ourselves, using the MAC address from
* the interface config.
*/
if (virNetDevBridgeFDBAdd(&net->mac, net->ifname,
VIR_NETDEVBRIDGE_FDB_FLAG_MASTER |
VIR_NETDEVBRIDGE_FDB_FLAG_TEMP) < 0)
return -1;
}
break;
case VIR_DOMAIN_NET_TYPE_DIRECT: {
const char *physdev = virDomainNetGetActualDirectDev(net);
bool isOnline = true;
/* set the physdev online if necessary. It may already be up,
* in which case we shouldn't re-up it just in case that causes
* some sort of "blip" in the physdev's status.
*/
if (physdev && virNetDevGetOnline(physdev, &isOnline) < 0)
return -1;
if (!isOnline && virNetDevSetOnline(physdev, true) < 0)
return -1;
/* macvtap devices share their MAC address with the guest
* domain, and if they are set online prior to the domain CPUs
* being started, the host may send out traffic from this
* device that could confuse other entities on the network (in
* particular, if this new domain is the destination of a
* migration, and the source domain is still running, another
* host may mistakenly direct traffic for the guest to the
* destination domain rather than source domain). To prevent
* this, we create the macvtap device with IFF_UP false
* (i.e. "offline") then wait to bring it online until just as
* we are starting the domain CPUs.
*/
if (virNetDevSetOnline(net->ifname, true) < 0)
return -1;
break;
}
case VIR_DOMAIN_NET_TYPE_ETHERNET:
if (virNetDevIPInfoAddToDev(net->ifname, &net->hostIP) < 0)
return -1;
break;
case VIR_DOMAIN_NET_TYPE_USER:
case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
case VIR_DOMAIN_NET_TYPE_SERVER:
case VIR_DOMAIN_NET_TYPE_CLIENT:
case VIR_DOMAIN_NET_TYPE_MCAST:
case VIR_DOMAIN_NET_TYPE_UDP:
case VIR_DOMAIN_NET_TYPE_INTERNAL:
case VIR_DOMAIN_NET_TYPE_HOSTDEV:
case VIR_DOMAIN_NET_TYPE_VDPA:
case VIR_DOMAIN_NET_TYPE_NULL:
case VIR_DOMAIN_NET_TYPE_VDS:
case VIR_DOMAIN_NET_TYPE_LAST:
/* these types all require no action */
break;
}
return 0;
}
/**
* virDomainInterfaceStartDevices:
* @def: domain definition
*
* Set all ifaces associated with this domain to the online state.
*/
int
virDomainInterfaceStartDevices(virDomainDef *def)
{
size_t i;
for (i = 0; i < def->nnets; i++) {
if (virDomainInterfaceStartDevice(def->nets[i]) < 0)
return -1;
}
return 0;
}
/**
* virDomainInterfaceStopDevice:
* @net: net device to stop
*
* Based upon the type of device provided, perform the appropriate
* work to deactivate the device so that packets aren't forwarded to
* it from the rest of the network.
*/
int
virDomainInterfaceStopDevice(virDomainNetDef *net)
{
virDomainNetType actualType = virDomainNetGetActualType(net);
switch (actualType) {
case VIR_DOMAIN_NET_TYPE_BRIDGE:
case VIR_DOMAIN_NET_TYPE_NETWORK:
if (virDomainNetGetActualBridgeMACTableManager(net) ==
VIR_NETWORK_BRIDGE_MAC_TABLE_MANAGER_LIBVIRT) {
/* remove the FDB entries that were added during
* virDomainInterfaceStartDevices()
*/
if (virNetDevBridgeFDBDel(&net->mac, net->ifname,
VIR_NETDEVBRIDGE_FDB_FLAG_MASTER |
VIR_NETDEVBRIDGE_FDB_FLAG_TEMP) < 0)
return -1;
}
break;
case VIR_DOMAIN_NET_TYPE_DIRECT: {
const char *physdev = virDomainNetGetActualDirectDev(net);
/* macvtap interfaces need to be marked !IFF_UP (ie "down") to
* prevent any host-generated traffic sent from this interface
* from putting bad info into the arp caches of other machines
* on this network.
*/
if (virNetDevSetOnline(net->ifname, false) < 0)
return -1;
/* also mark the physdev down for passthrough macvtap, as the
* physdev has the same MAC address as the macvtap device.
*/
if (virDomainNetGetActualDirectMode(net) ==
VIR_NETDEV_MACVLAN_MODE_PASSTHRU &&
physdev && virNetDevSetOnline(physdev, false) < 0)
return -1;
break;
}
case VIR_DOMAIN_NET_TYPE_ETHERNET:
case VIR_DOMAIN_NET_TYPE_USER:
case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
case VIR_DOMAIN_NET_TYPE_SERVER:
case VIR_DOMAIN_NET_TYPE_CLIENT:
case VIR_DOMAIN_NET_TYPE_MCAST:
case VIR_DOMAIN_NET_TYPE_UDP:
case VIR_DOMAIN_NET_TYPE_INTERNAL:
case VIR_DOMAIN_NET_TYPE_HOSTDEV:
case VIR_DOMAIN_NET_TYPE_VDPA:
case VIR_DOMAIN_NET_TYPE_NULL:
case VIR_DOMAIN_NET_TYPE_VDS:
case VIR_DOMAIN_NET_TYPE_LAST:
/* these types all require no action */
break;
}
return 0;
}
/**
* virDomainInterfaceStopDevices:
* @def: domain definition
*
* Make all interfaces associated with this domain inaccessible from
* the rest of the network.
*/
int
virDomainInterfaceStopDevices(virDomainDef *def)
{
size_t i;
for (i = 0; i < def->nnets; i++) {
if (virDomainInterfaceStopDevice(def->nets[i]) < 0)
return -1;
}
return 0;
}
/**
* virDomainInterfaceVportRemove:
* @net: a net definition in the VM
*
* Removes vport profile from corresponding bridge.
* NOP if no vport profile is present in @net.
*/
void
virDomainInterfaceVportRemove(virDomainNetDef *net)
{
const virNetDevVPortProfile *vport = virDomainNetGetActualVirtPortProfile(net);
if (!vport)
return;
if (vport->virtPortType == VIR_NETDEV_VPORT_PROFILE_MIDONET) {
ignore_value(virNetDevMidonetUnbindPort(vport));
} else if (vport->virtPortType == VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH) {
ignore_value(virNetDevOpenvswitchRemovePort(net->ifname));
}
}
/**
* virDomainInterfaceDeleteDevice:
* @def: domain definition
* @net: a net definition in the VM
* @priv_net_created: whether private network created
* @stateDir: driver's stateDir
*
* Disconnect and delete domain interfaces.
*/
void
virDomainInterfaceDeleteDevice(virDomainDef *def,
virDomainNetDef *net,
bool priv_net_created,
char *stateDir)
{
g_autoptr(virConnect) conn = NULL;
switch (virDomainNetGetActualType(net)) {
case VIR_DOMAIN_NET_TYPE_DIRECT:
if (priv_net_created) {
virNetDevMacVLanDeleteWithVPortProfile(net->ifname, &net->mac,
virDomainNetGetActualDirectDev(net),
virDomainNetGetActualDirectMode(net),
virDomainNetGetActualVirtPortProfile(net),
stateDir);
}
break;
case VIR_DOMAIN_NET_TYPE_ETHERNET:
if (net->managed_tap != VIR_TRISTATE_BOOL_NO && net->ifname) {
ignore_value(virNetDevTapDelete(net->ifname, net->backend.tap));
VIR_FREE(net->ifname);
}
break;
case VIR_DOMAIN_NET_TYPE_BRIDGE:
case VIR_DOMAIN_NET_TYPE_NETWORK: {
#ifdef VIR_NETDEV_TAP_REQUIRE_MANUAL_CLEANUP
const virNetDevVPortProfile *vport = virDomainNetGetActualVirtPortProfile(net);
if (!(vport && vport->virtPortType == VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH))
ignore_value(virNetDevTapDelete(net->ifname, net->backend.tap));
#endif
}
break;
case VIR_DOMAIN_NET_TYPE_USER:
case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
case VIR_DOMAIN_NET_TYPE_SERVER:
case VIR_DOMAIN_NET_TYPE_CLIENT:
case VIR_DOMAIN_NET_TYPE_MCAST:
case VIR_DOMAIN_NET_TYPE_INTERNAL:
case VIR_DOMAIN_NET_TYPE_HOSTDEV:
case VIR_DOMAIN_NET_TYPE_UDP:
case VIR_DOMAIN_NET_TYPE_VDPA:
case VIR_DOMAIN_NET_TYPE_NULL:
case VIR_DOMAIN_NET_TYPE_VDS:
case VIR_DOMAIN_NET_TYPE_LAST:
/* No special cleanup procedure for these types. */
break;
}
/* release the physical device (or any other resources used by
* this interface in the network driver
*/
virDomainInterfaceVportRemove(net);
/* kick the device out of the hostdev list too */
virDomainNetRemoveHostdev(def, net);
if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK) {
if (conn || (conn = virGetConnectNetwork()))
virDomainNetReleaseActualDevice(conn, net);
else
VIR_WARN("Unable to release network device '%s'", NULLSTR(net->ifname));
}
}
/**
* virDomainInterfaceClearQoS
* @def: domain definition
* @net: a net definition in the VM
*
* For given interface @net clear its QoS settings in the
* host. NOP if @net has no QoS or is of a type that doesn't
* support QoS.
*
* Returns: 0 on success,
* -1 otherwise (with appropriate error reported)
*/
int
virDomainInterfaceClearQoS(virDomainDef *def,
virDomainNetDef *net)
{
if (!virDomainNetGetActualBandwidth(net))
return 0;
if (!virNetDevSupportsBandwidth(virDomainNetGetActualType(net)))
return 0;
if (virDomainNetDefIsOvsport(net)) {
return virNetDevOpenvswitchInterfaceClearQos(net->ifname, def->uuid);
}
return virNetDevBandwidthClear(net->ifname);
}
void
virDomainClearNetBandwidth(virDomainDef *def)
{
size_t i;
for (i = 0; i < def->nnets; i++) {
virDomainInterfaceClearQoS(def, def->nets[i]);
}
}
/**
* virDomainCreateInBridgePortWithHelper:
* @bridgeHelperName: name of the bridge helper program
* @brname: the bridge name
* @ifname: the returned interface name
* @tapfd: file descriptor return value for the new tap device
* @flags: OR of virNetDevTapCreateFlags:
* VIR_NETDEV_TAP_CREATE_VNET_HDR
* - Enable IFF_VNET_HDR on the tap device
*
* This function creates a new tap device on a bridge using an external
* helper. The final name for the bridge will be stored in @ifname.
*
* Returns 0 in case of success or -1 on failure
*/
#ifndef WIN32
static int
virDomainCreateInBridgePortWithHelper(const char *bridgeHelperName,
const char *brname,
char **ifname,
int *tapfd,
unsigned int flags)
{
const char *const bridgeHelperDirs[] = {
"/usr/libexec/qemu",
"/usr/libexec",
"/usr/lib/qemu",
"/usr/lib",
NULL,
};
g_autoptr(virCommand) cmd = NULL;
g_autofree char *bridgeHelperPath = NULL;
char *errbuf = NULL, *cmdstr = NULL;
int pair[2] = { -1, -1 };
if (!bridgeHelperName) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing bridge helper name"));
return -1;
}
if ((flags & ~VIR_NETDEV_TAP_CREATE_VNET_HDR) != VIR_NETDEV_TAP_CREATE_IFUP)
return -1;
if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) < 0) {
virReportSystemError(errno, "%s", _("failed to create socket"));
return -1;
}
bridgeHelperPath = virFindFileInPathFull(bridgeHelperName, bridgeHelperDirs);
if (!bridgeHelperPath) {
virReportSystemError(errno,
_("'%1$s' is not a suitable bridge helper"),
bridgeHelperName);
return -1;
}
VIR_DEBUG("Using qemu-bridge-helper: %s", bridgeHelperPath);
cmd = virCommandNew(bridgeHelperPath);
if (flags & VIR_NETDEV_TAP_CREATE_VNET_HDR)
virCommandAddArgFormat(cmd, "--use-vnet");
virCommandAddArgFormat(cmd, "--br=%s", brname);
virCommandAddArgFormat(cmd, "--fd=%d", pair[1]);
virCommandSetErrorBuffer(cmd, &errbuf);
virCommandDoAsyncIO(cmd);
virCommandPassFD(cmd, pair[1],
VIR_COMMAND_PASS_FD_CLOSE_PARENT);
virCommandClearCaps(cmd);
# ifdef CAP_NET_ADMIN
virCommandAllowCap(cmd, CAP_NET_ADMIN);
# endif
if (virCommandRunAsync(cmd, NULL) < 0) {
*tapfd = -1;
goto cleanup;
}
do {
*tapfd = virSocketRecvFD(pair[0], 0);
} while (*tapfd < 0 && errno == EINTR);
if (*tapfd < 0) {
char *errstr = NULL;
if (!(cmdstr = virCommandToString(cmd, false)))
goto cleanup;
virCommandAbort(cmd);
if (errbuf && *errbuf)
errstr = g_strdup_printf("stderr=%s", errbuf);
virReportSystemError(errno,
_("%1$s: failed to communicate with bridge helper: %2$s"),
cmdstr,
NULLSTR_EMPTY(errstr));
VIR_FREE(errstr);
goto cleanup;
}
if (virNetDevTapGetName(*tapfd, ifname) < 0 ||
virCommandWait(cmd, NULL) < 0) {
VIR_FORCE_CLOSE(*tapfd);
*tapfd = -1;
}
cleanup:
VIR_FREE(cmdstr);
VIR_FREE(errbuf);
VIR_FORCE_CLOSE(pair[0]);
return *tapfd < 0 ? -1 : 0;
}
#else /* WIN32 */
static int
virDomainCreateInBridgePortWithHelper(const char *bridgeHelperName G_GNUC_UNUSED,
const char *brname G_GNUC_UNUSED,
char **ifname G_GNUC_UNUSED,
int *tapfd G_GNUC_UNUSED,
unsigned int unusedflags G_GNUC_UNUSED)
{
virReportSystemError(ENOSYS, "%s",
_("bridge port creation is not supported on this platform"));
return -1;
}
#endif
/* virDomainInterfaceBridgeConnect:
* @def: the definition of the VM
* @net: pointer to the VM's interface description
* @tapfd: array of file descriptor return value for the new device
* @tapfdsize: number of file descriptors in @tapfd
* @privileged: whether running as privileged user
* @ebtables: ebtales context
* @macFilter: whether driver support mac Filtering
* @bridgeHelperName:name of the bridge helper program to run in non-privileged mode
*
* Called *only* called if actualType is VIR_DOMAIN_NET_TYPE_NETWORK or
* VIR_DOMAIN_NET_TYPE_BRIDGE (i.e. if the connection is made with a tap
* device connecting to a bridge device)
*/
int
virDomainInterfaceBridgeConnect(virDomainDef *def,
virDomainNetDef *net,
int *tapfd,
size_t *tapfdSize,
bool privileged,
ebtablesContext *ebtables,
bool macFilter,
const char *bridgeHelperName)
{
const char *brname;
int ret = -1;
unsigned int tap_create_flags = VIR_NETDEV_TAP_CREATE_IFUP;
bool template_ifname = false;
const char *tunpath = "/dev/net/tun";
if (net->backend.tap) {
tunpath = net->backend.tap;
if (!privileged) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("cannot use custom tap device in session mode"));
goto cleanup;
}
}
if (!(brname = virDomainNetGetActualBridgeName(net))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing bridge name"));
goto cleanup;
}
if (!net->ifname)
template_ifname = true;
if (virDomainInterfaceIsVnetCompatModel(net))
tap_create_flags |= VIR_NETDEV_TAP_CREATE_VNET_HDR;
if (privileged) {
if (virNetDevTapCreateInBridgePort(brname, &net->ifname, &net->mac,
def->uuid, tunpath, tapfd, *tapfdSize,
virDomainNetGetActualVirtPortProfile(net),
virDomainNetGetActualVlan(net),
virDomainNetGetActualPortOptionsIsolated(net),
net->coalesce, 0, NULL,
tap_create_flags) < 0) {
virDomainAuditNetDevice(def, net, tunpath, false);
goto cleanup;
}
if (virDomainNetGetActualBridgeMACTableManager(net)
== VIR_NETWORK_BRIDGE_MAC_TABLE_MANAGER_LIBVIRT) {
/* libvirt is managing the FDB of the bridge this device
* is attaching to, so we need to turn off learning and
* unicast_flood on the device to prevent the kernel from
* adding any FDB entries for it. We will add an fdb
* entry ourselves (during virDomainInterfaceStartDevices(),
* using the MAC address from the interface config.
*/
if (virNetDevBridgePortSetLearning(brname, net->ifname, false) < 0)
goto cleanup;
if (virNetDevBridgePortSetUnicastFlood(brname, net->ifname, false) < 0)
goto cleanup;
}
} else {
if (virDomainCreateInBridgePortWithHelper(bridgeHelperName, brname,
&net->ifname,
tapfd,
tap_create_flags) < 0) {
virDomainAuditNetDevice(def, net, tunpath, false);
goto cleanup;
}
/* virDomainCreateInBridgePortWithHelper can only create a single FD */
if (*tapfdSize > 1) {
VIR_WARN("Ignoring multiqueue network request");
*tapfdSize = 1;
}
}
virDomainAuditNetDevice(def, net, tunpath, true);
if (macFilter &&
ebtablesAddForwardAllowIn(ebtables,
net->ifname,
&net->mac) < 0)
goto cleanup;
if (net->filter &&
virDomainConfNWFilterInstantiate(def->name, def->uuid, net, false) < 0) {
goto cleanup;
}
ret = 0;
cleanup:
if (ret < 0) {
size_t i;
for (i = 0; i < *tapfdSize && tapfd[i] >= 0; i++)
VIR_FORCE_CLOSE(tapfd[i]);
if (template_ifname)
VIR_FREE(net->ifname);
}
return ret;
}