From 0b73a763f3c53595f53d94c61d8b2b66595a14e8 Mon Sep 17 00:00:00 2001 From: Gene Czarcinski Date: Fri, 15 Feb 2013 14:02:26 -0500 Subject: [PATCH] use client id for IPv6 DHCP host definition Originally, only a host name was used to associate a DHCPv6 request with a specific IPv6 address. Further testing demonstrates that this is an unreliable method and, instead, a client-id or DUID needs to be used. According to DHCPv6 standards, this id can be a duid-LLT, duid-LL, or duid-UUID even though dnsmasq will accept almost any text string. Although validity checking of a specified string makes sure it is hexadecimal notation with bytes separated by colons, there is no rigorous check to make sure it meets the standard. Documentation and schemas have been updated. Signed-off-by: Gene Czarcinski Signed-off-by: Laine Stump --- docs/formatnetwork.html.in | 18 +++++- docs/schemas/basictypes.rng | 59 +++++++++++++++++++ docs/schemas/network.rng | 5 +- src/conf/network_conf.c | 24 ++++++-- src/conf/network_conf.h | 1 + src/network/bridge_driver.c | 3 +- src/util/virdnsmasq.c | 23 ++++++-- src/util/virdnsmasq.h | 1 + .../networkxml2confdata/dhcp6-nat-network.xml | 5 +- tests/networkxml2confdata/dhcp6-network.xml | 5 +- .../dhcp6host-routed-network.xml | 5 +- .../dhcp6host-routed-network.xml | 22 +++++++ .../dhcp6host-routed-network.xml | 24 ++++++++ tests/networkxml2xmltest.c | 1 + 14 files changed, 179 insertions(+), 17 deletions(-) create mode 100644 tests/networkxml2xmlin/dhcp6host-routed-network.xml create mode 100644 tests/networkxml2xmlout/dhcp6host-routed-network.xml diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in index f7c483d39f..41a83fa9d1 100644 --- a/docs/formatnetwork.html.in +++ b/docs/formatnetwork.html.in @@ -782,6 +782,11 @@

Below is another IPv6 varition. Instead of a dhcp range being specified, this example has a couple of IPv6 host definitions. + Note that most of the dhcp host definitions use an "id" (client + id or DUID) since this has proven to be a more reliable way + of specifying the interface and its association with an IPv6 + address. The first is a DUID-LLT, the second a DUID-LL, and + the third a DUID-UUID. Since 1.0.3

@@ -797,7 +802,9 @@
         <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" />
+            <host id="0:1:0:1:18:aa:62:fe:0:16:3e:44:55:66"    ip="2001:db8:ca2:2:3::2" />
+            <host id="0:3:0:1:0:16:3e:11:22:33" name="ralph"  ip="2001:db8:ca2:2:3::3" />
+            <host id="0:4:7e:7d:f0:7d:a8:bc:c5:d2:13:32:11:ed:16:ea:84:63" name="badbob" ip="2001:db8:ca2:2:3::4" />
           </dhcp>
         </ip>
       </network>
@@ -828,6 +835,11 @@

This variation of an isolated network defines only IPv6. + Note that most of the dhcp host definitions use an "id" (client + id or DUID) since this has proven to be a more reliable way + of specifying the interface and its association with an IPv6 + address. The first is a DUID-LLT, the second a DUID-LL, and + the third a DUID-UUID. Since 1.0.3

@@ -837,7 +849,9 @@
         <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" />
+            <host id="0:1:0:1:18:aa:62:fe:0:16:3e:44:55:66" ip="2001:db8:ca2:6:6::2" />
+            <host id="0:3:0:1:0:16:3e:11:22:33" name="dariusz" ip="2001:db8:ca2:6:6::3" />
+            <host id="0:4:7e:7d:f0:7d:a8:bc:c5:d2:13:32:11:ed:16:ea:84:63" name="anita" ip="2001:db8:ca2:6:6::4" />
           </dhcp>
         </ip>
       </network>
