libvirt/src/conf/virnetworkportdef.c
Andrea Bolognani 06f5c092b8 conf: Format managed property of hostdev-pci ports correctly
The property is parsed using virTristateBoolTypeFromString() but
formatted as if it was a regular bool, which results in the
following incorrect conversion:

  BOOL_ABSENT -> managed='no'
  BOOL_YES    -> managed='yes'
  BOOL_NO     -> managed='yes'

Use the virTristateBoolTypeToString() helper to ensure the
setting can survive a roundtrip conversion.

Fixes: 4b4a981d60
Signed-off-by: Andrea Bolognani <abologna@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
2022-04-05 13:40:33 +02:00

476 lines
14 KiB
C

/*
* virnetworkportdef.c: network port XML processing
*
* Copyright (C) 2018 Red Hat, Inc.
*
* 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 <unistd.h>
#include "viralloc.h"
#include "virerror.h"
#include "virstring.h"
#include "virfile.h"
#include "virnetdevmacvlan.h"
#include "virnetworkportdef.h"
#include "network_conf.h"
#include "netdev_bandwidth_conf.h"
#include "netdev_vlan_conf.h"
#include "netdev_vport_profile_conf.h"
#define VIR_FROM_THIS VIR_FROM_NETWORK
VIR_ENUM_IMPL(virNetworkPortPlug,
VIR_NETWORK_PORT_PLUG_TYPE_LAST,
"none", "network", "bridge", "direct", "hostdev-pci");
void
virNetworkPortDefFree(virNetworkPortDef *def)
{
if (!def)
return;
g_free(def->ownername);
g_free(def->group);
virNetDevBandwidthFree(def->bandwidth);
virNetDevVlanClear(&def->vlan);
g_free(def->virtPortProfile);
switch ((virNetworkPortPlugType)def->plugtype) {
case VIR_NETWORK_PORT_PLUG_TYPE_NONE:
break;
case VIR_NETWORK_PORT_PLUG_TYPE_NETWORK:
case VIR_NETWORK_PORT_PLUG_TYPE_BRIDGE:
g_free(def->plug.bridge.brname);
break;
case VIR_NETWORK_PORT_PLUG_TYPE_DIRECT:
g_free(def->plug.direct.linkdev);
break;
case VIR_NETWORK_PORT_PLUG_TYPE_HOSTDEV_PCI:
break;
case VIR_NETWORK_PORT_PLUG_TYPE_LAST:
default:
break;
}
g_free(def);
}
static virNetworkPortDef *
virNetworkPortDefParseXML(xmlXPathContextPtr ctxt)
{
g_autoptr(virNetworkPortDef) def = NULL;
g_autofree char *uuid = NULL;
xmlNodePtr virtPortNode;
xmlNodePtr vlanNode;
xmlNodePtr bandwidthNode;
xmlNodePtr addressNode;
xmlNodePtr rxfiltersNode = NULL;
xmlNodePtr plugNode = NULL;
g_autofree char *mac = NULL;
g_autofree char *macmgr = NULL;
g_autofree char *mode = NULL;
g_autofree char *driver = NULL;
def = g_new0(virNetworkPortDef, 1);
uuid = virXPathString("string(./uuid)", ctxt);
if (!uuid) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("network port has no uuid"));
return NULL;
}
if (virUUIDParse(uuid, def->uuid) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to parse UUID '%s'"), uuid);
return NULL;
}
def->ownername = virXPathString("string(./owner/name)", ctxt);
if (!def->ownername) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("network port has no owner name"));
return NULL;
}
VIR_FREE(uuid);
uuid = virXPathString("string(./owner/uuid)", ctxt);
if (!uuid) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("network port has no owner UUID"));
return NULL;
}
if (virUUIDParse(uuid, def->owneruuid) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to parse UUID '%s'"), uuid);
return NULL;
}
def->group = virXPathString("string(./group)", ctxt);
virtPortNode = virXPathNode("./virtualport", ctxt);
if (virtPortNode &&
(!(def->virtPortProfile = virNetDevVPortProfileParse(virtPortNode, 0)))) {
return NULL;
}
mac = virXPathString("string(./mac/@address)", ctxt);
if (!mac) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("network port has no mac"));
return NULL;
}
if (virMacAddrParse(mac, &def->mac) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to parse MAC '%s'"), mac);
return NULL;
}
bandwidthNode = virXPathNode("./bandwidth", ctxt);
/*
* We don't know if the port will allow the "floor" param or
* not at this stage, so we must just tell virNetDevBandwidthParse
* to allow it regardless. Any bad config must be reported at
* time of use instead.
*/
if (bandwidthNode &&
virNetDevBandwidthParse(&def->bandwidth, &def->class_id,
bandwidthNode, true) < 0)
return NULL;
vlanNode = virXPathNode("./vlan", ctxt);
if (vlanNode && virNetDevVlanParse(vlanNode, ctxt, &def->vlan) < 0)
return NULL;
if (virNetworkPortOptionsParseXML(ctxt, &def->isolatedPort) < 0)
return NULL;
rxfiltersNode = virXPathNode("./rxfilters", ctxt);
if (virXMLPropTristateBool(rxfiltersNode, "trustGuest",
VIR_XML_PROP_NONE,
&def->trustGuestRxFilters) < 0)
return NULL;
plugNode = virXPathNode("./plug", ctxt);
if (virXMLPropEnum(plugNode, "type",
virNetworkPortPlugTypeFromString,
VIR_XML_PROP_NONE,
&def->plugtype) < 0)
return NULL;
switch (def->plugtype) {
case VIR_NETWORK_PORT_PLUG_TYPE_NONE:
break;
case VIR_NETWORK_PORT_PLUG_TYPE_NETWORK:
case VIR_NETWORK_PORT_PLUG_TYPE_BRIDGE:
if (!(def->plug.bridge.brname = virXMLPropString(plugNode, "bridge"))) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("Missing network port bridge name"));
return NULL;
}
macmgr = virXMLPropString(plugNode, "macTableManager");
if (macmgr &&
(def->plug.bridge.macTableManager =
virNetworkBridgeMACTableManagerTypeFromString(macmgr)) <= 0) {
virReportError(VIR_ERR_XML_ERROR,
_("Invalid macTableManager setting '%s' "
"in network port"), macmgr);
return NULL;
}
break;
case VIR_NETWORK_PORT_PLUG_TYPE_DIRECT:
if (!(def->plug.direct.linkdev = virXMLPropString(plugNode, "dev"))) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("Missing network port link device name"));
return NULL;
}
mode = virXMLPropString(plugNode, "mode");
if (mode &&
(def->plug.direct.mode =
virNetDevMacVLanModeTypeFromString(mode)) < 0) {
virReportError(VIR_ERR_XML_ERROR,
_("Invalid mode setting '%s' in network port"), mode);
return NULL;
}
break;
case VIR_NETWORK_PORT_PLUG_TYPE_HOSTDEV_PCI:
if (virXMLPropTristateBool(plugNode, "managed",
VIR_XML_PROP_NONE,
&def->plug.hostdevpci.managed) < 0)
return NULL;
driver = virXPathString("string(./plug/driver/@name)", ctxt);
if (driver &&
(def->plug.hostdevpci.driver =
virNetworkForwardDriverNameTypeFromString(driver)) <= 0) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("Missing network port driver name"));
return NULL;
}
if (!(addressNode = virXPathNode("./plug/address", ctxt))) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("Missing network port PCI address"));
return NULL;
}
if (virPCIDeviceAddressParseXML(addressNode, &def->plug.hostdevpci.addr) < 0)
return NULL;
break;
case VIR_NETWORK_PORT_PLUG_TYPE_LAST:
default:
virReportEnumRangeError(virNetworkPortPlugType, def->plugtype);
return NULL;
}
return g_steal_pointer(&def);
}
virNetworkPortDef *
virNetworkPortDefParseNode(xmlDocPtr xml,
xmlNodePtr root)
{
g_autoptr(xmlXPathContext) ctxt = NULL;
if (STRNEQ((const char *)root->name, "networkport")) {
virReportError(VIR_ERR_XML_ERROR,
"%s",
_("unknown root element for network port"));
return NULL;
}
if (!(ctxt = virXMLXPathContextNew(xml)))
return NULL;
ctxt->node = root;
return virNetworkPortDefParseXML(ctxt);
}
static virNetworkPortDef *
virNetworkPortDefParse(const char *xmlStr,
const char *filename,
unsigned int flags)
{
virNetworkPortDef *def = NULL;
g_autoptr(xmlDoc) xml = NULL;
if ((xml = virXMLParse(filename, xmlStr, _("(networkport_definition)"),
"networkport.rng",
flags & VIR_NETWORK_PORT_CREATE_VALIDATE))) {
def = virNetworkPortDefParseNode(xml, xmlDocGetRootElement(xml));
}
return def;
}
virNetworkPortDef *
virNetworkPortDefParseString(const char *xmlStr,
unsigned int flags)
{
return virNetworkPortDefParse(xmlStr, NULL, flags);
}
virNetworkPortDef *
virNetworkPortDefParseFile(const char *filename)
{
return virNetworkPortDefParse(NULL, filename, 0);
}
char *
virNetworkPortDefFormat(const virNetworkPortDef *def)
{
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
if (virNetworkPortDefFormatBuf(&buf, def) < 0)
return NULL;
return virBufferContentAndReset(&buf);
}
int
virNetworkPortDefFormatBuf(virBuffer *buf,
const virNetworkPortDef *def)
{
char uuid[VIR_UUID_STRING_BUFLEN];
char macaddr[VIR_MAC_STRING_BUFLEN];
virBufferAddLit(buf, "<networkport>\n");
virBufferAdjustIndent(buf, 2);
virUUIDFormat(def->uuid, uuid);
virBufferAsprintf(buf, "<uuid>%s</uuid>\n", uuid);
virBufferAddLit(buf, "<owner>\n");
virBufferAdjustIndent(buf, 2);
virBufferEscapeString(buf, "<name>%s</name>\n", def->ownername);
virUUIDFormat(def->owneruuid, uuid);
virBufferAsprintf(buf, "<uuid>%s</uuid>\n", uuid);
virBufferAdjustIndent(buf, -2);
virBufferAddLit(buf, "</owner>\n");
virBufferEscapeString(buf, "<group>%s</group>\n", def->group);
virMacAddrFormat(&def->mac, macaddr);
virBufferAsprintf(buf, "<mac address='%s'/>\n", macaddr);
if (virNetDevVPortProfileFormat(def->virtPortProfile, buf) < 0)
return -1;
if (def->bandwidth)
virNetDevBandwidthFormat(def->bandwidth, def->class_id, buf);
if (virNetDevVlanFormat(&def->vlan, buf) < 0)
return -1;
virNetworkPortOptionsFormat(def->isolatedPort, buf);
if (def->trustGuestRxFilters)
virBufferAsprintf(buf, "<rxfilters trustGuest='%s'/>\n",
virTristateBoolTypeToString(def->trustGuestRxFilters));
if (def->plugtype != VIR_NETWORK_PORT_PLUG_TYPE_NONE) {
virBufferAsprintf(buf, "<plug type='%s'",
virNetworkPortPlugTypeToString(def->plugtype));
switch (def->plugtype) {
case VIR_NETWORK_PORT_PLUG_TYPE_NONE:
break;
case VIR_NETWORK_PORT_PLUG_TYPE_NETWORK:
case VIR_NETWORK_PORT_PLUG_TYPE_BRIDGE:
virBufferEscapeString(buf, " bridge='%s'", def->plug.bridge.brname);
if (def->plug.bridge.macTableManager)
virBufferAsprintf(buf, " macTableManager='%s'",
virNetworkBridgeMACTableManagerTypeToString(
def->plug.bridge.macTableManager));
virBufferAddLit(buf, "/>\n");
break;
case VIR_NETWORK_PORT_PLUG_TYPE_DIRECT:
virBufferEscapeString(buf, " dev='%s'", def->plug.direct.linkdev);
virBufferAsprintf(buf, " mode='%s'",
virNetDevMacVLanModeTypeToString(
def->plug.direct.mode));
virBufferAddLit(buf, "/>\n");
break;
case VIR_NETWORK_PORT_PLUG_TYPE_HOSTDEV_PCI:
if (def->plug.hostdevpci.managed) {
virBufferAsprintf(buf, " managed='%s'",
virTristateBoolTypeToString(def->plug.hostdevpci.managed));
}
virBufferAddLit(buf, ">\n");
virBufferAdjustIndent(buf, 2);
if (def->plug.hostdevpci.driver)
virBufferEscapeString(buf, "<driver name='%s'/>\n",
virNetworkForwardDriverNameTypeToString(
def->plug.hostdevpci.driver));
virPCIDeviceAddressFormat(buf, def->plug.hostdevpci.addr, false);
virBufferAdjustIndent(buf, -2);
virBufferAddLit(buf, "</plug>\n");
break;
case VIR_NETWORK_PORT_PLUG_TYPE_LAST:
default:
virReportEnumRangeError(virNetworkPortPlugType, def->plugtype);
return -1;
}
}
virBufferAdjustIndent(buf, -2);
virBufferAddLit(buf, "</networkport>\n");
return 0;
}
static char *
virNetworkPortDefConfigFile(const char *dir,
const char *name)
{
return g_strdup_printf("%s/%s.xml", dir, name);
}
int
virNetworkPortDefSaveStatus(virNetworkPortDef *def,
const char *dir)
{
char uuidstr[VIR_UUID_STRING_BUFLEN];
g_autofree char *path = NULL;
g_autofree char *xml = NULL;
virUUIDFormat(def->uuid, uuidstr);
if (g_mkdir_with_parents(dir, 0777) < 0)
return -1;
if (!(path = virNetworkPortDefConfigFile(dir, uuidstr)))
return -1;
if (!(xml = virNetworkPortDefFormat(def)))
return -1;
if (virXMLSaveFile(path, uuidstr, "net-port-create", xml) < 0)
return -1;
return 0;
}
int
virNetworkPortDefDeleteStatus(virNetworkPortDef *def,
const char *dir)
{
char uuidstr[VIR_UUID_STRING_BUFLEN];
char *path;
int ret = -1;
virUUIDFormat(def->uuid, uuidstr);
if (!(path = virNetworkPortDefConfigFile(dir, uuidstr)))
goto cleanup;
if (unlink(path) < 0 && errno != ENOENT) {
virReportSystemError(errno,
_("Unable to delete %s"), path);
goto cleanup;
}
ret = 0;
cleanup:
VIR_FREE(path);
return ret;
}