mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-10-31 10:23:09 +00:00
a5d88ffe0a
Signed-off-by: Ján Tomko <jtomko@redhat.com> Reviewed-by: Neal Gompa <ngompa13@gmail.com> Reviewed-by: Erik Skultety <eskultet@redhat.com>
486 lines
14 KiB
C
486 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(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, "<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:
|
|
virBufferAsprintf(buf, " managed='%s'>\n",
|
|
def->plug.hostdevpci.managed ? "yes" : "no");
|
|
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 (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;
|
|
}
|