diff --git a/docs/schemas/basictypes.rng b/docs/schemas/basictypes.rng index cfc820889b..ec1d940218 100644 --- a/docs/schemas/basictypes.rng +++ b/docs/schemas/basictypes.rng @@ -101,6 +101,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + [0]{1,2}:[0]{0,1}[1]:[0]{1,2}:[0]{0,1}[a-fA-F1-9](:[a-fA-F0-9]{1,2}){4}(:[a-fA-F0-9]{1,2}){6,8} + + + + + + + [0]{1,2}:[0]{0,1}[2](:[a-fA-F0-9]{1,2}){4}(:[a-fA-F0-9]{1,2}){1,124} + + + + + + + [0]{1,2}:[0]{0,1}[3]:[0]{1,2}:[0]{0,1}[a-fA-F1-9](:[a-fA-F0-9]{1,2}){6,8} + + + + + + + [0]{1,2}:[0]{0,1}[4](:[a-fA-F0-9]{1,2}){16} + + + + + + + + + + + + + diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng index fff169b982..da7d8adcde 100644 --- a/docs/schemas/network.rng +++ b/docs/schemas/network.rng @@ -280,7 +280,10 @@ - + + + + diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 34fd05abe7..3fc01cf584 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -118,6 +118,7 @@ static void virNetworkDHCPHostDefClear(virNetworkDHCPHostDefPtr def) { VIR_FREE(def->mac); + VIR_FREE(def->id); VIR_FREE(def->name); } @@ -678,7 +679,7 @@ virNetworkDHCPHostDefParseXML(const char *networkName, virNetworkDHCPHostDefPtr host, bool partialOkay) { - char *mac = NULL, *name = NULL, *ip = NULL; + char *mac = NULL, *name = NULL, *ip = NULL, *id = NULL; virMacAddr addr; virSocketAddr inaddr; int ret = -1; @@ -707,10 +708,20 @@ virNetworkDHCPHostDefParseXML(const char *networkName, } } + id = virXMLPropString(node, "id"); + if (id) { + char *cp = id + strspn(id, "0123456789abcdefABCDEF:"); + if (*cp) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid character '%c' in id '%s' of network '%s'"), + *cp, id, networkName); + } + } + name = virXMLPropString(node, "name"); if (name && (!c_isalpha(name[0]))) { virReportError(VIR_ERR_XML_ERROR, - _("Cannot use name address '%s' in network '%s'"), + _("Cannot use host name '%s' in network '%s'"), name, networkName); goto cleanup; } @@ -738,10 +749,10 @@ virNetworkDHCPHostDefParseXML(const char *networkName, * address or name (IPv4) */ if (VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET6)) { - if (!name) { + if (!(id || name)) { virReportError(VIR_ERR_XML_ERROR, _("Static host definition in IPv6 network '%s' " - "must have name attribute"), + "must have id or name attribute"), networkName); goto cleanup; } @@ -763,6 +774,8 @@ virNetworkDHCPHostDefParseXML(const char *networkName, host->mac = mac; mac = NULL; + host->id = id; + id = NULL; host->name = name; name = NULL; if (ip) @@ -771,6 +784,7 @@ virNetworkDHCPHostDefParseXML(const char *networkName, cleanup: VIR_FREE(mac); + VIR_FREE(id); VIR_FREE(name); VIR_FREE(ip); return ret; @@ -2189,6 +2203,8 @@ virNetworkIpDefFormat(virBufferPtr buf, virBufferAddLit(buf, "hosts[ii].mac) virBufferAsprintf(buf, "mac='%s' ", def->hosts[ii].mac); + if (def->hosts[ii].id) + virBufferAsprintf(buf, "id='%s' ", def->hosts[ii].id); if (def->hosts[ii].name) virBufferAsprintf(buf, "name='%s' ", def->hosts[ii].name); if (VIR_SOCKET_ADDR_VALID(&def->hosts[ii].ip)) { diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index e7a4f95f2b..c509915cc1 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -66,6 +66,7 @@ typedef struct _virNetworkDHCPHostDef virNetworkDHCPHostDef; typedef virNetworkDHCPHostDef *virNetworkDHCPHostDefPtr; struct _virNetworkDHCPHostDef { char *mac; + char *id; char *name; virSocketAddr ip; }; diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 0c3f778da8..0932cf8e5e 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -599,7 +599,8 @@ networkBuildDnsmasqDhcpHostsList(dnsmasqContext *dctx, for (i = 0; i < ipdef->nhosts; i++) { virNetworkDHCPHostDefPtr host = &(ipdef->hosts[i]); if (VIR_SOCKET_ADDR_VALID(&host->ip)) - if (dnsmasqAddDhcpHost(dctx, host->mac, &host->ip, host->name, ipv6) < 0) + if (dnsmasqAddDhcpHost(dctx, host->mac, &host->ip, + host->name, host->id, ipv6) < 0) return -1; } diff --git a/src/util/virdnsmasq.c b/src/util/virdnsmasq.c index 6637a8977d..2e63d83af2 100644 --- a/src/util/virdnsmasq.c +++ b/src/util/virdnsmasq.c @@ -303,6 +303,7 @@ hostsfileAdd(dnsmasqHostsfile *hostsfile, const char *mac, virSocketAddr *ip, const char *name, + const char *id, bool ipv6) { char *ipstr = NULL; @@ -314,11 +315,20 @@ hostsfileAdd(dnsmasqHostsfile *hostsfile, /* the first test determines if it is a dhcpv6 host */ if (ipv6) { - if (virAsprintf(&hostsfile->hosts[hostsfile->nhosts].host, "%s,[%s]", - name, ipstr) < 0) - goto alloc_error; - } - else if (name && mac) { + if (name && id) { + if (virAsprintf(&hostsfile->hosts[hostsfile->nhosts].host, + "id:%s,%s,[%s]", id, name, ipstr) < 0) + goto alloc_error; + } else if (name && !id) { + if (virAsprintf(&hostsfile->hosts[hostsfile->nhosts].host, + "%s,[%s]", name, ipstr) < 0) + goto alloc_error; + } else if (!name && id) { + if (virAsprintf(&hostsfile->hosts[hostsfile->nhosts].host, + "id:%s,[%s]", id, ipstr) < 0) + goto alloc_error; + } + } else if (name && mac) { if (virAsprintf(&hostsfile->hosts[hostsfile->nhosts].host, "%s,%s,%s", mac, ipstr, name) < 0) goto alloc_error; @@ -511,9 +521,10 @@ dnsmasqAddDhcpHost(dnsmasqContext *ctx, const char *mac, virSocketAddr *ip, const char *name, + const char *id, bool ipv6) { - return hostsfileAdd(ctx->hostsfile, mac, ip, name, ipv6); + return hostsfileAdd(ctx->hostsfile, mac, ip, name, id, ipv6); } /* diff --git a/src/util/virdnsmasq.h b/src/util/virdnsmasq.h index a97d3e6019..ed560da45d 100644 --- a/src/util/virdnsmasq.h +++ b/src/util/virdnsmasq.h @@ -86,6 +86,7 @@ int dnsmasqAddDhcpHost(dnsmasqContext *ctx, const char *mac, virSocketAddr *ip, const char *name, + const char *id, bool ipv6); int dnsmasqAddHost(dnsmasqContext *ctx, virSocketAddr *ip, diff --git a/tests/networkxml2confdata/dhcp6-nat-network.xml b/tests/networkxml2confdata/dhcp6-nat-network.xml index 72103f7139..4259173854 100644 --- a/tests/networkxml2confdata/dhcp6-nat-network.xml +++ b/tests/networkxml2confdata/dhcp6-nat-network.xml @@ -15,8 +15,11 @@ - + + + + diff --git a/tests/networkxml2confdata/dhcp6-network.xml b/tests/networkxml2confdata/dhcp6-network.xml index 311013ad52..776737e26c 100644 --- a/tests/networkxml2confdata/dhcp6-network.xml +++ b/tests/networkxml2confdata/dhcp6-network.xml @@ -7,8 +7,11 @@ - + + + + diff --git a/tests/networkxml2confdata/dhcp6host-routed-network.xml b/tests/networkxml2confdata/dhcp6host-routed-network.xml index 38d9ebf892..2693d872fc 100644 --- a/tests/networkxml2confdata/dhcp6host-routed-network.xml +++ b/tests/networkxml2confdata/dhcp6host-routed-network.xml @@ -12,8 +12,11 @@ - + + + + diff --git a/tests/networkxml2xmlin/dhcp6host-routed-network.xml b/tests/networkxml2xmlin/dhcp6host-routed-network.xml new file mode 100644 index 0000000000..2693d872fc --- /dev/null +++ b/tests/networkxml2xmlin/dhcp6host-routed-network.xml @@ -0,0 +1,22 @@ + + local + 81ff0d90-c91e-6742-64da-4a736edb9a9b + + + + + + + + + + + + + + + + + + + diff --git a/tests/networkxml2xmlout/dhcp6host-routed-network.xml b/tests/networkxml2xmlout/dhcp6host-routed-network.xml new file mode 100644 index 0000000000..7305043f37 --- /dev/null +++ b/tests/networkxml2xmlout/dhcp6host-routed-network.xml @@ -0,0 +1,24 @@ + + local + 81ff0d90-c91e-6742-64da-4a736edb9a9b + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/networkxml2xmltest.c b/tests/networkxml2xmltest.c index 1cd74d1018..1063435651 100644 --- a/tests/networkxml2xmltest.c +++ b/tests/networkxml2xmltest.c @@ -92,6 +92,7 @@ mymain(void) } while (0) #define DO_TEST(name) DO_TEST_FULL(name, 0) + DO_TEST("dhcp6host-routed-network"); DO_TEST("empty-allow-ipv6"); DO_TEST("isolated-network"); DO_TEST("routed-network");