/* * 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; }