/*
* 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
* .
*/
#include
#include
#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(virNetworkPortDefPtr def)
{
if (!def)
return;
VIR_FREE(def->ownername);
VIR_FREE(def->group);
virNetDevBandwidthFree(def->bandwidth);
virNetDevVlanClear(&def->vlan);
VIR_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:
VIR_FREE(def->plug.bridge.brname);
break;
case VIR_NETWORK_PORT_PLUG_TYPE_DIRECT:
VIR_FREE(def->plug.direct.linkdev);
break;
case VIR_NETWORK_PORT_PLUG_TYPE_HOSTDEV_PCI:
break;
case VIR_NETWORK_PORT_PLUG_TYPE_LAST:
default:
break;
}
VIR_FREE(def);
}
static virNetworkPortDefPtr
virNetworkPortDefParseXML(xmlXPathContextPtr ctxt)
{
g_autoptr(virNetworkPortDef) def = NULL;
g_autofree char *uuid = NULL;
xmlNodePtr virtPortNode;
xmlNodePtr vlanNode;
xmlNodePtr bandwidthNode;
xmlNodePtr addressNode;
g_autofree char *trustGuestRxFilters = NULL;
g_autofree char *mac = NULL;
g_autofree char *macmgr = NULL;
g_autofree char *mode = NULL;
g_autofree char *plugtype = NULL;
g_autofree char *managed = 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;
trustGuestRxFilters
= virXPathString("string(./rxfilters/@trustGuest)", ctxt);
if (trustGuestRxFilters) {
if ((def->trustGuestRxFilters
= virTristateBoolTypeFromString(trustGuestRxFilters)) <= 0) {
virReportError(VIR_ERR_XML_ERROR,
_("Invalid guest rx filters trust setting '%s' "),
trustGuestRxFilters);
return NULL;
}
}
plugtype = virXPathString("string(./plug/@type)", ctxt);
if (plugtype &&
(def->plugtype = virNetworkPortPlugTypeFromString(plugtype)) < 0) {
virReportError(VIR_ERR_XML_ERROR,
_("Invalid network prt plug type '%s'"), 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:
if (!(def->plug.bridge.brname = virXPathString("string(./plug/@bridge)", ctxt))) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("Missing network port bridge name"));
return NULL;
}
macmgr = virXPathString("string(./plug/@macTableManager)", ctxt);
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 = virXPathString("string(./plug/@dev)", ctxt))) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("Missing network port link device name"));
return NULL;
}
mode = virXPathString("string(./plug/@mode)", ctxt);
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:
managed = virXPathString("string(./plug/@managed)", ctxt);
if (managed &&
(def->plug.hostdevpci.managed =
virTristateBoolTypeFromString(managed)) < 0) {
virReportError(VIR_ERR_XML_ERROR,
_("Invalid managed setting '%s' in network port"), mode);
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);
}
virNetworkPortDefPtr
virNetworkPortDefParseNode(xmlDocPtr xml,
xmlNodePtr root)
{
xmlXPathContextPtr ctxt = NULL;
virNetworkPortDefPtr def = NULL;
if (STRNEQ((const char *)root->name, "networkport")) {
virReportError(VIR_ERR_XML_ERROR,
"%s",
_("unknown root element for network port"));
goto cleanup;
}
if (!(ctxt = virXMLXPathContextNew(xml)))
goto cleanup;
ctxt->node = root;
def = virNetworkPortDefParseXML(ctxt);
cleanup:
xmlXPathFreeContext(ctxt);
return def;
}
static virNetworkPortDefPtr
virNetworkPortDefParse(const char *xmlStr,
const char *filename)
{
virNetworkPortDefPtr def = NULL;
xmlDocPtr xml;
if ((xml = virXMLParse(filename, xmlStr, _("(networkport_definition)")))) {
def = virNetworkPortDefParseNode(xml, xmlDocGetRootElement(xml));
xmlFreeDoc(xml);
}
return def;
}
virNetworkPortDefPtr
virNetworkPortDefParseString(const char *xmlStr)
{
return virNetworkPortDefParse(xmlStr, NULL);
}
virNetworkPortDefPtr
virNetworkPortDefParseFile(const char *filename)
{
return virNetworkPortDefParse(NULL, filename);
}
char *
virNetworkPortDefFormat(const virNetworkPortDef *def)
{
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
if (virNetworkPortDefFormatBuf(&buf, def) < 0)
return NULL;
return virBufferContentAndReset(&buf);
}
int
virNetworkPortDefFormatBuf(virBufferPtr buf,
const virNetworkPortDef *def)
{
char uuid[VIR_UUID_STRING_BUFLEN];
char macaddr[VIR_MAC_STRING_BUFLEN];
virBufferAddLit(buf, "\n");
virBufferAdjustIndent(buf, 2);
virUUIDFormat(def->uuid, uuid);
virBufferAsprintf(buf, "%s\n", uuid);
virBufferAddLit(buf, "\n");
virBufferAdjustIndent(buf, 2);
virBufferEscapeString(buf, "%s\n", def->ownername);
virUUIDFormat(def->owneruuid, uuid);
virBufferAsprintf(buf, "%s\n", uuid);
virBufferAdjustIndent(buf, -2);
virBufferAddLit(buf, "\n");
virBufferEscapeString(buf, "%s\n", def->group);
virMacAddrFormat(&def->mac, macaddr);
virBufferAsprintf(buf, "\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, "\n",
virTristateBoolTypeToString(def->trustGuestRxFilters));
if (def->plugtype != VIR_NETWORK_PORT_PLUG_TYPE_NONE) {
virBufferAsprintf(buf, "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:
virBufferAsprintf(buf, " managed='%s'>\n",
def->plug.hostdevpci.managed ? "yes" : "no");
virBufferAdjustIndent(buf, 2);
if (def->plug.hostdevpci.driver)
virBufferEscapeString(buf, "\n",
virNetworkForwardDriverNameTypeToString(
def->plug.hostdevpci.driver));
virPCIDeviceAddressFormat(buf, def->plug.hostdevpci.addr, false);
virBufferAdjustIndent(buf, -2);
virBufferAddLit(buf, "\n");
break;
case VIR_NETWORK_PORT_PLUG_TYPE_LAST:
default:
virReportEnumRangeError(virNetworkPortPlugType, def->plugtype);
return -1;
}
}
virBufferAdjustIndent(buf, -2);
virBufferAddLit(buf, "\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 (virFileMakePath(dir) < 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;
}