mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-11-02 11:21:12 +00:00
52d064f42d
The above option helps to differentiate between implicit and explicit interface pools.
1922 lines
58 KiB
C
1922 lines
58 KiB
C
/*
|
|
* network_conf.c: network XML handling
|
|
*
|
|
* Copyright (C) 2006-2012 Red Hat, Inc.
|
|
* Copyright (C) 2006-2008 Daniel P. Berrange
|
|
*
|
|
* 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, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
* Author: Daniel P. Berrange <berrange@redhat.com>
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <unistd.h>
|
|
#include <arpa/inet.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <dirent.h>
|
|
|
|
#include "virterror_internal.h"
|
|
#include "datatypes.h"
|
|
#include "network_conf.h"
|
|
#include "netdev_vport_profile_conf.h"
|
|
#include "netdev_bandwidth_conf.h"
|
|
#include "memory.h"
|
|
#include "xml.h"
|
|
#include "uuid.h"
|
|
#include "util.h"
|
|
#include "buf.h"
|
|
#include "c-ctype.h"
|
|
#include "virfile.h"
|
|
|
|
#define MAX_BRIDGE_ID 256
|
|
#define VIR_FROM_THIS VIR_FROM_NETWORK
|
|
|
|
VIR_ENUM_DECL(virNetworkForward)
|
|
|
|
VIR_ENUM_IMPL(virNetworkForward,
|
|
VIR_NETWORK_FORWARD_LAST,
|
|
"none", "nat", "route", "bridge", "private", "vepa", "passthrough" )
|
|
|
|
#define virNetworkReportError(code, ...) \
|
|
virReportErrorHelper(VIR_FROM_NETWORK, code, __FILE__, \
|
|
__FUNCTION__, __LINE__, __VA_ARGS__)
|
|
|
|
virNetworkObjPtr virNetworkFindByUUID(const virNetworkObjListPtr nets,
|
|
const unsigned char *uuid)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0 ; i < nets->count ; i++) {
|
|
virNetworkObjLock(nets->objs[i]);
|
|
if (!memcmp(nets->objs[i]->def->uuid, uuid, VIR_UUID_BUFLEN))
|
|
return nets->objs[i];
|
|
virNetworkObjUnlock(nets->objs[i]);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
virNetworkObjPtr virNetworkFindByName(const virNetworkObjListPtr nets,
|
|
const char *name)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0 ; i < nets->count ; i++) {
|
|
virNetworkObjLock(nets->objs[i]);
|
|
if (STREQ(nets->objs[i]->def->name, name))
|
|
return nets->objs[i];
|
|
virNetworkObjUnlock(nets->objs[i]);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static void
|
|
virPortGroupDefClear(virPortGroupDefPtr def)
|
|
{
|
|
VIR_FREE(def->name);
|
|
VIR_FREE(def->virtPortProfile);
|
|
virNetDevBandwidthFree(def->bandwidth);
|
|
def->bandwidth = NULL;
|
|
}
|
|
|
|
static void
|
|
virNetworkForwardIfDefClear(virNetworkForwardIfDefPtr def)
|
|
{
|
|
VIR_FREE(def->dev);
|
|
}
|
|
|
|
static void virNetworkIpDefClear(virNetworkIpDefPtr def)
|
|
{
|
|
int ii;
|
|
|
|
VIR_FREE(def->family);
|
|
VIR_FREE(def->ranges);
|
|
|
|
for (ii = 0 ; ii < def->nhosts && def->hosts ; ii++) {
|
|
VIR_FREE(def->hosts[ii].mac);
|
|
VIR_FREE(def->hosts[ii].name);
|
|
}
|
|
|
|
VIR_FREE(def->hosts);
|
|
VIR_FREE(def->tftproot);
|
|
VIR_FREE(def->bootfile);
|
|
}
|
|
|
|
static void virNetworkDNSDefFree(virNetworkDNSDefPtr def)
|
|
{
|
|
if (def) {
|
|
if (def->txtrecords) {
|
|
while (def->ntxtrecords--) {
|
|
VIR_FREE(def->txtrecords[def->ntxtrecords].name);
|
|
VIR_FREE(def->txtrecords[def->ntxtrecords].value);
|
|
}
|
|
}
|
|
VIR_FREE(def->txtrecords);
|
|
if (def->nhosts) {
|
|
while (def->nhosts--) {
|
|
while (def->hosts[def->nhosts].nnames--)
|
|
VIR_FREE(def->hosts[def->nhosts].names[def->hosts[def->nhosts].nnames]);
|
|
VIR_FREE(def->hosts[def->nhosts].names);
|
|
}
|
|
}
|
|
VIR_FREE(def->hosts);
|
|
if (def->nsrvrecords) {
|
|
while (def->nsrvrecords--) {
|
|
VIR_FREE(def->srvrecords[def->nsrvrecords].domain);
|
|
VIR_FREE(def->srvrecords[def->nsrvrecords].service);
|
|
VIR_FREE(def->srvrecords[def->nsrvrecords].protocol);
|
|
VIR_FREE(def->srvrecords[def->nsrvrecords].target);
|
|
}
|
|
}
|
|
VIR_FREE(def->srvrecords);
|
|
VIR_FREE(def);
|
|
}
|
|
}
|
|
|
|
void virNetworkDefFree(virNetworkDefPtr def)
|
|
{
|
|
int ii;
|
|
|
|
if (!def)
|
|
return;
|
|
|
|
VIR_FREE(def->name);
|
|
VIR_FREE(def->bridge);
|
|
VIR_FREE(def->domain);
|
|
|
|
for (ii = 0 ; ii < def->nForwardPfs && def->forwardPfs ; ii++) {
|
|
virNetworkForwardIfDefClear(&def->forwardPfs[ii]);
|
|
}
|
|
VIR_FREE(def->forwardPfs);
|
|
|
|
for (ii = 0 ; ii < def->nForwardIfs && def->forwardIfs ; ii++) {
|
|
virNetworkForwardIfDefClear(&def->forwardIfs[ii]);
|
|
}
|
|
VIR_FREE(def->forwardIfs);
|
|
|
|
for (ii = 0 ; ii < def->nips && def->ips ; ii++) {
|
|
virNetworkIpDefClear(&def->ips[ii]);
|
|
}
|
|
VIR_FREE(def->ips);
|
|
|
|
for (ii = 0; ii < def->nPortGroups && def->portGroups; ii++) {
|
|
virPortGroupDefClear(&def->portGroups[ii]);
|
|
}
|
|
VIR_FREE(def->portGroups);
|
|
|
|
virNetworkDNSDefFree(def->dns);
|
|
|
|
VIR_FREE(def->virtPortProfile);
|
|
|
|
virNetDevBandwidthFree(def->bandwidth);
|
|
|
|
VIR_FREE(def);
|
|
}
|
|
|
|
void virNetworkObjFree(virNetworkObjPtr net)
|
|
{
|
|
if (!net)
|
|
return;
|
|
|
|
virNetworkDefFree(net->def);
|
|
virNetworkDefFree(net->newDef);
|
|
|
|
virMutexDestroy(&net->lock);
|
|
|
|
VIR_FREE(net);
|
|
}
|
|
|
|
void virNetworkObjListFree(virNetworkObjListPtr nets)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0 ; i < nets->count ; i++)
|
|
virNetworkObjFree(nets->objs[i]);
|
|
|
|
VIR_FREE(nets->objs);
|
|
nets->count = 0;
|
|
}
|
|
|
|
virNetworkObjPtr virNetworkAssignDef(virNetworkObjListPtr nets,
|
|
const virNetworkDefPtr def)
|
|
{
|
|
virNetworkObjPtr network;
|
|
|
|
if ((network = virNetworkFindByName(nets, def->name))) {
|
|
if (!virNetworkObjIsActive(network)) {
|
|
virNetworkDefFree(network->def);
|
|
network->def = def;
|
|
} else {
|
|
virNetworkDefFree(network->newDef);
|
|
network->newDef = def;
|
|
}
|
|
|
|
return network;
|
|
}
|
|
|
|
if (VIR_ALLOC(network) < 0) {
|
|
virReportOOMError();
|
|
return NULL;
|
|
}
|
|
if (virMutexInit(&network->lock) < 0) {
|
|
virNetworkReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("cannot initialize mutex"));
|
|
VIR_FREE(network);
|
|
return NULL;
|
|
}
|
|
virNetworkObjLock(network);
|
|
network->def = def;
|
|
|
|
if (VIR_REALLOC_N(nets->objs, nets->count + 1) < 0) {
|
|
virReportOOMError();
|
|
VIR_FREE(network);
|
|
return NULL;
|
|
}
|
|
|
|
nets->objs[nets->count] = network;
|
|
nets->count++;
|
|
|
|
return network;
|
|
|
|
}
|
|
|
|
void virNetworkRemoveInactive(virNetworkObjListPtr nets,
|
|
const virNetworkObjPtr net)
|
|
{
|
|
unsigned int i;
|
|
|
|
virNetworkObjUnlock(net);
|
|
for (i = 0 ; i < nets->count ; i++) {
|
|
virNetworkObjLock(nets->objs[i]);
|
|
if (nets->objs[i] == net) {
|
|
virNetworkObjUnlock(nets->objs[i]);
|
|
virNetworkObjFree(nets->objs[i]);
|
|
|
|
if (i < (nets->count - 1))
|
|
memmove(nets->objs + i, nets->objs + i + 1,
|
|
sizeof(*(nets->objs)) * (nets->count - (i + 1)));
|
|
|
|
if (VIR_REALLOC_N(nets->objs, nets->count - 1) < 0) {
|
|
; /* Failure to reduce memory allocation isn't fatal */
|
|
}
|
|
nets->count--;
|
|
|
|
break;
|
|
}
|
|
virNetworkObjUnlock(nets->objs[i]);
|
|
}
|
|
}
|
|
|
|
/* return ips[index], or NULL if there aren't enough ips */
|
|
virNetworkIpDefPtr
|
|
virNetworkDefGetIpByIndex(const virNetworkDefPtr def,
|
|
int family, size_t n)
|
|
{
|
|
int ii;
|
|
|
|
if (!def->ips || n >= def->nips)
|
|
return NULL;
|
|
|
|
if (family == AF_UNSPEC) {
|
|
return &def->ips[n];
|
|
}
|
|
|
|
/* find the nth ip of type "family" */
|
|
for (ii = 0; ii < def->nips; ii++) {
|
|
if (VIR_SOCKET_ADDR_IS_FAMILY(&def->ips[ii].address, family)
|
|
&& (n-- <= 0)) {
|
|
return &def->ips[ii];
|
|
}
|
|
}
|
|
/* failed to find enough of the right family */
|
|
return NULL;
|
|
}
|
|
|
|
/* return number of 1 bits in netmask for the network's ipAddress,
|
|
* or -1 on error
|
|
*/
|
|
int virNetworkIpDefPrefix(const virNetworkIpDefPtr def)
|
|
{
|
|
if (def->prefix > 0) {
|
|
return def->prefix;
|
|
} else if (VIR_SOCKET_ADDR_VALID(&def->netmask)) {
|
|
return virSocketAddrGetNumNetmaskBits(&def->netmask);
|
|
} else if (VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET)) {
|
|
/* Return the natural prefix for the network's ip address.
|
|
* On Linux we could use the IN_CLASSx() macros, but those
|
|
* aren't guaranteed on all platforms, so we just deal with
|
|
* the bits ourselves.
|
|
*/
|
|
unsigned char octet
|
|
= ntohl(def->address.data.inet4.sin_addr.s_addr) >> 24;
|
|
if ((octet & 0x80) == 0) {
|
|
/* Class A network */
|
|
return 8;
|
|
} else if ((octet & 0xC0) == 0x80) {
|
|
/* Class B network */
|
|
return 16;
|
|
} else if ((octet & 0xE0) == 0xC0) {
|
|
/* Class C network */
|
|
return 24;
|
|
}
|
|
return -1;
|
|
} else if (VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET6)) {
|
|
return 64;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* Fill in a virSocketAddr with the proper netmask for this
|
|
* definition, based on either the definition's netmask, or its
|
|
* prefix. Return -1 on error (and set the netmask family to AF_UNSPEC)
|
|
*/
|
|
int virNetworkIpDefNetmask(const virNetworkIpDefPtr def,
|
|
virSocketAddrPtr netmask)
|
|
{
|
|
if (VIR_SOCKET_ADDR_IS_FAMILY(&def->netmask, AF_INET)) {
|
|
*netmask = def->netmask;
|
|
return 0;
|
|
}
|
|
|
|
return virSocketAddrPrefixToNetmask(virNetworkIpDefPrefix(def), netmask,
|
|
VIR_SOCKET_ADDR_FAMILY(&def->address));
|
|
}
|
|
|
|
|
|
static int
|
|
virNetworkDHCPRangeDefParseXML(const char *networkName,
|
|
virNetworkIpDefPtr def,
|
|
xmlNodePtr node)
|
|
{
|
|
|
|
xmlNodePtr cur;
|
|
|
|
cur = node->children;
|
|
while (cur != NULL) {
|
|
if (cur->type == XML_ELEMENT_NODE &&
|
|
xmlStrEqual(cur->name, BAD_CAST "range")) {
|
|
char *start, *end;
|
|
virSocketAddr saddr, eaddr;
|
|
int range;
|
|
|
|
if (!(start = virXMLPropString(cur, "start"))) {
|
|
cur = cur->next;
|
|
continue;
|
|
}
|
|
if (!(end = virXMLPropString(cur, "end"))) {
|
|
VIR_FREE(start);
|
|
cur = cur->next;
|
|
continue;
|
|
}
|
|
|
|
if (virSocketAddrParse(&saddr, start, AF_UNSPEC) < 0) {
|
|
VIR_FREE(start);
|
|
VIR_FREE(end);
|
|
return -1;
|
|
}
|
|
if (virSocketAddrParse(&eaddr, end, AF_UNSPEC) < 0) {
|
|
VIR_FREE(start);
|
|
VIR_FREE(end);
|
|
return -1;
|
|
}
|
|
|
|
range = virSocketAddrGetRange(&saddr, &eaddr);
|
|
if (range < 0) {
|
|
virNetworkReportError(VIR_ERR_XML_ERROR,
|
|
_("Invalid dhcp range '%s' to '%s' in network '%s'"),
|
|
start, end, networkName);
|
|
VIR_FREE(start);
|
|
VIR_FREE(end);
|
|
return -1;
|
|
}
|
|
VIR_FREE(start);
|
|
VIR_FREE(end);
|
|
|
|
if (VIR_REALLOC_N(def->ranges, def->nranges + 1) < 0) {
|
|
virReportOOMError();
|
|
return -1;
|
|
}
|
|
def->ranges[def->nranges].start = saddr;
|
|
def->ranges[def->nranges].end = eaddr;
|
|
def->nranges++;
|
|
} else if (cur->type == XML_ELEMENT_NODE &&
|
|
xmlStrEqual(cur->name, BAD_CAST "host")) {
|
|
char *mac, *name, *ip;
|
|
unsigned char addr[6];
|
|
virSocketAddr inaddr;
|
|
|
|
mac = virXMLPropString(cur, "mac");
|
|
if ((mac != NULL) &&
|
|
(virParseMacAddr(mac, &addr[0]) != 0)) {
|
|
virNetworkReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Cannot parse MAC address '%s' in network '%s'"),
|
|
mac, networkName);
|
|
VIR_FREE(mac);
|
|
}
|
|
name = virXMLPropString(cur, "name");
|
|
if ((name != NULL) && (!c_isalpha(name[0]))) {
|
|
virNetworkReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Cannot use name address '%s' in network '%s'"),
|
|
name, networkName);
|
|
VIR_FREE(name);
|
|
}
|
|
/*
|
|
* You need at least one MAC address or one host name
|
|
*/
|
|
if ((mac == NULL) && (name == NULL)) {
|
|
virNetworkReportError(VIR_ERR_XML_ERROR,
|
|
_("Static host definition in network '%s' must have mac or name attribute"),
|
|
networkName);
|
|
return -1;
|
|
}
|
|
ip = virXMLPropString(cur, "ip");
|
|
if ((ip == NULL) ||
|
|
(virSocketAddrParse(&inaddr, ip, AF_UNSPEC) < 0)) {
|
|
virNetworkReportError(VIR_ERR_XML_ERROR,
|
|
_("Missing IP address in static host definition for network '%s'"),
|
|
networkName);
|
|
VIR_FREE(ip);
|
|
VIR_FREE(mac);
|
|
VIR_FREE(name);
|
|
return -1;
|
|
}
|
|
VIR_FREE(ip);
|
|
if (VIR_REALLOC_N(def->hosts, def->nhosts + 1) < 0) {
|
|
VIR_FREE(mac);
|
|
VIR_FREE(name);
|
|
virReportOOMError();
|
|
return -1;
|
|
}
|
|
def->hosts[def->nhosts].mac = mac;
|
|
def->hosts[def->nhosts].name = name;
|
|
def->hosts[def->nhosts].ip = inaddr;
|
|
def->nhosts++;
|
|
|
|
} else if (cur->type == XML_ELEMENT_NODE &&
|
|
xmlStrEqual(cur->name, BAD_CAST "bootp")) {
|
|
char *file;
|
|
char *server;
|
|
virSocketAddr inaddr;
|
|
memset(&inaddr, 0, sizeof(inaddr));
|
|
|
|
if (!(file = virXMLPropString(cur, "file"))) {
|
|
cur = cur->next;
|
|
continue;
|
|
}
|
|
server = virXMLPropString(cur, "server");
|
|
|
|
if (server &&
|
|
virSocketAddrParse(&inaddr, server, AF_UNSPEC) < 0) {
|
|
VIR_FREE(file);
|
|
VIR_FREE(server);
|
|
return -1;
|
|
}
|
|
|
|
def->bootfile = file;
|
|
def->bootserver = inaddr;
|
|
VIR_FREE(server);
|
|
}
|
|
|
|
cur = cur->next;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
virNetworkDNSHostsDefParseXML(virNetworkDNSDefPtr def,
|
|
xmlNodePtr node)
|
|
{
|
|
xmlNodePtr cur;
|
|
char *ip;
|
|
virSocketAddr inaddr;
|
|
int ret = -1;
|
|
|
|
if (def->hosts == NULL) {
|
|
if (VIR_ALLOC(def->hosts) < 0) {
|
|
virReportOOMError();
|
|
goto error;
|
|
}
|
|
def->nhosts = 0;
|
|
}
|
|
|
|
if (!(ip = virXMLPropString(node, "ip")) ||
|
|
(virSocketAddrParse(&inaddr, ip, AF_UNSPEC) < 0)) {
|
|
virNetworkReportError(VIR_ERR_XML_DETAIL,
|
|
_("Missing IP address in DNS host definition"));
|
|
VIR_FREE(ip);
|
|
goto error;
|
|
}
|
|
VIR_FREE(ip);
|
|
|
|
if (VIR_REALLOC_N(def->hosts, def->nhosts + 1) < 0) {
|
|
virReportOOMError();
|
|
goto error;
|
|
}
|
|
|
|
def->hosts[def->nhosts].ip = inaddr;
|
|
def->hosts[def->nhosts].nnames = 0;
|
|
|
|
if (VIR_ALLOC(def->hosts[def->nhosts].names) < 0) {
|
|
virReportOOMError();
|
|
goto error;
|
|
}
|
|
|
|
cur = node->children;
|
|
while (cur != NULL) {
|
|
if (cur->type == XML_ELEMENT_NODE &&
|
|
xmlStrEqual(cur->name, BAD_CAST "hostname")) {
|
|
if (cur->children != NULL) {
|
|
if (VIR_REALLOC_N(def->hosts[def->nhosts].names, def->hosts[def->nhosts].nnames + 1) < 0) {
|
|
virReportOOMError();
|
|
goto error;
|
|
}
|
|
|
|
def->hosts[def->nhosts].names[def->hosts[def->nhosts].nnames] = strdup((char *)cur->children->content);
|
|
def->hosts[def->nhosts].nnames++;
|
|
}
|
|
}
|
|
|
|
cur = cur->next;
|
|
}
|
|
|
|
def->nhosts++;
|
|
|
|
ret = 0;
|
|
|
|
error:
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
virNetworkDNSSrvDefParseXML(virNetworkDNSDefPtr def,
|
|
xmlNodePtr cur,
|
|
xmlXPathContextPtr ctxt)
|
|
{
|
|
char *domain;
|
|
char *service;
|
|
char *protocol;
|
|
char *target;
|
|
int port;
|
|
int priority;
|
|
int weight;
|
|
int ret = 0;
|
|
|
|
if (!(service = virXMLPropString(cur, "service"))) {
|
|
virNetworkReportError(VIR_ERR_XML_DETAIL,
|
|
"%s", _("Missing required service attribute in dns srv record"));
|
|
goto error;
|
|
}
|
|
|
|
if (strlen(service) > DNS_RECORD_LENGTH_SRV) {
|
|
char *name = NULL;
|
|
|
|
virAsprintf(&name, _("Service name is too long, limit is %d bytes"), DNS_RECORD_LENGTH_SRV);
|
|
virNetworkReportError(VIR_ERR_XML_DETAIL,
|
|
"%s", name);
|
|
free(name);
|
|
goto error;
|
|
}
|
|
|
|
if (!(protocol = virXMLPropString(cur, "protocol"))) {
|
|
virNetworkReportError(VIR_ERR_XML_DETAIL,
|
|
_("Missing required protocol attribute in dns srv record '%s'"), service);
|
|
goto error;
|
|
}
|
|
|
|
/* Check whether protocol value is the supported one */
|
|
if (STRNEQ(protocol, "tcp") && (STRNEQ(protocol, "udp"))) {
|
|
virNetworkReportError(VIR_ERR_XML_DETAIL,
|
|
_("Invalid protocol attribute value '%s'"), protocol);
|
|
goto error;
|
|
}
|
|
|
|
if (VIR_REALLOC_N(def->srvrecords, def->nsrvrecords + 1) < 0) {
|
|
virReportOOMError();
|
|
goto error;
|
|
}
|
|
|
|
def->srvrecords[def->nsrvrecords].service = service;
|
|
def->srvrecords[def->nsrvrecords].protocol = protocol;
|
|
def->srvrecords[def->nsrvrecords].domain = NULL;
|
|
def->srvrecords[def->nsrvrecords].target = NULL;
|
|
def->srvrecords[def->nsrvrecords].port = 0;
|
|
def->srvrecords[def->nsrvrecords].priority = 0;
|
|
def->srvrecords[def->nsrvrecords].weight = 0;
|
|
|
|
/* Following attributes are optional but we had to make sure they're NULL above */
|
|
if ((target = virXMLPropString(cur, "target")) && (domain = virXMLPropString(cur, "domain"))) {
|
|
xmlNodePtr save_ctxt = ctxt->node;
|
|
|
|
ctxt->node = cur;
|
|
if (virXPathInt("string(./@port)", ctxt, &port))
|
|
def->srvrecords[def->nsrvrecords].port = port;
|
|
|
|
if (virXPathInt("string(./@priority)", ctxt, &priority))
|
|
def->srvrecords[def->nsrvrecords].priority = priority;
|
|
|
|
if (virXPathInt("string(./@weight)", ctxt, &weight))
|
|
def->srvrecords[def->nsrvrecords].weight = weight;
|
|
ctxt->node = save_ctxt;
|
|
|
|
def->srvrecords[def->nsrvrecords].domain = domain;
|
|
def->srvrecords[def->nsrvrecords].target = target;
|
|
def->srvrecords[def->nsrvrecords].port = port;
|
|
def->srvrecords[def->nsrvrecords].priority = priority;
|
|
def->srvrecords[def->nsrvrecords].weight = weight;
|
|
}
|
|
|
|
def->nsrvrecords++;
|
|
|
|
goto cleanup;
|
|
|
|
error:
|
|
VIR_FREE(domain);
|
|
VIR_FREE(service);
|
|
VIR_FREE(protocol);
|
|
VIR_FREE(target);
|
|
|
|
ret = -1;
|
|
|
|
cleanup:
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
virNetworkDNSDefParseXML(virNetworkDNSDefPtr *dnsdef,
|
|
xmlNodePtr node,
|
|
xmlXPathContextPtr ctxt)
|
|
{
|
|
xmlNodePtr cur;
|
|
int ret = -1;
|
|
char *name = NULL;
|
|
char *value = NULL;
|
|
virNetworkDNSDefPtr def = NULL;
|
|
|
|
if (VIR_ALLOC(def) < 0) {
|
|
virReportOOMError();
|
|
goto error;
|
|
}
|
|
|
|
cur = node->children;
|
|
while (cur != NULL) {
|
|
if (cur->type == XML_ELEMENT_NODE &&
|
|
xmlStrEqual(cur->name, BAD_CAST "txt")) {
|
|
if (!(name = virXMLPropString(cur, "name"))) {
|
|
virNetworkReportError(VIR_ERR_XML_DETAIL,
|
|
"%s", _("Missing required name attribute in dns txt record"));
|
|
goto error;
|
|
}
|
|
if (!(value = virXMLPropString(cur, "value"))) {
|
|
virNetworkReportError(VIR_ERR_XML_DETAIL,
|
|
_("Missing required value attribute in dns txt record '%s'"), name);
|
|
goto error;
|
|
}
|
|
|
|
if (strchr(name, ' ') != NULL) {
|
|
virNetworkReportError(VIR_ERR_XML_DETAIL,
|
|
_("spaces are not allowed in DNS TXT record names (name is '%s')"), name);
|
|
goto error;
|
|
}
|
|
|
|
if (VIR_REALLOC_N(def->txtrecords, def->ntxtrecords + 1) < 0) {
|
|
virReportOOMError();
|
|
goto error;
|
|
}
|
|
|
|
def->txtrecords[def->ntxtrecords].name = name;
|
|
def->txtrecords[def->ntxtrecords].value = value;
|
|
def->ntxtrecords++;
|
|
name = NULL;
|
|
value = NULL;
|
|
} else if (cur->type == XML_ELEMENT_NODE &&
|
|
xmlStrEqual(cur->name, BAD_CAST "srv")) {
|
|
ret = virNetworkDNSSrvDefParseXML(def, cur, ctxt);
|
|
if (ret < 0)
|
|
goto error;
|
|
} else if (cur->type == XML_ELEMENT_NODE &&
|
|
xmlStrEqual(cur->name, BAD_CAST "host")) {
|
|
ret = virNetworkDNSHostsDefParseXML(def, cur);
|
|
if (ret < 0)
|
|
goto error;
|
|
}
|
|
|
|
cur = cur->next;
|
|
}
|
|
|
|
ret = 0;
|
|
error:
|
|
if (ret < 0) {
|
|
VIR_FREE(name);
|
|
VIR_FREE(value);
|
|
virNetworkDNSDefFree(def);
|
|
} else {
|
|
*dnsdef = def;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
virNetworkIPParseXML(const char *networkName,
|
|
virNetworkIpDefPtr def,
|
|
xmlNodePtr node,
|
|
xmlXPathContextPtr ctxt)
|
|
{
|
|
/*
|
|
* virNetworkIpDef object is already allocated as part of an array.
|
|
* On failure clear it out, but don't free it.
|
|
*/
|
|
|
|
xmlNodePtr cur, save;
|
|
char *address = NULL, *netmask = NULL;
|
|
unsigned long prefix;
|
|
int result = -1;
|
|
|
|
save = ctxt->node;
|
|
ctxt->node = node;
|
|
|
|
/* grab raw data from XML */
|
|
def->family = virXPathString("string(./@family)", ctxt);
|
|
address = virXPathString("string(./@address)", ctxt);
|
|
if (virXPathULong("string(./@prefix)", ctxt, &prefix) < 0)
|
|
def->prefix = 0;
|
|
else
|
|
def->prefix = prefix;
|
|
|
|
netmask = virXPathString("string(./@netmask)", ctxt);
|
|
|
|
if (address) {
|
|
if (virSocketAddrParse(&def->address, address, AF_UNSPEC) < 0) {
|
|
virNetworkReportError(VIR_ERR_XML_ERROR,
|
|
_("Bad address '%s' in definition of network '%s'"),
|
|
address, networkName);
|
|
goto error;
|
|
}
|
|
|
|
}
|
|
|
|
/* validate family vs. address */
|
|
if (def->family == NULL) {
|
|
if (!(VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET) ||
|
|
VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_UNSPEC))) {
|
|
virNetworkReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("no family specified for non-IPv4 address address '%s' in network '%s'"),
|
|
address, networkName);
|
|
goto error;
|
|
}
|
|
} else if (STREQ(def->family, "ipv4")) {
|
|
if (!VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET)) {
|
|
virNetworkReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("family 'ipv4' specified for non-IPv4 address '%s' in network '%s'"),
|
|
address, networkName);
|
|
goto error;
|
|
}
|
|
} else if (STREQ(def->family, "ipv6")) {
|
|
if (!VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET6)) {
|
|
virNetworkReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("family 'ipv6' specified for non-IPv6 address '%s' in network '%s'"),
|
|
address, networkName);
|
|
goto error;
|
|
}
|
|
} else {
|
|
virNetworkReportError(VIR_ERR_XML_ERROR,
|
|
_("Unrecognized family '%s' in definition of network '%s'"),
|
|
def->family, networkName);
|
|
goto error;
|
|
}
|
|
|
|
/* parse/validate netmask */
|
|
if (netmask) {
|
|
if (address == NULL) {
|
|
/* netmask is meaningless without an address */
|
|
virNetworkReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("netmask specified without address in network '%s'"),
|
|
networkName);
|
|
goto error;
|
|
}
|
|
|
|
if (!VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET)) {
|
|
virNetworkReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("netmask not supported for address '%s' in network '%s' (IPv4 only)"),
|
|
address, networkName);
|
|
goto error;
|
|
}
|
|
|
|
if (def->prefix > 0) {
|
|
/* can't have both netmask and prefix at the same time */
|
|
virNetworkReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("network '%s' cannot have both prefix='%u' and a netmask"),
|
|
networkName, def->prefix);
|
|
goto error;
|
|
}
|
|
|
|
if (virSocketAddrParse(&def->netmask, netmask, AF_UNSPEC) < 0)
|
|
goto error;
|
|
|
|
if (!VIR_SOCKET_ADDR_IS_FAMILY(&def->netmask, AF_INET)) {
|
|
virNetworkReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("network '%s' has invalid netmask '%s' for address '%s' (both must be IPv4)"),
|
|
networkName, netmask, address);
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
if (VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET)) {
|
|
/* parse IPv4-related info */
|
|
cur = node->children;
|
|
while (cur != NULL) {
|
|
if (cur->type == XML_ELEMENT_NODE &&
|
|
xmlStrEqual(cur->name, BAD_CAST "dhcp")) {
|
|
result = virNetworkDHCPRangeDefParseXML(networkName, def, cur);
|
|
if (result)
|
|
goto error;
|
|
|
|
} else if (cur->type == XML_ELEMENT_NODE &&
|
|
xmlStrEqual(cur->name, BAD_CAST "tftp")) {
|
|
char *root;
|
|
|
|
if (!(root = virXMLPropString(cur, "root"))) {
|
|
cur = cur->next;
|
|
continue;
|
|
}
|
|
|
|
def->tftproot = (char *)root;
|
|
}
|
|
|
|
cur = cur->next;
|
|
}
|
|
}
|
|
|
|
result = 0;
|
|
|
|
error:
|
|
if (result < 0) {
|
|
virNetworkIpDefClear(def);
|
|
}
|
|
VIR_FREE(address);
|
|
VIR_FREE(netmask);
|
|
|
|
ctxt->node = save;
|
|
return result;
|
|
}
|
|
|
|
static int
|
|
virNetworkPortGroupParseXML(virPortGroupDefPtr def,
|
|
xmlNodePtr node,
|
|
xmlXPathContextPtr ctxt)
|
|
{
|
|
/*
|
|
* virPortGroupDef object is already allocated as part of an array.
|
|
* On failure clear it out, but don't free it.
|
|
*/
|
|
|
|
xmlNodePtr save;
|
|
xmlNodePtr virtPortNode;
|
|
xmlNodePtr bandwidth_node;
|
|
char *isDefault = NULL;
|
|
|
|
int result = -1;
|
|
|
|
save = ctxt->node;
|
|
ctxt->node = node;
|
|
|
|
/* grab raw data from XML */
|
|
def->name = virXPathString("string(./@name)", ctxt);
|
|
isDefault = virXPathString("string(./@default)", ctxt);
|
|
def->isDefault = isDefault && STRCASEEQ(isDefault, "yes");
|
|
|
|
virtPortNode = virXPathNode("./virtualport", ctxt);
|
|
if (virtPortNode &&
|
|
(!(def->virtPortProfile = virNetDevVPortProfileParse(virtPortNode))))
|
|
goto error;
|
|
|
|
bandwidth_node = virXPathNode("./bandwidth", ctxt);
|
|
if (bandwidth_node &&
|
|
!(def->bandwidth = virNetDevBandwidthParse(bandwidth_node))) {
|
|
goto error;
|
|
}
|
|
|
|
result = 0;
|
|
error:
|
|
if (result < 0) {
|
|
virPortGroupDefClear(def);
|
|
}
|
|
VIR_FREE(isDefault);
|
|
|
|
ctxt->node = save;
|
|
return result;
|
|
}
|
|
|
|
static virNetworkDefPtr
|
|
virNetworkDefParseXML(xmlXPathContextPtr ctxt)
|
|
{
|
|
virNetworkDefPtr def;
|
|
char *tmp;
|
|
char *stp = NULL;
|
|
xmlNodePtr *ipNodes = NULL;
|
|
xmlNodePtr *portGroupNodes = NULL;
|
|
xmlNodePtr *forwardIfNodes = NULL;
|
|
xmlNodePtr *forwardPfNodes = NULL;
|
|
xmlNodePtr dnsNode = NULL;
|
|
xmlNodePtr virtPortNode = NULL;
|
|
xmlNodePtr forwardNode = NULL;
|
|
int nIps, nPortGroups, nForwardIfs, nForwardPfs;
|
|
char *forwardDev = NULL;
|
|
xmlNodePtr save = ctxt->node;
|
|
xmlNodePtr bandwidthNode = NULL;
|
|
|
|
if (VIR_ALLOC(def) < 0) {
|
|
virReportOOMError();
|
|
return NULL;
|
|
}
|
|
|
|
/* Extract network name */
|
|
def->name = virXPathString("string(./name[1])", ctxt);
|
|
if (!def->name) {
|
|
virNetworkReportError(VIR_ERR_NO_NAME, NULL);
|
|
goto error;
|
|
}
|
|
|
|
/* Extract network uuid */
|
|
tmp = virXPathString("string(./uuid[1])", ctxt);
|
|
if (!tmp) {
|
|
if (virUUIDGenerate(def->uuid)) {
|
|
virNetworkReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("Failed to generate UUID"));
|
|
goto error;
|
|
}
|
|
} else {
|
|
if (virUUIDParse(tmp, def->uuid) < 0) {
|
|
VIR_FREE(tmp);
|
|
virNetworkReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("malformed uuid element"));
|
|
goto error;
|
|
}
|
|
VIR_FREE(tmp);
|
|
}
|
|
|
|
/* Parse network domain information */
|
|
def->domain = virXPathString("string(./domain[1]/@name)", ctxt);
|
|
|
|
if ((bandwidthNode = virXPathNode("./bandwidth", ctxt)) != NULL &&
|
|
(def->bandwidth = virNetDevBandwidthParse(bandwidthNode)) == NULL)
|
|
goto error;
|
|
|
|
/* Parse bridge information */
|
|
def->bridge = virXPathString("string(./bridge[1]/@name)", ctxt);
|
|
stp = virXPathString("string(./bridge[1]/@stp)", ctxt);
|
|
|
|
if (virXPathULong("string(./bridge[1]/@delay)", ctxt, &def->delay) < 0)
|
|
def->delay = 0;
|
|
|
|
tmp = virXPathString("string(./mac[1]/@address)", ctxt);
|
|
if (tmp) {
|
|
if (virParseMacAddr(tmp, def->mac) < 0) {
|
|
virNetworkReportError(VIR_ERR_XML_ERROR,
|
|
_("Invalid bridge mac address '%s' in network '%s'"),
|
|
tmp, def->name);
|
|
VIR_FREE(tmp);
|
|
goto error;
|
|
}
|
|
VIR_FREE(tmp);
|
|
def->mac_specified = true;
|
|
}
|
|
|
|
dnsNode = virXPathNode("./dns", ctxt);
|
|
if (dnsNode != NULL) {
|
|
if (virNetworkDNSDefParseXML(&def->dns, dnsNode, ctxt) < 0)
|
|
goto error;
|
|
}
|
|
|
|
virtPortNode = virXPathNode("./virtualport", ctxt);
|
|
if (virtPortNode &&
|
|
(!(def->virtPortProfile = virNetDevVPortProfileParse(virtPortNode))))
|
|
goto error;
|
|
|
|
nPortGroups = virXPathNodeSet("./portgroup", ctxt, &portGroupNodes);
|
|
if (nPortGroups < 0)
|
|
goto error;
|
|
|
|
if (nPortGroups > 0) {
|
|
int ii;
|
|
|
|
/* allocate array to hold all the portgroups */
|
|
if (VIR_ALLOC_N(def->portGroups, nPortGroups) < 0) {
|
|
virReportOOMError();
|
|
goto error;
|
|
}
|
|
/* parse each portgroup */
|
|
for (ii = 0; ii < nPortGroups; ii++) {
|
|
int ret = virNetworkPortGroupParseXML(&def->portGroups[ii],
|
|
portGroupNodes[ii], ctxt);
|
|
if (ret < 0)
|
|
goto error;
|
|
def->nPortGroups++;
|
|
}
|
|
}
|
|
VIR_FREE(portGroupNodes);
|
|
|
|
nIps = virXPathNodeSet("./ip", ctxt, &ipNodes);
|
|
if (nIps < 0)
|
|
goto error;
|
|
|
|
if (nIps > 0) {
|
|
int ii;
|
|
|
|
/* allocate array to hold all the addrs */
|
|
if (VIR_ALLOC_N(def->ips, nIps) < 0) {
|
|
virReportOOMError();
|
|
goto error;
|
|
}
|
|
/* parse each addr */
|
|
for (ii = 0; ii < nIps; ii++) {
|
|
int ret = virNetworkIPParseXML(def->name, &def->ips[ii],
|
|
ipNodes[ii], ctxt);
|
|
if (ret < 0)
|
|
goto error;
|
|
def->nips++;
|
|
}
|
|
}
|
|
VIR_FREE(ipNodes);
|
|
|
|
forwardNode = virXPathNode("./forward", ctxt);
|
|
if (!forwardNode) {
|
|
def->forwardType = VIR_NETWORK_FORWARD_NONE;
|
|
def->stp = (stp && STREQ(stp, "off")) ? 0 : 1;
|
|
} else {
|
|
ctxt->node = forwardNode;
|
|
tmp = virXPathString("string(./@mode)", ctxt);
|
|
if (tmp) {
|
|
if ((def->forwardType = virNetworkForwardTypeFromString(tmp)) < 0) {
|
|
virNetworkReportError(VIR_ERR_XML_ERROR,
|
|
_("unknown forwarding type '%s'"), tmp);
|
|
VIR_FREE(tmp);
|
|
goto error;
|
|
}
|
|
VIR_FREE(tmp);
|
|
} else {
|
|
def->forwardType = VIR_NETWORK_FORWARD_NAT;
|
|
}
|
|
|
|
forwardDev = virXPathString("string(./@dev)", ctxt);
|
|
|
|
/* all of these modes can use a pool of physical interfaces */
|
|
nForwardIfs = virXPathNodeSet("./interface", ctxt, &forwardIfNodes);
|
|
nForwardPfs = virXPathNodeSet("./pf", ctxt, &forwardPfNodes);
|
|
|
|
if (nForwardIfs < 0 || nForwardPfs < 0) {
|
|
virNetworkReportError(VIR_ERR_XML_ERROR,
|
|
_("No interface pool or SRIOV physical device given"));
|
|
goto error;
|
|
}
|
|
|
|
if (nForwardPfs == 1) {
|
|
if (VIR_ALLOC_N(def->forwardPfs, nForwardPfs) < 0) {
|
|
virReportOOMError();
|
|
goto error;
|
|
}
|
|
|
|
if (forwardDev) {
|
|
virNetworkReportError(VIR_ERR_XML_ERROR,
|
|
_("A forward Dev should not be used when using a SRIOV PF"));
|
|
goto error;
|
|
}
|
|
|
|
forwardDev = virXMLPropString(*forwardPfNodes, "dev");
|
|
if (!forwardDev) {
|
|
virNetworkReportError(VIR_ERR_XML_ERROR,
|
|
_("Missing required dev attribute in network '%s' pf element"),
|
|
def->name);
|
|
goto error;
|
|
}
|
|
|
|
def->forwardPfs->usageCount = 0;
|
|
def->forwardPfs->dev = forwardDev;
|
|
forwardDev = NULL;
|
|
def->nForwardPfs++;
|
|
} else if (nForwardPfs > 1) {
|
|
virNetworkReportError(VIR_ERR_XML_ERROR,
|
|
_("Use of more than one physical interface is not allowed"));
|
|
goto error;
|
|
}
|
|
if (nForwardIfs > 0 || forwardDev) {
|
|
int ii;
|
|
|
|
/* allocate array to hold all the portgroups */
|
|
if (VIR_ALLOC_N(def->forwardIfs, MAX(nForwardIfs, 1)) < 0) {
|
|
virReportOOMError();
|
|
goto error;
|
|
}
|
|
|
|
if (forwardDev) {
|
|
def->forwardIfs[0].usageCount = 0;
|
|
def->forwardIfs[0].dev = forwardDev;
|
|
forwardDev = NULL;
|
|
def->nForwardIfs++;
|
|
}
|
|
|
|
/* parse each forwardIf */
|
|
for (ii = 0; ii < nForwardIfs; ii++) {
|
|
forwardDev = virXMLPropString(forwardIfNodes[ii], "dev");
|
|
if (!forwardDev) {
|
|
virNetworkReportError(VIR_ERR_XML_ERROR,
|
|
_("Missing required dev attribute in network '%s' forward interface element"),
|
|
def->name);
|
|
goto error;
|
|
}
|
|
|
|
if ((ii == 0) && (def->nForwardIfs == 1)) {
|
|
/* both forwardDev and an interface element are present.
|
|
* If they don't match, it's an error. */
|
|
if (STRNEQ(forwardDev, def->forwardIfs[0].dev)) {
|
|
virNetworkReportError(VIR_ERR_XML_ERROR,
|
|
_("forward dev '%s' must match first interface element dev '%s' in network '%s'"),
|
|
def->forwardIfs[0].dev,
|
|
forwardDev, def->name);
|
|
goto error;
|
|
}
|
|
VIR_FREE(forwardDev);
|
|
continue;
|
|
}
|
|
|
|
def->forwardIfs[ii].dev = forwardDev;
|
|
forwardDev = NULL;
|
|
def->forwardIfs[ii].usageCount = 0;
|
|
def->nForwardIfs++;
|
|
}
|
|
}
|
|
VIR_FREE(forwardDev);
|
|
VIR_FREE(forwardPfNodes);
|
|
VIR_FREE(forwardIfNodes);
|
|
|
|
switch (def->forwardType) {
|
|
case VIR_NETWORK_FORWARD_ROUTE:
|
|
case VIR_NETWORK_FORWARD_NAT:
|
|
/* It's pointless to specify L3 forwarding without specifying
|
|
* the network we're on.
|
|
*/
|
|
if (def->nips == 0) {
|
|
virNetworkReportError(VIR_ERR_XML_ERROR,
|
|
_("%s forwarding requested, but no IP address provided for network '%s'"),
|
|
virNetworkForwardTypeToString(def->forwardType),
|
|
def->name);
|
|
goto error;
|
|
}
|
|
if (def->nForwardIfs > 1) {
|
|
virNetworkReportError(VIR_ERR_XML_ERROR,
|
|
_("multiple forwarding interfaces specified for network '%s', only one is supported"),
|
|
def->name);
|
|
goto error;
|
|
}
|
|
def->stp = (stp && STREQ(stp, "off")) ? 0 : 1;
|
|
break;
|
|
case VIR_NETWORK_FORWARD_PRIVATE:
|
|
case VIR_NETWORK_FORWARD_VEPA:
|
|
case VIR_NETWORK_FORWARD_PASSTHROUGH:
|
|
if (def->bridge) {
|
|
virNetworkReportError(VIR_ERR_XML_ERROR,
|
|
_("bridge name not allowed in %s mode (network '%s'"),
|
|
virNetworkForwardTypeToString(def->forwardType),
|
|
def->name);
|
|
goto error;
|
|
}
|
|
/* fall through to next case */
|
|
case VIR_NETWORK_FORWARD_BRIDGE:
|
|
if (def->delay || stp) {
|
|
virNetworkReportError(VIR_ERR_XML_ERROR,
|
|
_("bridge delay/stp options only allowed in route, nat, and isolated mode, not in %s (network '%s')"),
|
|
virNetworkForwardTypeToString(def->forwardType),
|
|
def->name);
|
|
goto error;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
VIR_FREE(stp);
|
|
ctxt->node = save;
|
|
return def;
|
|
|
|
error:
|
|
VIR_FREE(stp);
|
|
virNetworkDefFree(def);
|
|
VIR_FREE(ipNodes);
|
|
VIR_FREE(portGroupNodes);
|
|
VIR_FREE(forwardIfNodes);
|
|
VIR_FREE(forwardPfNodes);
|
|
VIR_FREE(forwardDev);
|
|
ctxt->node = save;
|
|
return NULL;
|
|
}
|
|
|
|
static virNetworkDefPtr
|
|
virNetworkDefParse(const char *xmlStr,
|
|
const char *filename)
|
|
{
|
|
xmlDocPtr xml;
|
|
virNetworkDefPtr def = NULL;
|
|
|
|
if ((xml = virXMLParse(filename, xmlStr, _("(network_definition)")))) {
|
|
def = virNetworkDefParseNode(xml, xmlDocGetRootElement(xml));
|
|
xmlFreeDoc(xml);
|
|
}
|
|
|
|
return def;
|
|
}
|
|
|
|
virNetworkDefPtr virNetworkDefParseString(const char *xmlStr)
|
|
{
|
|
return virNetworkDefParse(xmlStr, NULL);
|
|
}
|
|
|
|
virNetworkDefPtr virNetworkDefParseFile(const char *filename)
|
|
{
|
|
return virNetworkDefParse(NULL, filename);
|
|
}
|
|
|
|
|
|
virNetworkDefPtr virNetworkDefParseNode(xmlDocPtr xml,
|
|
xmlNodePtr root)
|
|
{
|
|
xmlXPathContextPtr ctxt = NULL;
|
|
virNetworkDefPtr def = NULL;
|
|
|
|
if (!xmlStrEqual(root->name, BAD_CAST "network")) {
|
|
virNetworkReportError(VIR_ERR_XML_ERROR,
|
|
_("unexpected root element <%s>, "
|
|
"expecting <network>"),
|
|
root->name);
|
|
return NULL;
|
|
}
|
|
|
|
ctxt = xmlXPathNewContext(xml);
|
|
if (ctxt == NULL) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
|
|
ctxt->node = root;
|
|
def = virNetworkDefParseXML(ctxt);
|
|
|
|
cleanup:
|
|
xmlXPathFreeContext(ctxt);
|
|
return def;
|
|
}
|
|
|
|
static int
|
|
virNetworkDNSDefFormat(virBufferPtr buf,
|
|
virNetworkDNSDefPtr def)
|
|
{
|
|
int result = 0;
|
|
int i;
|
|
|
|
if (def == NULL)
|
|
goto out;
|
|
|
|
virBufferAddLit(buf, " <dns>\n");
|
|
|
|
for (i = 0 ; i < def->ntxtrecords ; i++) {
|
|
virBufferAsprintf(buf, " <txt name='%s' value='%s' />\n",
|
|
def->txtrecords[i].name,
|
|
def->txtrecords[i].value);
|
|
}
|
|
|
|
for (i = 0 ; i < def->nsrvrecords ; i++) {
|
|
if (def->srvrecords[i].service && def->srvrecords[i].protocol) {
|
|
virBufferAsprintf(buf, " <srv service='%s' protocol='%s'",
|
|
def->srvrecords[i].service,
|
|
def->srvrecords[i].protocol);
|
|
|
|
if (def->srvrecords[i].domain)
|
|
virBufferAsprintf(buf, " domain='%s'", def->srvrecords[i].domain);
|
|
if (def->srvrecords[i].target)
|
|
virBufferAsprintf(buf, " target='%s'", def->srvrecords[i].target);
|
|
if (def->srvrecords[i].port)
|
|
virBufferAsprintf(buf, " port='%d'", def->srvrecords[i].port);
|
|
if (def->srvrecords[i].priority)
|
|
virBufferAsprintf(buf, " priority='%d'", def->srvrecords[i].priority);
|
|
if (def->srvrecords[i].weight)
|
|
virBufferAsprintf(buf, " weight='%d'", def->srvrecords[i].weight);
|
|
|
|
virBufferAsprintf(buf, "/>\n");
|
|
}
|
|
}
|
|
|
|
if (def->nhosts) {
|
|
int ii, j;
|
|
|
|
for (ii = 0 ; ii < def->nhosts; ii++) {
|
|
char *ip = virSocketAddrFormat(&def->hosts[ii].ip);
|
|
|
|
virBufferAsprintf(buf, " <host ip='%s'>\n", ip);
|
|
|
|
for (j = 0; j < def->hosts[ii].nnames; j++)
|
|
virBufferAsprintf(buf, " <hostname>%s</hostname>\n",
|
|
def->hosts[ii].names[j]);
|
|
|
|
virBufferAsprintf(buf, " </host>\n");
|
|
VIR_FREE(ip);
|
|
}
|
|
}
|
|
|
|
virBufferAddLit(buf, " </dns>\n");
|
|
out:
|
|
return result;
|
|
}
|
|
|
|
static int
|
|
virNetworkIpDefFormat(virBufferPtr buf,
|
|
const virNetworkIpDefPtr def)
|
|
{
|
|
int result = -1;
|
|
|
|
virBufferAddLit(buf, " <ip");
|
|
|
|
if (def->family) {
|
|
virBufferAsprintf(buf, " family='%s'", def->family);
|
|
}
|
|
if (VIR_SOCKET_ADDR_VALID(&def->address)) {
|
|
char *addr = virSocketAddrFormat(&def->address);
|
|
if (!addr)
|
|
goto error;
|
|
virBufferAsprintf(buf, " address='%s'", addr);
|
|
VIR_FREE(addr);
|
|
}
|
|
if (VIR_SOCKET_ADDR_VALID(&def->netmask)) {
|
|
char *addr = virSocketAddrFormat(&def->netmask);
|
|
if (!addr)
|
|
goto error;
|
|
virBufferAsprintf(buf, " netmask='%s'", addr);
|
|
VIR_FREE(addr);
|
|
}
|
|
if (def->prefix > 0) {
|
|
virBufferAsprintf(buf," prefix='%u'", def->prefix);
|
|
}
|
|
virBufferAddLit(buf, ">\n");
|
|
|
|
if (def->tftproot) {
|
|
virBufferEscapeString(buf, " <tftp root='%s' />\n",
|
|
def->tftproot);
|
|
}
|
|
if ((def->nranges || def->nhosts)) {
|
|
int ii;
|
|
virBufferAddLit(buf, " <dhcp>\n");
|
|
for (ii = 0 ; ii < def->nranges ; ii++) {
|
|
char *saddr = virSocketAddrFormat(&def->ranges[ii].start);
|
|
if (!saddr)
|
|
goto error;
|
|
char *eaddr = virSocketAddrFormat(&def->ranges[ii].end);
|
|
if (!eaddr) {
|
|
VIR_FREE(saddr);
|
|
goto error;
|
|
}
|
|
virBufferAsprintf(buf, " <range start='%s' end='%s' />\n",
|
|
saddr, eaddr);
|
|
VIR_FREE(saddr);
|
|
VIR_FREE(eaddr);
|
|
}
|
|
for (ii = 0 ; ii < def->nhosts ; ii++) {
|
|
virBufferAddLit(buf, " <host ");
|
|
if (def->hosts[ii].mac)
|
|
virBufferAsprintf(buf, "mac='%s' ", def->hosts[ii].mac);
|
|
if (def->hosts[ii].name)
|
|
virBufferAsprintf(buf, "name='%s' ", def->hosts[ii].name);
|
|
if (VIR_SOCKET_ADDR_VALID(&def->hosts[ii].ip)) {
|
|
char *ipaddr = virSocketAddrFormat(&def->hosts[ii].ip);
|
|
if (!ipaddr)
|
|
goto error;
|
|
virBufferAsprintf(buf, "ip='%s' ", ipaddr);
|
|
VIR_FREE(ipaddr);
|
|
}
|
|
virBufferAddLit(buf, "/>\n");
|
|
}
|
|
if (def->bootfile) {
|
|
virBufferEscapeString(buf, " <bootp file='%s' ",
|
|
def->bootfile);
|
|
if (VIR_SOCKET_ADDR_VALID(&def->bootserver)) {
|
|
char *ipaddr = virSocketAddrFormat(&def->bootserver);
|
|
if (!ipaddr)
|
|
goto error;
|
|
virBufferEscapeString(buf, "server='%s' ", ipaddr);
|
|
VIR_FREE(ipaddr);
|
|
}
|
|
virBufferAddLit(buf, "/>\n");
|
|
}
|
|
|
|
virBufferAddLit(buf, " </dhcp>\n");
|
|
}
|
|
|
|
virBufferAddLit(buf, " </ip>\n");
|
|
|
|
result = 0;
|
|
error:
|
|
return result;
|
|
}
|
|
|
|
static int
|
|
virPortGroupDefFormat(virBufferPtr buf,
|
|
const virPortGroupDefPtr def)
|
|
{
|
|
virBufferAsprintf(buf, " <portgroup name='%s'", def->name);
|
|
if (def->isDefault) {
|
|
virBufferAddLit(buf, " default='yes'");
|
|
}
|
|
virBufferAddLit(buf, ">\n");
|
|
virBufferAdjustIndent(buf, 4);
|
|
if (virNetDevVPortProfileFormat(def->virtPortProfile, buf) < 0)
|
|
return -1;
|
|
virNetDevBandwidthFormat(def->bandwidth, buf);
|
|
virBufferAdjustIndent(buf, -4);
|
|
virBufferAddLit(buf, " </portgroup>\n");
|
|
return 0;
|
|
}
|
|
|
|
char *virNetworkDefFormat(const virNetworkDefPtr def, unsigned int flags)
|
|
{
|
|
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
|
unsigned char *uuid;
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
int ii;
|
|
|
|
virBufferAddLit(&buf, "<network>\n");
|
|
virBufferEscapeString(&buf, " <name>%s</name>\n", def->name);
|
|
|
|
uuid = def->uuid;
|
|
virUUIDFormat(uuid, uuidstr);
|
|
virBufferAsprintf(&buf, " <uuid>%s</uuid>\n", uuidstr);
|
|
|
|
if (def->forwardType != VIR_NETWORK_FORWARD_NONE) {
|
|
const char *dev = NULL;
|
|
if (!def->nForwardPfs)
|
|
dev = virNetworkDefForwardIf(def, 0);
|
|
const char *mode = virNetworkForwardTypeToString(def->forwardType);
|
|
|
|
if (!mode) {
|
|
virNetworkReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Unknown forward type %d in network '%s'"),
|
|
def->forwardType, def->name);
|
|
goto error;
|
|
}
|
|
virBufferAddLit(&buf, " <forward");
|
|
virBufferEscapeString(&buf, " dev='%s'", dev);
|
|
virBufferAsprintf(&buf, " mode='%s'%s>\n", mode,
|
|
(def->nForwardIfs || def->nForwardPfs) ? "" : "/");
|
|
|
|
/* For now, hard-coded to at most 1 forwardPfs */
|
|
if (def->nForwardPfs)
|
|
virBufferEscapeString(&buf, " <pf dev='%s'/>\n",
|
|
def->forwardPfs[0].dev);
|
|
|
|
if (def->nForwardIfs &&
|
|
(!def->nForwardPfs || !(flags & VIR_NETWORK_XML_INACTIVE))) {
|
|
for (ii = 0; ii < def->nForwardIfs; ii++) {
|
|
virBufferEscapeString(&buf, " <interface dev='%s'/>\n",
|
|
def->forwardIfs[ii].dev);
|
|
}
|
|
}
|
|
if (def->nForwardPfs || def->nForwardIfs)
|
|
virBufferAddLit(&buf, " </forward>\n");
|
|
}
|
|
|
|
if (def->forwardType == VIR_NETWORK_FORWARD_NONE ||
|
|
def->forwardType == VIR_NETWORK_FORWARD_NAT ||
|
|
def->forwardType == VIR_NETWORK_FORWARD_ROUTE) {
|
|
|
|
virBufferAddLit(&buf, " <bridge");
|
|
if (def->bridge)
|
|
virBufferEscapeString(&buf, " name='%s'", def->bridge);
|
|
virBufferAsprintf(&buf, " stp='%s' delay='%ld' />\n",
|
|
def->stp ? "on" : "off",
|
|
def->delay);
|
|
} else if (def->forwardType == VIR_NETWORK_FORWARD_BRIDGE &&
|
|
def->bridge) {
|
|
virBufferEscapeString(&buf, " <bridge name='%s' />\n", def->bridge);
|
|
}
|
|
|
|
|
|
if (def->mac_specified) {
|
|
char macaddr[VIR_MAC_STRING_BUFLEN];
|
|
virFormatMacAddr(def->mac, macaddr);
|
|
virBufferAsprintf(&buf, " <mac address='%s'/>\n", macaddr);
|
|
}
|
|
|
|
if (def->domain)
|
|
virBufferAsprintf(&buf, " <domain name='%s'/>\n", def->domain);
|
|
|
|
if (virNetworkDNSDefFormat(&buf, def->dns) < 0)
|
|
goto error;
|
|
|
|
virBufferAdjustIndent(&buf, 2);
|
|
if (virNetDevBandwidthFormat(def->bandwidth, &buf) < 0)
|
|
goto error;
|
|
virBufferAdjustIndent(&buf, -2);
|
|
|
|
for (ii = 0; ii < def->nips; ii++) {
|
|
if (virNetworkIpDefFormat(&buf, &def->ips[ii]) < 0)
|
|
goto error;
|
|
}
|
|
|
|
virBufferAdjustIndent(&buf, 2);
|
|
if (virNetDevVPortProfileFormat(def->virtPortProfile, &buf) < 0)
|
|
goto error;
|
|
virBufferAdjustIndent(&buf, -2);
|
|
|
|
for (ii = 0; ii < def->nPortGroups; ii++)
|
|
if (virPortGroupDefFormat(&buf, &def->portGroups[ii]) < 0)
|
|
goto error;
|
|
|
|
virBufferAddLit(&buf, "</network>\n");
|
|
|
|
if (virBufferError(&buf))
|
|
goto no_memory;
|
|
|
|
return virBufferContentAndReset(&buf);
|
|
|
|
no_memory:
|
|
virReportOOMError();
|
|
error:
|
|
virBufferFreeAndReset(&buf);
|
|
return NULL;
|
|
}
|
|
|
|
virPortGroupDefPtr virPortGroupFindByName(virNetworkDefPtr net,
|
|
const char *portgroup)
|
|
{
|
|
int ii;
|
|
for (ii = 0; ii < net->nPortGroups; ii++) {
|
|
if (portgroup) {
|
|
if (STREQ(portgroup, net->portGroups[ii].name))
|
|
return &net->portGroups[ii];
|
|
} else {
|
|
if (net->portGroups[ii].isDefault)
|
|
return &net->portGroups[ii];
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int virNetworkSaveXML(const char *configDir,
|
|
virNetworkDefPtr def,
|
|
const char *xml)
|
|
{
|
|
char *configFile = NULL;
|
|
int ret = -1;
|
|
|
|
if ((configFile = virNetworkConfigFile(configDir, def->name)) == NULL)
|
|
goto cleanup;
|
|
|
|
if (virFileMakePath(configDir) < 0) {
|
|
virReportSystemError(errno,
|
|
_("cannot create config directory '%s'"),
|
|
configDir);
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = virXMLSaveFile(configFile, def->name, "net-edit", xml);
|
|
|
|
cleanup:
|
|
VIR_FREE(configFile);
|
|
return ret;
|
|
}
|
|
|
|
int virNetworkSaveConfig(const char *configDir,
|
|
virNetworkDefPtr def)
|
|
{
|
|
int ret = -1;
|
|
char *xml;
|
|
|
|
if (!(xml = virNetworkDefFormat(def, 0)))
|
|
goto cleanup;
|
|
|
|
if (virNetworkSaveXML(configDir, def, xml))
|
|
goto cleanup;
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
VIR_FREE(xml);
|
|
return ret;
|
|
}
|
|
|
|
|
|
virNetworkObjPtr virNetworkLoadConfig(virNetworkObjListPtr nets,
|
|
const char *configDir,
|
|
const char *autostartDir,
|
|
const char *name)
|
|
{
|
|
char *configFile = NULL, *autostartLink = NULL;
|
|
virNetworkDefPtr def = NULL;
|
|
virNetworkObjPtr net;
|
|
int autostart;
|
|
|
|
if ((configFile = virNetworkConfigFile(configDir, name)) == NULL)
|
|
goto error;
|
|
if ((autostartLink = virNetworkConfigFile(autostartDir, name)) == NULL)
|
|
goto error;
|
|
|
|
if ((autostart = virFileLinkPointsTo(autostartLink, configFile)) < 0)
|
|
goto error;
|
|
|
|
if (!(def = virNetworkDefParseFile(configFile)))
|
|
goto error;
|
|
|
|
if (!STREQ(name, def->name)) {
|
|
virNetworkReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Network config filename '%s'"
|
|
" does not match network name '%s'"),
|
|
configFile, def->name);
|
|
goto error;
|
|
}
|
|
|
|
if (def->forwardType == VIR_NETWORK_FORWARD_NONE ||
|
|
def->forwardType == VIR_NETWORK_FORWARD_NAT ||
|
|
def->forwardType == VIR_NETWORK_FORWARD_ROUTE) {
|
|
|
|
/* Generate a bridge if none is specified, but don't check for collisions
|
|
* if a bridge is hardcoded, so the network is at least defined.
|
|
*/
|
|
if (virNetworkSetBridgeName(nets, def, 0))
|
|
goto error;
|
|
}
|
|
|
|
if (!(net = virNetworkAssignDef(nets, def)))
|
|
goto error;
|
|
|
|
net->autostart = autostart;
|
|
net->persistent = 1;
|
|
|
|
VIR_FREE(configFile);
|
|
VIR_FREE(autostartLink);
|
|
|
|
return net;
|
|
|
|
error:
|
|
VIR_FREE(configFile);
|
|
VIR_FREE(autostartLink);
|
|
virNetworkDefFree(def);
|
|
return NULL;
|
|
}
|
|
|
|
int virNetworkLoadAllConfigs(virNetworkObjListPtr nets,
|
|
const char *configDir,
|
|
const char *autostartDir)
|
|
{
|
|
DIR *dir;
|
|
struct dirent *entry;
|
|
|
|
if (!(dir = opendir(configDir))) {
|
|
if (errno == ENOENT)
|
|
return 0;
|
|
virReportSystemError(errno,
|
|
_("Failed to open dir '%s'"),
|
|
configDir);
|
|
return -1;
|
|
}
|
|
|
|
while ((entry = readdir(dir))) {
|
|
virNetworkObjPtr net;
|
|
|
|
if (entry->d_name[0] == '.')
|
|
continue;
|
|
|
|
if (!virFileStripSuffix(entry->d_name, ".xml"))
|
|
continue;
|
|
|
|
/* NB: ignoring errors, so one malformed config doesn't
|
|
kill the whole process */
|
|
net = virNetworkLoadConfig(nets,
|
|
configDir,
|
|
autostartDir,
|
|
entry->d_name);
|
|
if (net)
|
|
virNetworkObjUnlock(net);
|
|
}
|
|
|
|
closedir(dir);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int virNetworkDeleteConfig(const char *configDir,
|
|
const char *autostartDir,
|
|
virNetworkObjPtr net)
|
|
{
|
|
char *configFile = NULL;
|
|
char *autostartLink = NULL;
|
|
int ret = -1;
|
|
|
|
if ((configFile = virNetworkConfigFile(configDir, net->def->name)) == NULL)
|
|
goto error;
|
|
if ((autostartLink = virNetworkConfigFile(autostartDir, net->def->name)) == NULL)
|
|
goto error;
|
|
|
|
/* Not fatal if this doesn't work */
|
|
unlink(autostartLink);
|
|
|
|
if (unlink(configFile) < 0) {
|
|
virReportSystemError(errno,
|
|
_("cannot remove config file '%s'"),
|
|
configFile);
|
|
goto error;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
error:
|
|
VIR_FREE(configFile);
|
|
VIR_FREE(autostartLink);
|
|
return ret;
|
|
}
|
|
|
|
char *virNetworkConfigFile(const char *dir,
|
|
const char *name)
|
|
{
|
|
char *ret = NULL;
|
|
|
|
if (virAsprintf(&ret, "%s/%s.xml", dir, name) < 0) {
|
|
virReportOOMError();
|
|
return NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int virNetworkBridgeInUse(const virNetworkObjListPtr nets,
|
|
const char *bridge,
|
|
const char *skipname)
|
|
{
|
|
unsigned int i;
|
|
unsigned int ret = 0;
|
|
|
|
for (i = 0 ; i < nets->count ; i++) {
|
|
virNetworkObjLock(nets->objs[i]);
|
|
if (nets->objs[i]->def->bridge &&
|
|
STREQ(nets->objs[i]->def->bridge, bridge) &&
|
|
!(skipname && STREQ(nets->objs[i]->def->name, skipname)))
|
|
ret = 1;
|
|
virNetworkObjUnlock(nets->objs[i]);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
char *virNetworkAllocateBridge(const virNetworkObjListPtr nets,
|
|
const char *template)
|
|
{
|
|
|
|
int id = 0;
|
|
char *newname;
|
|
|
|
if (!template)
|
|
template = "virbr%d";
|
|
|
|
do {
|
|
if (virAsprintf(&newname, template, id) < 0) {
|
|
virReportOOMError();
|
|
return NULL;
|
|
}
|
|
if (!virNetworkBridgeInUse(nets, newname, NULL)) {
|
|
return newname;
|
|
}
|
|
VIR_FREE(newname);
|
|
|
|
id++;
|
|
} while (id <= MAX_BRIDGE_ID);
|
|
|
|
virNetworkReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Bridge generation exceeded max id %d"),
|
|
MAX_BRIDGE_ID);
|
|
return NULL;
|
|
}
|
|
|
|
int virNetworkSetBridgeName(const virNetworkObjListPtr nets,
|
|
virNetworkDefPtr def,
|
|
int check_collision) {
|
|
|
|
int ret = -1;
|
|
|
|
if (def->bridge && !strstr(def->bridge, "%d")) {
|
|
/* We may want to skip collision detection in this case (ex. when
|
|
* loading configs at daemon startup, so the network is at least
|
|
* defined. */
|
|
if (check_collision &&
|
|
virNetworkBridgeInUse(nets, def->bridge, def->name)) {
|
|
virNetworkReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("bridge name '%s' already in use."),
|
|
def->bridge);
|
|
goto error;
|
|
}
|
|
} else {
|
|
/* Allocate a bridge name */
|
|
if (!(def->bridge = virNetworkAllocateBridge(nets, def->bridge)))
|
|
goto error;
|
|
}
|
|
|
|
ret = 0;
|
|
error:
|
|
return ret;
|
|
}
|
|
|
|
|
|
void virNetworkSetBridgeMacAddr(virNetworkDefPtr def)
|
|
{
|
|
if (!def->mac_specified) {
|
|
/* if the bridge doesn't have a mac address explicitly defined,
|
|
* autogenerate a random one.
|
|
*/
|
|
virGenerateMacAddr((unsigned char[]){ 0x52, 0x54, 0 },
|
|
def->mac);
|
|
def->mac_specified = true;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* virNetworkObjIsDuplicate:
|
|
* @doms : virNetworkObjListPtr to search
|
|
* @def : virNetworkDefPtr definition of network to lookup
|
|
* @check_active: If true, ensure that network is not active
|
|
*
|
|
* Returns: -1 on error
|
|
* 0 if network is new
|
|
* 1 if network is a duplicate
|
|
*/
|
|
int
|
|
virNetworkObjIsDuplicate(virNetworkObjListPtr doms,
|
|
virNetworkDefPtr def,
|
|
unsigned int check_active)
|
|
{
|
|
int ret = -1;
|
|
int dupVM = 0;
|
|
virNetworkObjPtr vm = NULL;
|
|
|
|
/* See if a VM with matching UUID already exists */
|
|
vm = virNetworkFindByUUID(doms, def->uuid);
|
|
if (vm) {
|
|
/* UUID matches, but if names don't match, refuse it */
|
|
if (STRNEQ(vm->def->name, def->name)) {
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
virUUIDFormat(vm->def->uuid, uuidstr);
|
|
virNetworkReportError(VIR_ERR_OPERATION_FAILED,
|
|
_("network '%s' is already defined with uuid %s"),
|
|
vm->def->name, uuidstr);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (check_active) {
|
|
/* UUID & name match, but if VM is already active, refuse it */
|
|
if (virNetworkObjIsActive(vm)) {
|
|
virNetworkReportError(VIR_ERR_OPERATION_INVALID,
|
|
_("network is already active as '%s'"),
|
|
vm->def->name);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
dupVM = 1;
|
|
} else {
|
|
/* UUID does not match, but if a name matches, refuse it */
|
|
vm = virNetworkFindByName(doms, def->name);
|
|
if (vm) {
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
virUUIDFormat(vm->def->uuid, uuidstr);
|
|
virNetworkReportError(VIR_ERR_OPERATION_FAILED,
|
|
_("network '%s' already exists with uuid %s"),
|
|
def->name, uuidstr);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
ret = dupVM;
|
|
cleanup:
|
|
if (vm)
|
|
virNetworkObjUnlock(vm);
|
|
return ret;
|
|
}
|
|
|
|
|
|
void virNetworkObjLock(virNetworkObjPtr obj)
|
|
{
|
|
virMutexLock(&obj->lock);
|
|
}
|
|
|
|
void virNetworkObjUnlock(virNetworkObjPtr obj)
|
|
{
|
|
virMutexUnlock(&obj->lock);
|
|
}
|