[libvirt] [PATCH 0/3] IPv6 enhancements plus dnsmasq conf-file

The three patches are submitted with "one" because there is a dependency between them. That is, patch 2 (DHCPv6) assumes that patch 1 (guest-to-guest IPV6) has been applied and patch 3 (dnsmasq conf-file) assumes that patch 2 has been applied. While other files are also changed, most of the changes occur in src/network/bridge_driver.c and result in the conflicts. Yes, patch 1 was previously submitted as a stand-alone patch but it became obvious that it was not really standalone. This submittal is a "re-do" of my previous submittal which involved similar changes. Specifically, the order of the patches have been changed so that the conf-file changes are after the DHCPv6 changes. In addition, some code has been cleaned up. For example, there is no need to modify the radvd configuration since both DHCPv6 and dnsmasq's handling of RA are both keyed to dnsmasq version 2.64 or later. Gene Czarcinski (3): v1: allow guest to guest IPv6 without gateway definition v7.1 add support for DHCPv6 v7.2: put dnsmasq parameters into conf-file docs/formatnetwork.html.in | 126 +++++- src/conf/network_conf.c | 100 +++-- src/conf/network_conf.h | 1 + src/network/bridge_driver.c | 461 +++++++++++++++------ src/network/bridge_driver.h | 7 +- src/util/dnsmasq.c | 9 +- tests/networkxml2argvdata/dhcp6-network.argv | 17 + tests/networkxml2argvdata/dhcp6-network.xml | 16 + tests/networkxml2argvdata/isolated-network.argv | 25 +- tests/networkxml2argvdata/nat-network-dhcp6.argv | 20 + tests/networkxml2argvdata/nat-network-dhcp6.xml | 26 ++ .../networkxml2argvdata/nat-network-dns-hosts.argv | 15 +- .../nat-network-dns-srv-record-minimal.argv | 37 +- .../nat-network-dns-srv-record.argv | 37 +- .../nat-network-dns-txt-record.argv | 31 +- tests/networkxml2argvdata/nat-network.argv | 29 +- tests/networkxml2argvdata/netboot-network.argv | 29 +- .../networkxml2argvdata/netboot-proxy-network.argv | 26 +- .../routed-network-dhcphost.argv | 15 + .../routed-network-dhcphost.xml | 19 + tests/networkxml2argvdata/routed-network.argv | 13 +- tests/networkxml2argvtest.c | 46 +- 22 files changed, 790 insertions(+), 315 deletions(-) create mode 100644 tests/networkxml2argvdata/dhcp6-network.argv create mode 100644 tests/networkxml2argvdata/dhcp6-network.xml create mode 100644 tests/networkxml2argvdata/nat-network-dhcp6.argv create mode 100644 tests/networkxml2argvdata/nat-network-dhcp6.xml create mode 100644 tests/networkxml2argvdata/routed-network-dhcphost.argv create mode 100644 tests/networkxml2argvdata/routed-network-dhcphost.xml -- 1.7.11.7

