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@xxxxxxxxx> --- 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