mirror of
https://github.com/libvirt/libvirt.git
synced 2025-02-25 18:55:26 -06:00
The generic helper also has helper code to validate the root element and create an XPath context. Many places in the code duplicate code for doing these operations. Extend the helper to provide all arguments and fix all callers. In many cases this patch refactors the passing of the 'validate' field into a separate variable to avoid quirky looking arguments. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Ján Tomko <jtomko@redhat.com>
1001 lines
29 KiB
C
1001 lines
29 KiB
C
/*
|
|
* interface_conf.c: interfaces XML handling
|
|
*
|
|
* Copyright (C) 2006-2010, 2013-2015 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 "virerror.h"
|
|
|
|
#include "interface_conf.h"
|
|
|
|
#include "virxml.h"
|
|
#include "virbuffer.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_INTERFACE
|
|
|
|
VIR_ENUM_IMPL(virInterface,
|
|
VIR_INTERFACE_TYPE_LAST,
|
|
"ethernet", "bridge", "bond", "vlan",
|
|
);
|
|
|
|
static virInterfaceDef *
|
|
virInterfaceDefParseXML(xmlXPathContextPtr ctxt, int parentIfType);
|
|
|
|
static int
|
|
virInterfaceDefDevFormat(virBuffer *buf, const virInterfaceDef *def,
|
|
virInterfaceType parentIfType);
|
|
|
|
static void
|
|
virInterfaceIPDefFree(virInterfaceIPDef *def)
|
|
{
|
|
if (def == NULL)
|
|
return;
|
|
g_free(def->address);
|
|
g_free(def);
|
|
}
|
|
|
|
|
|
static void
|
|
virInterfaceProtocolDefFree(virInterfaceProtocolDef *def)
|
|
{
|
|
size_t i;
|
|
|
|
if (def == NULL)
|
|
return;
|
|
for (i = 0; i < def->nips; i++)
|
|
virInterfaceIPDefFree(def->ips[i]);
|
|
g_free(def->ips);
|
|
g_free(def->family);
|
|
g_free(def->gateway);
|
|
g_free(def);
|
|
}
|
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC(virInterfaceProtocolDef, virInterfaceProtocolDefFree);
|
|
|
|
|
|
void
|
|
virInterfaceDefFree(virInterfaceDef *def)
|
|
{
|
|
size_t i;
|
|
int pp;
|
|
|
|
if (def == NULL)
|
|
return;
|
|
|
|
g_free(def->name);
|
|
g_free(def->mac);
|
|
|
|
switch (def->type) {
|
|
case VIR_INTERFACE_TYPE_BRIDGE:
|
|
g_free(def->data.bridge.delay);
|
|
for (i = 0; i < def->data.bridge.nbItf; i++) {
|
|
if (def->data.bridge.itf[i] == NULL)
|
|
break; /* to cope with half parsed data on errors */
|
|
virInterfaceDefFree(def->data.bridge.itf[i]);
|
|
}
|
|
g_free(def->data.bridge.itf);
|
|
break;
|
|
case VIR_INTERFACE_TYPE_BOND:
|
|
g_free(def->data.bond.target);
|
|
for (i = 0; i < def->data.bond.nbItf; i++) {
|
|
if (def->data.bond.itf[i] == NULL)
|
|
break; /* to cope with half parsed data on errors */
|
|
virInterfaceDefFree(def->data.bond.itf[i]);
|
|
}
|
|
g_free(def->data.bond.itf);
|
|
break;
|
|
case VIR_INTERFACE_TYPE_VLAN:
|
|
g_free(def->data.vlan.tag);
|
|
g_free(def->data.vlan.dev_name);
|
|
break;
|
|
}
|
|
|
|
/* free all protos */
|
|
for (pp = 0; pp < def->nprotos; pp++)
|
|
virInterfaceProtocolDefFree(def->protos[pp]);
|
|
g_free(def->protos);
|
|
g_free(def);
|
|
}
|
|
|
|
|
|
static int
|
|
virInterfaceDefParseMtu(virInterfaceDef *def,
|
|
xmlXPathContextPtr ctxt)
|
|
{
|
|
unsigned long mtu;
|
|
int ret;
|
|
|
|
ret = virXPathULong("string(./mtu/@size)", ctxt, &mtu);
|
|
if ((ret == -2) || ((ret == 0) && (mtu > 100000))) {
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
"%s", _("interface mtu value is improper"));
|
|
return -1;
|
|
} else if (ret == 0) {
|
|
def->mtu = (unsigned int) mtu;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virInterfaceDefParseStartMode(virInterfaceDef *def,
|
|
xmlXPathContextPtr ctxt)
|
|
{
|
|
g_autofree char *tmp = virXPathString("string(./start/@mode)", ctxt);
|
|
|
|
if (tmp == NULL) {
|
|
def->startmode = VIR_INTERFACE_START_UNSPECIFIED;
|
|
} else if (STREQ(tmp, "onboot")) {
|
|
def->startmode = VIR_INTERFACE_START_ONBOOT;
|
|
} else if (STREQ(tmp, "hotplug")) {
|
|
def->startmode = VIR_INTERFACE_START_HOTPLUG;
|
|
} else if (STREQ(tmp, "none")) {
|
|
def->startmode = VIR_INTERFACE_START_NONE;
|
|
} else {
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
_("unknown interface startmode %s"), tmp);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virInterfaceDefParseBondMode(xmlXPathContextPtr ctxt)
|
|
{
|
|
g_autofree char *tmp = virXPathString("string(./@mode)", ctxt);
|
|
|
|
if (tmp == NULL)
|
|
return VIR_INTERFACE_BOND_NONE;
|
|
if (STREQ(tmp, "balance-rr")) {
|
|
return VIR_INTERFACE_BOND_BALRR;
|
|
} else if (STREQ(tmp, "active-backup")) {
|
|
return VIR_INTERFACE_BOND_ABACKUP;
|
|
} else if (STREQ(tmp, "balance-xor")) {
|
|
return VIR_INTERFACE_BOND_BALXOR;
|
|
} else if (STREQ(tmp, "broadcast")) {
|
|
return VIR_INTERFACE_BOND_BCAST;
|
|
} else if (STREQ(tmp, "802.3ad")) {
|
|
return VIR_INTERFACE_BOND_8023AD;
|
|
} else if (STREQ(tmp, "balance-tlb")) {
|
|
return VIR_INTERFACE_BOND_BALTLB;
|
|
} else if (STREQ(tmp, "balance-alb")) {
|
|
return VIR_INTERFACE_BOND_BALALB;
|
|
}
|
|
|
|
virReportError(VIR_ERR_XML_ERROR, _("unknown bonding mode %s"), tmp);
|
|
return -1;
|
|
}
|
|
|
|
|
|
static int
|
|
virInterfaceDefParseBondMiiCarrier(xmlXPathContextPtr ctxt)
|
|
{
|
|
g_autofree char *tmp = virXPathString("string(./miimon/@carrier)", ctxt);
|
|
|
|
if (tmp == NULL)
|
|
return VIR_INTERFACE_BOND_MII_NONE;
|
|
if (STREQ(tmp, "ioctl")) {
|
|
return VIR_INTERFACE_BOND_MII_IOCTL;
|
|
} else if (STREQ(tmp, "netif")) {
|
|
return VIR_INTERFACE_BOND_MII_NETIF;
|
|
}
|
|
|
|
virReportError(VIR_ERR_XML_ERROR, _("unknown mii bonding carrier %s"), tmp);
|
|
return -1;
|
|
}
|
|
|
|
|
|
static int
|
|
virInterfaceDefParseBondArpValid(xmlXPathContextPtr ctxt)
|
|
{
|
|
g_autofree char *tmp = virXPathString("string(./arpmon/@validate)", ctxt);
|
|
|
|
if (tmp == NULL)
|
|
return VIR_INTERFACE_BOND_ARP_NONE;
|
|
if (STREQ(tmp, "active")) {
|
|
return VIR_INTERFACE_BOND_ARP_ACTIVE;
|
|
} else if (STREQ(tmp, "backup")) {
|
|
return VIR_INTERFACE_BOND_ARP_BACKUP;
|
|
} else if (STREQ(tmp, "all")) {
|
|
return VIR_INTERFACE_BOND_ARP_ALL;
|
|
}
|
|
|
|
virReportError(VIR_ERR_XML_ERROR, _("unknown arp bonding validate %s"), tmp);
|
|
return -1;
|
|
}
|
|
|
|
|
|
static int
|
|
virInterfaceDefParseDhcp(virInterfaceProtocolDef *def,
|
|
xmlNodePtr dhcp)
|
|
{
|
|
virTristateBool peerdns;
|
|
|
|
def->dhcp = 1;
|
|
def->peerdns = -1;
|
|
|
|
if (virXMLPropTristateBool(dhcp, "peerdns", VIR_XML_PROP_NONE, &peerdns) < 0)
|
|
return -1;
|
|
|
|
if (peerdns != VIR_TRISTATE_BOOL_ABSENT) {
|
|
def->peerdns = peerdns == VIR_TRISTATE_BOOL_YES ? 1 : 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virInterfaceDefParseIP(virInterfaceIPDef *def,
|
|
xmlNodePtr node)
|
|
{
|
|
if (!(def->address = virXMLPropString(node, "address")))
|
|
return 0;
|
|
|
|
if (virXMLPropInt(node, "prefix", 0, VIR_XML_PROP_NONE, &def->prefix, 0) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virInterfaceDefParseProtoIPv4(virInterfaceProtocolDef *def,
|
|
xmlXPathContextPtr ctxt)
|
|
{
|
|
xmlNodePtr dhcp;
|
|
g_autofree xmlNodePtr *ipNodes = NULL;
|
|
int nipNodes;
|
|
size_t i;
|
|
|
|
def->gateway = virXPathString("string(./route[1]/@gateway)", ctxt);
|
|
|
|
dhcp = virXPathNode("./dhcp", ctxt);
|
|
if (dhcp != NULL) {
|
|
if (virInterfaceDefParseDhcp(def, dhcp) < 0)
|
|
return -1;
|
|
}
|
|
|
|
nipNodes = virXPathNodeSet("./ip", ctxt, &ipNodes);
|
|
if (nipNodes < 0)
|
|
return -1;
|
|
if (ipNodes == NULL)
|
|
return 0;
|
|
|
|
def->ips = g_new0(virInterfaceIPDef *, nipNodes);
|
|
|
|
def->nips = 0;
|
|
for (i = 0; i < nipNodes; i++) {
|
|
virInterfaceIPDef *ip = g_new0(virInterfaceIPDef, 1);
|
|
|
|
if (virInterfaceDefParseIP(ip, ipNodes[i]) < 0) {
|
|
virInterfaceIPDefFree(ip);
|
|
return -1;
|
|
}
|
|
def->ips[def->nips++] = ip;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virInterfaceDefParseProtoIPv6(virInterfaceProtocolDef *def,
|
|
xmlXPathContextPtr ctxt)
|
|
{
|
|
xmlNodePtr dhcp;
|
|
g_autofree xmlNodePtr *ipNodes = NULL;
|
|
int nipNodes;
|
|
size_t i;
|
|
|
|
def->gateway = virXPathString("string(./route[1]/@gateway)", ctxt);
|
|
|
|
if (virXPathNode("./autoconf", ctxt) != NULL)
|
|
def->autoconf = 1;
|
|
|
|
dhcp = virXPathNode("./dhcp", ctxt);
|
|
if (dhcp != NULL) {
|
|
if (virInterfaceDefParseDhcp(def, dhcp) < 0)
|
|
return -1;
|
|
}
|
|
|
|
nipNodes = virXPathNodeSet("./ip", ctxt, &ipNodes);
|
|
if (nipNodes < 0)
|
|
return -1;
|
|
if (ipNodes == NULL)
|
|
return 0;
|
|
|
|
def->ips = g_new0(virInterfaceIPDef *, nipNodes);
|
|
|
|
def->nips = 0;
|
|
for (i = 0; i < nipNodes; i++) {
|
|
virInterfaceIPDef *ip = g_new0(virInterfaceIPDef, 1);
|
|
|
|
if (virInterfaceDefParseIP(ip, ipNodes[i]) < 0) {
|
|
virInterfaceIPDefFree(ip);
|
|
return -1;
|
|
}
|
|
def->ips[def->nips++] = ip;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virInterfaceDefParseIfAdressing(virInterfaceDef *def,
|
|
xmlXPathContextPtr ctxt)
|
|
{
|
|
VIR_XPATH_NODE_AUTORESTORE(ctxt)
|
|
g_autofree xmlNodePtr *protoNodes = NULL;
|
|
int nProtoNodes, pp;
|
|
|
|
nProtoNodes = virXPathNodeSet("./protocol", ctxt, &protoNodes);
|
|
if (nProtoNodes < 0)
|
|
return -1;
|
|
|
|
if (nProtoNodes == 0) {
|
|
/* no protocols is an acceptable outcome */
|
|
return 0;
|
|
}
|
|
|
|
def->protos = g_new0(virInterfaceProtocolDef *, nProtoNodes);
|
|
|
|
def->nprotos = 0;
|
|
for (pp = 0; pp < nProtoNodes; pp++) {
|
|
g_autoptr(virInterfaceProtocolDef) proto = g_new0(virInterfaceProtocolDef, 1);
|
|
|
|
if (!(proto->family = virXMLPropString(protoNodes[pp], "family"))) {
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
"%s", _("protocol misses the family attribute"));
|
|
return -1;
|
|
}
|
|
|
|
ctxt->node = protoNodes[pp];
|
|
if (STREQ(proto->family, "ipv4")) {
|
|
if (virInterfaceDefParseProtoIPv4(proto, ctxt) != 0)
|
|
return -1;
|
|
} else if (STREQ(proto->family, "ipv6")) {
|
|
if (virInterfaceDefParseProtoIPv6(proto, ctxt) != 0)
|
|
return -1;
|
|
} else {
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
_("unsupported protocol family '%s'"), proto->family);
|
|
return -1;
|
|
}
|
|
def->protos[def->nprotos++] = g_steal_pointer(&proto);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virInterfaceDefParseBridge(virInterfaceDef *def,
|
|
xmlXPathContextPtr ctxt)
|
|
{
|
|
VIR_XPATH_NODE_AUTORESTORE(ctxt)
|
|
g_autofree xmlNodePtr *interfaces = NULL;
|
|
virInterfaceDef *itf;
|
|
g_autofree char *tmp = NULL;
|
|
int nbItf;
|
|
size_t i;
|
|
|
|
def->data.bridge.stp = -1;
|
|
if ((tmp = virXMLPropString(ctxt->node, "stp"))) {
|
|
if (STREQ(tmp, "on")) {
|
|
def->data.bridge.stp = 1;
|
|
} else if (STREQ(tmp, "off")) {
|
|
def->data.bridge.stp = 0;
|
|
} else {
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
_("bridge interface stp should be on or off got %s"),
|
|
tmp);
|
|
return 0;
|
|
}
|
|
}
|
|
def->data.bridge.delay = virXMLPropString(ctxt->node, "delay");
|
|
|
|
nbItf = virXPathNodeSet("./interface", ctxt, &interfaces);
|
|
if (nbItf < 0) {
|
|
return -1;
|
|
}
|
|
if (nbItf > 0) {
|
|
def->data.bridge.itf = g_new0(struct _virInterfaceDef *, nbItf);
|
|
def->data.bridge.nbItf = nbItf;
|
|
|
|
for (i = 0; i < nbItf; i++) {
|
|
ctxt->node = interfaces[i];
|
|
itf = virInterfaceDefParseXML(ctxt, VIR_INTERFACE_TYPE_BRIDGE);
|
|
if (itf == NULL) {
|
|
def->data.bridge.nbItf = i;
|
|
return -1;
|
|
}
|
|
def->data.bridge.itf[i] = itf;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virInterfaceDefParseBondItfs(virInterfaceDef *def,
|
|
xmlXPathContextPtr ctxt)
|
|
{
|
|
VIR_XPATH_NODE_AUTORESTORE(ctxt)
|
|
g_autofree xmlNodePtr *interfaces = NULL;
|
|
virInterfaceDef *itf;
|
|
int nbItf;
|
|
size_t i;
|
|
|
|
nbItf = virXPathNodeSet("./interface", ctxt, &interfaces);
|
|
if (nbItf < 0)
|
|
return -1;
|
|
|
|
if (nbItf == 0) {
|
|
return 0;
|
|
}
|
|
|
|
def->data.bond.itf = g_new0(struct _virInterfaceDef *, nbItf);
|
|
|
|
def->data.bond.nbItf = nbItf;
|
|
|
|
for (i = 0; i < nbItf; i++) {
|
|
ctxt->node = interfaces[i];
|
|
itf = virInterfaceDefParseXML(ctxt, VIR_INTERFACE_TYPE_BOND);
|
|
if (itf == NULL) {
|
|
def->data.bond.nbItf = i;
|
|
return -1;
|
|
}
|
|
def->data.bond.itf[i] = itf;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virInterfaceDefParseBond(virInterfaceDef *def,
|
|
xmlXPathContextPtr ctxt)
|
|
{
|
|
int res;
|
|
|
|
def->data.bond.mode = virInterfaceDefParseBondMode(ctxt);
|
|
if (def->data.bond.mode < 0)
|
|
return -1;
|
|
|
|
if (virInterfaceDefParseBondItfs(def, ctxt) != 0)
|
|
return -1;
|
|
|
|
if (virXPathNode("./miimon[1]", ctxt) != NULL) {
|
|
def->data.bond.monit = VIR_INTERFACE_BOND_MONIT_MII;
|
|
|
|
res = virXPathInt("string(./miimon/@freq)", ctxt,
|
|
&def->data.bond.frequency);
|
|
if ((res == -2) || (res == -1)) {
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
"%s", _("bond interface miimon freq missing or invalid"));
|
|
return -1;
|
|
}
|
|
|
|
res = virXPathInt("string(./miimon/@downdelay)", ctxt,
|
|
&def->data.bond.downdelay);
|
|
if (res == -2) {
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
"%s", _("bond interface miimon downdelay invalid"));
|
|
return -1;
|
|
}
|
|
|
|
res = virXPathInt("string(./miimon/@updelay)", ctxt,
|
|
&def->data.bond.updelay);
|
|
if (res == -2) {
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
"%s", _("bond interface miimon updelay invalid"));
|
|
return -1;
|
|
}
|
|
|
|
def->data.bond.carrier = virInterfaceDefParseBondMiiCarrier(ctxt);
|
|
if (def->data.bond.carrier < 0)
|
|
return -1;
|
|
|
|
} else if (virXPathNode("./arpmon[1]", ctxt) != NULL) {
|
|
|
|
def->data.bond.monit = VIR_INTERFACE_BOND_MONIT_ARP;
|
|
|
|
res = virXPathInt("string(./arpmon/@interval)", ctxt,
|
|
&def->data.bond.interval);
|
|
if ((res == -2) || (res == -1)) {
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
"%s", _("bond interface arpmon interval missing or invalid"));
|
|
return -1;
|
|
}
|
|
|
|
def->data.bond.target =
|
|
virXPathString("string(./arpmon/@target)", ctxt);
|
|
if (def->data.bond.target == NULL) {
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
"%s", _("bond interface arpmon target missing"));
|
|
return -1;
|
|
}
|
|
|
|
def->data.bond.validate = virInterfaceDefParseBondArpValid(ctxt);
|
|
if (def->data.bond.validate < 0)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virInterfaceDefParseVlan(virInterfaceDef *def,
|
|
xmlXPathContextPtr ctxt)
|
|
{
|
|
def->data.vlan.tag = virXPathString("string(./@tag)", ctxt);
|
|
if (def->data.vlan.tag == NULL) {
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
"%s", _("vlan interface misses the tag attribute"));
|
|
return -1;
|
|
}
|
|
|
|
def->data.vlan.dev_name =
|
|
virXPathString("string(./interface/@name)", ctxt);
|
|
if (def->data.vlan.dev_name == NULL) {
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
"%s", _("vlan interface misses name attribute"));
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static virInterfaceDef *
|
|
virInterfaceDefParseXML(xmlXPathContextPtr ctxt,
|
|
int parentIfType)
|
|
{
|
|
VIR_XPATH_NODE_AUTORESTORE(ctxt)
|
|
g_autoptr(virInterfaceDef) def = NULL;
|
|
virInterfaceType type;
|
|
xmlNodePtr lnk;
|
|
|
|
if (virXMLPropEnum(ctxt->node, "type", virInterfaceTypeFromString,
|
|
VIR_XML_PROP_REQUIRED, &type) < 0)
|
|
return NULL;
|
|
|
|
def = g_new0(virInterfaceDef, 1);
|
|
|
|
if (((parentIfType == VIR_INTERFACE_TYPE_BOND)
|
|
&& (type != VIR_INTERFACE_TYPE_ETHERNET))
|
|
|| ((parentIfType == VIR_INTERFACE_TYPE_BRIDGE)
|
|
&& (type != VIR_INTERFACE_TYPE_ETHERNET)
|
|
&& (type != VIR_INTERFACE_TYPE_BOND)
|
|
&& (type != VIR_INTERFACE_TYPE_VLAN))
|
|
|| (parentIfType == VIR_INTERFACE_TYPE_ETHERNET)
|
|
|| (parentIfType == VIR_INTERFACE_TYPE_VLAN))
|
|
{
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
_("interface has unsupported type '%s'"),
|
|
virInterfaceTypeToString(type));
|
|
return NULL;
|
|
}
|
|
def->type = type;
|
|
|
|
if (!(def->name = virXMLPropString(ctxt->node, "name"))) {
|
|
virReportError(VIR_ERR_XML_ERROR, "%s", _("interface has no name"));
|
|
return NULL;
|
|
}
|
|
|
|
if (parentIfType == VIR_INTERFACE_TYPE_LAST) {
|
|
/* only recognize these in toplevel bond interfaces */
|
|
if (virInterfaceDefParseStartMode(def, ctxt) < 0)
|
|
return NULL;
|
|
if (virInterfaceDefParseMtu(def, ctxt) < 0)
|
|
return NULL;
|
|
if (virInterfaceDefParseIfAdressing(def, ctxt) < 0)
|
|
return NULL;
|
|
}
|
|
|
|
if (type != VIR_INTERFACE_TYPE_BRIDGE) {
|
|
/* link status makes no sense for a bridge */
|
|
lnk = virXPathNode("./link", ctxt);
|
|
if (lnk && virInterfaceLinkParseXML(lnk, &def->lnk) < 0)
|
|
return NULL;
|
|
}
|
|
|
|
switch (type) {
|
|
case VIR_INTERFACE_TYPE_ETHERNET: {
|
|
char *mac = virXPathString("string(./mac/@address)", ctxt);
|
|
if (mac != NULL)
|
|
def->mac = mac;
|
|
break;
|
|
}
|
|
case VIR_INTERFACE_TYPE_BRIDGE: {
|
|
xmlNodePtr bridge;
|
|
|
|
if (!(bridge = virXPathNode("./bridge[1]", ctxt))) {
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
"%s", _("bridge interface misses the bridge element"));
|
|
return NULL;
|
|
}
|
|
ctxt->node = bridge;
|
|
if (virInterfaceDefParseBridge(def, ctxt) < 0)
|
|
return NULL;
|
|
break;
|
|
}
|
|
case VIR_INTERFACE_TYPE_BOND: {
|
|
xmlNodePtr bond;
|
|
|
|
if (!(bond = virXPathNode("./bond[1]", ctxt))) {
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
"%s", _("bond interface misses the bond element"));
|
|
return NULL;
|
|
}
|
|
ctxt->node = bond;
|
|
if (virInterfaceDefParseBond(def, ctxt) < 0)
|
|
return NULL;
|
|
break;
|
|
}
|
|
case VIR_INTERFACE_TYPE_VLAN: {
|
|
xmlNodePtr vlan;
|
|
|
|
if (!(vlan = virXPathNode("./vlan[1]", ctxt))) {
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
"%s", _("vlan interface misses the vlan element"));
|
|
return NULL;
|
|
}
|
|
ctxt->node = vlan;
|
|
if (virInterfaceDefParseVlan(def, ctxt) < 0)
|
|
return NULL;
|
|
break;
|
|
}
|
|
case VIR_INTERFACE_TYPE_LAST:
|
|
return NULL;
|
|
}
|
|
|
|
return g_steal_pointer(&def);
|
|
}
|
|
|
|
|
|
virInterfaceDef *
|
|
virInterfaceDefParseNode(xmlDocPtr xml,
|
|
xmlNodePtr root)
|
|
{
|
|
g_autoptr(xmlXPathContext) ctxt = NULL;
|
|
|
|
if (!virXMLNodeNameEqual(root, "interface")) {
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
_("unexpected root element <%s>, "
|
|
"expecting <interface>"),
|
|
root->name);
|
|
return NULL;
|
|
}
|
|
|
|
if (!(ctxt = virXMLXPathContextNew(xml)))
|
|
return NULL;
|
|
|
|
ctxt->node = root;
|
|
return virInterfaceDefParseXML(ctxt, VIR_INTERFACE_TYPE_LAST);
|
|
}
|
|
|
|
|
|
static virInterfaceDef *
|
|
virInterfaceDefParse(const char *xmlStr,
|
|
const char *filename,
|
|
unsigned int flags)
|
|
{
|
|
g_autoptr(xmlDoc) xml = NULL;
|
|
bool validate = flags & VIR_INTERFACE_DEFINE_VALIDATE;
|
|
|
|
xml = virXMLParse(filename, xmlStr, _("(interface_definition)"),
|
|
NULL, NULL, "interface.rng", validate);
|
|
if (!xml)
|
|
return NULL;
|
|
|
|
return virInterfaceDefParseNode(xml, xmlDocGetRootElement(xml));
|
|
}
|
|
|
|
|
|
virInterfaceDef *
|
|
virInterfaceDefParseString(const char *xmlStr,
|
|
unsigned int flags)
|
|
{
|
|
return virInterfaceDefParse(xmlStr, NULL, flags);
|
|
}
|
|
|
|
|
|
virInterfaceDef *
|
|
virInterfaceDefParseFile(const char *filename)
|
|
{
|
|
return virInterfaceDefParse(NULL, filename, 0);
|
|
}
|
|
|
|
|
|
static int
|
|
virInterfaceBridgeDefFormat(virBuffer *buf,
|
|
const virInterfaceDef *def)
|
|
{
|
|
size_t i;
|
|
|
|
virBufferAddLit(buf, "<bridge");
|
|
if (def->data.bridge.stp == 1)
|
|
virBufferAddLit(buf, " stp='on'");
|
|
else if (def->data.bridge.stp == 0)
|
|
virBufferAddLit(buf, " stp='off'");
|
|
if (def->data.bridge.delay != NULL)
|
|
virBufferAsprintf(buf, " delay='%s'", def->data.bridge.delay);
|
|
virBufferAddLit(buf, ">\n");
|
|
virBufferAdjustIndent(buf, 2);
|
|
|
|
for (i = 0; i < def->data.bridge.nbItf; i++) {
|
|
if (virInterfaceDefDevFormat(buf, def->data.bridge.itf[i],
|
|
VIR_INTERFACE_TYPE_BRIDGE) < 0)
|
|
return -1;
|
|
}
|
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
virBufferAddLit(buf, "</bridge>\n");
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virInterfaceBondDefFormat(virBuffer *buf,
|
|
const virInterfaceDef *def)
|
|
{
|
|
size_t i;
|
|
|
|
virBufferAddLit(buf, "<bond");
|
|
if (def->data.bond.mode == VIR_INTERFACE_BOND_BALRR)
|
|
virBufferAddLit(buf, " mode='balance-rr'");
|
|
else if (def->data.bond.mode == VIR_INTERFACE_BOND_ABACKUP)
|
|
virBufferAddLit(buf, " mode='active-backup'");
|
|
else if (def->data.bond.mode == VIR_INTERFACE_BOND_BALXOR)
|
|
virBufferAddLit(buf, " mode='balance-xor'");
|
|
else if (def->data.bond.mode == VIR_INTERFACE_BOND_BCAST)
|
|
virBufferAddLit(buf, " mode='broadcast'");
|
|
else if (def->data.bond.mode == VIR_INTERFACE_BOND_8023AD)
|
|
virBufferAddLit(buf, " mode='802.3ad'");
|
|
else if (def->data.bond.mode == VIR_INTERFACE_BOND_BALTLB)
|
|
virBufferAddLit(buf, " mode='balance-tlb'");
|
|
else if (def->data.bond.mode == VIR_INTERFACE_BOND_BALALB)
|
|
virBufferAddLit(buf, " mode='balance-alb'");
|
|
virBufferAddLit(buf, ">\n");
|
|
virBufferAdjustIndent(buf, 2);
|
|
|
|
if (def->data.bond.monit == VIR_INTERFACE_BOND_MONIT_MII) {
|
|
virBufferAsprintf(buf, "<miimon freq='%d'",
|
|
def->data.bond.frequency);
|
|
if (def->data.bond.downdelay > 0)
|
|
virBufferAsprintf(buf, " downdelay='%d'", def->data.bond.downdelay);
|
|
if (def->data.bond.updelay > 0)
|
|
virBufferAsprintf(buf, " updelay='%d'", def->data.bond.updelay);
|
|
if (def->data.bond.carrier == VIR_INTERFACE_BOND_MII_IOCTL)
|
|
virBufferAddLit(buf, " carrier='ioctl'");
|
|
else if (def->data.bond.carrier == VIR_INTERFACE_BOND_MII_NETIF)
|
|
virBufferAddLit(buf, " carrier='netif'");
|
|
virBufferAddLit(buf, "/>\n");
|
|
} else if (def->data.bond.monit == VIR_INTERFACE_BOND_MONIT_ARP) {
|
|
if (def->data.bond.target == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("bond arp monitoring has no target"));
|
|
return -1;
|
|
}
|
|
virBufferAsprintf(buf, "<arpmon interval='%d' target='%s'",
|
|
def->data.bond.interval, def->data.bond.target);
|
|
if (def->data.bond.validate == VIR_INTERFACE_BOND_ARP_ACTIVE)
|
|
virBufferAddLit(buf, " validate='active'");
|
|
else if (def->data.bond.validate == VIR_INTERFACE_BOND_ARP_BACKUP)
|
|
virBufferAddLit(buf, " validate='backup'");
|
|
else if (def->data.bond.validate == VIR_INTERFACE_BOND_ARP_ALL)
|
|
virBufferAddLit(buf, " validate='all'");
|
|
virBufferAddLit(buf, "/>\n");
|
|
}
|
|
for (i = 0; i < def->data.bond.nbItf; i++) {
|
|
if (virInterfaceDefDevFormat(buf, def->data.bond.itf[i],
|
|
VIR_INTERFACE_TYPE_BOND) < 0)
|
|
return -1;
|
|
}
|
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
virBufferAddLit(buf, "</bond>\n");
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virInterfaceVlanDefFormat(virBuffer *buf,
|
|
const virInterfaceDef *def)
|
|
{
|
|
if (def->data.vlan.tag == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("vlan misses the tag name"));
|
|
return -1;
|
|
}
|
|
|
|
virBufferAsprintf(buf, "<vlan tag='%s'", def->data.vlan.tag);
|
|
if (def->data.vlan.dev_name != NULL) {
|
|
virBufferAddLit(buf, ">\n");
|
|
virBufferAdjustIndent(buf, 2);
|
|
virBufferAsprintf(buf, "<interface name='%s'/>\n",
|
|
def->data.vlan.dev_name);
|
|
virBufferAdjustIndent(buf, -2);
|
|
virBufferAddLit(buf, "</vlan>\n");
|
|
} else {
|
|
virBufferAddLit(buf, "/>\n");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virInterfaceProtocolDefFormat(virBuffer *buf,
|
|
const virInterfaceDef *def)
|
|
{
|
|
size_t i, j;
|
|
|
|
for (i = 0; i < def->nprotos; i++) {
|
|
|
|
virBufferAsprintf(buf, "<protocol family='%s'>\n",
|
|
def->protos[i]->family);
|
|
virBufferAdjustIndent(buf, 2);
|
|
|
|
if (def->protos[i]->autoconf)
|
|
virBufferAddLit(buf, "<autoconf/>\n");
|
|
if (def->protos[i]->dhcp) {
|
|
if (def->protos[i]->peerdns == 0)
|
|
virBufferAddLit(buf, "<dhcp peerdns='no'/>\n");
|
|
else if (def->protos[i]->peerdns == 1)
|
|
virBufferAddLit(buf, "<dhcp peerdns='yes'/>\n");
|
|
else
|
|
virBufferAddLit(buf, "<dhcp/>\n");
|
|
}
|
|
|
|
for (j = 0; j < def->protos[i]->nips; j++) {
|
|
if (def->protos[i]->ips[j]->address != NULL) {
|
|
|
|
virBufferAsprintf(buf, "<ip address='%s'",
|
|
def->protos[i]->ips[j]->address);
|
|
if (def->protos[i]->ips[j]->prefix != 0) {
|
|
virBufferAsprintf(buf, " prefix='%d'",
|
|
def->protos[i]->ips[j]->prefix);
|
|
}
|
|
virBufferAddLit(buf, "/>\n");
|
|
}
|
|
}
|
|
if (def->protos[i]->gateway != NULL) {
|
|
virBufferAsprintf(buf, "<route gateway='%s'/>\n",
|
|
def->protos[i]->gateway);
|
|
}
|
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
virBufferAddLit(buf, "</protocol>\n");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virInterfaceStartmodeDefFormat(virBuffer *buf,
|
|
virInterfaceStartMode startmode)
|
|
{
|
|
const char *mode;
|
|
switch (startmode) {
|
|
case VIR_INTERFACE_START_UNSPECIFIED:
|
|
return 0;
|
|
case VIR_INTERFACE_START_NONE:
|
|
mode = "none";
|
|
break;
|
|
case VIR_INTERFACE_START_ONBOOT:
|
|
mode = "onboot";
|
|
break;
|
|
case VIR_INTERFACE_START_HOTPLUG:
|
|
mode = "hotplug";
|
|
break;
|
|
default:
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("virInterfaceDefFormat unknown startmode"));
|
|
return -1;
|
|
}
|
|
virBufferAsprintf(buf, "<start mode='%s'/>\n", mode);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virInterfaceDefDevFormat(virBuffer *buf,
|
|
const virInterfaceDef *def,
|
|
virInterfaceType parentIfType)
|
|
{
|
|
const char *type = NULL;
|
|
|
|
if (def == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("virInterfaceDefFormat NULL def"));
|
|
return -1;
|
|
}
|
|
|
|
if ((def->name == NULL) && (def->type != VIR_INTERFACE_TYPE_VLAN)) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("virInterfaceDefFormat missing interface name"));
|
|
return -1;
|
|
}
|
|
|
|
if (!(type = virInterfaceTypeToString(def->type))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("unexpected interface type %d"), def->type);
|
|
return -1;
|
|
}
|
|
|
|
virBufferAsprintf(buf, "<interface type='%s' ", type);
|
|
if (def->name != NULL)
|
|
virBufferEscapeString(buf, "name='%s'", def->name);
|
|
virBufferAddLit(buf, ">\n");
|
|
virBufferAdjustIndent(buf, 2);
|
|
|
|
if (parentIfType == VIR_INTERFACE_TYPE_LAST) {
|
|
/* these elements are only valid on top-level interfaces - IP
|
|
* address info ("protocol") only makes sense for the
|
|
* top-level, and subordinate interfaces inherit the toplevel
|
|
* setting for mtu and start mode, which cannot be overridden.
|
|
*/
|
|
virInterfaceStartmodeDefFormat(buf, def->startmode);
|
|
if (def->mtu)
|
|
virBufferAsprintf(buf, "<mtu size='%d'/>\n", def->mtu);
|
|
virInterfaceProtocolDefFormat(buf, def);
|
|
}
|
|
|
|
if (def->type != VIR_INTERFACE_TYPE_BRIDGE)
|
|
virInterfaceLinkFormat(buf, &def->lnk);
|
|
switch (def->type) {
|
|
case VIR_INTERFACE_TYPE_ETHERNET:
|
|
if (def->mac)
|
|
virBufferAsprintf(buf, "<mac address='%s'/>\n", def->mac);
|
|
break;
|
|
case VIR_INTERFACE_TYPE_BRIDGE:
|
|
if (virInterfaceBridgeDefFormat(buf, def) < 0)
|
|
return -1;
|
|
break;
|
|
case VIR_INTERFACE_TYPE_BOND:
|
|
if (virInterfaceBondDefFormat(buf, def) < 0)
|
|
return -1;
|
|
break;
|
|
case VIR_INTERFACE_TYPE_VLAN:
|
|
if (virInterfaceVlanDefFormat(buf, def) < 0)
|
|
return -1;
|
|
break;
|
|
}
|
|
|
|
virBufferAdjustIndent(buf, -2);
|
|
virBufferAddLit(buf, "</interface>\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
char *
|
|
virInterfaceDefFormat(const virInterfaceDef *def)
|
|
{
|
|
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
|
|
|
if (virInterfaceDefDevFormat(&buf, def, VIR_INTERFACE_TYPE_LAST) < 0)
|
|
return NULL;
|
|
|
|
return virBufferContentAndReset(&buf);
|
|
}
|