This patch adds the capability for virtual guests to do IPv6 communication via a virtual network interface with no IPv6 (gateway) addresses specified. That is, ip6tables rules are added to permit the communications. This capability currently exists for IPv4. This patch allows creation of a completely isolated IPv6 network. Note that virtual guests cannot communication with the virtualization host via this interface. Also note that: net.ipv6.conf.<interface_name>.disable_ipv6 = 1 which is the same as if no ip6tables rules were added. --- docs/formatnetwork.html.in | 18 ++++++++++++++++++ src/network/bridge_driver.c | 22 ++++++++++++++-------- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in index 49206dd..7b3b25c 100644 --- a/docs/formatnetwork.html.in +++ b/docs/formatnetwork.html.in @@ -773,5 +773,23 @@ </forward> </network></pre> + <h3><a name="examplesNoGateway">Network config with no gateway addresses</a></h3> + + <p> + A valid network definition can contain no IPv4 or IPv6 addresses. Such a definition + can be used for a "very private" or "very isolated" network since it will not be + possible to communicate with the virtualization host via this network. However, + this virtual network interface can be used for communication between virtual guest + systems. This works for IPv4 and <span class="since">(Since 1.0.1)</span> IPv6. + </p> + + <pre> + <network> + <name>nogw</name> + <uuid>7a3b7497-1ec7-8aef-6d5c-38dff9109e93</uuid> + <bridge name="virbr2" stp="on" delay="0" /> + <mac address='00:16:3E:5D:C7:9E'/> + </network></pre> + </body> </html> diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index c153d36..9c67348 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -1568,15 +1568,16 @@ networkRemoveRoutingIptablesRules(struct network_driver *driver, } } -/* Add all once/network rules required for IPv6 (if any IPv6 addresses are defined) */ +/* Add all once/network rules required for IPv6. + * Even if no IPv6 addresses are defined, allow IPv6 commuinications + * between virtual systems. If any IPv6 addresses are defined, then + * add the rules for regular operation. + */ static int networkAddGeneralIp6tablesRules(struct network_driver *driver, virNetworkObjPtr network) { - if (!virNetworkDefGetIpByIndex(network->def, AF_INET6, 0)) - return 0; - /* Catch all rules to block forwarding to/from bridges */ if (iptablesAddForwardRejectOut(driver->iptables, AF_INET6, @@ -1604,6 +1605,10 @@ networkAddGeneralIp6tablesRules(struct network_driver *driver, goto err3; } + /* if no IPv6 addresses are defined, we are done. */ + if (!virNetworkDefGetIpByIndex(network->def, AF_INET6, 0)) + return 0; + /* allow DNS over IPv6 */ if (iptablesAddTcpInput(driver->iptables, AF_INET6, network->def->bridge, 53) < 0) { @@ -1640,11 +1645,12 @@ static void networkRemoveGeneralIp6tablesRules(struct network_driver *driver, virNetworkObjPtr network) { - if (!virNetworkDefGetIpByIndex(network->def, AF_INET6, 0)) - return; + if (virNetworkDefGetIpByIndex(network->def, AF_INET6, 0)) { + iptablesRemoveUdpInput(driver->iptables, AF_INET6, network->def->bridge, 53); + iptablesRemoveTcpInput(driver->iptables, AF_INET6, network->def->bridge, 53); + } - iptablesRemoveUdpInput(driver->iptables, AF_INET6, network->def->bridge, 53); - iptablesRemoveTcpInput(driver->iptables, AF_INET6, network->def->bridge, 53); + /* the following rules are there even if no IPv6 address has been defined */ iptablesRemoveForwardAllowCross(driver->iptables, AF_INET6, network->def->bridge); iptablesRemoveForwardRejectIn(driver->iptables, AF_INET6, network->def->bridge); iptablesRemoveForwardRejectOut(driver->iptables, AF_INET6, network->def->bridge); -- 1.7.11.7

The DHCPv6 support includes IPV6 dhcp-range and dhcp-host for one IPv6 subnetwork on one interface. This support will only work if dnsmasq version >= 2.64; otherwise an error occurs if dhcp-range or dhcp-host is specified. Note that the check for dnsmasq version is performed at network start and only then will an error condition occur. Thus, it is possible to define/edit a network configuration which will not start. The error message indicates that dnsmasq version 2.64 or later is required. This patch provides the same DHCP support for IPv6 which has been available for IPv4. With dnsmasq >= 2.64, support for the RA service is now provided by dnsmasq (radvd is no longer needed/used/started). Documentation has been updated to reflect the new support. --- docs/formatnetwork.html.in | 108 ++++++- src/conf/network_conf.c | 100 ++++--- src/conf/network_conf.h | 1 + src/network/bridge_driver.c | 333 +++++++++++++++------ src/util/dnsmasq.c | 9 +- tests/networkxml2argvdata/dhcp6-network.argv | 17 ++ tests/networkxml2argvdata/dhcp6-network.xml | 16 + tests/networkxml2argvdata/isolated-network.argv | 15 +- tests/networkxml2argvdata/nat-network-dhcp6.argv | 20 ++ tests/networkxml2argvdata/nat-network-dhcp6.xml | 26 ++ .../networkxml2argvdata/nat-network-dns-hosts.argv | 15 +- .../nat-network-dns-srv-record-minimal.argv | 11 +- .../nat-network-dns-srv-record.argv | 11 +- .../nat-network-dns-txt-record.argv | 23 +- tests/networkxml2argvdata/nat-network.argv | 23 +- tests/networkxml2argvdata/netboot-network.argv | 23 +- .../networkxml2argvdata/netboot-proxy-network.argv | 20 +- .../routed-network-dhcphost.argv | 15 + .../routed-network-dhcphost.xml | 19 ++ tests/networkxml2argvdata/routed-network.argv | 11 +- tests/networkxml2argvtest.c | 4 + 21 files changed, 633 insertions(+), 187 deletions(-) create mode 100644 tests/networkxml2argvdata/dhcp6-network.argv create mode 100644 tests/networkxml2argvdata/dhcp6-network.xml create mode 100644 tests/networkxml2argvdata/nat-network-dhcp6.argv create mode 100644 tests/networkxml2argvdata/nat-network-dhcp6.xml create mode 100644 tests/networkxml2argvdata/routed-network-dhcphost.argv create mode 100644 tests/networkxml2argvdata/routed-network-dhcphost.xml diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in index 7b3b25c..50e00f9 100644 --- a/docs/formatnetwork.html.in +++ b/docs/formatnetwork.html.in @@ -577,8 +577,10 @@ dotted-decimal format, or an IPv6 address in standard colon-separated hexadecimal format, that will be configured on the bridge - device associated with the virtual network. To the guests this - address will be their default route. For IPv4 addresses, the <code>netmask</code> + device associated with the virtual network. To the guests this IPv4 + address will be their IPv4 default route. For IPv6, the default route is + established via Router Advertisement. + For IPv4 addresses, the <code>netmask</code> attribute defines the significant bits of the network address, again specified in dotted-decimal format. For IPv6 addresses, and as an alternate method for IPv4 addresses, you can specify @@ -587,10 +589,13 @@ could also be given as <code>prefix='24'</code>. The <code>family</code> attribute is used to specify the type of address - 'ipv4' or 'ipv6'; if no <code>family</code> is given, 'ipv4' is assumed. A network can have more than - one of each family of address defined, but only a single address can have a - <code>dhcp</code> or <code>tftp</code> element. <span class="since">Since 0.3.0; + one of each family of address defined, but only a single IPv4 address can have a + <code>dhcp</code> or <code>tftp</code> element. <span class="since">Since 0.3.0 </span> IPv6, multiple addresses on a single network, <code>family</code>, and - <code>prefix</code> since 0.8.7</span> + <code>prefix</code>. <span class="since">Since 0.8.7</span> In addition + to the one IPv4 address which has a <code>dhcp</code> definition, one IPv6 + address can have a <code>dhcp</code> definition. + <span class="since"> Since 1.0.1</span> <dl> <dt><code>tftp</code></dt> <dd>Immediately within @@ -611,27 +616,46 @@ <code>dhcp</code> element is not supported for IPv6, and is only supported on a single IP address per network for IPv4. <span class="since">Since 0.3.0</span> + The <code>dhcp</code> element is now supported for IPv6. + Again, there is a restriction that only one IPv6 address definition + is able to have a <code>dhcp</code> element. + <span class="since">Since 1.0.1</span> <dl> <dt><code>range</code></dt> <dd>The <code>start</code> and <code>end</code> attributes on the <code>range</code> element specify the boundaries of a pool of - IPv4 addresses to be provided to DHCP clients. These two addresses + addresses to be provided to DHCP clients. These two addresses must lie within the scope of the network defined on the parent - <code>ip</code> element. <span class="since">Since 0.3.0</span> + <code>ip</code> element. There may be zero or more + <code>range</code> elements specified. + <span class="since">Since 0.3.0</span> + <code>Range</code> can be specified for one IPv4 address, + one IPv6 address, or both. <span class="since">Since 1.0.1</span> </dd> <dt><code>host</code></dt> <dd>Within the <code>dhcp</code> element there may be zero or more - <code>host</code> elements; these specify hosts which will be given + <code>host</code> elements. These specify hosts which will be given names and predefined IP addresses by the built-in DHCP server. Any - such element must specify the MAC address of the host to be assigned + such IPv4 element must specify the MAC address of the host to be assigned a given name (via the <code>mac</code> attribute), the IP to be assigned to that host (via the <code>ip</code> attribute), and the name to be given that host by the DHCP server (via the <code>name</code> attribute). <span class="since">Since 0.4.5</span> + Within the IPv6 <code>dhcp</code> element zero or more + <code>host</code> elements are now supported. The definition for + an IPv6 <code>host</code> element differs from that for IPv4: + there is no <code>mac</code> attribute since a MAC address has no + defined meaning in IPv6. Instead, the <code>name</code> attribute is + used to identify the host to be assigned the IPv6 address. For DHCPv6, + the name is the plain name of the client host sent by the + client to the server. Note that this method of assigning a + specific IP address can be used instead of the <code>mac</code> + attribute for IPv4. <span class="since">Since 1.0.1</span> </dd> <dt><code>bootp</code></dt> <dd>The optional <code>bootp</code> - element specifies BOOTP options to be provided by the DHCP server. + element specifies BOOTP options to be provided by the DHCP + server for IPv4 only. Two attributes are supported: <code>file</code> is mandatory and gives the file to be used for the boot image; <code>server</code> is optional and gives the address of the TFTP server from which the boot @@ -674,6 +698,29 @@ <ip family="ipv6" address="2001:db8:ca2:2::1" prefix="64" /> </network></pre> + + <p> + Below is a variation of the above example which adds an IPv6 + dhcp range definition. + </p> + + <pre> + <network> + <name>default6</name> + <bridge name="virbr0" /> + <forward mode="nat"/> + <ip address="192.168.122.1" netmask="255.255.255.0"> + <dhcp> + <range start="192.168.122.2" end="192.168.122.254" /> + </dhcp> + </ip> + <ip family="ipv6" address="2001:db8:ca2:2::1" prefix="64" > + <dhcp> + <range start="2001:db8:ca2:2:1::10" end="2001:db8:ca2:2:1::ff" /> + </dhcp> + </ip> + </network></pre> + <h3><a name="examplesRoute">Routed network config</a></h3> <p> @@ -698,6 +745,29 @@ <ip family="ipv6" address="2001:db8:ca2:2::1" prefix="64" /> </network></pre> + <p> + Below is another IPv6 varition. Instead of a dhcp range being + specified, this example has a couple of IPv6 host definitions. + </p> + + <pre> + <network> + <name>local6</name> + <bridge name="virbr1" /> + <forward mode="route" dev="eth1"/> + <ip address="192.168.122.1" netmask="255.255.255.0"> + <dhcp> + <range start="192.168.122.2" end="192.168.122.254" /> + </dhcp> + </ip> + <ip family="ipv6" address="2001:db8:ca2:2::1" prefix="64" > + <dhcp> + <host name="paul" ip="2001:db8:ca2:2:3::1" /> + <host name="bob" ip="2001:db8:ca2:2:3::2" /> + </dhcp> + </ip> + </network></pre> + <h3><a name="examplesPrivate">Isolated network config</a></h3> <p> @@ -720,6 +790,24 @@ <ip family="ipv6" address="2001:db8:ca2:3::1" prefix="64" /> </network></pre> + <h3><a name="examplesPrivate6">Isolated IPv6 network config</a></h3> + + <p> + This variation of an isolated network defines only IPv6. + </p> + + <pre> + <network> + <name>sixnet</name> + <bridge name="virbr6" /> + <ip family="ipv6" address="2001:db8:ca2:6::1" prefix="64" > + <dhcp> + <host name="peter" ip="2001:db8:ca2:6:6::1" /> + <host name="dariusz" ip="2001:db8:ca2:6:6::2" /> + </dhcp> + </ip> + </network></pre> + <h3><a name="examplesBridge">Using an existing host bridge</a></h3> <p> diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 228951d..a56b2e6 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -633,6 +633,7 @@ cleanup: static int virNetworkDHCPHostDefParse(const char *networkName, + virNetworkIpDefPtr def, xmlNodePtr node, virNetworkDHCPHostDefPtr host, bool partialOkay) @@ -644,6 +645,13 @@ virNetworkDHCPHostDefParse(const char *networkName, 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 IPv6 network '%s'"), + mac, networkName); + goto cleanup; + } if (virMacAddrParse(mac, &addr) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Cannot parse MAC address '%s' in network '%s'"), @@ -686,10 +694,19 @@ virNetworkDHCPHostDefParse(const char *networkName, networkName); } } else { + if (VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET6)) { + if (!name) { + virReportError(VIR_ERR_XML_ERROR, + _("Static host definition in IPv6 network '%s' " + "must have name attribute"), + networkName); + goto cleanup; + } + } /* normal usage - you need at least one MAC address or one host name */ - if (!(mac || name)) { + else if (!(mac || name)) { virReportError(VIR_ERR_XML_ERROR, - _("Static host definition in network '%s' " + _("Static host definition in IPv4 network '%s' " "must have mac or name attribute"), networkName); goto cleanup; @@ -748,36 +765,39 @@ virNetworkDHCPDefParse(const char *networkName, virReportOOMError(); return -1; } - if (virNetworkDHCPHostDefParse(networkName, cur, + if (virNetworkDHCPHostDefParse(networkName, def, cur, &def->hosts[def->nhosts], false) < 0) { return -1; } 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"); + } else if (VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET)) { + /* the following only applies to IPv4 */ + 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; + } - if (server && - virSocketAddrParse(&inaddr, server, AF_UNSPEC) < 0) { - VIR_FREE(file); + def->bootfile = file; + def->bootserver = inaddr; VIR_FREE(server); - return -1; } - - def->bootfile = file; - def->bootserver = inaddr; - VIR_FREE(server); } cur = cur->next; @@ -1139,6 +1159,20 @@ virNetworkIPParseXML(const char *networkName, } } + if (VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET6)) { + /* parse IPv6-related info */ + cur = node->children; + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE && + xmlStrEqual(cur->name, BAD_CAST "dhcp")) { + result = virNetworkDHCPDefParse(networkName, def, cur); + if (result) + goto error; + } + cur = cur->next; + } + } + result = 0; error: @@ -2347,11 +2381,9 @@ virNetworkIpDefByIndex(virNetworkDefPtr def, int parentIndex) /* first find which ip element's dhcp host list to work on */ if (parentIndex >= 0) { ipdef = virNetworkDefGetIpByIndex(def, AF_UNSPEC, parentIndex); - if (!(ipdef && - VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET))) { + if (!(ipdef)) { virReportError(VIR_ERR_OPERATION_INVALID, _("couldn't update dhcp host entry - " - "no <ip family='ipv4'> " "element found at index %d in network '%s'"), parentIndex, def->name); } @@ -2364,17 +2396,17 @@ virNetworkIpDefByIndex(virNetworkDefPtr def, int parentIndex) for (ii = 0; (ipdef = virNetworkDefGetIpByIndex(def, AF_UNSPEC, ii)); ii++) { - if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET) && - (ipdef->nranges || ipdef->nhosts)) { + if (ipdef->nranges || ipdef->nhosts) break; - } } - if (!ipdef) + if (!ipdef) { ipdef = virNetworkDefGetIpByIndex(def, AF_INET, 0); + if (!ipdef) + ipdef = virNetworkDefGetIpByIndex(def, AF_INET6, 0); + } if (!ipdef) { virReportError(VIR_ERR_OPERATION_INVALID, _("couldn't update dhcp host entry - " - "no <ip family='ipv4'> " "element found in network '%s'"), def->name); } return ipdef; @@ -2404,7 +2436,7 @@ virNetworkDefUpdateIPDHCPHost(virNetworkDefPtr def, /* parse the xml into a virNetworkDHCPHostDef */ if (command == VIR_NETWORK_UPDATE_COMMAND_MODIFY) { - if (virNetworkDHCPHostDefParse(def->name, ctxt->node, &host, false) < 0) + if (virNetworkDHCPHostDefParse(def->name, ipdef, ctxt->node, &host, false) < 0) goto cleanup; /* search for the entry with this (mac|name), @@ -2437,7 +2469,7 @@ virNetworkDefUpdateIPDHCPHost(virNetworkDefPtr def, } else if ((command == VIR_NETWORK_UPDATE_COMMAND_ADD_FIRST) || (command == VIR_NETWORK_UPDATE_COMMAND_ADD_LAST)) { - if (virNetworkDHCPHostDefParse(def->name, ctxt->node, &host, true) < 0) + if (virNetworkDHCPHostDefParse(def->name, ipdef, ctxt->node, &host, true) < 0) goto cleanup; /* log error if an entry with same name/address/ip already exists */ @@ -2483,7 +2515,7 @@ virNetworkDefUpdateIPDHCPHost(virNetworkDefPtr def, } else if (command == VIR_NETWORK_UPDATE_COMMAND_DELETE) { - if (virNetworkDHCPHostDefParse(def->name, ctxt->node, &host, false) < 0) + if (virNetworkDHCPHostDefParse(def->name, ipdef, ctxt->node, &host, false) < 0) goto cleanup; /* find matching entry - all specified attributes must match */ diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index 3e46304..226da04 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -218,6 +218,7 @@ struct _virNetworkObj { unsigned int active : 1; unsigned int autostart : 1; unsigned int persistent : 1; + unsigned int dnsmasqVersion; virNetworkDefPtr def; /* The current definition */ virNetworkDefPtr newDef; /* New definition to activate at shutdown */ diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 9c67348..19a9170 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -75,6 +75,11 @@ #define VIR_FROM_THIS VIR_FROM_NETWORK +#define DNSMASQ_PREFIX_TEXT "Dnsmasq version " + +#define CHECK_DNSMASQ_VERSION() \ + (network->dnsmasqVersion >= 2064) + /* Main driver state */ struct network_driver { virMutex lock; @@ -582,20 +587,32 @@ cleanup: return ret; } + /* the following does not build a file, it builds a list + * which is later saved into a file + */ + static int -networkBuildDnsmasqHostsfile(dnsmasqContext *dctx, - virNetworkIpDefPtr ipdef, - virNetworkDNSDefPtr dnsdef) +networkBuildDnsmasqDhcpHostsList(dnsmasqContext *dctx, + virNetworkIpDefPtr ipdef) { - unsigned int i, j; + unsigned int i; for (i = 0; i < ipdef->nhosts; i++) { virNetworkDHCPHostDefPtr host = &(ipdef->hosts[i]); - if ((host->mac) && VIR_SOCKET_ADDR_VALID(&host->ip)) + if (VIR_SOCKET_ADDR_VALID(&host->ip)) if (dnsmasqAddDhcpHost(dctx, host->mac, &host->ip, host->name) < 0) return -1; } + return 0; +} + +static int +networkBuildDnsmasqHostsList(dnsmasqContext *dctx, + virNetworkDNSDefPtr dnsdef) +{ + unsigned int i, j; + if (dnsdef) { for (i = 0; i < dnsdef->nhosts; i++) { virNetworkDNSHostsDefPtr host = &(dnsdef->hosts[i]); @@ -613,7 +630,6 @@ networkBuildDnsmasqHostsfile(dnsmasqContext *dctx, static int networkBuildDnsmasqArgv(virNetworkObjPtr network, - virNetworkIpDefPtr ipdef, const char *pidfile, virCommandPtr cmd, dnsmasqContext *dctx) @@ -625,7 +641,8 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, char *recordPort = NULL; char *recordWeight = NULL; char *recordPriority = NULL; - virNetworkIpDefPtr tmpipdef; + virNetworkIpDefPtr tmpipdef, ipdef, ipv4def, ipv6def; + bool dhcp4flag, dhcp6flag; /* * NB, be careful about syntax for dnsmasq options in long format. @@ -650,14 +667,19 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, * Needed to ensure dnsmasq uses same algorithm for processing * multiple namedriver entries in /etc/resolv.conf as GLibC. */ - virCommandAddArgList(cmd, "--strict-order", "--bind-interfaces", NULL); + virCommandAddArgList(cmd, "--strict-order", + "--bind-interfaces", + "--domain-needed", + "--except-interface=lo", + NULL); - if (network->def->domain) + if (network->def->domain) { virCommandAddArgPair(cmd, "--domain", network->def->domain); + virCommandAddArg(cmd, "--expand-hosts"); + } /* need to specify local even if no domain specified */ virCommandAddArgFormat(cmd, "--local=/%s/", network->def->domain ? network->def->domain : ""); - virCommandAddArg(cmd, "--domain-needed"); if (pidfile) virCommandAddArgPair(cmd, "--pid-file", pidfile); @@ -665,10 +687,6 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, /* *no* conf file */ virCommandAddArg(cmd, "--conf-file="); - virCommandAddArgList(cmd, - "--except-interface", "lo", - NULL); - /* If this is an isolated network, set the default route option * (3) to be empty to avoid setting a default route that's * guaranteed to not work, and set --no-resolv so that no dns @@ -751,7 +769,39 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, VIR_FREE(ipaddr); } - if (ipdef) { + /* Find the first dhcp for both IPv4 and IPv6 */ + for (ii = 0, ipv4def = NULL, ipv6def = NULL, dhcp4flag = false, dhcp6flag = false; + (ipdef = virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, ii)); + ii++) { + if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET)) { + if (ipdef->nranges || ipdef->nhosts) { + if (!ipv4def) { + ipv4def = ipdef; + dhcp4flag = true; + } + } + } + if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6)) { + if (ipdef->nranges || ipdef->nhosts) { + if (!CHECK_DNSMASQ_VERSION()) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("dnsmasq version >= 2.64 required to specify DHCPv6 range or host")); + goto cleanup; + } + if (!ipv6def) { + ipv6def = ipdef; + dhcp6flag = true; + } + } + } + } + + if (ipv4def) + ipdef = ipv4def; + else + ipdef = ipv6def; + + while (ipdef) { for (r = 0 ; r < ipdef->nranges ; r++) { char *saddr = virSocketAddrFormat(&ipdef->ranges[r].start); if (!saddr) @@ -772,7 +822,7 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, /* * For static-only DHCP, i.e. with no range but at least one host element, * we have to add a special --dhcp-range option to enable the service in - * dnsmasq. + * dnsmasq. [this is for dhcp-hosts= support] */ if (!ipdef->nranges && ipdef->nhosts) { char *bridgeaddr = virSocketAddrFormat(&ipdef->address); @@ -783,61 +833,93 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, VIR_FREE(bridgeaddr); } - if (ipdef->nranges > 0) { - char *leasefile = networkDnsmasqLeaseFileName(network->def->name); - if (!leasefile) - goto cleanup; - virCommandAddArgFormat(cmd, "--dhcp-leasefile=%s", leasefile); - VIR_FREE(leasefile); - virCommandAddArgFormat(cmd, "--dhcp-lease-max=%d", nbleases); - } + if (networkBuildDnsmasqDhcpHostsList(dctx, ipdef) < 0) + goto cleanup; - if (ipdef->nranges || ipdef->nhosts) - virCommandAddArg(cmd, "--dhcp-no-override"); + /* Note: the following is IPv4 only */ + if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET)) { + if (ipdef->nranges || ipdef->nhosts) + virCommandAddArg(cmd, "--dhcp-no-override"); - /* add domain to any non-qualified hostnames in /etc/hosts or addn-hosts */ - if (network->def->domain) - virCommandAddArg(cmd, "--expand-hosts"); + if (ipdef->tftproot) { + virCommandAddArgList(cmd, "--enable-tftp", + "--tftp-root", ipdef->tftproot, + NULL); + } - if (networkBuildDnsmasqHostsfile(dctx, ipdef, network->def->dns) < 0) - goto cleanup; + if (ipdef->bootfile) { + virCommandAddArg(cmd, "--dhcp-boot"); + if (VIR_SOCKET_ADDR_VALID(&ipdef->bootserver)) { + char *bootserver = virSocketAddrFormat(&ipdef->bootserver); - /* Even if there are currently no static hosts, if we're - * listening for DHCP, we should write a 0-length hosts - * file to allow for runtime additions. - */ - if (ipdef->nranges || ipdef->nhosts) - virCommandAddArgPair(cmd, "--dhcp-hostsfile", - dctx->hostsfile->path); + if (!bootserver) + goto cleanup; + virCommandAddArgFormat(cmd, "%s%s%s", + ipdef->bootfile, ",,", bootserver); + VIR_FREE(bootserver); + } else { + virCommandAddArg(cmd, ipdef->bootfile); + } + } + } + if (ipdef == ipv6def) + ipdef = NULL; + else + ipdef = ipv6def; + } - /* Likewise, always create this file and put it on the commandline, to allow for - * for runtime additions. - */ - virCommandAddArgPair(cmd, "--addn-hosts", - dctx->addnhostsfile->path); + if (nbleases > 0) { + char *leasefile = networkDnsmasqLeaseFileName(network->def->name); + if (!leasefile) + goto cleanup; + virCommandAddArgFormat(cmd, "--dhcp-leasefile=%s", leasefile); + VIR_FREE(leasefile); + virCommandAddArgFormat(cmd, "--dhcp-lease-max=%d", nbleases); + } - if (ipdef->tftproot) { - virCommandAddArgList(cmd, "--enable-tftp", - "--tftp-root", ipdef->tftproot, - NULL); - } - if (ipdef->bootfile) { - virCommandAddArg(cmd, "--dhcp-boot"); - if (VIR_SOCKET_ADDR_VALID(&ipdef->bootserver)) { - char *bootserver = virSocketAddrFormat(&ipdef->bootserver); + /* this is done once per interface */ + if (networkBuildDnsmasqHostsList(dctx, network->def->dns) < 0) + goto cleanup; - if (!bootserver) + /* Even if there are currently no static hosts, if we're + * listening for DHCP, we should write a 0-length hosts + * file to allow for runtime additions. + */ + if (dhcp4flag || dhcp6flag) + virCommandAddArgPair(cmd, "--dhcp-hostsfile", + dctx->hostsfile->path); + + /* Likewise, always create this file and put it on the commandline, to allow for + * for runtime additions. + */ + virCommandAddArgPair(cmd, "--addn-hosts", + dctx->addnhostsfile->path); + + /* we are doing RA instead of radvd */ + if (CHECK_DNSMASQ_VERSION()) { + if (dhcp6flag) + virCommandAddArg(cmd, "--enable-ra"); + else { + char *bridgeaddr = NULL; + ipdef = virNetworkDefGetIpByIndex(network->def, AF_INET6, 0); + if (ipdef) { + bridgeaddr = virSocketAddrFormat(&ipdef->address); + if (bridgeaddr) { + virCommandAddArgFormat(cmd, + "--dhcp-range=%s,ra-only", bridgeaddr); + } else { + virReportError(VIR_ERR_XML_ERROR, + _("invalid IPv6 configuration for %s ra-only"), + network->def->name); goto cleanup; - virCommandAddArgFormat(cmd, "%s%s%s", - ipdef->bootfile, ",,", bootserver); - VIR_FREE(bootserver); - } else { - virCommandAddArg(cmd, ipdef->bootfile); + } + VIR_FREE(bridgeaddr); } } } ret = 0; + cleanup: VIR_FREE(record); VIR_FREE(recordPort); @@ -877,7 +959,7 @@ networkBuildDhcpDaemonCommandLine(virNetworkObjPtr network, virCommandPtr *cmdou return 0; cmd = virCommandNew(DNSMASQ); - if (networkBuildDnsmasqArgv(network, ipdef, pidfile, cmd, dctx) < 0) { + if (networkBuildDnsmasqArgv(network, pidfile, cmd, dctx) < 0) { goto cleanup; } @@ -894,18 +976,45 @@ static int networkStartDhcpDaemon(virNetworkObjPtr network) { virCommandPtr cmd = NULL; + const char *cmdname = DNSMASQ; + char *tmp, *version = NULL; + unsigned int major = 0, minor = 0; char *pidfile = NULL; int ret = -1; dnsmasqContext *dctx = NULL; - virNetworkIpDefPtr ipdef; - int i; if (!virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, 0)) { - /* no IPv6 addresses, so we don't need to run radvd */ + /* no IP addresses, so we don't need to run */ ret = 0; goto cleanup; } + if (!virFileIsExecutable(cmdname)) { + VIR_WARN("file %s missing or not executable", cmdname); + goto cleanup; + } + VIR_INFO("starting dhcp daemon (%s)", cmdname); + network->dnsmasqVersion = 0; + + cmd = virCommandNew(cmdname); + virCommandAddArg(cmd, "--version"); + virCommandSetOutputBuffer(cmd, &version); + if (virCommandRun(cmd, NULL) < 0) + goto cleanup; + virCommandFree(cmd); + cmd = NULL; + + if ((version!=NULL) && + (STREQLEN(version, DNSMASQ_PREFIX_TEXT, strlen(DNSMASQ_PREFIX_TEXT)))) { + tmp = version + strlen(DNSMASQ_PREFIX_TEXT); + if (virStrToLong_ui(tmp, &tmp, 10, &major) >= 0) { + if ((*tmp == '.') && + virStrToLong_ui(tmp + 1, &tmp, 10, &minor) >= 0) { + network->dnsmasqVersion = (major * 1000) + minor; + } + } + } + if (virFileMakePath(NETWORK_PID_DIR) < 0) { virReportSystemError(errno, _("cannot create directory %s"), @@ -939,18 +1048,6 @@ networkStartDhcpDaemon(virNetworkObjPtr network) if (ret < 0) goto cleanup; - /* populate dnsmasq hosts file */ - for (i = 0; (ipdef = virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, i)); i++) { - if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET) && - (ipdef->nranges || ipdef->nhosts)) { - if (networkBuildDnsmasqHostsfile(dctx, ipdef, - network->def->dns) < 0) - goto cleanup; - - break; - } - } - ret = dnsmasqSave(dctx); if (ret < 0) goto cleanup; @@ -976,6 +1073,7 @@ networkStartDhcpDaemon(virNetworkObjPtr network) ret = 0; cleanup: VIR_FREE(pidfile); + VIR_FREE(version); virCommandFree(cmd); dnsmasqContextFree(dctx); return ret; @@ -994,31 +1092,35 @@ networkRefreshDhcpDaemon(virNetworkObjPtr network) virNetworkIpDefPtr ipdef; dnsmasqContext *dctx = NULL; + /* if no IP addresses specified, nothing to do */ + if (virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, 0)) + return 0; + /* if there's no running dnsmasq, just start it */ if (network->dnsmasqPid <= 0 || (kill(network->dnsmasqPid, 0) < 0)) return networkStartDhcpDaemon(network); - /* Look for first IPv4 address that has dhcp defined. */ - /* We support dhcp config on 1 IPv4 interface only. */ + VIR_INFO("REFRESH: DhcpDaemon: for %s", network->def->bridge); + if (!(dctx = dnsmasqContextNew(network->def->name, DNSMASQ_STATE_DIR))) + goto cleanup; + + /* Look for first IPv4 address that has dhcp defined. + * We only support dhcp-host config on 1 IPv4 interface. + */ for (ii = 0; (ipdef = virNetworkDefGetIpByIndex(network->def, AF_INET, ii)); ii++) { if (ipdef->nranges || ipdef->nhosts) break; } - /* If no IPv4 addresses had dhcp info, pick the first (if there were any). */ if (!ipdef) ipdef = virNetworkDefGetIpByIndex(network->def, AF_INET, 0); - if (!ipdef) { - /* no <ip> elements, so nothing to do */ - return 0; - } - - if (!(dctx = dnsmasqContextNew(network->def->name, DNSMASQ_STATE_DIR))) - goto cleanup; + if (ipdef) + if (networkBuildDnsmasqDhcpHostsList(dctx, ipdef) < 0) + goto cleanup; - if (networkBuildDnsmasqHostsfile(dctx, ipdef, network->def->dns) < 0) + if (networkBuildDnsmasqHostsList(dctx, network->def->dns) < 0) goto cleanup; if ((ret = dnsmasqSave(dctx)) < 0) @@ -1170,6 +1272,12 @@ networkStartRadvd(virNetworkObjPtr network) virCommandPtr cmd = NULL; int ret = -1; + /* is dnsmasq handling RA */ + if (CHECK_DNSMASQ_VERSION()) { + ret = 0; + goto cleanup; + } + network->radvdPid = -1; if (!virNetworkDefGetIpByIndex(network->def, AF_INET6, 0)) { @@ -1247,6 +1355,10 @@ cleanup: static int networkRefreshRadvd(virNetworkObjPtr network) { + /* is dnsmasq handling RA */ + if (CHECK_DNSMASQ_VERSION()) + return 0; + /* if there's no running radvd, just start it */ if (network->radvdPid <= 0 || (kill(network->radvdPid, 0) < 0)) return networkStartRadvd(network); @@ -1626,9 +1738,19 @@ networkAddGeneralIp6tablesRules(struct network_driver *driver, goto err5; } + if (iptablesAddUdpInput(driver->iptables, AF_INET6, + network->def->bridge, 547) < 0) { + virReportError(VIR_ERR_SYSTEM_ERROR, + _("failed to add ip6tables rule to allow DHCP6 requests from '%s'"), + network->def->bridge); + goto err6; + } + return 0; /* unwind in reverse order from the point of failure */ +err6: + iptablesRemoveUdpInput(driver->iptables, AF_INET6, network->def->bridge, 53); err5: iptablesRemoveTcpInput(driver->iptables, AF_INET6, network->def->bridge, 53); err4: @@ -1646,6 +1768,7 @@ networkRemoveGeneralIp6tablesRules(struct network_driver *driver, virNetworkObjPtr network) { if (virNetworkDefGetIpByIndex(network->def, AF_INET6, 0)) { + iptablesRemoveUdpInput(driver->iptables, AF_INET6, network->def->bridge, 547); iptablesRemoveUdpInput(driver->iptables, AF_INET6, network->def->bridge, 53); iptablesRemoveTcpInput(driver->iptables, AF_INET6, network->def->bridge, 53); } @@ -2695,8 +2818,7 @@ networkValidate(struct network_driver *driver, bool vlanUsed, vlanAllowed, badVlanUse = false; virPortGroupDefPtr defaultPortGroup = NULL; virNetworkIpDefPtr ipdef; - bool ipv4def = false; - int i; + bool ipv4def = false, ipv6def = false; /* check for duplicate networks */ if (virNetworkObjIsDuplicate(&driver->networks, def, check_active) < 0) @@ -2715,17 +2837,36 @@ networkValidate(struct network_driver *driver, virNetworkSetBridgeMacAddr(def); } - /* We only support dhcp on one IPv4 address per defined network */ - for (i = 0; (ipdef = virNetworkDefGetIpByIndex(def, AF_INET, i)); i++) { - if (ipdef->nranges || ipdef->nhosts) { - if (ipv4def) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Multiple dhcp sections found. " + /* We only support dhcp on one IPv4 address and + * on one IPv6 address per defined network + */ + for (ii = 0; + (ipdef = virNetworkDefGetIpByIndex(def, AF_UNSPEC, ii)); + ii++) { + if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET)) { + if (ipdef->nranges || ipdef->nhosts) { + if (ipv4def) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Multiple IPv4 dhcp sections found -- " "dhcp is supported only for a " "single IPv4 address on each network")); - return -1; - } else { - ipv4def = true; + return -1; + } else { + ipv4def = true; + } + } + } + if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6)) { + if (ipdef->nranges || ipdef->nhosts) { + if (ipv6def) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Multiple IPv6 dhcp sections found -- " + "dhcp is supported only for a " + "single IPv6 address on each network")); + return -1; + } else { + ipv6def = true; + } } } } diff --git a/src/util/dnsmasq.c b/src/util/dnsmasq.c index 9d1c07b..b0cf3ce 100644 --- a/src/util/dnsmasq.c +++ b/src/util/dnsmasq.c @@ -304,7 +304,14 @@ hostsfileAdd(dnsmasqHostsfile *hostsfile, if (!(ipstr = virSocketAddrFormat(ip))) return -1; - if (name) { + /* the first test determins if it is a dhcpv6 host */ + if (mac==NULL) { + if (virAsprintf(&hostsfile->hosts[hostsfile->nhosts].host, "%s,[%s]", + name, ipstr) < 0) { + goto alloc_error; + } + } + else if (name) { if (virAsprintf(&hostsfile->hosts[hostsfile->nhosts].host, "%s,%s,%s", mac, ipstr, name) < 0) { goto alloc_error; diff --git a/tests/networkxml2argvdata/dhcp6-network.argv b/tests/networkxml2argvdata/dhcp6-network.argv new file mode 100644 index 0000000..87c68ac --- /dev/null +++ b/tests/networkxml2argvdata/dhcp6-network.argv @@ -0,0 +1,17 @@ +@DNSMASQ@ \ +--strict-order \ +--bind-interfaces \ +--domain-needed \ +--except-interface=lo \ +--domain=mynet \ +--expand-hosts \ +--local=/mynet/ \ +--conf-file= \ +--listen-address 2001:db8:ac10:fe01::1 \ +--listen-address 2001:db8:ac10:fd01::1 \ +--dhcp-range 2001:db8:ac10:fd01::1:10,2001:db8:ac10:fd01::1:ff \ +--dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \ +--dhcp-lease-max=240 \ +--dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile \ +--addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts \ +--enable-ra\ diff --git a/tests/networkxml2argvdata/dhcp6-network.xml b/tests/networkxml2argvdata/dhcp6-network.xml new file mode 100644 index 0000000..990b403 --- /dev/null +++ b/tests/networkxml2argvdata/dhcp6-network.xml @@ -0,0 +1,16 @@ +<network> + <name>default</name> + <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid> + <forward dev='eth1' mode='nat'/> + <bridge name='virbr0' stp='on' delay='0' /> + <domain name='mynet'/> + <ip family='ipv6' address='2001:db8:ac10:fe01::1' prefix='64'> + </ip> + <ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'> + <dhcp> + <range start='2001:db8:ac10:fd01::1:10' end='2001:db8:ac10:fd01::1:ff' /> + <host name='ralph' ip='2001:db8:ac10:fd01::1:20' /> + <host name='paul' ip='2001:db8:ac10:fd01::1:21' /> + </dhcp> + </ip> +</network> diff --git a/tests/networkxml2argvdata/isolated-network.argv b/tests/networkxml2argvdata/isolated-network.argv index 13e77b2..97f7b11 100644 --- a/tests/networkxml2argvdata/isolated-network.argv +++ b/tests/networkxml2argvdata/isolated-network.argv @@ -1,9 +1,16 @@ -@DNSMASQ@ --strict-order --bind-interfaces \ ---local=// --domain-needed --conf-file= \ ---except-interface lo --dhcp-option=3 --no-resolv \ +@DNSMASQ@ \ +--strict-order \ +--bind-interfaces \ +--domain-needed \ +--except-interface=lo \ +--local=// \ +--conf-file= \ +--dhcp-option=3 \ +--no-resolv \ --listen-address 192.168.152.1 \ --dhcp-range 192.168.152.2,192.168.152.254 \ ---dhcp-leasefile=/var/lib/libvirt/dnsmasq/private.leases --dhcp-lease-max=253 \ --dhcp-no-override \ +--dhcp-leasefile=/var/lib/libvirt/dnsmasq/private.leases \ +--dhcp-lease-max=253 \ --dhcp-hostsfile=/var/lib/libvirt/dnsmasq/private.hostsfile \ --addn-hosts=/var/lib/libvirt/dnsmasq/private.addnhosts\ diff --git a/tests/networkxml2argvdata/nat-network-dhcp6.argv b/tests/networkxml2argvdata/nat-network-dhcp6.argv new file mode 100644 index 0000000..a4795e5 --- /dev/null +++ b/tests/networkxml2argvdata/nat-network-dhcp6.argv @@ -0,0 +1,20 @@ +@DNSMASQ@ \ +--strict-order \ +--bind-interfaces \ +--domain-needed \ +--except-interface=lo \ +--local=// \ +--conf-file= \ +--listen-address 192.168.122.1 \ +--listen-address 192.168.123.1 \ +--listen-address 2001:db8:ac10:fe01::1 \ +--listen-address 2001:db8:ac10:fd01::1 \ +--listen-address 10.24.10.1 \ +--dhcp-range 192.168.122.2,192.168.122.254 \ +--dhcp-no-override \ +--dhcp-range 2001:db8:ac10:fd01::1:10,2001:db8:ac10:fd01::1:ff \ +--dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \ +--dhcp-lease-max=493 \ +--dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile \ +--addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts \ +--enable-ra\ diff --git a/tests/networkxml2argvdata/nat-network-dhcp6.xml b/tests/networkxml2argvdata/nat-network-dhcp6.xml new file mode 100644 index 0000000..f993a26 --- /dev/null +++ b/tests/networkxml2argvdata/nat-network-dhcp6.xml @@ -0,0 +1,26 @@ +<network> + <name>default</name> + <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid> + <forward dev='eth1' mode='nat'/> + <bridge name='virbr0' stp='on' delay='0' /> + <ip address='192.168.122.1' netmask='255.255.255.0'> + <dhcp> + <range start='192.168.122.2' end='192.168.122.254' /> + <host mac='00:16:3e:77:e2:ed' name='a.example.com' ip='192.168.122.10' /> + <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'> + <dhcp> + <range start='2001:db8:ac10:fd01::1:10' end='2001:db8:ac10:fd01::1:ff' /> + <host name='ralph' ip='2001:db8:ac10:fd01::1:20' /> + <host name='paul' ip='2001:db8:ac10:fd01::1:21' /> + </dhcp> + </ip> + <ip family='ipv4' address='10.24.10.1'> + </ip> +</network> diff --git a/tests/networkxml2argvdata/nat-network-dns-hosts.argv b/tests/networkxml2argvdata/nat-network-dns-hosts.argv index 03a0676..32ad19f 100644 --- a/tests/networkxml2argvdata/nat-network-dns-hosts.argv +++ b/tests/networkxml2argvdata/nat-network-dns-hosts.argv @@ -1,4 +1,11 @@ -@DNSMASQ@ --strict-order --bind-interfaces --domain=example.com \ ---local=/example.com/ --domain-needed \ ---conf-file= --except-interface lo --listen-address 192.168.122.1 \ ---expand-hosts --addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts\ +@DNSMASQ@ \ +--strict-order \ +--bind-interfaces \ +--domain-needed \ +--except-interface=lo \ +--domain=example.com \ +--expand-hosts \ +--local=/example.com/ \ +--conf-file= \ +--listen-address 192.168.122.1 \ +--addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts\ diff --git a/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv b/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv index 210a60c..4c21ff1 100644 --- a/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv +++ b/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv @@ -1,8 +1,10 @@ @DNSMASQ@ \ --strict-order \ --bind-interfaces \ ---local=// --domain-needed --conf-file= \ ---except-interface lo \ +--domain-needed \ +--except-interface=lo \ +--local=// \ +--conf-file= \ --srv-host=name.tcp.,,,, \ --listen-address 192.168.122.1 \ --listen-address 192.168.123.1 \ @@ -10,8 +12,9 @@ --listen-address 2001:db8:ac10:fd01::1 \ --listen-address 10.24.10.1 \ --dhcp-range 192.168.122.2,192.168.122.254 \ +--dhcp-no-override \ --dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \ --dhcp-lease-max=253 \ ---dhcp-no-override \ --dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile \ ---addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts\ +--addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts \ +--dhcp-range=2001:db8:ac10:fe01::1,ra-only\ diff --git a/tests/networkxml2argvdata/nat-network-dns-srv-record.argv b/tests/networkxml2argvdata/nat-network-dns-srv-record.argv index 833d3cd..246dbeb 100644 --- a/tests/networkxml2argvdata/nat-network-dns-srv-record.argv +++ b/tests/networkxml2argvdata/nat-network-dns-srv-record.argv @@ -1,8 +1,10 @@ @DNSMASQ@ \ --strict-order \ --bind-interfaces \ ---local=// --domain-needed --conf-file= \ ---except-interface lo \ +--domain-needed \ +--except-interface=lo \ +--local=// \ +--conf-file= \ --srv-host=name.tcp.test-domain-name,.,1024,10,10 \ --listen-address 192.168.122.1 \ --listen-address 192.168.123.1 \ @@ -10,8 +12,9 @@ --listen-address 2001:db8:ac10:fd01::1 \ --listen-address 10.24.10.1 \ --dhcp-range 192.168.122.2,192.168.122.254 \ +--dhcp-no-override \ --dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \ --dhcp-lease-max=253 \ ---dhcp-no-override \ --dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile \ ---addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts\ +--addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts \ +--dhcp-range=2001:db8:ac10:fe01::1,ra-only\ diff --git a/tests/networkxml2argvdata/nat-network-dns-txt-record.argv b/tests/networkxml2argvdata/nat-network-dns-txt-record.argv index 3481507..8d18f3f 100644 --- a/tests/networkxml2argvdata/nat-network-dns-txt-record.argv +++ b/tests/networkxml2argvdata/nat-network-dns-txt-record.argv @@ -1,11 +1,20 @@ -@DNSMASQ@ --strict-order --bind-interfaces \ ---local=// --domain-needed --conf-file= \ ---except-interface lo '--txt-record=example,example value' \ ---listen-address 192.168.122.1 --listen-address 192.168.123.1 \ +@DNSMASQ@ \ +--strict-order \ +--bind-interfaces \ +--domain-needed \ +--except-interface=lo \ +--local=// \ +--conf-file= \ +'--txt-record=example,example value' \ +--listen-address 192.168.122.1 \ +--listen-address 192.168.123.1 \ --listen-address 2001:db8:ac10:fe01::1 \ ---listen-address 2001:db8:ac10:fd01::1 --listen-address 10.24.10.1 \ +--listen-address 2001:db8:ac10:fd01::1 \ +--listen-address 10.24.10.1 \ --dhcp-range 192.168.122.2,192.168.122.254 \ +--dhcp-no-override \ --dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \ ---dhcp-lease-max=253 --dhcp-no-override \ +--dhcp-lease-max=253 \ --dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile \ ---addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts\ +--addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts \ +--dhcp-range=2001:db8:ac10:fe01::1,ra-only\ diff --git a/tests/networkxml2argvdata/nat-network.argv b/tests/networkxml2argvdata/nat-network.argv index 37fd2fc..32afeaa 100644 --- a/tests/networkxml2argvdata/nat-network.argv +++ b/tests/networkxml2argvdata/nat-network.argv @@ -1,10 +1,19 @@ -@DNSMASQ@ --strict-order --bind-interfaces \ ---local=// --domain-needed --conf-file= \ ---except-interface lo --listen-address 192.168.122.1 \ ---listen-address 192.168.123.1 --listen-address 2001:db8:ac10:fe01::1 \ ---listen-address 2001:db8:ac10:fd01::1 --listen-address 10.24.10.1 \ +@DNSMASQ@ \ +--strict-order \ +--bind-interfaces \ +--domain-needed \ +--except-interface=lo \ +--local=// \ +--conf-file= \ +--listen-address 192.168.122.1 \ +--listen-address 192.168.123.1 \ +--listen-address 2001:db8:ac10:fe01::1 \ +--listen-address 2001:db8:ac10:fd01::1 \ +--listen-address 10.24.10.1 \ --dhcp-range 192.168.122.2,192.168.122.254 \ +--dhcp-no-override \ --dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \ ---dhcp-lease-max=253 --dhcp-no-override \ +--dhcp-lease-max=253 \ --dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile \ ---addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts\ +--addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts \ +--dhcp-range=2001:db8:ac10:fe01::1,ra-only\ diff --git a/tests/networkxml2argvdata/netboot-network.argv b/tests/networkxml2argvdata/netboot-network.argv index 5408eb7..41e9416 100644 --- a/tests/networkxml2argvdata/netboot-network.argv +++ b/tests/networkxml2argvdata/netboot-network.argv @@ -1,10 +1,19 @@ -@DNSMASQ@ --strict-order --bind-interfaces --domain=example.com \ ---local=/example.com/ --domain-needed --conf-file= \ ---except-interface lo --listen-address 192.168.122.1 \ +@DNSMASQ@ \ +--strict-order \ +--bind-interfaces \ +--domain-needed \ +--except-interface=lo \ +--domain=example.com \ +--expand-hosts \ +--local=/example.com/ \ +--conf-file= \ +--listen-address 192.168.122.1 \ --dhcp-range 192.168.122.2,192.168.122.254 \ +--dhcp-no-override \ +--enable-tftp \ +--tftp-root /var/lib/tftproot \ +--dhcp-boot pxeboot.img \ --dhcp-leasefile=/var/lib/libvirt/dnsmasq/netboot.leases \ ---dhcp-lease-max=253 --dhcp-no-override --expand-hosts \ +--dhcp-lease-max=253 \ --dhcp-hostsfile=/var/lib/libvirt/dnsmasq/netboot.hostsfile \ ---addn-hosts=/var/lib/libvirt/dnsmasq/netboot.addnhosts \ ---enable-tftp \ ---tftp-root /var/lib/tftproot --dhcp-boot pxeboot.img\ +--addn-hosts=/var/lib/libvirt/dnsmasq/netboot.addnhosts\ diff --git a/tests/networkxml2argvdata/netboot-proxy-network.argv b/tests/networkxml2argvdata/netboot-proxy-network.argv index 21e01e3..eebe560 100644 --- a/tests/networkxml2argvdata/netboot-proxy-network.argv +++ b/tests/networkxml2argvdata/netboot-proxy-network.argv @@ -1,9 +1,17 @@ -@DNSMASQ@ --strict-order --bind-interfaces --domain=example.com \ ---local=/example.com/ --domain-needed --conf-file= \ ---except-interface lo --listen-address 192.168.122.1 \ +@DNSMASQ@ \ +--strict-order \ +--bind-interfaces \ +--domain-needed \ +--except-interface=lo \ +--domain=example.com \ +--expand-hosts \ +--local=/example.com/ \ +--conf-file= \ +--listen-address 192.168.122.1 \ --dhcp-range 192.168.122.2,192.168.122.254 \ +--dhcp-no-override \ +--dhcp-boot pxeboot.img,,10.20.30.40 \ --dhcp-leasefile=/var/lib/libvirt/dnsmasq/netboot.leases \ ---dhcp-lease-max=253 --dhcp-no-override --expand-hosts \ +--dhcp-lease-max=253 \ --dhcp-hostsfile=/var/lib/libvirt/dnsmasq/netboot.hostsfile \ ---addn-hosts=/var/lib/libvirt/dnsmasq/netboot.addnhosts \ ---dhcp-boot pxeboot.img,,10.20.30.40\ +--addn-hosts=/var/lib/libvirt/dnsmasq/netboot.addnhosts\ diff --git a/tests/networkxml2argvdata/routed-network-dhcphost.argv b/tests/networkxml2argvdata/routed-network-dhcphost.argv new file mode 100644 index 0000000..9f52b98 --- /dev/null +++ b/tests/networkxml2argvdata/routed-network-dhcphost.argv @@ -0,0 +1,15 @@ +@DNSMASQ@ \ +--strict-order \ +--bind-interfaces \ +--domain-needed \ +--except-interface=lo \ +--local=// \ +--conf-file= \ +--listen-address 192.168.122.1 \ +--listen-address 2001:db8:ac10:fd01::1 \ +--dhcp-range 192.168.122.1,static \ +--dhcp-no-override \ +--dhcp-range 2001:db8:ac10:fd01::1,static \ +--dhcp-hostsfile=/var/lib/libvirt/dnsmasq/local.hostsfile \ +--addn-hosts=/var/lib/libvirt/dnsmasq/local.addnhosts \ +--enable-ra\ diff --git a/tests/networkxml2argvdata/routed-network-dhcphost.xml b/tests/networkxml2argvdata/routed-network-dhcphost.xml new file mode 100644 index 0000000..38d9ebf --- /dev/null +++ b/tests/networkxml2argvdata/routed-network-dhcphost.xml @@ -0,0 +1,19 @@ +<network> + <name>local</name> + <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid> + <forward dev='eth1' mode='route'/> + <bridge name='virbr1' stp='on' delay='0' /> + <mac address='12:34:56:78:9A:BC'/> + <ip address='192.168.122.1' netmask='255.255.255.0'> + <dhcp> + <host mac='00:16:3e:77:e2:ed' name='a.example.com' ip='192.168.122.10' /> + <host mac='00:16:3e:3e:a9:1a' name='b.example.com' ip='192.168.122.11' /> + </dhcp> + </ip> + <ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'> + <dhcp> + <host name='ralph' ip='2001:db8:ac10:fd01::1:20' /> + <host name='paul' ip='2001:db8:ac10:fd01::1:21' /> + </dhcp> + </ip> +</network> diff --git a/tests/networkxml2argvdata/routed-network.argv b/tests/networkxml2argvdata/routed-network.argv index 9fedb2b..78ad8a2 100644 --- a/tests/networkxml2argvdata/routed-network.argv +++ b/tests/networkxml2argvdata/routed-network.argv @@ -1,4 +1,9 @@ -@DNSMASQ@ --strict-order --bind-interfaces \ ---local=// --domain-needed --conf-file= \ ---except-interface lo --listen-address 192.168.122.1 \ +@DNSMASQ@ \ +--strict-order \ +--bind-interfaces \ +--domain-needed \ +--except-interface=lo \ +--local=// \ +--conf-file= \ +--listen-address 192.168.122.1 \ --addn-hosts=/var/lib/libvirt/dnsmasq/local.addnhosts\ diff --git a/tests/networkxml2argvtest.c b/tests/networkxml2argvtest.c index 87519e4..206e2d7 100644 --- a/tests/networkxml2argvtest.c +++ b/tests/networkxml2argvtest.c @@ -73,6 +73,7 @@ static int testCompareXMLToArgvFiles(const char *inxml, const char *outargv) { goto fail; obj->def = dev; + obj->dnsmasqVersion = 2064; dctx = dnsmasqContextNew(dev->name, "/var/lib/libvirt/dnsmasq"); if (dctx == NULL) @@ -157,6 +158,9 @@ mymain(void) DO_TEST("nat-network-dns-srv-record"); DO_TEST("nat-network-dns-srv-record-minimal"); DO_TEST("nat-network-dns-hosts"); + DO_TEST("nat-network-dhcp6"); + DO_TEST("routed-network-dhcphost"); + DO_TEST("dhcp6-network"); return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE; } -- 1.7.11.7

On 11/21/2012 04:18 PM, Gene Czarcinski wrote:
The DHCPv6 support includes IPV6 dhcp-range and dhcp-host for one IPv6 subnetwork on one interface. This support will only work if dnsmasq version >= 2.64; otherwise an error occurs if dhcp-range or dhcp-host is specified.
Note that the check for dnsmasq version is performed at network start and only then will an error condition occur. Thus, it is possible to define/edit a network configuration which will not start. The error message indicates that dnsmasq version 2.64 or later is required.
This patch provides the same DHCP support for IPv6 which has been available for IPv4.
With dnsmasq >= 2.64, support for the RA service is now provided by dnsmasq (radvd is no longer needed/used/started).
Documentation has been updated to reflect the new support.
Oops. I did not realize that there are schemas that should be updated. I have most of what is needed figured out but there are a couple of things not working cleanly. I should have a update patch submitted "real soon now". Suggestion: add something to the developers guidance so that you check to make sure that the schemas are updated properly. I wonder if the tests would pass testing? Gene
--- docs/formatnetwork.html.in | 108 ++++++- src/conf/network_conf.c | 100 ++++--- src/conf/network_conf.h | 1 + src/network/bridge_driver.c | 333 +++++++++++++++------ src/util/dnsmasq.c | 9 +- tests/networkxml2argvdata/dhcp6-network.argv | 17 ++ tests/networkxml2argvdata/dhcp6-network.xml | 16 + tests/networkxml2argvdata/isolated-network.argv | 15 +- tests/networkxml2argvdata/nat-network-dhcp6.argv | 20 ++ tests/networkxml2argvdata/nat-network-dhcp6.xml | 26 ++ .../networkxml2argvdata/nat-network-dns-hosts.argv | 15 +- .../nat-network-dns-srv-record-minimal.argv | 11 +- .../nat-network-dns-srv-record.argv | 11 +- .../nat-network-dns-txt-record.argv | 23 +- tests/networkxml2argvdata/nat-network.argv | 23 +- tests/networkxml2argvdata/netboot-network.argv | 23 +- .../networkxml2argvdata/netboot-proxy-network.argv | 20 +- .../routed-network-dhcphost.argv | 15 + .../routed-network-dhcphost.xml | 19 ++ tests/networkxml2argvdata/routed-network.argv | 11 +- tests/networkxml2argvtest.c | 4 + 21 files changed, 633 insertions(+), 187 deletions(-) create mode 100644 tests/networkxml2argvdata/dhcp6-network.argv create mode 100644 tests/networkxml2argvdata/dhcp6-network.xml create mode 100644 tests/networkxml2argvdata/nat-network-dhcp6.argv create mode 100644 tests/networkxml2argvdata/nat-network-dhcp6.xml create mode 100644 tests/networkxml2argvdata/routed-network-dhcphost.argv create mode 100644 tests/networkxml2argvdata/routed-network-dhcphost.xml
diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in index 7b3b25c..50e00f9 100644 --- a/docs/formatnetwork.html.in +++ b/docs/formatnetwork.html.in @@ -577,8 +577,10 @@ dotted-decimal format, or an IPv6 address in standard colon-separated hexadecimal format, that will be configured on the bridge - device associated with the virtual network. To the guests this - address will be their default route. For IPv4 addresses, the <code>netmask</code> + device associated with the virtual network. To the guests this IPv4 + address will be their IPv4 default route. For IPv6, the default route is + established via Router Advertisement. + For IPv4 addresses, the <code>netmask</code> attribute defines the significant bits of the network address, again specified in dotted-decimal format. For IPv6 addresses, and as an alternate method for IPv4 addresses, you can specify @@ -587,10 +589,13 @@ could also be given as <code>prefix='24'</code>. The <code>family</code> attribute is used to specify the type of address - 'ipv4' or 'ipv6'; if no <code>family</code> is given, 'ipv4' is assumed. A network can have more than - one of each family of address defined, but only a single address can have a - <code>dhcp</code> or <code>tftp</code> element. <span class="since">Since 0.3.0; + one of each family of address defined, but only a single IPv4 address can have a + <code>dhcp</code> or <code>tftp</code> element. <span class="since">Since 0.3.0 </span> IPv6, multiple addresses on a single network, <code>family</code>, and - <code>prefix</code> since 0.8.7</span> + <code>prefix</code>. <span class="since">Since 0.8.7</span> In addition + to the one IPv4 address which has a <code>dhcp</code> definition, one IPv6 + address can have a <code>dhcp</code> definition. + <span class="since"> Since 1.0.1</span> <dl> <dt><code>tftp</code></dt> <dd>Immediately within @@ -611,27 +616,46 @@ <code>dhcp</code> element is not supported for IPv6, and is only supported on a single IP address per network for IPv4. <span class="since">Since 0.3.0</span> + The <code>dhcp</code> element is now supported for IPv6. + Again, there is a restriction that only one IPv6 address definition + is able to have a <code>dhcp</code> element. + <span class="since">Since 1.0.1</span> <dl> <dt><code>range</code></dt> <dd>The <code>start</code> and <code>end</code> attributes on the <code>range</code> element specify the boundaries of a pool of - IPv4 addresses to be provided to DHCP clients. These two addresses + addresses to be provided to DHCP clients. These two addresses must lie within the scope of the network defined on the parent - <code>ip</code> element. <span class="since">Since 0.3.0</span> + <code>ip</code> element. There may be zero or more + <code>range</code> elements specified. + <span class="since">Since 0.3.0</span> + <code>Range</code> can be specified for one IPv4 address, + one IPv6 address, or both. <span class="since">Since 1.0.1</span> </dd> <dt><code>host</code></dt> <dd>Within the <code>dhcp</code> element there may be zero or more - <code>host</code> elements; these specify hosts which will be given + <code>host</code> elements. These specify hosts which will be given names and predefined IP addresses by the built-in DHCP server. Any - such element must specify the MAC address of the host to be assigned + such IPv4 element must specify the MAC address of the host to be assigned a given name (via the <code>mac</code> attribute), the IP to be assigned to that host (via the <code>ip</code> attribute), and the name to be given that host by the DHCP server (via the <code>name</code> attribute). <span class="since">Since 0.4.5</span> + Within the IPv6 <code>dhcp</code> element zero or more + <code>host</code> elements are now supported. The definition for + an IPv6 <code>host</code> element differs from that for IPv4: + there is no <code>mac</code> attribute since a MAC address has no + defined meaning in IPv6. Instead, the <code>name</code> attribute is + used to identify the host to be assigned the IPv6 address. For DHCPv6, + the name is the plain name of the client host sent by the + client to the server. Note that this method of assigning a + specific IP address can be used instead of the <code>mac</code> + attribute for IPv4. <span class="since">Since 1.0.1</span> </dd> <dt><code>bootp</code></dt> <dd>The optional <code>bootp</code> - element specifies BOOTP options to be provided by the DHCP server. + element specifies BOOTP options to be provided by the DHCP + server for IPv4 only. Two attributes are supported: <code>file</code> is mandatory and gives the file to be used for the boot image; <code>server</code> is optional and gives the address of the TFTP server from which the boot @@ -674,6 +698,29 @@ <ip family="ipv6" address="2001:db8:ca2:2::1" prefix="64" /> </network></pre>
+ + <p> + Below is a variation of the above example which adds an IPv6 + dhcp range definition. + </p> + + <pre> + <network> + <name>default6</name> + <bridge name="virbr0" /> + <forward mode="nat"/> + <ip address="192.168.122.1" netmask="255.255.255.0"> + <dhcp> + <range start="192.168.122.2" end="192.168.122.254" /> + </dhcp> + </ip> + <ip family="ipv6" address="2001:db8:ca2:2::1" prefix="64" > + <dhcp> + <range start="2001:db8:ca2:2:1::10" end="2001:db8:ca2:2:1::ff" /> + </dhcp> + </ip> + </network></pre> + <h3><a name="examplesRoute">Routed network config</a></h3>
<p> @@ -698,6 +745,29 @@ <ip family="ipv6" address="2001:db8:ca2:2::1" prefix="64" /> </network></pre>
+ <p> + Below is another IPv6 varition. Instead of a dhcp range being + specified, this example has a couple of IPv6 host definitions. + </p> + + <pre> + <network> + <name>local6</name> + <bridge name="virbr1" /> + <forward mode="route" dev="eth1"/> + <ip address="192.168.122.1" netmask="255.255.255.0"> + <dhcp> + <range start="192.168.122.2" end="192.168.122.254" /> + </dhcp> + </ip> + <ip family="ipv6" address="2001:db8:ca2:2::1" prefix="64" > + <dhcp> + <host name="paul" ip="2001:db8:ca2:2:3::1" /> + <host name="bob" ip="2001:db8:ca2:2:3::2" /> + </dhcp> + </ip> + </network></pre> + <h3><a name="examplesPrivate">Isolated network config</a></h3>
<p> @@ -720,6 +790,24 @@ <ip family="ipv6" address="2001:db8:ca2:3::1" prefix="64" /> </network></pre>
+ <h3><a name="examplesPrivate6">Isolated IPv6 network config</a></h3> + + <p> + This variation of an isolated network defines only IPv6. + </p> + + <pre> + <network> + <name>sixnet</name> + <bridge name="virbr6" /> + <ip family="ipv6" address="2001:db8:ca2:6::1" prefix="64" > + <dhcp> + <host name="peter" ip="2001:db8:ca2:6:6::1" /> + <host name="dariusz" ip="2001:db8:ca2:6:6::2" /> + </dhcp> + </ip> + </network></pre> + <h3><a name="examplesBridge">Using an existing host bridge</a></h3>
<p> diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 228951d..a56b2e6 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -633,6 +633,7 @@ cleanup:
static int virNetworkDHCPHostDefParse(const char *networkName, + virNetworkIpDefPtr def, xmlNodePtr node, virNetworkDHCPHostDefPtr host, bool partialOkay) @@ -644,6 +645,13 @@ virNetworkDHCPHostDefParse(const char *networkName,
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 IPv6 network '%s'"), + mac, networkName); + goto cleanup; + } if (virMacAddrParse(mac, &addr) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Cannot parse MAC address '%s' in network '%s'"), @@ -686,10 +694,19 @@ virNetworkDHCPHostDefParse(const char *networkName, networkName); } } else { + if (VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET6)) { + if (!name) { + virReportError(VIR_ERR_XML_ERROR, + _("Static host definition in IPv6 network '%s' " + "must have name attribute"), + networkName); + goto cleanup; + } + } /* normal usage - you need at least one MAC address or one host name */ - if (!(mac || name)) { + else if (!(mac || name)) { virReportError(VIR_ERR_XML_ERROR, - _("Static host definition in network '%s' " + _("Static host definition in IPv4 network '%s' " "must have mac or name attribute"), networkName); goto cleanup; @@ -748,36 +765,39 @@ virNetworkDHCPDefParse(const char *networkName, virReportOOMError(); return -1; } - if (virNetworkDHCPHostDefParse(networkName, cur, + if (virNetworkDHCPHostDefParse(networkName, def, cur, &def->hosts[def->nhosts], false) < 0) { return -1; } 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"); + } else if (VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET)) { + /* the following only applies to IPv4 */ + 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; + }
- if (server && - virSocketAddrParse(&inaddr, server, AF_UNSPEC) < 0) { - VIR_FREE(file); + def->bootfile = file; + def->bootserver = inaddr; VIR_FREE(server); - return -1; } - - def->bootfile = file; - def->bootserver = inaddr; - VIR_FREE(server); }
cur = cur->next; @@ -1139,6 +1159,20 @@ virNetworkIPParseXML(const char *networkName, } }
+ if (VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET6)) { + /* parse IPv6-related info */ + cur = node->children; + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE && + xmlStrEqual(cur->name, BAD_CAST "dhcp")) { + result = virNetworkDHCPDefParse(networkName, def, cur); + if (result) + goto error; + } + cur = cur->next; + } + } + result = 0;
error: @@ -2347,11 +2381,9 @@ virNetworkIpDefByIndex(virNetworkDefPtr def, int parentIndex) /* first find which ip element's dhcp host list to work on */ if (parentIndex >= 0) { ipdef = virNetworkDefGetIpByIndex(def, AF_UNSPEC, parentIndex); - if (!(ipdef && - VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET))) { + if (!(ipdef)) { virReportError(VIR_ERR_OPERATION_INVALID, _("couldn't update dhcp host entry - " - "no <ip family='ipv4'> " "element found at index %d in network '%s'"), parentIndex, def->name); } @@ -2364,17 +2396,17 @@ virNetworkIpDefByIndex(virNetworkDefPtr def, int parentIndex) for (ii = 0; (ipdef = virNetworkDefGetIpByIndex(def, AF_UNSPEC, ii)); ii++) { - if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET) && - (ipdef->nranges || ipdef->nhosts)) { + if (ipdef->nranges || ipdef->nhosts) break; - } } - if (!ipdef) + if (!ipdef) { ipdef = virNetworkDefGetIpByIndex(def, AF_INET, 0); + if (!ipdef) + ipdef = virNetworkDefGetIpByIndex(def, AF_INET6, 0); + } if (!ipdef) { virReportError(VIR_ERR_OPERATION_INVALID, _("couldn't update dhcp host entry - " - "no <ip family='ipv4'> " "element found in network '%s'"), def->name); } return ipdef; @@ -2404,7 +2436,7 @@ virNetworkDefUpdateIPDHCPHost(virNetworkDefPtr def, /* parse the xml into a virNetworkDHCPHostDef */ if (command == VIR_NETWORK_UPDATE_COMMAND_MODIFY) {
- if (virNetworkDHCPHostDefParse(def->name, ctxt->node, &host, false) < 0) + if (virNetworkDHCPHostDefParse(def->name, ipdef, ctxt->node, &host, false) < 0) goto cleanup;
/* search for the entry with this (mac|name), @@ -2437,7 +2469,7 @@ virNetworkDefUpdateIPDHCPHost(virNetworkDefPtr def, } else if ((command == VIR_NETWORK_UPDATE_COMMAND_ADD_FIRST) || (command == VIR_NETWORK_UPDATE_COMMAND_ADD_LAST)) {
- if (virNetworkDHCPHostDefParse(def->name, ctxt->node, &host, true) < 0) + if (virNetworkDHCPHostDefParse(def->name, ipdef, ctxt->node, &host, true) < 0) goto cleanup;
/* log error if an entry with same name/address/ip already exists */ @@ -2483,7 +2515,7 @@ virNetworkDefUpdateIPDHCPHost(virNetworkDefPtr def,
} else if (command == VIR_NETWORK_UPDATE_COMMAND_DELETE) {
- if (virNetworkDHCPHostDefParse(def->name, ctxt->node, &host, false) < 0) + if (virNetworkDHCPHostDefParse(def->name, ipdef, ctxt->node, &host, false) < 0) goto cleanup;
/* find matching entry - all specified attributes must match */ diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index 3e46304..226da04 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -218,6 +218,7 @@ struct _virNetworkObj { unsigned int active : 1; unsigned int autostart : 1; unsigned int persistent : 1; + unsigned int dnsmasqVersion;
virNetworkDefPtr def; /* The current definition */ virNetworkDefPtr newDef; /* New definition to activate at shutdown */ diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 9c67348..19a9170 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -75,6 +75,11 @@
#define VIR_FROM_THIS VIR_FROM_NETWORK
+#define DNSMASQ_PREFIX_TEXT "Dnsmasq version " + +#define CHECK_DNSMASQ_VERSION() \ + (network->dnsmasqVersion >= 2064) + /* Main driver state */ struct network_driver { virMutex lock; @@ -582,20 +587,32 @@ cleanup: return ret; }
+ /* the following does not build a file, it builds a list + * which is later saved into a file + */ + static int -networkBuildDnsmasqHostsfile(dnsmasqContext *dctx, - virNetworkIpDefPtr ipdef, - virNetworkDNSDefPtr dnsdef) +networkBuildDnsmasqDhcpHostsList(dnsmasqContext *dctx, + virNetworkIpDefPtr ipdef) { - unsigned int i, j; + unsigned int i;
for (i = 0; i < ipdef->nhosts; i++) { virNetworkDHCPHostDefPtr host = &(ipdef->hosts[i]); - if ((host->mac) && VIR_SOCKET_ADDR_VALID(&host->ip)) + if (VIR_SOCKET_ADDR_VALID(&host->ip)) if (dnsmasqAddDhcpHost(dctx, host->mac, &host->ip, host->name) < 0) return -1; }
+ return 0; +} + +static int +networkBuildDnsmasqHostsList(dnsmasqContext *dctx, + virNetworkDNSDefPtr dnsdef) +{ + unsigned int i, j; + if (dnsdef) { for (i = 0; i < dnsdef->nhosts; i++) { virNetworkDNSHostsDefPtr host = &(dnsdef->hosts[i]); @@ -613,7 +630,6 @@ networkBuildDnsmasqHostsfile(dnsmasqContext *dctx,
static int networkBuildDnsmasqArgv(virNetworkObjPtr network, - virNetworkIpDefPtr ipdef, const char *pidfile, virCommandPtr cmd, dnsmasqContext *dctx) @@ -625,7 +641,8 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, char *recordPort = NULL; char *recordWeight = NULL; char *recordPriority = NULL; - virNetworkIpDefPtr tmpipdef; + virNetworkIpDefPtr tmpipdef, ipdef, ipv4def, ipv6def; + bool dhcp4flag, dhcp6flag;
/* * NB, be careful about syntax for dnsmasq options in long format. @@ -650,14 +667,19 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, * Needed to ensure dnsmasq uses same algorithm for processing * multiple namedriver entries in /etc/resolv.conf as GLibC. */ - virCommandAddArgList(cmd, "--strict-order", "--bind-interfaces", NULL); + virCommandAddArgList(cmd, "--strict-order", + "--bind-interfaces", + "--domain-needed", + "--except-interface=lo", + NULL);
- if (network->def->domain) + if (network->def->domain) { virCommandAddArgPair(cmd, "--domain", network->def->domain); + virCommandAddArg(cmd, "--expand-hosts"); + } /* need to specify local even if no domain specified */ virCommandAddArgFormat(cmd, "--local=/%s/", network->def->domain ? network->def->domain : ""); - virCommandAddArg(cmd, "--domain-needed");
if (pidfile) virCommandAddArgPair(cmd, "--pid-file", pidfile); @@ -665,10 +687,6 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, /* *no* conf file */ virCommandAddArg(cmd, "--conf-file=");
- virCommandAddArgList(cmd, - "--except-interface", "lo", - NULL); - /* If this is an isolated network, set the default route option * (3) to be empty to avoid setting a default route that's * guaranteed to not work, and set --no-resolv so that no dns @@ -751,7 +769,39 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, VIR_FREE(ipaddr); }
- if (ipdef) { + /* Find the first dhcp for both IPv4 and IPv6 */ + for (ii = 0, ipv4def = NULL, ipv6def = NULL, dhcp4flag = false, dhcp6flag = false; + (ipdef = virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, ii)); + ii++) { + if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET)) { + if (ipdef->nranges || ipdef->nhosts) { + if (!ipv4def) { + ipv4def = ipdef; + dhcp4flag = true; + } + } + } + if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6)) { + if (ipdef->nranges || ipdef->nhosts) { + if (!CHECK_DNSMASQ_VERSION()) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("dnsmasq version >= 2.64 required to specify DHCPv6 range or host")); + goto cleanup; + } + if (!ipv6def) { + ipv6def = ipdef; + dhcp6flag = true; + } + } + } + } + + if (ipv4def) + ipdef = ipv4def; + else + ipdef = ipv6def; + + while (ipdef) { for (r = 0 ; r < ipdef->nranges ; r++) { char *saddr = virSocketAddrFormat(&ipdef->ranges[r].start); if (!saddr) @@ -772,7 +822,7 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, /* * For static-only DHCP, i.e. with no range but at least one host element, * we have to add a special --dhcp-range option to enable the service in - * dnsmasq. + * dnsmasq. [this is for dhcp-hosts= support] */ if (!ipdef->nranges && ipdef->nhosts) { char *bridgeaddr = virSocketAddrFormat(&ipdef->address); @@ -783,61 +833,93 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, VIR_FREE(bridgeaddr); }
- if (ipdef->nranges > 0) { - char *leasefile = networkDnsmasqLeaseFileName(network->def->name); - if (!leasefile) - goto cleanup; - virCommandAddArgFormat(cmd, "--dhcp-leasefile=%s", leasefile); - VIR_FREE(leasefile); - virCommandAddArgFormat(cmd, "--dhcp-lease-max=%d", nbleases); - } + if (networkBuildDnsmasqDhcpHostsList(dctx, ipdef) < 0) + goto cleanup;
- if (ipdef->nranges || ipdef->nhosts) - virCommandAddArg(cmd, "--dhcp-no-override"); + /* Note: the following is IPv4 only */ + if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET)) { + if (ipdef->nranges || ipdef->nhosts) + virCommandAddArg(cmd, "--dhcp-no-override");
- /* add domain to any non-qualified hostnames in /etc/hosts or addn-hosts */ - if (network->def->domain) - virCommandAddArg(cmd, "--expand-hosts"); + if (ipdef->tftproot) { + virCommandAddArgList(cmd, "--enable-tftp", + "--tftp-root", ipdef->tftproot, + NULL); + }
- if (networkBuildDnsmasqHostsfile(dctx, ipdef, network->def->dns) < 0) - goto cleanup; + if (ipdef->bootfile) { + virCommandAddArg(cmd, "--dhcp-boot"); + if (VIR_SOCKET_ADDR_VALID(&ipdef->bootserver)) { + char *bootserver = virSocketAddrFormat(&ipdef->bootserver);
- /* Even if there are currently no static hosts, if we're - * listening for DHCP, we should write a 0-length hosts - * file to allow for runtime additions. - */ - if (ipdef->nranges || ipdef->nhosts) - virCommandAddArgPair(cmd, "--dhcp-hostsfile", - dctx->hostsfile->path); + if (!bootserver) + goto cleanup; + virCommandAddArgFormat(cmd, "%s%s%s", + ipdef->bootfile, ",,", bootserver); + VIR_FREE(bootserver); + } else { + virCommandAddArg(cmd, ipdef->bootfile); + } + } + } + if (ipdef == ipv6def) + ipdef = NULL; + else + ipdef = ipv6def; + }
- /* Likewise, always create this file and put it on the commandline, to allow for - * for runtime additions. - */ - virCommandAddArgPair(cmd, "--addn-hosts", - dctx->addnhostsfile->path); + if (nbleases > 0) { + char *leasefile = networkDnsmasqLeaseFileName(network->def->name); + if (!leasefile) + goto cleanup; + virCommandAddArgFormat(cmd, "--dhcp-leasefile=%s", leasefile); + VIR_FREE(leasefile); + virCommandAddArgFormat(cmd, "--dhcp-lease-max=%d", nbleases); + }
- if (ipdef->tftproot) { - virCommandAddArgList(cmd, "--enable-tftp", - "--tftp-root", ipdef->tftproot, - NULL); - } - if (ipdef->bootfile) { - virCommandAddArg(cmd, "--dhcp-boot"); - if (VIR_SOCKET_ADDR_VALID(&ipdef->bootserver)) { - char *bootserver = virSocketAddrFormat(&ipdef->bootserver); + /* this is done once per interface */ + if (networkBuildDnsmasqHostsList(dctx, network->def->dns) < 0) + goto cleanup;
- if (!bootserver) + /* Even if there are currently no static hosts, if we're + * listening for DHCP, we should write a 0-length hosts + * file to allow for runtime additions. + */ + if (dhcp4flag || dhcp6flag) + virCommandAddArgPair(cmd, "--dhcp-hostsfile", + dctx->hostsfile->path); + + /* Likewise, always create this file and put it on the commandline, to allow for + * for runtime additions. + */ + virCommandAddArgPair(cmd, "--addn-hosts", + dctx->addnhostsfile->path); + + /* we are doing RA instead of radvd */ + if (CHECK_DNSMASQ_VERSION()) { + if (dhcp6flag) + virCommandAddArg(cmd, "--enable-ra"); + else { + char *bridgeaddr = NULL; + ipdef = virNetworkDefGetIpByIndex(network->def, AF_INET6, 0); + if (ipdef) { + bridgeaddr = virSocketAddrFormat(&ipdef->address); + if (bridgeaddr) { + virCommandAddArgFormat(cmd, + "--dhcp-range=%s,ra-only", bridgeaddr); + } else { + virReportError(VIR_ERR_XML_ERROR, + _("invalid IPv6 configuration for %s ra-only"), + network->def->name); goto cleanup; - virCommandAddArgFormat(cmd, "%s%s%s", - ipdef->bootfile, ",,", bootserver); - VIR_FREE(bootserver); - } else { - virCommandAddArg(cmd, ipdef->bootfile); + } + VIR_FREE(bridgeaddr); } } }
ret = 0; + cleanup: VIR_FREE(record); VIR_FREE(recordPort); @@ -877,7 +959,7 @@ networkBuildDhcpDaemonCommandLine(virNetworkObjPtr network, virCommandPtr *cmdou return 0;
cmd = virCommandNew(DNSMASQ); - if (networkBuildDnsmasqArgv(network, ipdef, pidfile, cmd, dctx) < 0) { + if (networkBuildDnsmasqArgv(network, pidfile, cmd, dctx) < 0) { goto cleanup; }
@@ -894,18 +976,45 @@ static int networkStartDhcpDaemon(virNetworkObjPtr network) { virCommandPtr cmd = NULL; + const char *cmdname = DNSMASQ; + char *tmp, *version = NULL; + unsigned int major = 0, minor = 0; char *pidfile = NULL; int ret = -1; dnsmasqContext *dctx = NULL; - virNetworkIpDefPtr ipdef; - int i;
if (!virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, 0)) { - /* no IPv6 addresses, so we don't need to run radvd */ + /* no IP addresses, so we don't need to run */ ret = 0; goto cleanup; }
+ if (!virFileIsExecutable(cmdname)) { + VIR_WARN("file %s missing or not executable", cmdname); + goto cleanup; + } + VIR_INFO("starting dhcp daemon (%s)", cmdname); + network->dnsmasqVersion = 0; + + cmd = virCommandNew(cmdname); + virCommandAddArg(cmd, "--version"); + virCommandSetOutputBuffer(cmd, &version); + if (virCommandRun(cmd, NULL) < 0) + goto cleanup; + virCommandFree(cmd); + cmd = NULL; + + if ((version!=NULL) && + (STREQLEN(version, DNSMASQ_PREFIX_TEXT, strlen(DNSMASQ_PREFIX_TEXT)))) { + tmp = version + strlen(DNSMASQ_PREFIX_TEXT); + if (virStrToLong_ui(tmp, &tmp, 10, &major) >= 0) { + if ((*tmp == '.') && + virStrToLong_ui(tmp + 1, &tmp, 10, &minor) >= 0) { + network->dnsmasqVersion = (major * 1000) + minor; + } + } + } + if (virFileMakePath(NETWORK_PID_DIR) < 0) { virReportSystemError(errno, _("cannot create directory %s"), @@ -939,18 +1048,6 @@ networkStartDhcpDaemon(virNetworkObjPtr network) if (ret < 0) goto cleanup;
- /* populate dnsmasq hosts file */ - for (i = 0; (ipdef = virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, i)); i++) { - if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET) && - (ipdef->nranges || ipdef->nhosts)) { - if (networkBuildDnsmasqHostsfile(dctx, ipdef, - network->def->dns) < 0) - goto cleanup; - - break; - } - } - ret = dnsmasqSave(dctx); if (ret < 0) goto cleanup; @@ -976,6 +1073,7 @@ networkStartDhcpDaemon(virNetworkObjPtr network) ret = 0; cleanup: VIR_FREE(pidfile); + VIR_FREE(version); virCommandFree(cmd); dnsmasqContextFree(dctx); return ret; @@ -994,31 +1092,35 @@ networkRefreshDhcpDaemon(virNetworkObjPtr network) virNetworkIpDefPtr ipdef; dnsmasqContext *dctx = NULL;
+ /* if no IP addresses specified, nothing to do */ + if (virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, 0)) + return 0; + /* if there's no running dnsmasq, just start it */ if (network->dnsmasqPid <= 0 || (kill(network->dnsmasqPid, 0) < 0)) return networkStartDhcpDaemon(network);
- /* Look for first IPv4 address that has dhcp defined. */ - /* We support dhcp config on 1 IPv4 interface only. */ + VIR_INFO("REFRESH: DhcpDaemon: for %s", network->def->bridge); + if (!(dctx = dnsmasqContextNew(network->def->name, DNSMASQ_STATE_DIR))) + goto cleanup; + + /* Look for first IPv4 address that has dhcp defined. + * We only support dhcp-host config on 1 IPv4 interface. + */ for (ii = 0; (ipdef = virNetworkDefGetIpByIndex(network->def, AF_INET, ii)); ii++) { if (ipdef->nranges || ipdef->nhosts) break; } - /* If no IPv4 addresses had dhcp info, pick the first (if there were any). */ if (!ipdef) ipdef = virNetworkDefGetIpByIndex(network->def, AF_INET, 0);
- if (!ipdef) { - /* no <ip> elements, so nothing to do */ - return 0; - } - - if (!(dctx = dnsmasqContextNew(network->def->name, DNSMASQ_STATE_DIR))) - goto cleanup; + if (ipdef) + if (networkBuildDnsmasqDhcpHostsList(dctx, ipdef) < 0) + goto cleanup;
- if (networkBuildDnsmasqHostsfile(dctx, ipdef, network->def->dns) < 0) + if (networkBuildDnsmasqHostsList(dctx, network->def->dns) < 0) goto cleanup;
if ((ret = dnsmasqSave(dctx)) < 0) @@ -1170,6 +1272,12 @@ networkStartRadvd(virNetworkObjPtr network) virCommandPtr cmd = NULL; int ret = -1;
+ /* is dnsmasq handling RA */ + if (CHECK_DNSMASQ_VERSION()) { + ret = 0; + goto cleanup; + } + network->radvdPid = -1;
if (!virNetworkDefGetIpByIndex(network->def, AF_INET6, 0)) { @@ -1247,6 +1355,10 @@ cleanup: static int networkRefreshRadvd(virNetworkObjPtr network) { + /* is dnsmasq handling RA */ + if (CHECK_DNSMASQ_VERSION()) + return 0; + /* if there's no running radvd, just start it */ if (network->radvdPid <= 0 || (kill(network->radvdPid, 0) < 0)) return networkStartRadvd(network); @@ -1626,9 +1738,19 @@ networkAddGeneralIp6tablesRules(struct network_driver *driver, goto err5; }
+ if (iptablesAddUdpInput(driver->iptables, AF_INET6, + network->def->bridge, 547) < 0) { + virReportError(VIR_ERR_SYSTEM_ERROR, + _("failed to add ip6tables rule to allow DHCP6 requests from '%s'"), + network->def->bridge); + goto err6; + } + return 0;
/* unwind in reverse order from the point of failure */ +err6: + iptablesRemoveUdpInput(driver->iptables, AF_INET6, network->def->bridge, 53); err5: iptablesRemoveTcpInput(driver->iptables, AF_INET6, network->def->bridge, 53); err4: @@ -1646,6 +1768,7 @@ networkRemoveGeneralIp6tablesRules(struct network_driver *driver, virNetworkObjPtr network) { if (virNetworkDefGetIpByIndex(network->def, AF_INET6, 0)) { + iptablesRemoveUdpInput(driver->iptables, AF_INET6, network->def->bridge, 547); iptablesRemoveUdpInput(driver->iptables, AF_INET6, network->def->bridge, 53); iptablesRemoveTcpInput(driver->iptables, AF_INET6, network->def->bridge, 53); } @@ -2695,8 +2818,7 @@ networkValidate(struct network_driver *driver, bool vlanUsed, vlanAllowed, badVlanUse = false; virPortGroupDefPtr defaultPortGroup = NULL; virNetworkIpDefPtr ipdef; - bool ipv4def = false; - int i; + bool ipv4def = false, ipv6def = false;
/* check for duplicate networks */ if (virNetworkObjIsDuplicate(&driver->networks, def, check_active) < 0) @@ -2715,17 +2837,36 @@ networkValidate(struct network_driver *driver, virNetworkSetBridgeMacAddr(def); }
- /* We only support dhcp on one IPv4 address per defined network */ - for (i = 0; (ipdef = virNetworkDefGetIpByIndex(def, AF_INET, i)); i++) { - if (ipdef->nranges || ipdef->nhosts) { - if (ipv4def) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Multiple dhcp sections found. " + /* We only support dhcp on one IPv4 address and + * on one IPv6 address per defined network + */ + for (ii = 0; + (ipdef = virNetworkDefGetIpByIndex(def, AF_UNSPEC, ii)); + ii++) { + if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET)) { + if (ipdef->nranges || ipdef->nhosts) { + if (ipv4def) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Multiple IPv4 dhcp sections found -- " "dhcp is supported only for a " "single IPv4 address on each network")); - return -1; - } else { - ipv4def = true; + return -1; + } else { + ipv4def = true; + } + } + } + if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6)) { + if (ipdef->nranges || ipdef->nhosts) { + if (ipv6def) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Multiple IPv6 dhcp sections found -- " + "dhcp is supported only for a " + "single IPv6 address on each network")); + return -1; + } else { + ipv6def = true; + } } } } diff --git a/src/util/dnsmasq.c b/src/util/dnsmasq.c index 9d1c07b..b0cf3ce 100644 --- a/src/util/dnsmasq.c +++ b/src/util/dnsmasq.c @@ -304,7 +304,14 @@ hostsfileAdd(dnsmasqHostsfile *hostsfile, if (!(ipstr = virSocketAddrFormat(ip))) return -1;
- if (name) { + /* the first test determins if it is a dhcpv6 host */ + if (mac==NULL) { + if (virAsprintf(&hostsfile->hosts[hostsfile->nhosts].host, "%s,[%s]", + name, ipstr) < 0) { + goto alloc_error; + } + } + else if (name) { if (virAsprintf(&hostsfile->hosts[hostsfile->nhosts].host, "%s,%s,%s", mac, ipstr, name) < 0) { goto alloc_error; diff --git a/tests/networkxml2argvdata/dhcp6-network.argv b/tests/networkxml2argvdata/dhcp6-network.argv new file mode 100644 index 0000000..87c68ac --- /dev/null +++ b/tests/networkxml2argvdata/dhcp6-network.argv @@ -0,0 +1,17 @@ +@DNSMASQ@ \ +--strict-order \ +--bind-interfaces \ +--domain-needed \ +--except-interface=lo \ +--domain=mynet \ +--expand-hosts \ +--local=/mynet/ \ +--conf-file= \ +--listen-address 2001:db8:ac10:fe01::1 \ +--listen-address 2001:db8:ac10:fd01::1 \ +--dhcp-range 2001:db8:ac10:fd01::1:10,2001:db8:ac10:fd01::1:ff \ +--dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \ +--dhcp-lease-max=240 \ +--dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile \ +--addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts \ +--enable-ra\ diff --git a/tests/networkxml2argvdata/dhcp6-network.xml b/tests/networkxml2argvdata/dhcp6-network.xml new file mode 100644 index 0000000..990b403 --- /dev/null +++ b/tests/networkxml2argvdata/dhcp6-network.xml @@ -0,0 +1,16 @@ +<network> + <name>default</name> + <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid> + <forward dev='eth1' mode='nat'/> + <bridge name='virbr0' stp='on' delay='0' /> + <domain name='mynet'/> + <ip family='ipv6' address='2001:db8:ac10:fe01::1' prefix='64'> + </ip> + <ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'> + <dhcp> + <range start='2001:db8:ac10:fd01::1:10' end='2001:db8:ac10:fd01::1:ff' /> + <host name='ralph' ip='2001:db8:ac10:fd01::1:20' /> + <host name='paul' ip='2001:db8:ac10:fd01::1:21' /> + </dhcp> + </ip> +</network> diff --git a/tests/networkxml2argvdata/isolated-network.argv b/tests/networkxml2argvdata/isolated-network.argv index 13e77b2..97f7b11 100644 --- a/tests/networkxml2argvdata/isolated-network.argv +++ b/tests/networkxml2argvdata/isolated-network.argv @@ -1,9 +1,16 @@ -@DNSMASQ@ --strict-order --bind-interfaces \ ---local=// --domain-needed --conf-file= \ ---except-interface lo --dhcp-option=3 --no-resolv \ +@DNSMASQ@ \ +--strict-order \ +--bind-interfaces \ +--domain-needed \ +--except-interface=lo \ +--local=// \ +--conf-file= \ +--dhcp-option=3 \ +--no-resolv \ --listen-address 192.168.152.1 \ --dhcp-range 192.168.152.2,192.168.152.254 \ ---dhcp-leasefile=/var/lib/libvirt/dnsmasq/private.leases --dhcp-lease-max=253 \ --dhcp-no-override \ +--dhcp-leasefile=/var/lib/libvirt/dnsmasq/private.leases \ +--dhcp-lease-max=253 \ --dhcp-hostsfile=/var/lib/libvirt/dnsmasq/private.hostsfile \ --addn-hosts=/var/lib/libvirt/dnsmasq/private.addnhosts\ diff --git a/tests/networkxml2argvdata/nat-network-dhcp6.argv b/tests/networkxml2argvdata/nat-network-dhcp6.argv new file mode 100644 index 0000000..a4795e5 --- /dev/null +++ b/tests/networkxml2argvdata/nat-network-dhcp6.argv @@ -0,0 +1,20 @@ +@DNSMASQ@ \ +--strict-order \ +--bind-interfaces \ +--domain-needed \ +--except-interface=lo \ +--local=// \ +--conf-file= \ +--listen-address 192.168.122.1 \ +--listen-address 192.168.123.1 \ +--listen-address 2001:db8:ac10:fe01::1 \ +--listen-address 2001:db8:ac10:fd01::1 \ +--listen-address 10.24.10.1 \ +--dhcp-range 192.168.122.2,192.168.122.254 \ +--dhcp-no-override \ +--dhcp-range 2001:db8:ac10:fd01::1:10,2001:db8:ac10:fd01::1:ff \ +--dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \ +--dhcp-lease-max=493 \ +--dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile \ +--addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts \ +--enable-ra\ diff --git a/tests/networkxml2argvdata/nat-network-dhcp6.xml b/tests/networkxml2argvdata/nat-network-dhcp6.xml new file mode 100644 index 0000000..f993a26 --- /dev/null +++ b/tests/networkxml2argvdata/nat-network-dhcp6.xml @@ -0,0 +1,26 @@ +<network> + <name>default</name> + <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid> + <forward dev='eth1' mode='nat'/> + <bridge name='virbr0' stp='on' delay='0' /> + <ip address='192.168.122.1' netmask='255.255.255.0'> + <dhcp> + <range start='192.168.122.2' end='192.168.122.254' /> + <host mac='00:16:3e:77:e2:ed' name='a.example.com' ip='192.168.122.10' /> + <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'> + <dhcp> + <range start='2001:db8:ac10:fd01::1:10' end='2001:db8:ac10:fd01::1:ff' /> + <host name='ralph' ip='2001:db8:ac10:fd01::1:20' /> + <host name='paul' ip='2001:db8:ac10:fd01::1:21' /> + </dhcp> + </ip> + <ip family='ipv4' address='10.24.10.1'> + </ip> +</network> diff --git a/tests/networkxml2argvdata/nat-network-dns-hosts.argv b/tests/networkxml2argvdata/nat-network-dns-hosts.argv index 03a0676..32ad19f 100644 --- a/tests/networkxml2argvdata/nat-network-dns-hosts.argv +++ b/tests/networkxml2argvdata/nat-network-dns-hosts.argv @@ -1,4 +1,11 @@ -@DNSMASQ@ --strict-order --bind-interfaces --domain=example.com \ ---local=/example.com/ --domain-needed \ ---conf-file= --except-interface lo --listen-address 192.168.122.1 \ ---expand-hosts --addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts\ +@DNSMASQ@ \ +--strict-order \ +--bind-interfaces \ +--domain-needed \ +--except-interface=lo \ +--domain=example.com \ +--expand-hosts \ +--local=/example.com/ \ +--conf-file= \ +--listen-address 192.168.122.1 \ +--addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts\ diff --git a/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv b/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv index 210a60c..4c21ff1 100644 --- a/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv +++ b/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv @@ -1,8 +1,10 @@ @DNSMASQ@ \ --strict-order \ --bind-interfaces \ ---local=// --domain-needed --conf-file= \ ---except-interface lo \ +--domain-needed \ +--except-interface=lo \ +--local=// \ +--conf-file= \ --srv-host=name.tcp.,,,, \ --listen-address 192.168.122.1 \ --listen-address 192.168.123.1 \ @@ -10,8 +12,9 @@ --listen-address 2001:db8:ac10:fd01::1 \ --listen-address 10.24.10.1 \ --dhcp-range 192.168.122.2,192.168.122.254 \ +--dhcp-no-override \ --dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \ --dhcp-lease-max=253 \ ---dhcp-no-override \ --dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile \ ---addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts\ +--addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts \ +--dhcp-range=2001:db8:ac10:fe01::1,ra-only\ diff --git a/tests/networkxml2argvdata/nat-network-dns-srv-record.argv b/tests/networkxml2argvdata/nat-network-dns-srv-record.argv index 833d3cd..246dbeb 100644 --- a/tests/networkxml2argvdata/nat-network-dns-srv-record.argv +++ b/tests/networkxml2argvdata/nat-network-dns-srv-record.argv @@ -1,8 +1,10 @@ @DNSMASQ@ \ --strict-order \ --bind-interfaces \ ---local=// --domain-needed --conf-file= \ ---except-interface lo \ +--domain-needed \ +--except-interface=lo \ +--local=// \ +--conf-file= \ --srv-host=name.tcp.test-domain-name,.,1024,10,10 \ --listen-address 192.168.122.1 \ --listen-address 192.168.123.1 \ @@ -10,8 +12,9 @@ --listen-address 2001:db8:ac10:fd01::1 \ --listen-address 10.24.10.1 \ --dhcp-range 192.168.122.2,192.168.122.254 \ +--dhcp-no-override \ --dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \ --dhcp-lease-max=253 \ ---dhcp-no-override \ --dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile \ ---addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts\ +--addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts \ +--dhcp-range=2001:db8:ac10:fe01::1,ra-only\ diff --git a/tests/networkxml2argvdata/nat-network-dns-txt-record.argv b/tests/networkxml2argvdata/nat-network-dns-txt-record.argv index 3481507..8d18f3f 100644 --- a/tests/networkxml2argvdata/nat-network-dns-txt-record.argv +++ b/tests/networkxml2argvdata/nat-network-dns-txt-record.argv @@ -1,11 +1,20 @@ -@DNSMASQ@ --strict-order --bind-interfaces \ ---local=// --domain-needed --conf-file= \ ---except-interface lo '--txt-record=example,example value' \ ---listen-address 192.168.122.1 --listen-address 192.168.123.1 \ +@DNSMASQ@ \ +--strict-order \ +--bind-interfaces \ +--domain-needed \ +--except-interface=lo \ +--local=// \ +--conf-file= \ +'--txt-record=example,example value' \ +--listen-address 192.168.122.1 \ +--listen-address 192.168.123.1 \ --listen-address 2001:db8:ac10:fe01::1 \ ---listen-address 2001:db8:ac10:fd01::1 --listen-address 10.24.10.1 \ +--listen-address 2001:db8:ac10:fd01::1 \ +--listen-address 10.24.10.1 \ --dhcp-range 192.168.122.2,192.168.122.254 \ +--dhcp-no-override \ --dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \ ---dhcp-lease-max=253 --dhcp-no-override \ +--dhcp-lease-max=253 \ --dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile \ ---addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts\ +--addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts \ +--dhcp-range=2001:db8:ac10:fe01::1,ra-only\ diff --git a/tests/networkxml2argvdata/nat-network.argv b/tests/networkxml2argvdata/nat-network.argv index 37fd2fc..32afeaa 100644 --- a/tests/networkxml2argvdata/nat-network.argv +++ b/tests/networkxml2argvdata/nat-network.argv @@ -1,10 +1,19 @@ -@DNSMASQ@ --strict-order --bind-interfaces \ ---local=// --domain-needed --conf-file= \ ---except-interface lo --listen-address 192.168.122.1 \ ---listen-address 192.168.123.1 --listen-address 2001:db8:ac10:fe01::1 \ ---listen-address 2001:db8:ac10:fd01::1 --listen-address 10.24.10.1 \ +@DNSMASQ@ \ +--strict-order \ +--bind-interfaces \ +--domain-needed \ +--except-interface=lo \ +--local=// \ +--conf-file= \ +--listen-address 192.168.122.1 \ +--listen-address 192.168.123.1 \ +--listen-address 2001:db8:ac10:fe01::1 \ +--listen-address 2001:db8:ac10:fd01::1 \ +--listen-address 10.24.10.1 \ --dhcp-range 192.168.122.2,192.168.122.254 \ +--dhcp-no-override \ --dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \ ---dhcp-lease-max=253 --dhcp-no-override \ +--dhcp-lease-max=253 \ --dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile \ ---addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts\ +--addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts \ +--dhcp-range=2001:db8:ac10:fe01::1,ra-only\ diff --git a/tests/networkxml2argvdata/netboot-network.argv b/tests/networkxml2argvdata/netboot-network.argv index 5408eb7..41e9416 100644 --- a/tests/networkxml2argvdata/netboot-network.argv +++ b/tests/networkxml2argvdata/netboot-network.argv @@ -1,10 +1,19 @@ -@DNSMASQ@ --strict-order --bind-interfaces --domain=example.com \ ---local=/example.com/ --domain-needed --conf-file= \ ---except-interface lo --listen-address 192.168.122.1 \ +@DNSMASQ@ \ +--strict-order \ +--bind-interfaces \ +--domain-needed \ +--except-interface=lo \ +--domain=example.com \ +--expand-hosts \ +--local=/example.com/ \ +--conf-file= \ +--listen-address 192.168.122.1 \ --dhcp-range 192.168.122.2,192.168.122.254 \ +--dhcp-no-override \ +--enable-tftp \ +--tftp-root /var/lib/tftproot \ +--dhcp-boot pxeboot.img \ --dhcp-leasefile=/var/lib/libvirt/dnsmasq/netboot.leases \ ---dhcp-lease-max=253 --dhcp-no-override --expand-hosts \ +--dhcp-lease-max=253 \ --dhcp-hostsfile=/var/lib/libvirt/dnsmasq/netboot.hostsfile \ ---addn-hosts=/var/lib/libvirt/dnsmasq/netboot.addnhosts \ ---enable-tftp \ ---tftp-root /var/lib/tftproot --dhcp-boot pxeboot.img\ +--addn-hosts=/var/lib/libvirt/dnsmasq/netboot.addnhosts\ diff --git a/tests/networkxml2argvdata/netboot-proxy-network.argv b/tests/networkxml2argvdata/netboot-proxy-network.argv index 21e01e3..eebe560 100644 --- a/tests/networkxml2argvdata/netboot-proxy-network.argv +++ b/tests/networkxml2argvdata/netboot-proxy-network.argv @@ -1,9 +1,17 @@ -@DNSMASQ@ --strict-order --bind-interfaces --domain=example.com \ ---local=/example.com/ --domain-needed --conf-file= \ ---except-interface lo --listen-address 192.168.122.1 \ +@DNSMASQ@ \ +--strict-order \ +--bind-interfaces \ +--domain-needed \ +--except-interface=lo \ +--domain=example.com \ +--expand-hosts \ +--local=/example.com/ \ +--conf-file= \ +--listen-address 192.168.122.1 \ --dhcp-range 192.168.122.2,192.168.122.254 \ +--dhcp-no-override \ +--dhcp-boot pxeboot.img,,10.20.30.40 \ --dhcp-leasefile=/var/lib/libvirt/dnsmasq/netboot.leases \ ---dhcp-lease-max=253 --dhcp-no-override --expand-hosts \ +--dhcp-lease-max=253 \ --dhcp-hostsfile=/var/lib/libvirt/dnsmasq/netboot.hostsfile \ ---addn-hosts=/var/lib/libvirt/dnsmasq/netboot.addnhosts \ ---dhcp-boot pxeboot.img,,10.20.30.40\ +--addn-hosts=/var/lib/libvirt/dnsmasq/netboot.addnhosts\ diff --git a/tests/networkxml2argvdata/routed-network-dhcphost.argv b/tests/networkxml2argvdata/routed-network-dhcphost.argv new file mode 100644 index 0000000..9f52b98 --- /dev/null +++ b/tests/networkxml2argvdata/routed-network-dhcphost.argv @@ -0,0 +1,15 @@ +@DNSMASQ@ \ +--strict-order \ +--bind-interfaces \ +--domain-needed \ +--except-interface=lo \ +--local=// \ +--conf-file= \ +--listen-address 192.168.122.1 \ +--listen-address 2001:db8:ac10:fd01::1 \ +--dhcp-range 192.168.122.1,static \ +--dhcp-no-override \ +--dhcp-range 2001:db8:ac10:fd01::1,static \ +--dhcp-hostsfile=/var/lib/libvirt/dnsmasq/local.hostsfile \ +--addn-hosts=/var/lib/libvirt/dnsmasq/local.addnhosts \ +--enable-ra\ diff --git a/tests/networkxml2argvdata/routed-network-dhcphost.xml b/tests/networkxml2argvdata/routed-network-dhcphost.xml new file mode 100644 index 0000000..38d9ebf --- /dev/null +++ b/tests/networkxml2argvdata/routed-network-dhcphost.xml @@ -0,0 +1,19 @@ +<network> + <name>local</name> + <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid> + <forward dev='eth1' mode='route'/> + <bridge name='virbr1' stp='on' delay='0' /> + <mac address='12:34:56:78:9A:BC'/> + <ip address='192.168.122.1' netmask='255.255.255.0'> + <dhcp> + <host mac='00:16:3e:77:e2:ed' name='a.example.com' ip='192.168.122.10' /> + <host mac='00:16:3e:3e:a9:1a' name='b.example.com' ip='192.168.122.11' /> + </dhcp> + </ip> + <ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'> + <dhcp> + <host name='ralph' ip='2001:db8:ac10:fd01::1:20' /> + <host name='paul' ip='2001:db8:ac10:fd01::1:21' /> + </dhcp> + </ip> +</network> diff --git a/tests/networkxml2argvdata/routed-network.argv b/tests/networkxml2argvdata/routed-network.argv index 9fedb2b..78ad8a2 100644 --- a/tests/networkxml2argvdata/routed-network.argv +++ b/tests/networkxml2argvdata/routed-network.argv @@ -1,4 +1,9 @@ -@DNSMASQ@ --strict-order --bind-interfaces \ ---local=// --domain-needed --conf-file= \ ---except-interface lo --listen-address 192.168.122.1 \ +@DNSMASQ@ \ +--strict-order \ +--bind-interfaces \ +--domain-needed \ +--except-interface=lo \ +--local=// \ +--conf-file= \ +--listen-address 192.168.122.1 \ --addn-hosts=/var/lib/libvirt/dnsmasq/local.addnhosts\ diff --git a/tests/networkxml2argvtest.c b/tests/networkxml2argvtest.c index 87519e4..206e2d7 100644 --- a/tests/networkxml2argvtest.c +++ b/tests/networkxml2argvtest.c @@ -73,6 +73,7 @@ static int testCompareXMLToArgvFiles(const char *inxml, const char *outargv) { goto fail;
obj->def = dev; + obj->dnsmasqVersion = 2064; dctx = dnsmasqContextNew(dev->name, "/var/lib/libvirt/dnsmasq");
if (dctx == NULL) @@ -157,6 +158,9 @@ mymain(void) DO_TEST("nat-network-dns-srv-record"); DO_TEST("nat-network-dns-srv-record-minimal"); DO_TEST("nat-network-dns-hosts"); + DO_TEST("nat-network-dhcp6"); + DO_TEST("routed-network-dhcphost"); + DO_TEST("dhcp6-network");
return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE; }

On 11/22/2012 02:21 PM, Gene Czarcinski wrote:
On 11/21/2012 04:18 PM, Gene Czarcinski wrote:
The DHCPv6 support includes IPV6 dhcp-range and dhcp-host for one IPv6 subnetwork on one interface. This support will only work if dnsmasq version >= 2.64; otherwise an error occurs if dhcp-range or dhcp-host is specified.
Note that the check for dnsmasq version is performed at network start and only then will an error condition occur. Thus, it is possible to define/edit a network configuration which will not start. The error message indicates that dnsmasq version 2.64 or later is required.
This patch provides the same DHCP support for IPv6 which has been available for IPv4.
With dnsmasq >= 2.64, support for the RA service is now provided by dnsmasq (radvd is no longer needed/used/started).
Documentation has been updated to reflect the new support.
Oops. I did not realize that there are schemas that should be updated. I have most of what is needed figured out but there are a couple of things not working cleanly. I should have a update patch submitted "real soon now".
Suggestion: add something to the developers guidance so that you check to make sure that the schemas are updated properly. I wonder if the tests would pass testing?
OK, I now have the schema for network (network.rng) so that it works as best I can figure for the changes involving IPv6. 1. I believe there is currently a bug in the schema involving specifying an IPv6 address for a dns host (the schema calls the IPv6 address an error). Changing "ipv4Addr" to "ipAddr" fixed things. 2. The other changes mostly involved changing "ipv4Addr" to "ipAddr". 3. For specifying a host under the IPv6 DHCP specification, I used the fact of the mac being ommitted as indicating IPv6 since the mac-address is not meaningful with respect to the IPv6 definition. For IPv6, for purposes of nailing a specific IPv6-address, the host name us used as the identifier. This works. but the schema gripped about it and called it an error until I wrapped the MAC specification in <optional> ... </optional>. This is not completely correct since it is not optional for a IPv4 specification but I did not know how to reflect this in the schema. 4. Some help here would be appreciated. One alternative would be to define a new thing called "host6" for IPv6 nailed host specification but that seemed to me to be overkill (and it still does). Gene

This patch changes how parameters are passed to dnsmasq. Instead of being on the command line, the parameters are put into a file (one parameter per line) and the command line parameter --conf-file= to specify the location of the file. This file is located in the same directory as the leases file. This also adds the dnsmasq parameter interface=<net-name> The change of putting the parameters into a configuration file allows them to be examined and more easily understood than examining the command lines displayed by "ps ax". This is especially true when a number of networks have been started. I suspect that when the use of dnsmasq was originally done, the command line was simple but has gotten more complicated over time and will likely become even more complicated in the future. I believe that if use of dnsmasq was done today with the current requirements for dnsmasq, a configuration file would have been used. Many (most?) daemons use configuration files as oppose to command line parameters. One potential addition to dnsmasq parameters is to specify the reverse-lookup queries which are not to be passed on up the chain. For IPv4, such queries are rather simple and take the form: <ipv4_address>.in-addr.arpa but ipv6 queries involve a lot longer specification. The IPv6 query will be of the form: <ipv6_address>.ip6.arpa where the above query expands to a 44 character string which is specified by --local=/<ipv6_address>.ip6.arpa/ which is certainly a lot of clutter to add to the command line. --- src/network/bridge_driver.c | 158 +++++++++++++-------- src/network/bridge_driver.h | 7 +- tests/networkxml2argvdata/dhcp6-network.argv | 34 ++--- tests/networkxml2argvdata/isolated-network.argv | 32 ++--- tests/networkxml2argvdata/nat-network-dhcp6.argv | 40 +++--- .../networkxml2argvdata/nat-network-dns-hosts.argv | 22 +-- .../nat-network-dns-srv-record-minimal.argv | 40 +++--- .../nat-network-dns-srv-record.argv | 40 +++--- .../nat-network-dns-txt-record.argv | 40 +++--- tests/networkxml2argvdata/nat-network.argv | 38 ++--- tests/networkxml2argvdata/netboot-network.argv | 38 ++--- .../networkxml2argvdata/netboot-proxy-network.argv | 34 ++--- .../routed-network-dhcphost.argv | 30 ++-- tests/networkxml2argvdata/routed-network.argv | 18 +-- tests/networkxml2argvtest.c | 42 +----- 15 files changed, 309 insertions(+), 304 deletions(-) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 19a9170..6cb4c16 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -141,6 +141,16 @@ networkDnsmasqLeaseFileNameFunc networkDnsmasqLeaseFileName = networkDnsmasqLeaseFileNameDefault; static char * +networkDnsmasqConfigFileName(const char *netname) +{ + char *conffile; + + ignore_value(virAsprintf(&conffile, DNSMASQ_STATE_DIR "/%s.conf", + netname)); + return conffile; +} + +static char * networkRadvdPidfileBasename(const char *netname) { /* this is simple but we want to be sure it's consistently done */ @@ -168,6 +178,7 @@ networkRemoveInactive(struct network_driver *driver, char *leasefile = NULL; char *radvdconfigfile = NULL; char *radvdpidbase = NULL; + char *configfile = NULL; dnsmasqContext *dctx = NULL; virNetworkDefPtr def = virNetworkObjGetPersistentDef(net); @@ -186,9 +197,13 @@ networkRemoveInactive(struct network_driver *driver, if (!(radvdpidbase = networkRadvdPidfileBasename(def->name))) goto no_memory; + if (!(configfile = networkDnsmasqConfigFileName(def->name))) + goto no_memory; + /* dnsmasq */ dnsmasqDelete(dctx); unlink(leasefile); + unlink(configfile); /* radvd */ unlink(radvdconfigfile); @@ -201,6 +216,7 @@ networkRemoveInactive(struct network_driver *driver, cleanup: VIR_FREE(leasefile); + VIR_FREE(configfile); VIR_FREE(radvdconfigfile); VIR_FREE(radvdpidbase); dnsmasqContextFree(dctx); @@ -627,13 +643,14 @@ networkBuildDnsmasqHostsList(dnsmasqContext *dctx, return 0; } - + /* build the dnsmasq conf file contents */ static int -networkBuildDnsmasqArgv(virNetworkObjPtr network, +networkDnsmasqConfContents(virNetworkObjPtr network, const char *pidfile, - virCommandPtr cmd, + char **configstr, dnsmasqContext *dctx) { + virBuffer configbuf = VIR_BUFFER_INITIALIZER;; int r, ret = -1; int nbleases = 0; int ii; @@ -644,6 +661,8 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, virNetworkIpDefPtr tmpipdef, ipdef, ipv4def, ipv6def; bool dhcp4flag, dhcp6flag; + *configstr = NULL; + /* * NB, be careful about syntax for dnsmasq options in long format. * @@ -663,29 +682,27 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, * very explicit on this. */ - /* - * Needed to ensure dnsmasq uses same algorithm for processing - * multiple namedriver entries in /etc/resolv.conf as GLibC. - */ - virCommandAddArgList(cmd, "--strict-order", - "--bind-interfaces", - "--domain-needed", - "--except-interface=lo", - NULL); + /* create dnsmasq config file appropriate for this network */ + virBufferAsprintf(&configbuf, + "# dnsmasq conf file created by libvirt\n" + "strict-order\n" + "bind-interfaces\n" + "domain-needed\n" + "except-interface=lo\n"); if (network->def->domain) { - virCommandAddArgPair(cmd, "--domain", network->def->domain); - virCommandAddArg(cmd, "--expand-hosts"); + virBufferAsprintf(&configbuf, + "domain=%s\n" + "expand-hosts\n", + network->def->domain); } /* need to specify local even if no domain specified */ - virCommandAddArgFormat(cmd, "--local=/%s/", - network->def->domain ? network->def->domain : ""); + virBufferAsprintf(&configbuf, + "local=/%s/\n", + network->def->domain ? network->def->domain : ""); if (pidfile) - virCommandAddArgPair(cmd, "--pid-file", pidfile); - - /* *no* conf file */ - virCommandAddArg(cmd, "--conf-file="); + virBufferAsprintf(&configbuf, "pid-file=%s\n", pidfile); /* If this is an isolated network, set the default route option * (3) to be empty to avoid setting a default route that's @@ -695,8 +712,8 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, * to build a connection to the outside). */ if (network->def->forwardType == VIR_NETWORK_FORWARD_NONE) { - virCommandAddArgList(cmd, "--dhcp-option=3", - "--no-resolv", NULL); + virBufferAsprintf(&configbuf, "dhcp-option=3\n" + "no-resolv\n"); } if (network->def->dns != NULL) { @@ -704,7 +721,7 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, int i; for (i = 0; i < dns->ntxtrecords; i++) { - virCommandAddArgFormat(cmd, "--txt-record=%s,%s", + virBufferAsprintf(&configbuf, "txt-record=%s,%s\n", dns->txtrecords[i].name, dns->txtrecords[i].value); } @@ -742,7 +759,7 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, goto cleanup; } - virCommandAddArgPair(cmd, "--srv-host", record); + virBufferAsprintf(&configbuf, "srv-host=%s\n", record); VIR_FREE(record); VIR_FREE(recordPort); VIR_FREE(recordWeight); @@ -751,21 +768,15 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, } } - /* - * --interface does not actually work with dnsmasq < 2.47, - * due to DAD for ipv6 addresses on the interface. - * - * virCommandAddArgList(cmd, "--interface", ipdef->bridge, NULL); - * - * So listen on all defined IPv[46] addresses - */ + virBufferAsprintf(&configbuf, "interface=%s\n", network->def->bridge); + for (ii = 0; (tmpipdef = virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, ii)); ii++) { char *ipaddr = virSocketAddrFormat(&tmpipdef->address); if (!ipaddr) goto cleanup; - virCommandAddArgList(cmd, "--listen-address", ipaddr, NULL); + virBufferAsprintf(&configbuf, "listen-address=%s\n", ipaddr); VIR_FREE(ipaddr); } @@ -811,8 +822,8 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, VIR_FREE(saddr); goto cleanup; } - virCommandAddArg(cmd, "--dhcp-range"); - virCommandAddArgFormat(cmd, "%s,%s", saddr, eaddr); + virBufferAsprintf(&configbuf, "dhcp-range=%s,%s\n", + saddr, eaddr); VIR_FREE(saddr); VIR_FREE(eaddr); nbleases += virSocketAddrGetRange(&ipdef->ranges[r].start, @@ -828,8 +839,7 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, char *bridgeaddr = virSocketAddrFormat(&ipdef->address); if (!bridgeaddr) goto cleanup; - virCommandAddArg(cmd, "--dhcp-range"); - virCommandAddArgFormat(cmd, "%s,static", bridgeaddr); + virBufferAsprintf(&configbuf, "dhcp-range=%s,static\n", bridgeaddr); VIR_FREE(bridgeaddr); } @@ -839,26 +849,23 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, /* Note: the following is IPv4 only */ if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET)) { if (ipdef->nranges || ipdef->nhosts) - virCommandAddArg(cmd, "--dhcp-no-override"); + virBufferAsprintf(&configbuf, "dhcp-no-override\n"); if (ipdef->tftproot) { - virCommandAddArgList(cmd, "--enable-tftp", - "--tftp-root", ipdef->tftproot, - NULL); + virBufferAsprintf(&configbuf, "enable-tftp\n"); + virBufferAsprintf(&configbuf, "tftp-root=%s\n", ipdef->tftproot); } - if (ipdef->bootfile) { - virCommandAddArg(cmd, "--dhcp-boot"); if (VIR_SOCKET_ADDR_VALID(&ipdef->bootserver)) { char *bootserver = virSocketAddrFormat(&ipdef->bootserver); if (!bootserver) goto cleanup; - virCommandAddArgFormat(cmd, "%s%s%s", + virBufferAsprintf(&configbuf, "dhcp-boot=%s%s%s\n", ipdef->bootfile, ",,", bootserver); VIR_FREE(bootserver); } else { - virCommandAddArg(cmd, ipdef->bootfile); + virBufferAsprintf(&configbuf, "dhcp-boot=%s\n", ipdef->bootfile); } } } @@ -872,9 +879,9 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, char *leasefile = networkDnsmasqLeaseFileName(network->def->name); if (!leasefile) goto cleanup; - virCommandAddArgFormat(cmd, "--dhcp-leasefile=%s", leasefile); + virBufferAsprintf(&configbuf, "dhcp-leasefile=%s\n", leasefile); VIR_FREE(leasefile); - virCommandAddArgFormat(cmd, "--dhcp-lease-max=%d", nbleases); + virBufferAsprintf(&configbuf, "dhcp-lease-max=%d\n", nbleases); } /* this is done once per interface */ @@ -886,27 +893,27 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, * file to allow for runtime additions. */ if (dhcp4flag || dhcp6flag) - virCommandAddArgPair(cmd, "--dhcp-hostsfile", - dctx->hostsfile->path); + virBufferAsprintf(&configbuf, "dhcp-hostsfile=%s\n", + dctx->hostsfile->path); /* Likewise, always create this file and put it on the commandline, to allow for * for runtime additions. */ - virCommandAddArgPair(cmd, "--addn-hosts", - dctx->addnhostsfile->path); + virBufferAsprintf(&configbuf, "addn-hosts=%s\n", + dctx->addnhostsfile->path); /* we are doing RA instead of radvd */ if (CHECK_DNSMASQ_VERSION()) { if (dhcp6flag) - virCommandAddArg(cmd, "--enable-ra"); + virBufferAsprintf(&configbuf, "enable-ra\n"); else { char *bridgeaddr = NULL; ipdef = virNetworkDefGetIpByIndex(network->def, AF_INET6, 0); if (ipdef) { bridgeaddr = virSocketAddrFormat(&ipdef->address); if (bridgeaddr) { - virCommandAddArgFormat(cmd, - "--dhcp-range=%s,ra-only", bridgeaddr); + virBufferAsprintf(&configbuf, + "dhcp-range=%s,ra-only\n", bridgeaddr); } else { virReportError(VIR_ERR_XML_ERROR, _("invalid IPv6 configuration for %s ra-only"), @@ -917,10 +924,15 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network, } } } + if (!(*configstr = virBufferContentAndReset(&configbuf))) { + virReportOOMError(); + goto cleanup; + } ret = 0; cleanup: + virBufferFreeAndReset(&configbuf); VIR_FREE(record); VIR_FREE(recordPort); VIR_FREE(recordWeight); @@ -928,13 +940,17 @@ cleanup: return ret; } + /* build the dnsmasq command line */ int networkBuildDhcpDaemonCommandLine(virNetworkObjPtr network, virCommandPtr *cmdout, - char *pidfile, dnsmasqContext *dctx) + char *pidfile, dnsmasqContext *dctx, + int testOnly, char **testConfigStr) { virCommandPtr cmd = NULL; int ret = -1, ii; virNetworkIpDefPtr ipdef; + char *configfile = NULL; + char *configstr = NULL; network->dnsmasqPid = -1; @@ -958,15 +974,39 @@ networkBuildDhcpDaemonCommandLine(virNetworkObjPtr network, virCommandPtr *cmdou if (!virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, 0)) return 0; - cmd = virCommandNew(DNSMASQ); - if (networkBuildDnsmasqArgv(network, pidfile, cmd, dctx) < 0) { + if (networkDnsmasqConfContents(network, pidfile, &configstr, dctx) < 0) + goto cleanup; + if (!configstr) + goto cleanup; + + if (testOnly) { + *testConfigStr = configstr; + return 0; + } + + /* construct the filename */ + if (!(configfile = networkDnsmasqConfigFileName(network->def->name))) { + virReportOOMError(); goto cleanup; } + /* Write the file */ + if (virFileWriteStr(configfile, configstr, 0600) < 0) { + virReportSystemError(errno, + _("couldn't write dnsmasq config file '%s'"), + configfile); + goto cleanup; + } + + cmd = virCommandNew(DNSMASQ); + virCommandAddArgFormat(cmd, "--conf-file=%s", configfile); + if (cmdout) *cmdout = cmd; ret = 0; cleanup: + VIR_FREE(configstr); + VIR_FREE(configfile); if (ret < 0) virCommandFree(cmd); return ret; @@ -980,6 +1020,7 @@ networkStartDhcpDaemon(virNetworkObjPtr network) char *tmp, *version = NULL; unsigned int major = 0, minor = 0; char *pidfile = NULL; + char *testconfigstr = NULL; int ret = -1; dnsmasqContext *dctx = NULL; @@ -1044,7 +1085,8 @@ networkStartDhcpDaemon(virNetworkObjPtr network) if (dctx == NULL) goto cleanup; - ret = networkBuildDhcpDaemonCommandLine(network, &cmd, pidfile, dctx); + ret = networkBuildDhcpDaemonCommandLine(network, &cmd, pidfile, dctx, + 0, &testconfigstr); if (ret < 0) goto cleanup; diff --git a/src/network/bridge_driver.h b/src/network/bridge_driver.h index 0fae275..b6dd6c9 100644 --- a/src/network/bridge_driver.h +++ b/src/network/bridge_driver.h @@ -48,15 +48,16 @@ int networkGetNetworkAddress(const char *netname, char **netaddr) int networkBuildDhcpDaemonCommandLine(virNetworkObjPtr network, virCommandPtr *cmdout, char *pidfile, - dnsmasqContext *dctx) - ; + dnsmasqContext *dctx, + int testOnly, char **testConfigStr); # else /* Define no-op replacements that don't drag in any link dependencies. */ # define networkAllocateActualDevice(iface) 0 # define networkNotifyActualDevice(iface) 0 # define networkReleaseActualDevice(iface) 0 # define networkGetNetworkAddress(netname, netaddr) (-2) -# define networkBuildDhcpDaemonCommandLine(network, cmdout, pidfile, dctx) 0 +# define networkBuildDhcpDaemonCommandLine(network, cmdout, pidfile, \ + dctx, testOnly, testConfigStr) 0 # endif typedef char *(*networkDnsmasqLeaseFileNameFunc)(const char *netname); diff --git a/tests/networkxml2argvdata/dhcp6-network.argv b/tests/networkxml2argvdata/dhcp6-network.argv index 87c68ac..3698eae 100644 --- a/tests/networkxml2argvdata/dhcp6-network.argv +++ b/tests/networkxml2argvdata/dhcp6-network.argv @@ -1,17 +1,17 @@ -@DNSMASQ@ \ ---strict-order \ ---bind-interfaces \ ---domain-needed \ ---except-interface=lo \ ---domain=mynet \ ---expand-hosts \ ---local=/mynet/ \ ---conf-file= \ ---listen-address 2001:db8:ac10:fe01::1 \ ---listen-address 2001:db8:ac10:fd01::1 \ ---dhcp-range 2001:db8:ac10:fd01::1:10,2001:db8:ac10:fd01::1:ff \ ---dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \ ---dhcp-lease-max=240 \ ---dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile \ ---addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts \ ---enable-ra\ +# dnsmasq conf file created by libvirt +strict-order +bind-interfaces +domain-needed +except-interface=lo +domain=mynet +expand-hosts +local=/mynet/ +interface=virbr0 +listen-address=2001:db8:ac10:fe01::1 +listen-address=2001:db8:ac10:fd01::1 +dhcp-range=2001:db8:ac10:fd01::1:10,2001:db8:ac10:fd01::1:ff +dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases +dhcp-lease-max=240 +dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile +addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts +enable-ra diff --git a/tests/networkxml2argvdata/isolated-network.argv b/tests/networkxml2argvdata/isolated-network.argv index 97f7b11..ef02dcb 100644 --- a/tests/networkxml2argvdata/isolated-network.argv +++ b/tests/networkxml2argvdata/isolated-network.argv @@ -1,16 +1,16 @@ -@DNSMASQ@ \ ---strict-order \ ---bind-interfaces \ ---domain-needed \ ---except-interface=lo \ ---local=// \ ---conf-file= \ ---dhcp-option=3 \ ---no-resolv \ ---listen-address 192.168.152.1 \ ---dhcp-range 192.168.152.2,192.168.152.254 \ ---dhcp-no-override \ ---dhcp-leasefile=/var/lib/libvirt/dnsmasq/private.leases \ ---dhcp-lease-max=253 \ ---dhcp-hostsfile=/var/lib/libvirt/dnsmasq/private.hostsfile \ ---addn-hosts=/var/lib/libvirt/dnsmasq/private.addnhosts\ +# dnsmasq conf file created by libvirt +strict-order +bind-interfaces +domain-needed +except-interface=lo +local=// +dhcp-option=3 +no-resolv +interface=virbr2 +listen-address=192.168.152.1 +dhcp-range=192.168.152.2,192.168.152.254 +dhcp-no-override +dhcp-leasefile=/var/lib/libvirt/dnsmasq/private.leases +dhcp-lease-max=253 +dhcp-hostsfile=/var/lib/libvirt/dnsmasq/private.hostsfile +addn-hosts=/var/lib/libvirt/dnsmasq/private.addnhosts diff --git a/tests/networkxml2argvdata/nat-network-dhcp6.argv b/tests/networkxml2argvdata/nat-network-dhcp6.argv index a4795e5..e148853 100644 --- a/tests/networkxml2argvdata/nat-network-dhcp6.argv +++ b/tests/networkxml2argvdata/nat-network-dhcp6.argv @@ -1,20 +1,20 @@ -@DNSMASQ@ \ ---strict-order \ ---bind-interfaces \ ---domain-needed \ ---except-interface=lo \ ---local=// \ ---conf-file= \ ---listen-address 192.168.122.1 \ ---listen-address 192.168.123.1 \ ---listen-address 2001:db8:ac10:fe01::1 \ ---listen-address 2001:db8:ac10:fd01::1 \ ---listen-address 10.24.10.1 \ ---dhcp-range 192.168.122.2,192.168.122.254 \ ---dhcp-no-override \ ---dhcp-range 2001:db8:ac10:fd01::1:10,2001:db8:ac10:fd01::1:ff \ ---dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \ ---dhcp-lease-max=493 \ ---dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile \ ---addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts \ ---enable-ra\ +# dnsmasq conf file created by libvirt +strict-order +bind-interfaces +domain-needed +except-interface=lo +local=// +interface=virbr0 +listen-address=192.168.122.1 +listen-address=192.168.123.1 +listen-address=2001:db8:ac10:fe01::1 +listen-address=2001:db8:ac10:fd01::1 +listen-address=10.24.10.1 +dhcp-range=192.168.122.2,192.168.122.254 +dhcp-no-override +dhcp-range=2001:db8:ac10:fd01::1:10,2001:db8:ac10:fd01::1:ff +dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases +dhcp-lease-max=493 +dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile +addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts +enable-ra diff --git a/tests/networkxml2argvdata/nat-network-dns-hosts.argv b/tests/networkxml2argvdata/nat-network-dns-hosts.argv index 32ad19f..66bc077 100644 --- a/tests/networkxml2argvdata/nat-network-dns-hosts.argv +++ b/tests/networkxml2argvdata/nat-network-dns-hosts.argv @@ -1,11 +1,11 @@ -@DNSMASQ@ \ ---strict-order \ ---bind-interfaces \ ---domain-needed \ ---except-interface=lo \ ---domain=example.com \ ---expand-hosts \ ---local=/example.com/ \ ---conf-file= \ ---listen-address 192.168.122.1 \ ---addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts\ +# dnsmasq conf file created by libvirt +strict-order +bind-interfaces +domain-needed +except-interface=lo +domain=example.com +expand-hosts +local=/example.com/ +interface=virbr0 +listen-address=192.168.122.1 +addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts diff --git a/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv b/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv index 4c21ff1..db0a8d2 100644 --- a/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv +++ b/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv @@ -1,20 +1,20 @@ -@DNSMASQ@ \ ---strict-order \ ---bind-interfaces \ ---domain-needed \ ---except-interface=lo \ ---local=// \ ---conf-file= \ ---srv-host=name.tcp.,,,, \ ---listen-address 192.168.122.1 \ ---listen-address 192.168.123.1 \ ---listen-address 2001:db8:ac10:fe01::1 \ ---listen-address 2001:db8:ac10:fd01::1 \ ---listen-address 10.24.10.1 \ ---dhcp-range 192.168.122.2,192.168.122.254 \ ---dhcp-no-override \ ---dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \ ---dhcp-lease-max=253 \ ---dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile \ ---addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts \ ---dhcp-range=2001:db8:ac10:fe01::1,ra-only\ +# dnsmasq conf file created by libvirt +strict-order +bind-interfaces +domain-needed +except-interface=lo +local=// +srv-host=name.tcp.,,,, +interface=virbr0 +listen-address=192.168.122.1 +listen-address=192.168.123.1 +listen-address=2001:db8:ac10:fe01::1 +listen-address=2001:db8:ac10:fd01::1 +listen-address=10.24.10.1 +dhcp-range=192.168.122.2,192.168.122.254 +dhcp-no-override +dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases +dhcp-lease-max=253 +dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile +addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts +dhcp-range=2001:db8:ac10:fe01::1,ra-only diff --git a/tests/networkxml2argvdata/nat-network-dns-srv-record.argv b/tests/networkxml2argvdata/nat-network-dns-srv-record.argv index 246dbeb..de01002 100644 --- a/tests/networkxml2argvdata/nat-network-dns-srv-record.argv +++ b/tests/networkxml2argvdata/nat-network-dns-srv-record.argv @@ -1,20 +1,20 @@ -@DNSMASQ@ \ ---strict-order \ ---bind-interfaces \ ---domain-needed \ ---except-interface=lo \ ---local=// \ ---conf-file= \ ---srv-host=name.tcp.test-domain-name,.,1024,10,10 \ ---listen-address 192.168.122.1 \ ---listen-address 192.168.123.1 \ ---listen-address 2001:db8:ac10:fe01::1 \ ---listen-address 2001:db8:ac10:fd01::1 \ ---listen-address 10.24.10.1 \ ---dhcp-range 192.168.122.2,192.168.122.254 \ ---dhcp-no-override \ ---dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \ ---dhcp-lease-max=253 \ ---dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile \ ---addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts \ ---dhcp-range=2001:db8:ac10:fe01::1,ra-only\ +# dnsmasq conf file created by libvirt +strict-order +bind-interfaces +domain-needed +except-interface=lo +local=// +srv-host=name.tcp.test-domain-name,.,1024,10,10 +interface=virbr0 +listen-address=192.168.122.1 +listen-address=192.168.123.1 +listen-address=2001:db8:ac10:fe01::1 +listen-address=2001:db8:ac10:fd01::1 +listen-address=10.24.10.1 +dhcp-range=192.168.122.2,192.168.122.254 +dhcp-no-override +dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases +dhcp-lease-max=253 +dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile +addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts +dhcp-range=2001:db8:ac10:fe01::1,ra-only diff --git a/tests/networkxml2argvdata/nat-network-dns-txt-record.argv b/tests/networkxml2argvdata/nat-network-dns-txt-record.argv index 8d18f3f..72455c0 100644 --- a/tests/networkxml2argvdata/nat-network-dns-txt-record.argv +++ b/tests/networkxml2argvdata/nat-network-dns-txt-record.argv @@ -1,20 +1,20 @@ -@DNSMASQ@ \ ---strict-order \ ---bind-interfaces \ ---domain-needed \ ---except-interface=lo \ ---local=// \ ---conf-file= \ -'--txt-record=example,example value' \ ---listen-address 192.168.122.1 \ ---listen-address 192.168.123.1 \ ---listen-address 2001:db8:ac10:fe01::1 \ ---listen-address 2001:db8:ac10:fd01::1 \ ---listen-address 10.24.10.1 \ ---dhcp-range 192.168.122.2,192.168.122.254 \ ---dhcp-no-override \ ---dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \ ---dhcp-lease-max=253 \ ---dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile \ ---addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts \ ---dhcp-range=2001:db8:ac10:fe01::1,ra-only\ +# dnsmasq conf file created by libvirt +strict-order +bind-interfaces +domain-needed +except-interface=lo +local=// +txt-record=example,example value +interface=virbr0 +listen-address=192.168.122.1 +listen-address=192.168.123.1 +listen-address=2001:db8:ac10:fe01::1 +listen-address=2001:db8:ac10:fd01::1 +listen-address=10.24.10.1 +dhcp-range=192.168.122.2,192.168.122.254 +dhcp-no-override +dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases +dhcp-lease-max=253 +dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile +addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts +dhcp-range=2001:db8:ac10:fe01::1,ra-only diff --git a/tests/networkxml2argvdata/nat-network.argv b/tests/networkxml2argvdata/nat-network.argv index 32afeaa..91090ef 100644 --- a/tests/networkxml2argvdata/nat-network.argv +++ b/tests/networkxml2argvdata/nat-network.argv @@ -1,19 +1,19 @@ -@DNSMASQ@ \ ---strict-order \ ---bind-interfaces \ ---domain-needed \ ---except-interface=lo \ ---local=// \ ---conf-file= \ ---listen-address 192.168.122.1 \ ---listen-address 192.168.123.1 \ ---listen-address 2001:db8:ac10:fe01::1 \ ---listen-address 2001:db8:ac10:fd01::1 \ ---listen-address 10.24.10.1 \ ---dhcp-range 192.168.122.2,192.168.122.254 \ ---dhcp-no-override \ ---dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \ ---dhcp-lease-max=253 \ ---dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile \ ---addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts \ ---dhcp-range=2001:db8:ac10:fe01::1,ra-only\ +# dnsmasq conf file created by libvirt +strict-order +bind-interfaces +domain-needed +except-interface=lo +local=// +interface=virbr0 +listen-address=192.168.122.1 +listen-address=192.168.123.1 +listen-address=2001:db8:ac10:fe01::1 +listen-address=2001:db8:ac10:fd01::1 +listen-address=10.24.10.1 +dhcp-range=192.168.122.2,192.168.122.254 +dhcp-no-override +dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases +dhcp-lease-max=253 +dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile +addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts +dhcp-range=2001:db8:ac10:fe01::1,ra-only diff --git a/tests/networkxml2argvdata/netboot-network.argv b/tests/networkxml2argvdata/netboot-network.argv index 41e9416..35b8e40 100644 --- a/tests/networkxml2argvdata/netboot-network.argv +++ b/tests/networkxml2argvdata/netboot-network.argv @@ -1,19 +1,19 @@ -@DNSMASQ@ \ ---strict-order \ ---bind-interfaces \ ---domain-needed \ ---except-interface=lo \ ---domain=example.com \ ---expand-hosts \ ---local=/example.com/ \ ---conf-file= \ ---listen-address 192.168.122.1 \ ---dhcp-range 192.168.122.2,192.168.122.254 \ ---dhcp-no-override \ ---enable-tftp \ ---tftp-root /var/lib/tftproot \ ---dhcp-boot pxeboot.img \ ---dhcp-leasefile=/var/lib/libvirt/dnsmasq/netboot.leases \ ---dhcp-lease-max=253 \ ---dhcp-hostsfile=/var/lib/libvirt/dnsmasq/netboot.hostsfile \ ---addn-hosts=/var/lib/libvirt/dnsmasq/netboot.addnhosts\ +# dnsmasq conf file created by libvirt +strict-order +bind-interfaces +domain-needed +except-interface=lo +domain=example.com +expand-hosts +local=/example.com/ +interface=virbr1 +listen-address=192.168.122.1 +dhcp-range=192.168.122.2,192.168.122.254 +dhcp-no-override +enable-tftp +tftp-root=/var/lib/tftproot +dhcp-boot=pxeboot.img +dhcp-leasefile=/var/lib/libvirt/dnsmasq/netboot.leases +dhcp-lease-max=253 +dhcp-hostsfile=/var/lib/libvirt/dnsmasq/netboot.hostsfile +addn-hosts=/var/lib/libvirt/dnsmasq/netboot.addnhosts diff --git a/tests/networkxml2argvdata/netboot-proxy-network.argv b/tests/networkxml2argvdata/netboot-proxy-network.argv index eebe560..f25feea 100644 --- a/tests/networkxml2argvdata/netboot-proxy-network.argv +++ b/tests/networkxml2argvdata/netboot-proxy-network.argv @@ -1,17 +1,17 @@ -@DNSMASQ@ \ ---strict-order \ ---bind-interfaces \ ---domain-needed \ ---except-interface=lo \ ---domain=example.com \ ---expand-hosts \ ---local=/example.com/ \ ---conf-file= \ ---listen-address 192.168.122.1 \ ---dhcp-range 192.168.122.2,192.168.122.254 \ ---dhcp-no-override \ ---dhcp-boot pxeboot.img,,10.20.30.40 \ ---dhcp-leasefile=/var/lib/libvirt/dnsmasq/netboot.leases \ ---dhcp-lease-max=253 \ ---dhcp-hostsfile=/var/lib/libvirt/dnsmasq/netboot.hostsfile \ ---addn-hosts=/var/lib/libvirt/dnsmasq/netboot.addnhosts\ +# dnsmasq conf file created by libvirt +strict-order +bind-interfaces +domain-needed +except-interface=lo +domain=example.com +expand-hosts +local=/example.com/ +interface=virbr1 +listen-address=192.168.122.1 +dhcp-range=192.168.122.2,192.168.122.254 +dhcp-no-override +dhcp-boot=pxeboot.img,,10.20.30.40 +dhcp-leasefile=/var/lib/libvirt/dnsmasq/netboot.leases +dhcp-lease-max=253 +dhcp-hostsfile=/var/lib/libvirt/dnsmasq/netboot.hostsfile +addn-hosts=/var/lib/libvirt/dnsmasq/netboot.addnhosts diff --git a/tests/networkxml2argvdata/routed-network-dhcphost.argv b/tests/networkxml2argvdata/routed-network-dhcphost.argv index 9f52b98..da6aad4 100644 --- a/tests/networkxml2argvdata/routed-network-dhcphost.argv +++ b/tests/networkxml2argvdata/routed-network-dhcphost.argv @@ -1,15 +1,15 @@ -@DNSMASQ@ \ ---strict-order \ ---bind-interfaces \ ---domain-needed \ ---except-interface=lo \ ---local=// \ ---conf-file= \ ---listen-address 192.168.122.1 \ ---listen-address 2001:db8:ac10:fd01::1 \ ---dhcp-range 192.168.122.1,static \ ---dhcp-no-override \ ---dhcp-range 2001:db8:ac10:fd01::1,static \ ---dhcp-hostsfile=/var/lib/libvirt/dnsmasq/local.hostsfile \ ---addn-hosts=/var/lib/libvirt/dnsmasq/local.addnhosts \ ---enable-ra\ +# dnsmasq conf file created by libvirt +strict-order +bind-interfaces +domain-needed +except-interface=lo +local=// +interface=virbr1 +listen-address=192.168.122.1 +listen-address=2001:db8:ac10:fd01::1 +dhcp-range=192.168.122.1,static +dhcp-no-override +dhcp-range=2001:db8:ac10:fd01::1,static +dhcp-hostsfile=/var/lib/libvirt/dnsmasq/local.hostsfile +addn-hosts=/var/lib/libvirt/dnsmasq/local.addnhosts +enable-ra diff --git a/tests/networkxml2argvdata/routed-network.argv b/tests/networkxml2argvdata/routed-network.argv index 78ad8a2..20240d3 100644 --- a/tests/networkxml2argvdata/routed-network.argv +++ b/tests/networkxml2argvdata/routed-network.argv @@ -1,9 +1,9 @@ -@DNSMASQ@ \ ---strict-order \ ---bind-interfaces \ ---domain-needed \ ---except-interface=lo \ ---local=// \ ---conf-file= \ ---listen-address 192.168.122.1 \ ---addn-hosts=/var/lib/libvirt/dnsmasq/local.addnhosts\ +# dnsmasq conf file created by libvirt +strict-order +bind-interfaces +domain-needed +except-interface=lo +local=// +interface=virbr1 +listen-address=192.168.122.1 +addn-hosts=/var/lib/libvirt/dnsmasq/local.addnhosts diff --git a/tests/networkxml2argvtest.c b/tests/networkxml2argvtest.c index 206e2d7..521f750 100644 --- a/tests/networkxml2argvtest.c +++ b/tests/networkxml2argvtest.c @@ -15,37 +15,6 @@ #include "memory.h" #include "network/bridge_driver.h" -/* Replace all occurrences of @token in @buf by @replacement and adjust size of - * @buf accordingly. Returns 0 on success and -1 on out-of-memory errors. */ -static int replaceTokens(char **buf, const char *token, const char *replacement) { - size_t token_start, token_end; - size_t buf_len, rest_len; - const size_t token_len = strlen(token); - const size_t replacement_len = strlen(replacement); - const int diff = replacement_len - token_len; - - buf_len = rest_len = strlen(*buf) + 1; - token_end = 0; - for (;;) { - char *match = strstr(*buf + token_end, token); - if (match == NULL) - break; - token_start = match - *buf; - rest_len -= token_start + token_len - token_end; - token_end = token_start + token_len; - buf_len += diff; - if (diff > 0) - if (VIR_REALLOC_N(*buf, buf_len) < 0) - return -1; - if (diff != 0) - memmove(*buf + token_end + diff, *buf + token_end, rest_len); - memcpy(*buf + token_start, replacement, replacement_len); - token_end += diff; - } - /* if diff < 0, we could shrink the buffer here... */ - return 0; -} - static int testCompareXMLToArgvFiles(const char *inxml, const char *outargv) { char *inXmlData = NULL; char *outArgvData = NULL; @@ -62,10 +31,6 @@ static int testCompareXMLToArgvFiles(const char *inxml, const char *outargv) { if (virtTestLoadFile(outargv, &outArgvData) < 0) goto fail; - - if (replaceTokens(&outArgvData, "@DNSMASQ@", DNSMASQ)) - goto fail; - if (!(dev = virNetworkDefParseString(inXmlData))) goto fail; @@ -79,12 +44,9 @@ static int testCompareXMLToArgvFiles(const char *inxml, const char *outargv) { if (dctx == NULL) goto fail; - if (networkBuildDhcpDaemonCommandLine(obj, &cmd, pidfile, dctx) < 0) - goto fail; - - if (!(actual = virCommandToString(cmd))) + if (networkBuildDhcpDaemonCommandLine(obj, &cmd, pidfile, + dctx, 1, &actual) < 0) goto fail; - if (STRNEQ(outArgvData, actual)) { virtTestDifference(stderr, outArgvData, actual); goto fail; -- 1.7.11.7
participants (1)
-
Gene Czarcinski