/* * network_conf.c: network XML handling * * Copyright (C) 2006-2013 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, see * . * * Author: Daniel P. Berrange */ #include #include #include #include #include #include #include #include #include "virerror.h" #include "datatypes.h" #include "network_conf.h" #include "netdev_vport_profile_conf.h" #include "netdev_bandwidth_conf.h" #include "netdev_vlan_conf.h" #include "viralloc.h" #include "virxml.h" #include "viruuid.h" #include "virutil.h" #include "virbuffer.h" #include "c-ctype.h" #include "virfile.h" #define MAX_BRIDGE_ID 256 #define VIR_FROM_THIS VIR_FROM_NETWORK #define NEXT_FREE_CLASS_ID 3 /* currently, /sbin/tc implementation allows up to 16 bits for minor class size */ #define CLASS_ID_BITMAP_SIZE (1<<16) VIR_ENUM_IMPL(virNetworkForward, VIR_NETWORK_FORWARD_LAST, "none", "nat", "route", "bridge", "private", "vepa", "passthrough", "hostdev") VIR_ENUM_DECL(virNetworkForwardHostdevDevice) VIR_ENUM_IMPL(virNetworkForwardHostdevDevice, VIR_NETWORK_FORWARD_HOSTDEV_DEVICE_LAST, "none", "pci", "netdev") 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); virNetDevVlanClear(&def->vlan); def->bandwidth = NULL; } static void virNetworkForwardIfDefClear(virNetworkForwardIfDefPtr def) { if (def->type == VIR_NETWORK_FORWARD_HOSTDEV_DEVICE_NETDEV) VIR_FREE(def->device.dev); } static void virNetworkForwardPfDefClear(virNetworkForwardPfDefPtr def) { VIR_FREE(def->dev); } static void virNetworkDHCPHostDefClear(virNetworkDHCPHostDefPtr def) { VIR_FREE(def->mac); VIR_FREE(def->id); VIR_FREE(def->name); } static void virNetworkIpDefClear(virNetworkIpDefPtr def) { VIR_FREE(def->family); VIR_FREE(def->ranges); while (def->nhosts--) virNetworkDHCPHostDefClear(&def->hosts[def->nhosts]); VIR_FREE(def->hosts); VIR_FREE(def->tftproot); VIR_FREE(def->bootfile); } static void virNetworkDNSTxtDefClear(virNetworkDNSTxtDefPtr def) { VIR_FREE(def->name); VIR_FREE(def->value); } static void virNetworkDNSHostDefClear(virNetworkDNSHostDefPtr def) { while (def->nnames--) VIR_FREE(def->names[def->nnames]); VIR_FREE(def->names); } static void virNetworkDNSSrvDefClear(virNetworkDNSSrvDefPtr def) { VIR_FREE(def->domain); VIR_FREE(def->service); VIR_FREE(def->protocol); VIR_FREE(def->target); } static void virNetworkDNSDefClear(virNetworkDNSDefPtr def) { if (def->txts) { while (def->ntxts--) virNetworkDNSTxtDefClear(&def->txts[def->ntxts]); VIR_FREE(def->txts); } if (def->hosts) { while (def->nhosts--) virNetworkDNSHostDefClear(&def->hosts[def->nhosts]); VIR_FREE(def->hosts); } if (def->srvs) { while (def->nsrvs--) virNetworkDNSSrvDefClear(&def->srvs[def->nsrvs]); VIR_FREE(def->srvs); } } static void virNetworkForwardDefClear(virNetworkForwardDefPtr def) { int ii; for (ii = 0 ; ii < def->npfs && def->pfs ; ii++) { virNetworkForwardPfDefClear(&def->pfs[ii]); } VIR_FREE(def->pfs); for (ii = 0 ; ii < def->nifs && def->ifs ; ii++) { virNetworkForwardIfDefClear(&def->ifs[ii]); } VIR_FREE(def->ifs); } void virNetworkDefFree(virNetworkDefPtr def) { int ii; if (!def) return; VIR_FREE(def->name); VIR_FREE(def->bridge); VIR_FREE(def->domain); virNetworkForwardDefClear(&def->forward); 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); virNetworkDNSDefClear(&def->dns); VIR_FREE(def->virtPortProfile); virNetDevBandwidthFree(def->bandwidth); virNetDevVlanClear(&def->vlan); VIR_FREE(def); } void virNetworkObjFree(virNetworkObjPtr net) { if (!net) return; virNetworkDefFree(net->def); virNetworkDefFree(net->newDef); virBitmapFree(net->class_id); 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; } /* * virNetworkObjAssignDef: * @network: the network object to update * @def: the new NetworkDef (will be consumed by this function iff successful) * @live: is this new def the "live" version, or the "persistent" version * * Replace the appropriate copy of the given network's NetworkDef * with def. Use "live" and current state of the network to determine * which to replace. * * Returns 0 on success, -1 on failure. */ int virNetworkObjAssignDef(virNetworkObjPtr network, const virNetworkDefPtr def, bool live) { if (virNetworkObjIsActive(network)) { if (live) { virNetworkDefFree(network->def); network->def = def; } else if (network->persistent) { /* save current configuration to be restored on network shutdown */ virNetworkDefFree(network->newDef); network->newDef = def; } else { virReportError(VIR_ERR_OPERATION_INVALID, _("cannot save persistent config of transient " "network '%s'"), network->def->name); return -1; } } else if (!live) { virNetworkDefFree(network->newDef); virNetworkDefFree(network->def); network->newDef = NULL; network->def = def; } else { virReportError(VIR_ERR_OPERATION_INVALID, _("cannot save live config of inactive " "network '%s'"), network->def->name); return -1; } return 0; } /* * virNetworkAssignDef: * @nets: list of all networks * @def: the new NetworkDef (will be consumed by this function iff successful) * @live: is this new def the "live" version, or the "persistent" version * * Either replace the appropriate copy of the NetworkDef with name * matching def->name or, if not found, create a new NetworkObj with * def. For an existing network, use "live" and current state of the * network to determine which to replace. * * Returns -1 on failure, 0 on success. */ virNetworkObjPtr virNetworkAssignDef(virNetworkObjListPtr nets, const virNetworkDefPtr def, bool live) { virNetworkObjPtr network; if ((network = virNetworkFindByName(nets, def->name))) { if (virNetworkObjAssignDef(network, def, live) < 0) { virNetworkObjUnlock(network); return NULL; } return network; } if (VIR_REALLOC_N(nets->objs, nets->count + 1) < 0) { virReportOOMError(); return NULL; } if (VIR_ALLOC(network) < 0) { virReportOOMError(); return NULL; } if (virMutexInit(&network->lock) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot initialize mutex")); VIR_FREE(network); return NULL; } virNetworkObjLock(network); network->def = def; if (!(network->class_id = virBitmapNew(CLASS_ID_BITMAP_SIZE))) { virReportOOMError(); goto error; } /* The first three class IDs are already taken */ ignore_value(virBitmapSetBit(network->class_id, 0)); ignore_value(virBitmapSetBit(network->class_id, 1)); ignore_value(virBitmapSetBit(network->class_id, 2)); network->def = def; nets->objs[nets->count] = network; nets->count++; return network; error: virNetworkObjUnlock(network); virNetworkObjFree(network); return NULL; } /* * virNetworkObjSetDefTransient: * @network: network object pointer * @live: if true, run this operation even for an inactive network. * this allows freely updated network->def with runtime defaults * before starting the network, which will be discarded on network * shutdown. Any cleanup paths need to be sure to handle newDef if * the network is never started. * * Mark the active network config as transient. Ensures live-only update * operations do not persist past network destroy. * * Returns 0 on success, -1 on failure */ int virNetworkObjSetDefTransient(virNetworkObjPtr network, bool live) { if (!virNetworkObjIsActive(network) && !live) return 0; if (!network->persistent || network->newDef) return 0; network->newDef = virNetworkDefCopy(network->def, VIR_NETWORK_XML_INACTIVE); return network->newDef ? 0 : -1; } /* virNetworkObjUnsetDefTransient: * * This *undoes* what virNetworkObjSetDefTransient did. */ void virNetworkObjUnsetDefTransient(virNetworkObjPtr network) { if (network->newDef) { virNetworkDefFree(network->def); network->def = network->newDef; network->newDef = NULL; } } /* * virNetworkObjGetPersistentDef: * @network: network object pointer * * Return the persistent network configuration. If network is transient, * return the running config. * * Returns NULL on error, virNetworkDefPtr on success. */ virNetworkDefPtr virNetworkObjGetPersistentDef(virNetworkObjPtr network) { if (network->newDef) return network->newDef; else return network->def; } /* * virNetworkObjReplacePersistentDef: * @network: network object pointer * @def: new virNetworkDef to replace current persistent config * * Replace the "persistent" network configuration with the given new * virNetworkDef. This pays attention to whether or not the network * is active. * * Returns -1 on error, 0 on success */ int virNetworkObjReplacePersistentDef(virNetworkObjPtr network, virNetworkDefPtr def) { if (virNetworkObjIsActive(network)) { virNetworkDefFree(network->newDef); network->newDef = def; } else { virNetworkDefFree(network->def); network->def = def; } return 0; } /* * virNetworkDefCopy: * @def: NetworkDef to copy * @flags: VIR_NETWORK_XML_INACTIVE if appropriate * * make a deep copy of the given NetworkDef * * Returns a new NetworkDef on success, or NULL on failure. */ virNetworkDefPtr virNetworkDefCopy(virNetworkDefPtr def, unsigned int flags) { char *xml = NULL; virNetworkDefPtr newDef = NULL; if (!def) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("NULL NetworkDef")); return NULL; } /* deep copy with a format/parse cycle */ if (!(xml = virNetworkDefFormat(def, flags))) goto cleanup; newDef = virNetworkDefParseString(xml); cleanup: VIR_FREE(xml); return newDef; } /* * virNetworkConfigChangeSetup: * * 1) checks whether network state is consistent with the requested * type of modification. * * 3) make sure there are separate "def" and "newDef" copies of * networkDef if appropriate. * * Returns 0 on success, -1 on error. */ int virNetworkConfigChangeSetup(virNetworkObjPtr network, unsigned int flags) { bool isActive; int ret = -1; isActive = virNetworkObjIsActive(network); if (!isActive && (flags & VIR_NETWORK_UPDATE_AFFECT_LIVE)) { virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("network is not running")); goto cleanup; } if (flags & VIR_NETWORK_UPDATE_AFFECT_CONFIG) { if (!network->persistent) { virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("cannot change persistent config of a " "transient network")); goto cleanup; } /* this should already have been done by the driver, but do it * anyway just in case. */ if (isActive && (virNetworkObjSetDefTransient(network, false) < 0)) goto cleanup; } ret = 0; cleanup: return ret; } 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 virSocketAddrRangeParseXML(const char *networkName, xmlNodePtr node, virSocketAddrRangePtr range) { char *start = NULL, *end = NULL; int ret = -1; if (!(start = virXMLPropString(node, "start"))) { virReportError(VIR_ERR_XML_ERROR, _("Missing 'start' attribute in dhcp range for network '%s'"), networkName); goto cleanup; } if (virSocketAddrParse(&range->start, start, AF_UNSPEC) < 0) goto cleanup; if (!(end = virXMLPropString(node, "end"))) { virReportError(VIR_ERR_XML_ERROR, _("Missing 'end' attribute in dhcp range for network '%s'"), networkName); goto cleanup; } if (virSocketAddrParse(&range->end, end, AF_UNSPEC) < 0) goto cleanup; /* do a sanity check of the range */ if (virSocketAddrGetRange(&range->start, &range->end) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Invalid dhcp range '%s' to '%s' in network '%s'"), start, end, networkName); goto cleanup; } ret = 0; cleanup: VIR_FREE(start); VIR_FREE(end); return ret; } static int virNetworkDHCPHostDefParseXML(const char *networkName, const virNetworkIpDefPtr def, xmlNodePtr node, virNetworkDHCPHostDefPtr host, bool partialOkay) { char *mac = NULL, *name = NULL, *ip = NULL, *id = NULL; virMacAddr addr; virSocketAddr inaddr; int ret = -1; mac = virXMLPropString(node, "mac"); if (mac != NULL) { if (VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET6)) { virReportError(VIR_ERR_XML_ERROR, _("Invalid to specify MAC address '%s' " "in network '%s' IPv6 static host definition"), mac, networkName); goto cleanup; } if (virMacAddrParse(mac, &addr) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Cannot parse MAC address '%s' in network '%s'"), mac, networkName); goto cleanup; } if (virMacAddrIsMulticast(&addr)) { virReportError(VIR_ERR_XML_ERROR, _("expected unicast mac address, found " "multicast '%s' in network '%s'"), (const char *)mac, networkName); goto cleanup; } } id = virXMLPropString(node, "id"); if (id) { char *cp = id + strspn(id, "0123456789abcdefABCDEF:"); if (*cp) { virReportError(VIR_ERR_XML_ERROR, _("Invalid character '%c' in id '%s' of network '%s'"), *cp, id, networkName); } } name = virXMLPropString(node, "name"); if (name && (!c_isalpha(name[0]))) { virReportError(VIR_ERR_XML_ERROR, _("Cannot use host name '%s' in network '%s'"), name, networkName); goto cleanup; } ip = virXMLPropString(node, "ip"); if (ip && (virSocketAddrParse(&inaddr, ip, AF_UNSPEC) < 0)) { virReportError(VIR_ERR_XML_ERROR, _("Invalid IP address in static host definition " "for network '%s'"), networkName); goto cleanup; } if (partialOkay) { /* for search/match, you just need one of the three */ if (!(mac || name || ip)) { virReportError(VIR_ERR_XML_ERROR, _("At least one of name, mac, or ip attribute " "must be specified for static host definition " "in network '%s' "), networkName); } } else { /* normal usage - you need at least name (IPv6) or one of MAC * address or name (IPv4) */ if (VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET6)) { if (!(id || name)) { virReportError(VIR_ERR_XML_ERROR, _("Static host definition in IPv6 network '%s' " "must have id or name attribute"), networkName); goto cleanup; } } else if (!(mac || name)) { virReportError(VIR_ERR_XML_ERROR, _("Static host definition in IPv4 network '%s' " "must have mac or name attribute"), networkName); goto cleanup; } if (!ip) { virReportError(VIR_ERR_XML_ERROR, _("Missing IP address in static host definition " "for network '%s'"), networkName); goto cleanup; } } host->mac = mac; mac = NULL; host->id = id; id = NULL; host->name = name; name = NULL; if (ip) host->ip = inaddr; ret = 0; cleanup: VIR_FREE(mac); VIR_FREE(id); VIR_FREE(name); VIR_FREE(ip); return ret; } static int virNetworkDHCPOptionDefParseXML(const char *networkName, xmlNodePtr node, virNetworkDHCPOptionDefPtr option) { char *number = NULL; int ret = -1; if (!(number = virXMLPropString(node, "number"))) { virReportError(VIR_ERR_XML_ERROR, _("Option definition in IPv4 network '%s' " "must have number attribute"), networkName); goto cleanup; } if (virStrToLong_ui(number, NULL, 10, &option->number) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Malformed