If an user is trying to configure a dhcp neetwork settings, it is not
possible to change the leasetime of a range or a host entry. This is
available using dnsmasq extra options, but they are associated with
dhcp-range or dhcp-hosts fields. This patch implements a default
leasetime for both. If this XML entry is defined, it applies leasetime
for each range or host defined under DHCP scope.
Resolves:
https://bugzilla.redhat.com/show_bug.cgi?id=913446
Signed-off-by: Julio Faracco <jcfaracco(a)gmail.com>
---
docs/schemas/basictypes.rng | 9 ++++++
docs/schemas/network.rng | 11 +++++++
src/conf/network_conf.c | 62 ++++++++++++++++++++++++++++++++++++-
src/conf/network_conf.h | 14 +++++++++
src/libvirt_private.syms | 2 ++
src/network/bridge_driver.c | 37 ++++++++++++++++++++--
src/util/virdnsmasq.c | 40 ++++++++++++------------
src/util/virdnsmasq.h | 1 +
8 files changed, 152 insertions(+), 24 deletions(-)
diff --git a/docs/schemas/basictypes.rng b/docs/schemas/basictypes.rng
index 81465273c8..12f085c101 100644
--- a/docs/schemas/basictypes.rng
+++ b/docs/schemas/basictypes.rng
@@ -607,4 +607,13 @@
</element>
</define>
+ <define name="leaseUnit">
+ <choice>
+ <value>seconds</value>
+ <value>minutes</value>
+ <value>hours</value>
+ <value>infinite</value>
+ </choice>
+ </define>
+
</grammar>
diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng
index 60453225d6..9a93529d52 100644
--- a/docs/schemas/network.rng
+++ b/docs/schemas/network.rng
@@ -398,6 +398,17 @@
</optional>
</element>
</optional>
+ <optional>
+ <element name="leasetime">
+ <optional>
+ <attribute name="unit"><ref
name="leaseUnit"/></attribute>
+ </optional>
+ <choice>
+ <data type="unsignedLong"/>
+ <empty/>
+ </choice>
+ </element>
+ </optional>
</interleave>
</element>
</optional>
diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index 819b645df7..e6e82ee9fc 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -70,6 +70,14 @@ VIR_ENUM_IMPL(virNetworkTaint,
"hook-script",
);
+VIR_ENUM_IMPL(virNetworkDHCPLeaseTimeUnit,
+ VIR_NETWORK_DHCP_LEASETIME_UNIT_LAST,
+ "seconds",
+ "minutes",
+ "hours",
+ "infinite",
+);
+
static virClassPtr virNetworkXMLOptionClass;
static void
@@ -552,9 +560,50 @@ virNetworkDHCPHostDefParseXML(const char *networkName,
}
+static int
+virNetworkDHCPLeaseTimeDefParseXML(virNetworkIPDefPtr def,
+ xmlNodePtr node,
+ xmlXPathContextPtr ctxt)
+{
+ g_autofree char *leasetime = NULL, *leaseunit = NULL;
+
+ if (!(leaseunit = virXMLPropString(node, "unit")))
+ def->leaseunit = VIR_NETWORK_DHCP_LEASETIME_UNIT_SECONDS;
+ else
+ def->leaseunit = virNetworkDHCPLeaseTimeUnitTypeFromString(leaseunit);
+
+ if (def->leaseunit == VIR_NETWORK_DHCP_LEASETIME_UNIT_INFINITE)
+ return 0;
+
+ if (!(leasetime = virXPathString("string(./dhcp/leasetime)", ctxt)))
+ return -1;
+
+ if (virStrToLong_ul(leasetime, NULL, 10, &def->leasetime) < 0)
+ return -1;
+
+ /* This boundary check is related to dnsmasq man page settings:
+ * "The minimum lease time is two minutes." */
+ if ((def->leaseunit == VIR_NETWORK_DHCP_LEASETIME_UNIT_SECONDS &&
+ def->leasetime < 120) ||
+ (def->leaseunit == VIR_NETWORK_DHCP_LEASETIME_UNIT_MINUTES &&
+ def->leasetime < 2)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("The minimum lease time should be greater "
+ "than 2 minutes"));
+ return -1;
+ }
+
+ if (def->leasetime > 0)
+ return 0;
+
+ return -1;
+}
+
+
static int
virNetworkDHCPDefParseXML(const char *networkName,
xmlNodePtr node,
+ xmlXPathContextPtr ctxt,
virNetworkIPDefPtr def)
{
int ret = -1;
@@ -583,7 +632,11 @@ virNetworkDHCPDefParseXML(const char *networkName,
goto cleanup;
if (VIR_APPEND_ELEMENT(def->hosts, def->nhosts, host) < 0)
goto cleanup;
+ } else if (cur->type == XML_ELEMENT_NODE &&
+ virXMLNodeNameEqual(cur, "leasetime")) {
+ if (virNetworkDHCPLeaseTimeDefParseXML(def, cur, ctxt) < 0)
+ goto cleanup;
} else if (VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET) &&
cur->type == XML_ELEMENT_NODE &&
virXMLNodeNameEqual(cur, "bootp")) {
@@ -1143,7 +1196,7 @@ virNetworkIPDefParseXML(const char *networkName,
}
if ((dhcp = virXPathNode("./dhcp[1]", ctxt)) &&
- virNetworkDHCPDefParseXML(networkName, dhcp, def) < 0)
+ virNetworkDHCPDefParseXML(networkName, dhcp, ctxt, def) < 0)
goto cleanup;
if (virXPathNode("./tftp[1]", ctxt)) {
@@ -2343,6 +2396,13 @@ virNetworkIPDefFormat(virBufferPtr buf,
virBufferAddLit(buf, "/>\n");
}
+ if (def->leaseunit == VIR_NETWORK_DHCP_LEASETIME_UNIT_INFINITE) {
+ virBufferAddLit(buf, "<leasetime
unit='infinite'/>\n");
+ } else if (def->leasetime) {
+ virBufferAsprintf(buf, "<leasetime
unit='%s'>%lu</leasetime>\n",
+
virNetworkDHCPLeaseTimeUnitTypeToString(def->leaseunit),
+ def->leasetime);
+ }
virBufferAdjustIndent(buf, -2);
virBufferAddLit(buf, "</dhcp>\n");
diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h
index db7243eef5..66a5e1ad4f 100644
--- a/src/conf/network_conf.h
+++ b/src/conf/network_conf.h
@@ -80,6 +80,17 @@ typedef enum {
VIR_NETWORK_FORWARD_HOSTDEV_DEVICE_LAST,
} virNetworkForwardHostdevDeviceType;
+typedef enum {
+ VIR_NETWORK_DHCP_LEASETIME_UNIT_SECONDS = 0,
+ VIR_NETWORK_DHCP_LEASETIME_UNIT_MINUTES,
+ VIR_NETWORK_DHCP_LEASETIME_UNIT_HOURS,
+ VIR_NETWORK_DHCP_LEASETIME_UNIT_INFINITE,
+
+ VIR_NETWORK_DHCP_LEASETIME_UNIT_LAST,
+} virNetworkDHCPLeaseTimeUnitType;
+
+VIR_ENUM_DECL(virNetworkDHCPLeaseTimeUnit);
+
/* The backend driver used for devices from the pool. Currently used
* only for PCI devices (vfio vs. kvm), but could be used for other
* device types in the future.
@@ -176,6 +187,9 @@ struct _virNetworkIPDef {
size_t nhosts; /* Zero or more dhcp hosts */
virNetworkDHCPHostDefPtr hosts;
+ unsigned long leasetime;
+ virNetworkDHCPLeaseTimeUnitType leaseunit;
+
char *tftproot;
char *bootfile;
virSocketAddr bootserver;
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index ec367653d5..79bb5cc160 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -770,6 +770,8 @@ virNetworkDefParseNode;
virNetworkDefParseString;
virNetworkDefParseXML;
virNetworkDefUpdateSection;
+virNetworkDHCPLeaseTimeUnitTypeFromString;
+virNetworkDHCPLeaseTimeUnitTypeToString;
virNetworkForwardTypeToString;
virNetworkIPDefNetmask;
virNetworkIPDefPrefix;
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index f06099297a..d587e266de 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -966,6 +966,27 @@ static int networkConnectIsAlive(virConnectPtr conn G_GNUC_UNUSED)
}
+static char *
+networkBuildDnsmasqLeaseTime(virNetworkIPDefPtr ipdef)
+{
+ char *leasetime = NULL;
+ const char *unit;
+ g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+
+ if (ipdef->leaseunit == VIR_NETWORK_DHCP_LEASETIME_UNIT_INFINITE) {
+ virBufferAddLit(&buf, "infinite");
+ } else if (ipdef->leasetime) {
+ unit = virNetworkDHCPLeaseTimeUnitTypeToString(ipdef->leaseunit);
+ /* We get only first compatible char from string: 's', 'm' or
'h' */
+ virBufferAsprintf(&buf, "%lu%c", ipdef->leasetime, unit[0]);
+ }
+
+ leasetime = virBufferContentAndReset(&buf);
+
+ return leasetime;
+}
+
+
/* the following does not build a file, it builds a list
* which is later saved into a file
*/
@@ -975,6 +996,9 @@ networkBuildDnsmasqDhcpHostsList(dnsmasqContext *dctx,
{
size_t i;
bool ipv6 = false;
+ g_autofree char *leasetime = NULL;
+
+ leasetime = networkBuildDnsmasqLeaseTime(ipdef);
if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6))
ipv6 = true;
@@ -982,7 +1006,8 @@ networkBuildDnsmasqDhcpHostsList(dnsmasqContext *dctx,
virNetworkDHCPHostDefPtr host = &(ipdef->hosts[i]);
if (VIR_SOCKET_ADDR_VALID(&host->ip))
if (dnsmasqAddDhcpHost(dctx, host->mac, &host->ip,
- host->name, host->id, ipv6) < 0)
+ host->name, host->id, leasetime,
+ ipv6) < 0)
return -1;
}
@@ -1381,13 +1406,14 @@ networkDnsmasqConfContents(virNetworkObjPtr obj,
}
for (r = 0; r < ipdef->nranges; r++) {
int thisRange;
+ g_autofree char *leasetime = NULL;
if (!(saddr = virSocketAddrFormat(&ipdef->ranges[r].start)) ||
!(eaddr = virSocketAddrFormat(&ipdef->ranges[r].end)))
goto cleanup;
if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6)) {
- virBufferAsprintf(&configbuf, "dhcp-range=%s,%s,%d\n",
+ virBufferAsprintf(&configbuf, "dhcp-range=%s,%s,%d",
saddr, eaddr, prefix);
} else {
/* IPv4 - dnsmasq requires a netmask rather than prefix */
@@ -1404,10 +1430,15 @@ networkDnsmasqConfContents(virNetworkObjPtr obj,
if (!(netmaskStr = virSocketAddrFormat(&netmask)))
goto cleanup;
- virBufferAsprintf(&configbuf, "dhcp-range=%s,%s,%s\n",
+ virBufferAsprintf(&configbuf, "dhcp-range=%s,%s,%s",
saddr, eaddr, netmaskStr);
}
+ if ((leasetime = networkBuildDnsmasqLeaseTime(ipdef)))
+ virBufferAsprintf(&configbuf, ",%s", leasetime);
+
+ virBufferAddLit(&configbuf, "\n");
+
VIR_FREE(saddr);
VIR_FREE(eaddr);
thisRange = virSocketAddrGetRange(&ipdef->ranges[r].start,
diff --git a/src/util/virdnsmasq.c b/src/util/virdnsmasq.c
index adc6f96bb6..fc977a47f5 100644
--- a/src/util/virdnsmasq.c
+++ b/src/util/virdnsmasq.c
@@ -296,11 +296,14 @@ hostsfileAdd(dnsmasqHostsfile *hostsfile,
virSocketAddr *ip,
const char *name,
const char *id,
+ const char *leasetime,
bool ipv6)
{
- char *ipstr = NULL;
+ g_autofree char *ipstr = NULL;
+ g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+
if (VIR_REALLOC_N(hostsfile->hosts, hostsfile->nhosts + 1) < 0)
- goto error;
+ return -1;
if (!(ipstr = virSocketAddrFormat(ip)))
return -1;
@@ -308,34 +311,30 @@ hostsfileAdd(dnsmasqHostsfile *hostsfile,
/* the first test determines if it is a dhcpv6 host */
if (ipv6) {
if (name && id) {
- hostsfile->hosts[hostsfile->nhosts].host =
g_strdup_printf("id:%s,%s,[%s]",
- id, name, ipstr);
+ virBufferAsprintf(&buf, "id:%s,%s", id, name);
} else if (name && !id) {
- hostsfile->hosts[hostsfile->nhosts].host =
g_strdup_printf("%s,[%s]",
- name, ipstr);
+ virBufferAsprintf(&buf, "%s", name);
} else if (!name && id) {
- hostsfile->hosts[hostsfile->nhosts].host =
g_strdup_printf("id:%s,[%s]",
- id, ipstr);
+ virBufferAsprintf(&buf, "id:%s", id);
}
+ virBufferAsprintf(&buf, ",[%s]", ipstr);
} else if (name && mac) {
- hostsfile->hosts[hostsfile->nhosts].host =
g_strdup_printf("%s,%s,%s",
- mac, ipstr, name);
+ virBufferAsprintf(&buf, "%s,%s,%s", mac, ipstr, name);
} else if (name && !mac) {
- hostsfile->hosts[hostsfile->nhosts].host =
g_strdup_printf("%s,%s", name,
- ipstr);
+ virBufferAsprintf(&buf, "%s,%s", name, ipstr);
} else {
- hostsfile->hosts[hostsfile->nhosts].host =
g_strdup_printf("%s,%s", mac,
- ipstr);
+ virBufferAsprintf(&buf, "%s,%s", mac, ipstr);
}
- VIR_FREE(ipstr);
+
+ if (leasetime)
+ virBufferAsprintf(&buf, ",%s", leasetime);
+
+ if (!(hostsfile->hosts[hostsfile->nhosts].host =
virBufferContentAndReset(&buf)))
+ return -1;
hostsfile->nhosts++;
return 0;
-
- error:
- VIR_FREE(ipstr);
- return -1;
}
static dnsmasqHostsfile *
@@ -501,9 +500,10 @@ dnsmasqAddDhcpHost(dnsmasqContext *ctx,
virSocketAddr *ip,
const char *name,
const char *id,
+ const char *leasetime,
bool ipv6)
{
- return hostsfileAdd(ctx->hostsfile, mac, ip, name, id, ipv6);
+ return hostsfileAdd(ctx->hostsfile, mac, ip, name, id, leasetime, ipv6);
}
/*
diff --git a/src/util/virdnsmasq.h b/src/util/virdnsmasq.h
index ff0e56d635..6fbc101e64 100644
--- a/src/util/virdnsmasq.h
+++ b/src/util/virdnsmasq.h
@@ -87,6 +87,7 @@ int dnsmasqAddDhcpHost(dnsmasqContext *ctx,
virSocketAddr *ip,
const char *name,
const char *id,
+ const char *leasetime,
bool ipv6);
int dnsmasqAddHost(dnsmasqContext *ctx,
virSocketAddr *ip,
--
2.24.1