This commit adds support for IPv6 parsing and formatting to the virtual network XML parser, including moving around data definitions to allow for multiple <ip> elements on a single network, but only changes the consumers of this API to accomodate for the changes in API/structure, not to add any actual IPv6 functionality. That will come in a later patch - this patch attempts to maintain the same final functionality in both drivers that use the network XML parser - vbox and "bridge" (the Linux bridge-based driver used by the qemu hypervisor driver). * src/libvirt_private.syms: Add new private API functions. * src/conf/network_conf.[ch]: Change C data structure and parsing/formatting. * src/network/bridge_driver.c: Update to use new parser/formatter. * src/vbox/vbox_tmpl.c: update to use new parser/formatter * docs/schemas/network.rng: changes to the schema - * there can now be more than one <ip> element. * ip address is now an ip-addr (ipv4 or ipv6) rather than ipv4-addr * new optional "prefix" attribute that can be used in place of "netmask" * new optional "family" attribute - "ipv4" or "ipv6" (will default to ipv4) * define data types for the above * tests/networkxml2xml(in|out)/nat-network.xml: add multiple <ip> elements (including IPv6) to a single network definition to verify they are being correctly parsed and formatted. --- V2 Changes: * prefix in virNetworkIpDef struct is now unsigned int rather than int. * caught a memory leak in an error patch of virNetworkIPParseXML(). docs/schemas/network.rng | 41 +++- src/conf/network_conf.c | 453 +++++++++++++++++++++---------- src/conf/network_conf.h | 50 +++- src/libvirt_private.syms | 5 +- src/network/bridge_driver.c | 248 ++++++++++-------- src/vbox/vbox_tmpl.c | 81 ++++-- tests/networkxml2xmlin/nat-network.xml | 8 + tests/networkxml2xmlout/nat-network.xml | 8 + 8 files changed, 590 insertions(+), 304 deletions(-) diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng index ac13af2..bc34ddc 100644 --- a/docs/schemas/network.rng +++ b/docs/schemas/network.rng @@ -80,15 +80,21 @@ </optional> <!-- <ip> element --> - <optional> + <zeroOrMore> <!-- The IP element sets up NAT'ing and an optional DHCP server local to the host. --> <element name="ip"> <optional> - <attribute name="address"><ref name="ipv4-addr"/></attribute> + <attribute name="address"><ref name="ip-addr"/></attribute> + </optional> + <optional> + <choice> + <attribute name="netmask"><ref name="ipv4-addr"/></attribute> + <attribute name="prefix"><ref name="ip-prefix"/></attribute> + </choice> </optional> <optional> - <attribute name="netmask"><ref name="ipv4-addr"/></attribute> + <attribute name="family"><ref name="addr-family"/></attribute> </optional> <optional> <element name="tftp"> @@ -123,7 +129,7 @@ </element> </optional> </element> - </optional> + </zeroOrMore> </interleave> </element> </define> @@ -135,6 +141,33 @@ </data> </define> + <!-- Based on http://blog.mes-stats.fr/2008/10/09/regex-ipv4-et-ipv6 --> + <define name='ipv6-addr'> + <data type='string'> + <!-- To understand this better, take apart the toplevel '|'s --> + <param name="pattern">(([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((((25[0-5])|(1[0-9]{2})|(2[0-4][0-9])|([0-9]{1,2})))\.){3}(((25[0-5])|(1[0-9]{2})|(2[0-4][0-9])|([0-9]{1,2}))))|(([0-9A-Fa-f]{1,4}:){0,5}:((((25[0-5])|(1[0-9]{2})|(2[0-4][0-9])|([0-9]{1,2})))\.){3}(((25[0-5])|(1[0-9]{2})|(2[0-4][0-9])|([0-9]{1,2}))))|(::([0-9A-Fa-f]{1,4}:){0,5}((((25[0-5])|(1[0-9]{2})|(2[0-4][0-9])|([0-9]{1,2})))\.){3}(((25[0-5])|(1[0-9]{2})|(2[0-4][0-9])|([0-9]{1,2}))))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:)</param> + </data> + </define> + + <define name='ip-addr'> + <choice> + <ref name='ipv4-addr'/> + <ref name='ipv6-addr'/> + </choice> + </define> + + <define name='ip-prefix'> + <data type='unsignedInt'> + <param name="maxInclusive">128</param> + </data> + </define> + + <define name='addr-family'> + <data type='string'> + <param name="pattern">(ipv4)|(ipv6)</param> + </data> + </define> + <!-- a 6 byte MAC address in ASCII-hex format, eg "12:34:56:78:9A:BC" --> <define name='mac-addr'> <data type='string'> diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index b17c708..86442ce 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -87,9 +87,26 @@ virNetworkObjPtr virNetworkFindByName(const virNetworkObjListPtr nets, } +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); +} + void virNetworkDefFree(virNetworkDefPtr def) { - int i; + int ii; if (!def) return; @@ -99,16 +116,10 @@ void virNetworkDefFree(virNetworkDefPtr def) VIR_FREE(def->forwardDev); VIR_FREE(def->domain); - VIR_FREE(def->ranges); - - for (i = 0 ; i < def->nhosts && def->hosts ; i++) { - VIR_FREE(def->hosts[i].mac); - VIR_FREE(def->hosts[i].name); + for (ii = 0 ; ii < def->nips && def->ips ; ii++) { + virNetworkIpDefClear(&def->ips[ii]); } - VIR_FREE(def->hosts); - - VIR_FREE(def->tftproot); - VIR_FREE(def->bootfile); + VIR_FREE(def->ips); VIR_FREE(def); } @@ -207,21 +218,48 @@ void virNetworkRemoveInactive(virNetworkObjListPtr nets, } } +/* return ips[index], or NULL if there aren't enough ips */ +virNetworkIpDefPtr +virNetworkDefGetIpByIndex(const virNetworkDefPtr def, + int family, int 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_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 virNetworkDefPrefix(const virNetworkDefPtr def) +int virNetworkIpDefPrefix(const virNetworkIpDefPtr def) { - if (VIR_SOCKET_HAS_ADDR(&def->netmask)) { + if (def->prefix > 0) { + return def->prefix; + } else if (VIR_SOCKET_HAS_ADDR(&def->netmask)) { return virSocketGetNumNetmaskBits(&def->netmask); - } else if (VIR_SOCKET_IS_FAMILY(&def->ipAddress, AF_INET)) { + } else if (VIR_SOCKET_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. */ const unsigned char *octets - = (const unsigned char *)(&def->ipAddress.data.inet4.sin_addr.s_addr); + = (const unsigned char *)(&def->address.data.inet4.sin_addr.s_addr); if ((octets[0] & 0x80) == 0) { /* Class A network */ return 8; @@ -233,6 +271,8 @@ int virNetworkDefPrefix(const virNetworkDefPtr def) return 24; } return -1; + } else if (VIR_SOCKET_IS_FAMILY(&def->address, AF_INET6)) { + return 64; } return -1; } @@ -241,22 +281,23 @@ int virNetworkDefPrefix(const virNetworkDefPtr def) * definition, based on either the definition's netmask, or its * prefix. Return -1 on error (and set the netmask family to AF_UNSPEC) */ -int virNetworkDefNetmask(const virNetworkDefPtr def, - virSocketAddrPtr netmask) +int virNetworkIpDefNetmask(const virNetworkIpDefPtr def, + virSocketAddrPtr netmask) { if (VIR_SOCKET_IS_FAMILY(&def->netmask, AF_INET)) { *netmask = def->netmask; return 0; } - return virSocketAddrPrefixToNetmask(virNetworkDefPrefix(def), netmask, - VIR_SOCKET_FAMILY(&def->ipAddress)); + return virSocketAddrPrefixToNetmask(virNetworkIpDefPrefix(def), netmask, + VIR_SOCKET_FAMILY(&def->address)); } static int -virNetworkDHCPRangeDefParseXML(virNetworkDefPtr def, - xmlNodePtr node) { +virNetworkDHCPRangeDefParseXML(virNetworkIpDefPtr def, + xmlNodePtr node) +{ xmlNodePtr cur; @@ -390,33 +431,147 @@ virNetworkDHCPRangeDefParseXML(virNetworkDefPtr def, } static int -virNetworkIPParseXML(virNetworkDefPtr def, - xmlNodePtr node) { - xmlNodePtr cur; +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. + */ - cur = node->children; - while (cur != NULL) { - if (cur->type == XML_ELEMENT_NODE && - xmlStrEqual(cur->name, BAD_CAST "dhcp")) { - int result = virNetworkDHCPRangeDefParseXML(def, cur); - if (result) - return result; + 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 (virSocketParseAddr(address, &def->address, AF_UNSPEC) < 0) { + virNetworkReportError(VIR_ERR_XML_ERROR, + _("Bad address '%s' in definition of network '%s'"), + address, networkName); + 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; + /* validate family vs. address */ + if (def->family == NULL) { + if (!(VIR_SOCKET_IS_FAMILY(&def->address, AF_INET) || + VIR_SOCKET_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_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_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_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' has prefix='%u' but no address"), + networkName, def->prefix); + goto error; + } + + if (virSocketParseAddr(netmask, &def->netmask, AF_UNSPEC) < 0) + goto error; + + if (!VIR_SOCKET_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_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(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; } - def->tftproot = root; + cur = cur->next; } + } - cur = cur->next; + result = 0; + +error: + if (result < 0) { + virNetworkIpDefClear(def); } - return 0; + VIR_FREE(address); + VIR_FREE(netmask); + + ctxt->node = save; + return result; } static virNetworkDefPtr @@ -424,8 +579,8 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt) { virNetworkDefPtr def; char *tmp; - char *ipAddress; - char *netmask; + xmlNodePtr *ipNodes = NULL; + int nIps; if (VIR_ALLOC(def) < 0) { virReportOOMError(); @@ -469,44 +624,32 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt) if (virXPathULong("string(./bridge[1]/@delay)", ctxt, &def->delay) < 0) def->delay = 0; - ipAddress = virXPathString("string(./ip[1]/@address)", ctxt); - if (ipAddress) { - xmlNodePtr ip; - - if (virSocketParseAddr(ipAddress, &def->ipAddress, AF_UNSPEC) < 0) - goto error; + nIps = virXPathNodeSet("./ip", ctxt, &ipNodes); + if (nIps > 0) { + int ii; - /* XXX someday we want IPv6, so will need to relax this */ - if (!VIR_SOCKET_IS_FAMILY(&def->ipAddress, AF_INET)) { - virNetworkReportError(VIR_ERR_CONFIG_UNSUPPORTED, - "%s", _("Only IPv4 addresses are supported")); + /* allocate array to hold all the addrs */ + if (VIR_ALLOC_N(def->ips, nIps) < 0) { + virReportOOMError(); goto error; } - - if ((ip = virXPathNode("./ip[1]", ctxt)) && - virNetworkIPParseXML(def, ip) < 0) - goto error; - } - VIR_FREE(ipAddress); - - netmask = virXPathString("string(./ip[1]/@netmask)", ctxt); - if (netmask) { - - if (virSocketParseAddr(netmask, &def->netmask, AF_UNSPEC) < 0) - goto error; - - /* XXX someday we want IPv6, so will need to relax this */ - if (!VIR_SOCKET_IS_FAMILY(&def->netmask, AF_INET)) { - virNetworkReportError(VIR_ERR_CONFIG_UNSUPPORTED, - "%s", _("Only IPv4 addresses are supported")); - 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(netmask); - /* IPv4 forwarding setup */ if (virXPathBoolean("count(./forward) > 0", ctxt)) { + if (def->nips == 0) { + virNetworkReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Forwarding requested, but no IP address provided")); + goto error; + } tmp = virXPathString("string(./forward[1]/@mode)", ctxt); if (tmp) { if ((def->forwardType = virNetworkForwardTypeFromString(tmp)) < 0) { @@ -585,11 +728,101 @@ cleanup: return def; } +static int +virNetworkIpDefFormat(virBufferPtr buf, + const virNetworkIpDefPtr def) +{ + int result = -1; + + virBufferAddLit(buf, " <ip"); + + if (def->family) { + virBufferVSprintf(buf, " family='%s'", def->family); + } + if (VIR_SOCKET_HAS_ADDR(&def->address)) { + char *addr = virSocketFormatAddr(&def->address); + if (!addr) + goto error; + virBufferVSprintf(buf, " address='%s'", addr); + VIR_FREE(addr); + } + if (VIR_SOCKET_HAS_ADDR(&def->netmask)) { + char *addr = virSocketFormatAddr(&def->netmask); + if (!addr) + goto error; + virBufferVSprintf(buf, " netmask='%s'", addr); + VIR_FREE(addr); + } + if (def->prefix > 0) { + virBufferVSprintf(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 = virSocketFormatAddr(&def->ranges[ii].start); + if (!saddr) + goto error; + char *eaddr = virSocketFormatAddr(&def->ranges[ii].end); + if (!eaddr) { + VIR_FREE(saddr); + goto error; + } + virBufferVSprintf(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) + virBufferVSprintf(buf, "mac='%s' ", def->hosts[ii].mac); + if (def->hosts[ii].name) + virBufferVSprintf(buf, "name='%s' ", def->hosts[ii].name); + if (VIR_SOCKET_HAS_ADDR(&def->hosts[ii].ip)) { + char *ipaddr = virSocketFormatAddr(&def->hosts[ii].ip); + if (!ipaddr) + goto error; + virBufferVSprintf(buf, "ip='%s' ", ipaddr); + VIR_FREE(ipaddr); + } + virBufferAddLit(buf, "/>\n"); + } + if (def->bootfile) { + virBufferEscapeString(buf, " <bootp file='%s' ", + def->bootfile); + if (VIR_SOCKET_HAS_ADDR(&def->bootserver)) { + char *ipaddr = virSocketFormatAddr(&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; +} + char *virNetworkDefFormat(const virNetworkDefPtr def) { 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); @@ -621,81 +854,9 @@ char *virNetworkDefFormat(const virNetworkDefPtr def) if (def->domain) virBufferVSprintf(&buf, " <domain name='%s'/>\n", def->domain); - if (VIR_SOCKET_HAS_ADDR(&def->ipAddress) || - VIR_SOCKET_HAS_ADDR(&def->netmask)) { - virBufferAddLit(&buf, " <ip"); - - if (VIR_SOCKET_HAS_ADDR(&def->ipAddress)) { - char *addr = virSocketFormatAddr(&def->ipAddress); - if (!addr) - goto error; - virBufferVSprintf(&buf, " address='%s'", addr); - VIR_FREE(addr); - } - - if (VIR_SOCKET_HAS_ADDR(&def->netmask)) { - char *addr = virSocketFormatAddr(&def->netmask); - if (!addr) - goto error; - virBufferVSprintf(&buf, " netmask='%s'", addr); - VIR_FREE(addr); - } - - virBufferAddLit(&buf, ">\n"); - - if (def->tftproot) { - virBufferEscapeString(&buf, " <tftp root='%s' />\n", - def->tftproot); - } - if ((def->nranges || def->nhosts)) { - int i; - virBufferAddLit(&buf, " <dhcp>\n"); - for (i = 0 ; i < def->nranges ; i++) { - char *saddr = virSocketFormatAddr(&def->ranges[i].start); - if (!saddr) - goto error; - char *eaddr = virSocketFormatAddr(&def->ranges[i].end); - if (!eaddr) { - VIR_FREE(saddr); - goto error; - } - virBufferVSprintf(&buf, " <range start='%s' end='%s' />\n", - saddr, eaddr); - VIR_FREE(saddr); - VIR_FREE(eaddr); - } - for (i = 0 ; i < def->nhosts ; i++) { - virBufferAddLit(&buf, " <host "); - if (def->hosts[i].mac) - virBufferVSprintf(&buf, "mac='%s' ", def->hosts[i].mac); - if (def->hosts[i].name) - virBufferVSprintf(&buf, "name='%s' ", def->hosts[i].name); - if (VIR_SOCKET_HAS_ADDR(&def->hosts[i].ip)) { - char *ipaddr = virSocketFormatAddr(&def->hosts[i].ip); - if (!ipaddr) - goto error; - virBufferVSprintf(&buf, "ip='%s' ", ipaddr); - VIR_FREE(ipaddr); - } - virBufferAddLit(&buf, "/>\n"); - } - if (def->bootfile) { - virBufferEscapeString(&buf, " <bootp file='%s' ", - def->bootfile); - if (VIR_SOCKET_HAS_ADDR(&def->bootserver)) { - char *ipaddr = virSocketFormatAddr(&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"); + for (ii = 0; ii < def->nips; ii++) { + if (virNetworkIpDefFormat(&buf, &def->ips[ii]) < 0) + goto error; } virBufferAddLit(&buf, "</network>\n"); diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index a922d28..a51794d 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -56,6 +56,33 @@ struct _virNetworkDHCPHostDef { virSocketAddr ip; }; +typedef struct _virNetworkIpDef virNetworkIpDef; +typedef virNetworkIpDef *virNetworkIpDefPtr; +struct _virNetworkIpDef { + char *family; /* ipv4 or ipv6 - default is ipv4 */ + virSocketAddr address; /* Bridge IP address */ + + /* The first two items below are taken directly from the XML (one + * or the other is given, but not both) and the 3rd is derived + * from the first two. When formatting XML, always use netMasktStr + * if it's non-NULL, but when using netmask in other code, use + * netmask, as it will automatically take into account prefix or + * natural netmasks (when no netmask or prefix is specified). + */ + unsigned int prefix; /* ipv6 - only prefix allowed */ + virSocketAddr netmask; /* ipv4 - either netmask or prefix specified */ + + unsigned int nranges; /* Zero or more dhcp ranges */ + virNetworkDHCPRangeDefPtr ranges; + + unsigned int nhosts; /* Zero or more dhcp hosts */ + virNetworkDHCPHostDefPtr hosts; + + char *tftproot; + char *bootfile; + virSocketAddr bootserver; + }; + typedef struct _virNetworkDef virNetworkDef; typedef virNetworkDef *virNetworkDefPtr; struct _virNetworkDef { @@ -70,18 +97,8 @@ struct _virNetworkDef { int forwardType; /* One of virNetworkForwardType constants */ char *forwardDev; /* Destination device for forwarding */ - virSocketAddr ipAddress; /* Bridge IP address */ - virSocketAddr netmask; - - unsigned int nranges; /* Zero or more dhcp ranges */ - virNetworkDHCPRangeDefPtr ranges; - - unsigned int nhosts; /* Zero or more dhcp hosts */ - virNetworkDHCPHostDefPtr hosts; - - char *tftproot; - char *bootfile; - virSocketAddr bootserver; + int nips; + virNetworkIpDefPtr ips; /* ptr to array of IP addresses on this network */ }; typedef struct _virNetworkObj virNetworkObj; @@ -133,9 +150,12 @@ virNetworkDefPtr virNetworkDefParseNode(xmlDocPtr xml, char *virNetworkDefFormat(const virNetworkDefPtr def); -int virNetworkDefPrefix(const virNetworkDefPtr def); -int virNetworkDefNetmask(const virNetworkDefPtr def, - virSocketAddrPtr netmask); +virNetworkIpDefPtr +virNetworkDefGetIpByIndex(const virNetworkDefPtr def, + int family, int n); +int virNetworkIpDefPrefix(const virNetworkIpDefPtr def); +int virNetworkIpDefNetmask(const virNetworkIpDefPtr def, + virSocketAddrPtr netmask); int virNetworkSaveXML(const char *configDir, virNetworkDefPtr def, diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index d9ba7b1..6cb8270 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -578,15 +578,16 @@ virNetworkAssignDef; virNetworkConfigFile; virNetworkDefFormat; virNetworkDefFree; -virNetworkDefNetmask; +virNetworkDefGetIpByIndex; virNetworkDefParseFile; virNetworkDefParseNode; virNetworkDefParseString; -virNetworkDefPrefix; virNetworkDeleteConfig; virNetworkFindByName; virNetworkFindByUUID; virNetworkLoadAllConfigs; +virNetworkIpDefNetmask; +virNetworkIpDefPrefix; virNetworkObjIsDuplicate; virNetworkObjListFree; virNetworkObjLock; diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index ba9ff96..0f8b050 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -145,8 +145,7 @@ networkFindActiveConfigs(struct network_driver *driver) { obj->active = 1; /* Finally try and read dnsmasq pid if any */ - if ((VIR_SOCKET_HAS_ADDR(&obj->def->ipAddress) || - obj->def->nranges) && + if (obj->def->ips && (obj->def->nips > 0) && virFileReadPid(NETWORK_PID_DIR, obj->def->name, &obj->dnsmasqPid) == 0) { @@ -366,7 +365,7 @@ networkShutdown(void) { static int -networkSaveDnsmasqHostsfile(virNetworkObjPtr network, +networkSaveDnsmasqHostsfile(virNetworkIpDefPtr ipdef, dnsmasqContext *dctx, bool force) { @@ -375,8 +374,8 @@ networkSaveDnsmasqHostsfile(virNetworkObjPtr network, if (! force && virFileExists(dctx->hostsfile->path)) return 0; - for (i = 0 ; i < network->def->nhosts ; i++) { - virNetworkDHCPHostDefPtr host = &(network->def->hosts[i]); + for (i = 0; i < ipdef->nhosts; i++) { + virNetworkDHCPHostDefPtr host = &(ipdef->hosts[i]); if ((host->mac) && VIR_SOCKET_HAS_ADDR(&host->ip)) dnsmasqAddDhcpHost(dctx, host->mac, &host->ip, host->name); } @@ -390,13 +389,14 @@ networkSaveDnsmasqHostsfile(virNetworkObjPtr network, static int networkBuildDnsmasqArgv(virNetworkObjPtr network, + virNetworkIpDefPtr ipdef, const char *pidfile, virCommandPtr cmd) { int r, ret = -1; int nbleases = 0; char *bridgeaddr; - if (!(bridgeaddr = virSocketFormatAddr(&network->def->ipAddress))) + if (!(bridgeaddr = virSocketFormatAddr(&ipdef->address))) goto cleanup; /* * NB, be careful about syntax for dnsmasq options in long format. @@ -438,18 +438,18 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, * clearly not practical * * virCommandAddArg(cmd, "--interface"); - * virCommandAddArg(cmd, network->def->bridge); + * virCommandAddArg(cmd, ipdef->bridge); */ virCommandAddArgList(cmd, "--listen-address", bridgeaddr, "--except-interface", "lo", NULL); - for (r = 0 ; r < network->def->nranges ; r++) { - char *saddr = virSocketFormatAddr(&network->def->ranges[r].start); + for (r = 0 ; r < ipdef->nranges ; r++) { + char *saddr = virSocketFormatAddr(&ipdef->ranges[r].start); if (!saddr) goto cleanup; - char *eaddr = virSocketFormatAddr(&network->def->ranges[r].end); + char *eaddr = virSocketFormatAddr(&ipdef->ranges[r].end); if (!eaddr) { VIR_FREE(saddr); goto cleanup; @@ -458,8 +458,8 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, virCommandAddArgFormat(cmd, "%s,%s", saddr, eaddr); VIR_FREE(saddr); VIR_FREE(eaddr); - nbleases += virSocketGetRange(&network->def->ranges[r].start, - &network->def->ranges[r].end); + nbleases += virSocketGetRange(&ipdef->ranges[r].start, + &ipdef->ranges[r].end); } /* @@ -467,19 +467,19 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, * we have to add a special --dhcp-range option to enable the service in * dnsmasq. */ - if (!network->def->nranges && network->def->nhosts) { + if (!ipdef->nranges && ipdef->nhosts) { virCommandAddArg(cmd, "--dhcp-range"); virCommandAddArgFormat(cmd, "%s,static", bridgeaddr); } - if (network->def->nranges > 0) { + if (ipdef->nranges > 0) { virCommandAddArgFormat(cmd, "--dhcp-lease-max=%d", nbleases); } - if (network->def->nranges || network->def->nhosts) + if (ipdef->nranges || ipdef->nhosts) virCommandAddArg(cmd, "--dhcp-no-override"); - if (network->def->nhosts > 0) { + if (ipdef->nhosts > 0) { dnsmasqContext *dctx = dnsmasqContextNew(network->def->name, DNSMASQ_STATE_DIR); if (dctx == NULL) { @@ -487,31 +487,30 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, goto cleanup; } - if (networkSaveDnsmasqHostsfile(network, dctx, false) < 0) { + if (networkSaveDnsmasqHostsfile(ipdef, dctx, false) < 0) { virCommandAddArgPair(cmd, "--dhcp-hostsfile", dctx->hostsfile->path); } dnsmasqContextFree(dctx); } - if (network->def->tftproot) { + if (ipdef->tftproot) { virCommandAddArgList(cmd, "--enable-tftp", - "--tftp-root", network->def->tftproot, + "--tftp-root", ipdef->tftproot, NULL); } - if (network->def->bootfile) { - + if (ipdef->bootfile) { virCommandAddArg(cmd, "--dhcp-boot"); - if (VIR_SOCKET_HAS_ADDR(&network->def->bootserver)) { - char *bootserver = virSocketFormatAddr(&network->def->bootserver); + if (VIR_SOCKET_HAS_ADDR(&ipdef->bootserver)) { + char *bootserver = virSocketFormatAddr(&ipdef->bootserver); if (!bootserver) goto cleanup; virCommandAddArgFormat(cmd, "%s%s%s", - network->def->bootfile, ",,", bootserver); + ipdef->bootfile, ",,", bootserver); VIR_FREE(bootserver); } else { - virCommandAddArg(cmd, network->def->bootfile); + virCommandAddArg(cmd, ipdef->bootfile); } } @@ -521,9 +520,9 @@ cleanup: return ret; } - static int -dhcpStartDhcpDaemon(virNetworkObjPtr network) +dhcpStartDhcpDaemon(virNetworkObjPtr network, + virNetworkIpDefPtr ipdef) { virCommandPtr cmd = NULL; char *pidfile = NULL; @@ -531,7 +530,7 @@ dhcpStartDhcpDaemon(virNetworkObjPtr network) network->dnsmasqPid = -1; - if (!VIR_SOCKET_IS_FAMILY(&network->def->ipAddress, AF_INET)) { + if (!VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET)) { networkReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot start dhcp daemon without IPv4 address for server")); goto cleanup; @@ -556,7 +555,7 @@ dhcpStartDhcpDaemon(virNetworkObjPtr network) } cmd = virCommandNew(DNSMASQ); - if (networkBuildDnsmasqArgv(network, pidfile, cmd) < 0) { + if (networkBuildDnsmasqArgv(network, ipdef, pidfile, cmd) < 0) { goto cleanup; } @@ -584,8 +583,10 @@ cleanup: static int networkAddMasqueradingIptablesRules(struct network_driver *driver, - virNetworkObjPtr network) { - int prefix = virNetworkDefPrefix(network->def); + virNetworkObjPtr network, + virNetworkIpDefPtr ipdef) +{ + int prefix = virNetworkIpDefPrefix(ipdef); if (prefix < 0) { networkReportError(VIR_ERR_INTERNAL_ERROR, @@ -596,7 +597,7 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver, /* allow forwarding packets from the bridge interface */ if (iptablesAddForwardAllowOut(driver->iptables, - &network->def->ipAddress, + &ipdef->address, prefix, network->def->bridge, network->def->forwardDev) < 0) { @@ -608,7 +609,7 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver, /* allow forwarding packets to the bridge interface if they are part of an existing connection */ if (iptablesAddForwardAllowRelatedIn(driver->iptables, - &network->def->ipAddress, + &ipdef->address, prefix, network->def->bridge, network->def->forwardDev) < 0) { @@ -643,7 +644,7 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver, /* First the generic masquerade rule for other protocols */ if (iptablesAddForwardMasquerade(driver->iptables, - &network->def->ipAddress, + &ipdef->address, prefix, network->def->forwardDev, NULL) < 0) { @@ -655,7 +656,7 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver, /* UDP with a source port restriction */ if (iptablesAddForwardMasquerade(driver->iptables, - &network->def->ipAddress, + &ipdef->address, prefix, network->def->forwardDev, "udp") < 0) { @@ -667,7 +668,7 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver, /* TCP with a source port restriction */ if (iptablesAddForwardMasquerade(driver->iptables, - &network->def->ipAddress, + &ipdef->address, prefix, network->def->forwardDev, "tcp") < 0) { @@ -681,25 +682,25 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver, masqerr5: iptablesRemoveForwardMasquerade(driver->iptables, - &network->def->ipAddress, + &ipdef->address, prefix, network->def->forwardDev, "udp"); masqerr4: iptablesRemoveForwardMasquerade(driver->iptables, - &network->def->ipAddress, + &ipdef->address, prefix, network->def->forwardDev, NULL); masqerr3: iptablesRemoveForwardAllowRelatedIn(driver->iptables, - &network->def->ipAddress, + &ipdef->address, prefix, network->def->bridge, network->def->forwardDev); masqerr2: iptablesRemoveForwardAllowOut(driver->iptables, - &network->def->ipAddress, + &ipdef->address, prefix, network->def->bridge, network->def->forwardDev); @@ -709,8 +710,9 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver, static int networkAddRoutingIptablesRules(struct network_driver *driver, - virNetworkObjPtr network) { - int prefix = virNetworkDefPrefix(network->def); + virNetworkObjPtr network, + virNetworkIpDefPtr ipdef) { + int prefix = virNetworkIpDefPrefix(ipdef); if (prefix < 0) { networkReportError(VIR_ERR_INTERNAL_ERROR, @@ -721,7 +723,7 @@ networkAddRoutingIptablesRules(struct network_driver *driver, /* allow routing packets from the bridge interface */ if (iptablesAddForwardAllowOut(driver->iptables, - &network->def->ipAddress, + &ipdef->address, prefix, network->def->bridge, network->def->forwardDev) < 0) { @@ -733,7 +735,7 @@ networkAddRoutingIptablesRules(struct network_driver *driver, /* allow routing packets to the bridge interface */ if (iptablesAddForwardAllowIn(driver->iptables, - &network->def->ipAddress, + &ipdef->address, prefix, network->def->bridge, network->def->forwardDev) < 0) { @@ -748,7 +750,7 @@ networkAddRoutingIptablesRules(struct network_driver *driver, routeerr2: iptablesRemoveForwardAllowOut(driver->iptables, - &network->def->ipAddress, + &ipdef->address, prefix, network->def->bridge, network->def->forwardDev); @@ -758,7 +760,8 @@ networkAddRoutingIptablesRules(struct network_driver *driver, static int networkAddIptablesRules(struct network_driver *driver, - virNetworkObjPtr network) { + virNetworkObjPtr network, + virNetworkIpDefPtr ipdef) { /* allow DHCP requests through to dnsmasq */ if (iptablesAddTcpInput(driver->iptables, network->def->bridge, 67) < 0) { @@ -791,7 +794,7 @@ networkAddIptablesRules(struct network_driver *driver, } /* allow TFTP requests through to dnsmasq */ - if (network->def->tftproot && + if (ipdef && ipdef->tftproot && iptablesAddUdpInput(driver->iptables, network->def->bridge, 69) < 0) { networkReportError(VIR_ERR_SYSTEM_ERROR, _("failed to add iptables rule to allow TFTP requests from '%s'"), @@ -824,29 +827,30 @@ networkAddIptablesRules(struct network_driver *driver, goto err7; } - - /* If masquerading is enabled, set up the rules*/ - if (network->def->forwardType == VIR_NETWORK_FORWARD_NAT && - networkAddMasqueradingIptablesRules(driver, network) < 0) - goto err8; - /* else if routing is enabled, set up the rules*/ - else if (network->def->forwardType == VIR_NETWORK_FORWARD_ROUTE && - networkAddRoutingIptablesRules(driver, network) < 0) - goto err8; - - /* If we are doing local DHCP service on this network, attempt to - * add a rule that will fixup the checksum of DHCP response - * packets back to the guests (but report failure without - * aborting, since not all iptables implementations support it). - */ - - if ((VIR_SOCKET_HAS_ADDR(&network->def->ipAddress) || - network->def->nranges) && - (iptablesAddOutputFixUdpChecksum(driver->iptables, - network->def->bridge, 68) < 0)) { - VIR_WARN("Could not add rule to fixup DHCP response checksums " - "on network '%s'.", network->def->name); - VIR_WARN0("May need to update iptables package & kernel to support CHECKSUM rule."); + if (ipdef) { + /* If masquerading is enabled, set up the rules*/ + if (network->def->forwardType == VIR_NETWORK_FORWARD_NAT && + networkAddMasqueradingIptablesRules(driver, network, ipdef) < 0) + goto err8; + /* else if routing is enabled, set up the rules*/ + else if (network->def->forwardType == VIR_NETWORK_FORWARD_ROUTE && + networkAddRoutingIptablesRules(driver, network, ipdef) < 0) + goto err8; + + /* If we are doing local DHCP service on this network, attempt to + * add a rule that will fixup the checksum of DHCP response + * packets back to the guests (but report failure without + * aborting, since not all iptables implementations support it). + */ + + if (ipdef && (VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET) || + ipdef->nranges) && + (iptablesAddOutputFixUdpChecksum(driver->iptables, + network->def->bridge, 68) < 0)) { + VIR_WARN("Could not add rule to fixup DHCP response checksums " + "on network '%s'.", network->def->name); + VIR_WARN0("May need to update iptables package & kernel to support CHECKSUM rule."); + } } return 0; @@ -861,7 +865,7 @@ networkAddIptablesRules(struct network_driver *driver, iptablesRemoveForwardRejectOut(driver->iptables, network->def->bridge); err5: - if (network->def->tftproot) { + if (ipdef && ipdef->tftproot) { iptablesRemoveUdpInput(driver->iptables, network->def->bridge, 69); } err4tftp: @@ -878,14 +882,16 @@ networkAddIptablesRules(struct network_driver *driver, static void networkRemoveIptablesRules(struct network_driver *driver, - virNetworkObjPtr network) { - if (VIR_SOCKET_HAS_ADDR(&network->def->ipAddress) || - network->def->nranges) { + virNetworkObjPtr network, + virNetworkIpDefPtr ipdef) { + + if (ipdef && (VIR_SOCKET_HAS_ADDR(&ipdef->address) || + ipdef->nranges)) { iptablesRemoveOutputFixUdpChecksum(driver->iptables, network->def->bridge, 68); } - if (network->def->forwardType != VIR_NETWORK_FORWARD_NONE) { - int prefix = virNetworkDefPrefix(network->def); + if (ipdef && network->def->forwardType != VIR_NETWORK_FORWARD_NONE) { + int prefix = virNetworkIpDefPrefix(ipdef); if (prefix < 0) { networkReportError(VIR_ERR_INTERNAL_ERROR, @@ -896,34 +902,35 @@ networkRemoveIptablesRules(struct network_driver *driver, if (network->def->forwardType == VIR_NETWORK_FORWARD_NAT) { iptablesRemoveForwardMasquerade(driver->iptables, - &network->def->ipAddress, + &ipdef->address, prefix, network->def->forwardDev, "tcp"); iptablesRemoveForwardMasquerade(driver->iptables, - &network->def->ipAddress, + &ipdef->address, prefix, network->def->forwardDev, "udp"); iptablesRemoveForwardMasquerade(driver->iptables, - &network->def->ipAddress, + &ipdef->address, prefix, network->def->forwardDev, NULL); iptablesRemoveForwardAllowRelatedIn(driver->iptables, - &network->def->ipAddress, + &ipdef->address, prefix, network->def->bridge, network->def->forwardDev); - } else if (network->def->forwardType == VIR_NETWORK_FORWARD_ROUTE) + } else if (network->def->forwardType == VIR_NETWORK_FORWARD_ROUTE) { iptablesRemoveForwardAllowIn(driver->iptables, - &network->def->ipAddress, + &ipdef->address, prefix, network->def->bridge, network->def->forwardDev); + } iptablesRemoveForwardAllowOut(driver->iptables, - &network->def->ipAddress, + &ipdef->address, prefix, network->def->bridge, network->def->forwardDev); @@ -932,7 +939,7 @@ error: iptablesRemoveForwardAllowCross(driver->iptables, network->def->bridge); iptablesRemoveForwardRejectIn(driver->iptables, network->def->bridge); iptablesRemoveForwardRejectOut(driver->iptables, network->def->bridge); - if (network->def->tftproot) + if (ipdef && ipdef->tftproot) iptablesRemoveUdpInput(driver->iptables, network->def->bridge, 69); iptablesRemoveUdpInput(driver->iptables, network->def->bridge, 53); iptablesRemoveTcpInput(driver->iptables, network->def->bridge, 53); @@ -951,10 +958,18 @@ networkReloadIptablesRules(struct network_driver *driver) virNetworkObjLock(driver->networks.objs[i]); if (virNetworkObjIsActive(driver->networks.objs[i])) { - networkRemoveIptablesRules(driver, driver->networks.objs[i]); - if (networkAddIptablesRules(driver, driver->networks.objs[i]) < 0) { - /* failed to add but already logged */ - } + virNetworkIpDefPtr ipv4def; + + /* Find the one allowed IPv4 ip address in the definition */ + /* Even if none is found, we still call the functions below */ + ipv4def = virNetworkDefGetIpByIndex(driver->networks.objs[i]->def, + AF_INET, 0); + networkRemoveIptablesRules(driver, driver->networks.objs[i], + ipv4def); + if (networkAddIptablesRules(driver, driver->networks.objs[i], + ipv4def) < 0) { + /* failed to add but already logged */ + } } virNetworkObjUnlock(driver->networks.objs[i]); @@ -1028,7 +1043,8 @@ cleanup: * other scenarios where we can ruin host network connectivity. * XXX: Using a proper library is preferred over parsing /proc */ -static int networkCheckRouteCollision(virNetworkObjPtr network) +static int networkCheckRouteCollision(virNetworkObjPtr network, + virNetworkIpDefPtr ipdef) { int ret = -1, len; unsigned int net_dest; @@ -1036,18 +1052,18 @@ static int networkCheckRouteCollision(virNetworkObjPtr network) char *cur, *buf = NULL; enum {MAX_ROUTE_SIZE = 1024*64}; - if (!VIR_SOCKET_IS_FAMILY(&network->def->ipAddress, AF_INET)) { + if (!VIR_SOCKET_IS_FAMILY(&ipdef->address, AF_INET)) { /* Only support collision check for IPv4 */ return 0; } - if (virNetworkDefNetmask(network->def, &netmask) < 0) { + if (virNetworkIpDefNetmask(ipdef, &netmask) < 0) { networkReportError(VIR_ERR_INTERNAL_ERROR, _("Failed to get netmask of '%s'"), network->def->bridge); } - net_dest = (network->def->ipAddress.data.inet4.sin_addr.s_addr & + net_dest = (ipdef->address.data.inet4.sin_addr.s_addr & netmask.data.inet4.sin_addr.s_addr); /* Read whole routing table into memory */ @@ -1121,6 +1137,7 @@ static int networkStartNetworkDaemon(struct network_driver *driver, { int err; virErrorPtr save_err; + virNetworkIpDefPtr ipv4def; if (virNetworkObjIsActive(network)) { networkReportError(VIR_ERR_INTERNAL_ERROR, @@ -1128,8 +1145,11 @@ static int networkStartNetworkDaemon(struct network_driver *driver, return -1; } + /* find the one allowed IPv4 ip address in the definition */ + ipv4def = virNetworkDefGetIpByIndex(network->def, AF_INET, 0); + /* Check to see if network collides with an existing route */ - if (networkCheckRouteCollision(network) < 0) + if (ipv4def && networkCheckRouteCollision(network, ipv4def) < 0) return -1; if ((err = brAddBridge(driver->brctl, network->def->bridge))) { @@ -1158,8 +1178,8 @@ static int networkStartNetworkDaemon(struct network_driver *driver, goto err_delbr; } - if (VIR_SOCKET_HAS_ADDR(&network->def->ipAddress)) { - int prefix = virNetworkDefPrefix(network->def); + if (ipv4def) { + int prefix = virNetworkIpDefPrefix(ipv4def); if (prefix < 0) { networkReportError(VIR_ERR_INTERNAL_ERROR, @@ -1169,7 +1189,7 @@ static int networkStartNetworkDaemon(struct network_driver *driver, } if ((err = brAddInetAddress(driver->brctl, network->def->bridge, - &network->def->ipAddress, prefix))) { + &ipv4def->address, prefix))) { networkReportError(VIR_ERR_INTERNAL_ERROR, _("cannot set IP address on bridge '%s'"), network->def->bridge); @@ -1184,7 +1204,7 @@ static int networkStartNetworkDaemon(struct network_driver *driver, goto err_delbr; } - if (networkAddIptablesRules(driver, network) < 0) + if (ipv4def && networkAddIptablesRules(driver, network, ipv4def) < 0) goto err_delbr1; if (network->def->forwardType != VIR_NETWORK_FORWARD_NONE && @@ -1194,12 +1214,13 @@ static int networkStartNetworkDaemon(struct network_driver *driver, goto err_delbr2; } - if ((VIR_SOCKET_HAS_ADDR(&network->def->ipAddress) || - network->def->nranges) && - dhcpStartDhcpDaemon(network) < 0) + /* + * Start the dhcp daemon for the 1st (and only supported) ipv4 + * address. + */ + if (ipv4def && dhcpStartDhcpDaemon(network, ipv4def) < 0) goto err_delbr2; - /* Persist the live configuration now we have bridge info */ if (virNetworkSaveConfig(NETWORK_STATE_DIR, network->def) < 0) { goto err_kill; @@ -1217,7 +1238,8 @@ static int networkStartNetworkDaemon(struct network_driver *driver, err_delbr2: save_err = virSaveLastError(); - networkRemoveIptablesRules(driver, network); + if (ipv4def) + networkRemoveIptablesRules(driver, network, ipv4def); if (save_err) { virSetError(save_err); virFreeError(save_err); @@ -1246,6 +1268,7 @@ static int networkShutdownNetworkDaemon(struct network_driver *driver, { int err; char *stateFile; + virNetworkIpDefPtr ipv4def; VIR_INFO(_("Shutting down network '%s'"), network->def->name); @@ -1262,7 +1285,10 @@ static int networkShutdownNetworkDaemon(struct network_driver *driver, if (network->dnsmasqPid > 0) kill(network->dnsmasqPid, SIGTERM); - networkRemoveIptablesRules(driver, network); + /* find the one allowed IPv4 ip address in the definition */ + ipv4def = virNetworkDefGetIpByIndex(network->def, AF_INET, 0); + if (ipv4def) + networkRemoveIptablesRules(driver, network, ipv4def); char ebuf[1024]; if ((err = brSetInterfaceUp(driver->brctl, network->def->bridge, 0))) { @@ -1526,6 +1552,7 @@ cleanup: static virNetworkPtr networkDefine(virConnectPtr conn, const char *xml) { struct network_driver *driver = conn->networkPrivateData; + virNetworkIpDefPtr ipv4def; virNetworkDefPtr def; virNetworkObjPtr network = NULL; virNetworkPtr ret = NULL; @@ -1556,12 +1583,15 @@ static virNetworkPtr networkDefine(virConnectPtr conn, const char *xml) { goto cleanup; } - if (network->def->nhosts > 0) { + /* we only support dhcp on one IPv4 address per defined network */ + ipv4def = virNetworkDefGetIpByIndex(network->def, AF_INET, 0); + + if (ipv4def && ipv4def->nhosts > 0) { dnsmasqContext *dctx = dnsmasqContextNew(network->def->name, DNSMASQ_STATE_DIR); if (dctx == NULL) goto cleanup; - networkSaveDnsmasqHostsfile(network, dctx, true); + networkSaveDnsmasqHostsfile(ipv4def, dctx, true); dnsmasqContextFree(dctx); } @@ -1577,7 +1607,8 @@ cleanup: static int networkUndefine(virNetworkPtr net) { struct network_driver *driver = net->conn->networkPrivateData; - virNetworkObjPtr network = NULL; + virNetworkObjPtr network; + virNetworkIpDefPtr ipv4def; int ret = -1; networkDriverLock(driver); @@ -1600,7 +1631,10 @@ static int networkUndefine(virNetworkPtr net) { network) < 0) goto cleanup; - if (network->def->nhosts > 0) { + /* find the one allowed IPv4 ip address in the definition */ + ipv4def = virNetworkDefGetIpByIndex(network->def, AF_INET, 0); + + if (ipv4def && ipv4def->nhosts > 0) { dnsmasqContext *dctx = dnsmasqContextNew(network->def->name, DNSMASQ_STATE_DIR); if (dctx == NULL) goto cleanup; diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index 728c501..03defb5 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -7038,9 +7038,23 @@ static virNetworkPtr vboxNetworkDefineCreateXML(virConnectPtr conn, const char * IHostNetworkInterface *networkInterface = NULL; virNetworkDefPtr def = virNetworkDefParseString(xml); + virNetworkIpDefPtr ipdef; + virSocketAddr netmask; if ( (!def) - || (def->forwardType != VIR_NETWORK_FORWARD_NONE)) + || (def->forwardType != VIR_NETWORK_FORWARD_NONE) + || (def->nips == 0 || !def->ips)) + goto cleanup; + + /* Look for the first IPv4 IP address definition and use that. + * If there weren't any IPv4 addresses, ignore the network (since it's + * required below to have an IPv4 address) + */ + ipdef = virNetworkDefGetIpByIndex(def, AF_INET, 0); + if (!ipdef) + goto cleanup; + + if (virNetworkIpDefNetmask(ipdef, &netmask) < 0) goto cleanup; /* the current limitation of hostonly network is that you can't @@ -7096,9 +7110,9 @@ static virNetworkPtr vboxNetworkDefineCreateXML(virConnectPtr conn, const char * /* Currently support only one dhcp server per network * with contigious address space from start to end */ - if ((def->nranges >= 1) && - VIR_SOCKET_HAS_ADDR(&def->ranges[0].start) && - VIR_SOCKET_HAS_ADDR(&def->ranges[0].end)) { + if ((ipdef->nranges >= 1) && + VIR_SOCKET_HAS_ADDR(&ipdef->ranges[0].start) && + VIR_SOCKET_HAS_ADDR(&ipdef->ranges[0].end)) { IDHCPServer *dhcpServer = NULL; data->vboxObj->vtbl->FindDHCPServerByNetworkName(data->vboxObj, @@ -7118,10 +7132,10 @@ static virNetworkPtr vboxNetworkDefineCreateXML(virConnectPtr conn, const char * PRUnichar *toIPAddressUtf16 = NULL; PRUnichar *trunkTypeUtf16 = NULL; - ipAddressUtf16 = vboxSocketFormatAddrUtf16(data, &def->ipAddress); - networkMaskUtf16 = vboxSocketFormatAddrUtf16(data, &def->netmask); - fromIPAddressUtf16 = vboxSocketFormatAddrUtf16(data, &def->ranges[0].start); - toIPAddressUtf16 = vboxSocketFormatAddrUtf16(data, &def->ranges[0].end); + ipAddressUtf16 = vboxSocketFormatAddrUtf16(data, &ipdef->address); + networkMaskUtf16 = vboxSocketFormatAddrUtf16(data, &netmask); + fromIPAddressUtf16 = vboxSocketFormatAddrUtf16(data, &ipdef->ranges[0].start); + toIPAddressUtf16 = vboxSocketFormatAddrUtf16(data, &ipdef->ranges[0].end); if (ipAddressUtf16 == NULL || networkMaskUtf16 == NULL || fromIPAddressUtf16 == NULL || toIPAddressUtf16 == NULL) { @@ -7158,13 +7172,13 @@ static virNetworkPtr vboxNetworkDefineCreateXML(virConnectPtr conn, const char * } } - if ((def->nhosts >= 1) && - VIR_SOCKET_HAS_ADDR(&def->hosts[0].ip)) { + if ((ipdef->nhosts >= 1) && + VIR_SOCKET_HAS_ADDR(&ipdef->hosts[0].ip)) { PRUnichar *ipAddressUtf16 = NULL; PRUnichar *networkMaskUtf16 = NULL; - ipAddressUtf16 = vboxSocketFormatAddrUtf16(data, &def->hosts[0].ip); - networkMaskUtf16 = vboxSocketFormatAddrUtf16(data, &def->netmask); + ipAddressUtf16 = vboxSocketFormatAddrUtf16(data, &ipdef->hosts[0].ip); + networkMaskUtf16 = vboxSocketFormatAddrUtf16(data, &netmask); if (ipAddressUtf16 == NULL || networkMaskUtf16 == NULL) { VBOX_UTF16_FREE(ipAddressUtf16); @@ -7385,12 +7399,19 @@ static int vboxNetworkDestroy(virNetworkPtr network) { static char *vboxNetworkDumpXML(virNetworkPtr network, int flags ATTRIBUTE_UNUSED) { VBOX_OBJECT_HOST_CHECK(network->conn, char *, NULL); virNetworkDefPtr def = NULL; + virNetworkIpDefPtr ipdef = NULL; char *networkNameUtf8 = NULL; if (VIR_ALLOC(def) < 0) { virReportOOMError(); goto cleanup; } + if (VIR_ALLOC(ipdef) < 0) { + virReportOOMError(); + goto cleanup; + } + def->ips = ipdef; + def->nips = 1; if (virAsprintf(&networkNameUtf8, "HostInterfaceNetworking-%s", network->name) < 0) { virReportOOMError(); @@ -7427,8 +7448,8 @@ static char *vboxNetworkDumpXML(virNetworkPtr network, int flags ATTRIBUTE_UNUSE networkNameUtf16, &dhcpServer); if (dhcpServer) { - def->nranges = 1; - if (VIR_ALLOC_N(def->ranges, def->nranges) >=0 ) { + ipdef->nranges = 1; + if (VIR_ALLOC_N(ipdef->ranges, ipdef->nranges) >=0 ) { PRUnichar *ipAddressUtf16 = NULL; PRUnichar *networkMaskUtf16 = NULL; PRUnichar *fromIPAddressUtf16 = NULL; @@ -7443,13 +7464,13 @@ static char *vboxNetworkDumpXML(virNetworkPtr network, int flags ATTRIBUTE_UNUSE * with contigious address space from start to end */ if (vboxSocketParseAddrUtf16(data, ipAddressUtf16, - &def->ipAddress) < 0 || + &ipdef->address) < 0 || vboxSocketParseAddrUtf16(data, networkMaskUtf16, - &def->netmask) < 0 || + &ipdef->netmask) < 0 || vboxSocketParseAddrUtf16(data, fromIPAddressUtf16, - &def->ranges[0].start) < 0 || + &ipdef->ranges[0].start) < 0 || vboxSocketParseAddrUtf16(data, toIPAddressUtf16, - &def->ranges[0].end) < 0) { + &ipdef->ranges[0].end) < 0) { errorOccurred = true; } @@ -7462,16 +7483,16 @@ static char *vboxNetworkDumpXML(virNetworkPtr network, int flags ATTRIBUTE_UNUSE goto cleanup; } } else { - def->nranges = 0; + ipdef->nranges = 0; virReportOOMError(); } - def->nhosts = 1; - if (VIR_ALLOC_N(def->hosts, def->nhosts) >=0 ) { - def->hosts[0].name = strdup(network->name); - if (def->hosts[0].name == NULL) { - VIR_FREE(def->hosts); - def->nhosts = 0; + ipdef->nhosts = 1; + if (VIR_ALLOC_N(ipdef->hosts, ipdef->nhosts) >=0 ) { + ipdef->hosts[0].name = strdup(network->name); + if (ipdef->hosts[0].name == NULL) { + VIR_FREE(ipdef->hosts); + ipdef->nhosts = 0; virReportOOMError(); } else { PRUnichar *macAddressUtf16 = NULL; @@ -7481,10 +7502,10 @@ static char *vboxNetworkDumpXML(virNetworkPtr network, int flags ATTRIBUTE_UNUSE networkInterface->vtbl->GetHardwareAddress(networkInterface, &macAddressUtf16); networkInterface->vtbl->GetIPAddress(networkInterface, &ipAddressUtf16); - VBOX_UTF16_TO_UTF8(macAddressUtf16, &def->hosts[0].mac); + VBOX_UTF16_TO_UTF8(macAddressUtf16, &ipdef->hosts[0].mac); if (vboxSocketParseAddrUtf16(data, ipAddressUtf16, - &def->hosts[0].ip) < 0) { + &ipdef->hosts[0].ip) < 0) { errorOccurred = true; } @@ -7496,7 +7517,7 @@ static char *vboxNetworkDumpXML(virNetworkPtr network, int flags ATTRIBUTE_UNUSE } } } else { - def->nhosts = 0; + ipdef->nhosts = 0; } VBOX_RELEASE(dhcpServer); @@ -7509,9 +7530,9 @@ static char *vboxNetworkDumpXML(virNetworkPtr network, int flags ATTRIBUTE_UNUSE networkInterface->vtbl->GetIPAddress(networkInterface, &ipAddressUtf16); if (vboxSocketParseAddrUtf16(data, networkMaskUtf16, - &def->netmask) < 0 || + &ipdef->netmask) < 0 || vboxSocketParseAddrUtf16(data, ipAddressUtf16, - &def->ipAddress) < 0) { + &ipdef->address) < 0) { errorOccurred = true; } diff --git a/tests/networkxml2xmlin/nat-network.xml b/tests/networkxml2xmlin/nat-network.xml index 93ab186..23f7fcb 100644 --- a/tests/networkxml2xmlin/nat-network.xml +++ b/tests/networkxml2xmlin/nat-network.xml @@ -10,4 +10,12 @@ <host mac="00:16:3e:3e:a9:1a" name="b.example.com" ip="192.168.122.11" /> </dhcp> </ip> + <ip family="ipv4" address="192.168.123.1" netmask="255.255.255.0"> + </ip> + <ip family="ipv6" address="2001:db8:ac10:fe01::1" prefix="64"> + </ip> + <ip family="ipv6" address="2001:db8:ac10:fd01::1" prefix="64"> + </ip> + <ip family="ipv4" address="10.24.10.1"> + </ip> </network> diff --git a/tests/networkxml2xmlout/nat-network.xml b/tests/networkxml2xmlout/nat-network.xml index 036d4fb..eb71d9e 100644 --- a/tests/networkxml2xmlout/nat-network.xml +++ b/tests/networkxml2xmlout/nat-network.xml @@ -10,4 +10,12 @@ <host mac='00:16:3e:3e:a9:1a' name='b.example.com' ip='192.168.122.11' /> </dhcp> </ip> + <ip family='ipv4' address='192.168.123.1' netmask='255.255.255.0'> + </ip> + <ip family='ipv6' address='2001:db8:ac10:fe01::1' prefix='64'> + </ip> + <ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'> + </ip> + <ip family='ipv4' address='10.24.10.1'> + </ip> </network> -- 1.7.3.4 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list