From nobody Mon Feb 9 09:57:49 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) client-ip=8.43.85.245; envelope-from=devel-bounces@lists.libvirt.org; helo=lists.libvirt.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [8.43.85.245]) by mx.zohomail.com with SMTPS id 172305140389560.109082197500584; Wed, 7 Aug 2024 10:23:23 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 996) id BA38F108B; Wed, 7 Aug 2024 13:23:22 -0400 (EDT) Received: from lists.libvirt.org (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id CD773135B; Wed, 7 Aug 2024 13:16:26 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 276A311A8; Wed, 7 Aug 2024 13:16:13 -0400 (EDT) Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by lists.libvirt.org (Postfix) with ESMTPS id C2EA7C90 for ; Wed, 7 Aug 2024 13:16:11 -0400 (EDT) Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-377-mMd8lHZjPr2KEyOBfUftfw-1; Wed, 07 Aug 2024 13:16:07 -0400 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id C876019560A6 for ; Wed, 7 Aug 2024 17:16:05 +0000 (UTC) Received: from vhost3.router.laine.org (unknown [10.22.32.31]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 55BE519560A3 for ; Wed, 7 Aug 2024 17:16:05 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-0.8 required=5.0 tests=DKIM_INVALID,DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=3.4.4 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1723050971; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Dk3ig8V/Oj+42y38nWI+QEVkZBiEOh9aZ8n7SLgwCns=; b=O9/PL0NTkYLE6aIdIUtirLTJ/FNhzos1/yjFm1WZaCJIIw/4er0+r5IgdBVucXJxzbOupp J7LeQKMLslUgqo0mzljSv4HVwd1Wbs2FiIqwOAGFkqAvF20qkmRPT/XpNrfdqlKNIiEjgB nL0sySoOqUgQiwz/g4oXy3AtY0ODHRE= X-MC-Unique: mMd8lHZjPr2KEyOBfUftfw-1 From: Laine Stump To: devel@lists.libvirt.org Subject: [PATCH 1/7] conf: add XML config for autoaddr networks Date: Wed, 7 Aug 2024 13:15:57 -0400 Message-ID: <20240807171603.218784-2-laine@redhat.com> In-Reply-To: <20240807171603.218784-1-laine@redhat.com> References: <20240807171603.218784-1-laine@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Message-ID-Hash: 3ZDCQNTJ6DF6OC26FO2EGMLACON3Y77C X-Message-ID-Hash: 3ZDCQNTJ6DF6OC26FO2EGMLACON3Y77C X-MailFrom: laine@redhat.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-config-1; header-match-config-2; header-match-config-3; header-match-devel.lists.libvirt.org-0; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; suspicious-header X-Mailman-Version: 3.2.2 Precedence: list List-Id: Development discussions about the libvirt library & tools Archived-At: List-Archive: List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1723051404220116600 Content-Type: text/plain; charset="utf-8"; x-default="true" If the element of a network has the attribute autoaddr=3D'yes', any specified IP address, netmask, prefix, or dhcp range will be used as a hint, but if the current network is already present in the host's routing table (i.e. it is in use) then libvirt will instead automatically find an unused subnet "somewhere", and use that instead. Since this patch is just the XML config bits, it has none of the details about how an unused subnet is found. That is coming later. autoaddr=3D'yes' is currently only supported for IPv4. IPv6 autoaddr should work differently (rather than selecting from a manually configured range of networks, I *think* it should semi-randomly select a network ala RFC 4193, and anyway conflicting IPv6 networks hasn't been an issue up to now). Signed-off-by: Laine Stump --- docs/formatnetwork.rst | 42 ++++++++++- src/conf/network_conf.c | 75 ++++++++++++++----- src/conf/network_conf.h | 7 ++ src/conf/schemas/network.rng | 5 ++ .../networkxml2xmlin/nat-network-autoaddr.xml | 11 +++ .../nat-network-autoaddr.xml | 11 +++ tests/networkxml2xmltest.c | 1 + 7 files changed, 132 insertions(+), 20 deletions(-) create mode 100644 tests/networkxml2xmlin/nat-network-autoaddr.xml create mode 100644 tests/networkxml2xmlout/nat-network-autoaddr.xml diff --git a/docs/formatnetwork.rst b/docs/formatnetwork.rst index 9b4ecbf31d..9c5e974002 100644 --- a/docs/formatnetwork.rst +++ b/docs/formatnetwork.rst @@ -805,8 +805,27 @@ of 'route' or 'nat'. divisible by 4 for IPv6) libvirt may be unable to compute the PTR domain automatically. The ``ip`` element is supported :since:`since 0.3.0`. IP= v6, multiple addresses on a single network, ``family``, and ``prefix`` are - supported :since:`since 0.8.7`. The ``ip`` element may contain the foll= owing - elements: + supported :since:`since 0.8.7`. + + Instead of (or in addition to) an IPv4 address and prefix (or + netmask), the attribute ``autoaddr`` can be set to ``yes``, and in + this case libvirt will search through a range of IPv4 subnets + (configured with the autoaddr_* settings in + /etc/libvirt/network.conf) to find an unused subnet :since: `since + 10.7.0`. If an address has been specified in addition to + ``autoaddr=3D'yes'`` then that address will be tried first, before + looking to the range given in network.conf. In any case, once an + unused subnet has been found, any dhcp range, static IP addresses, + or tftp boot servers will have the network part of their address + changed to the new subnet (if no ``address`` was given in the + config, the address will be set to the ".1" address on the newly + chosen network, and if there is an empty ```` subelement, it + will be given a range equal to the entire subnet exceot the ".1" + address). Once this is done, the updated network config is saved by + libvirt so that libvirt can attempt to use the same subnet the next + time the network is started. + + The ``ip`` element may contain the following sub-elements: =20 ``tftp`` The optional ``tftp`` element and its mandatory ``root`` attribute e= nable @@ -859,6 +878,11 @@ of 'route' or 'nat'. except when setting an infinite lease time (``expiry=3D'0'``). :since:`Since 6.3.0` =20 + If the ``ip`` element has ``autoaddr=3D'yes'``, then the ``dhcp`` + element can be empty, and in this case libvirt automatically + adds a ``range`` equivalent to the entire subnet (minus the .1 + address for the bridge device itself). :since: `Since 10.7.0` + Network namespaces ~~~~~~~~~~~~~~~~~~ =20 @@ -928,6 +952,20 @@ definition. =20 +And here is a NAT network that has its subnet (and thus its ip address +and dhcp range) chosen automatically by libvirt from the range of +"autoaddr" subnets configured in network.conf: + +:: + + + automagic + + + + + + IPv6 NAT based network ~~~~~~~~~~~~~~~~~~~~~~ =20 diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 3af4e1d036..2a6b95e7ac 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -599,7 +599,8 @@ virNetworkDHCPHostDefParseXML(const char *networkName, static int virNetworkDHCPDefParseXML(const char *networkName, xmlNodePtr node, - virNetworkIPDef *def) + virNetworkIPDef *def, + bool isIPv4) { =20 g_autoptr(GPtrArray) rangeNodes =3D virXMLNodeGetSubelementList(node, = "range"); @@ -607,6 +608,7 @@ virNetworkDHCPDefParseXML(const char *networkName, xmlNodePtr bootp =3D virXMLNodeGetSubelement(node, "bootp"); size_t i; =20 + def->dhcp =3D true; for (i =3D 0; i < rangeNodes->len; i++) { virNetworkDHCPRangeDef range =3D { 0 }; =20 @@ -629,8 +631,7 @@ virNetworkDHCPDefParseXML(const char *networkName, VIR_APPEND_ELEMENT(def->hosts, def->nhosts, host); } =20 - if (bootp && - VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET)) { + if (bootp && isIPv4) { g_autofree char *server =3D virXMLPropString(bootp, "server"); =20 if (!(def->bootfile =3D virXMLPropStringRequired(bootp, "file"))) @@ -991,24 +992,28 @@ virNetworkIPDefParseXML(const char *networkName, g_autofree char *address =3D NULL; g_autofree char *netmask =3D NULL; int ret =3D -1; + bool isIPv4 =3D true; =20 ctxt->node =3D node; =20 /* grab raw data from XML */ def->family =3D virXPathString("string(./@family)", ctxt); + if (STREQ_NULLABLE(def->family, "ipv6")) + isIPv4 =3D false; =20 - address =3D virXPathString("string(./@address)", ctxt); - if (!address) { - virReportError(VIR_ERR_XML_ERROR, - _("Missing required address attribute in network '%= 1$s'"), - networkName); - goto cleanup; - } - if (virSocketAddrParse(&def->address, address, AF_UNSPEC) < 0) { - virReportError(VIR_ERR_XML_ERROR, - _("Invalid address '%1$s' in network '%2$s'"), - address, networkName); + if (virXMLPropTristateBool(node, "autoaddr", VIR_XML_PROP_NONE, &def->= autoaddr) < 0) goto cleanup; + + address =3D virXPathString("string(./@address)", ctxt); + + if (address) { + if (virSocketAddrParse(&def->address, address, AF_UNSPEC) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid address '%1$s' in network '%2$s'"), + address, networkName); + goto cleanup; + } + isIPv4 =3D VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET); } =20 netmask =3D virXPathString("string(./@netmask)", ctxt); @@ -1028,6 +1033,27 @@ virNetworkIPDefParseXML(const char *networkName, &def->localPTR) < 0) goto cleanup; =20 + switch (def->autoaddr) { + case VIR_TRISTATE_BOOL_NO: + case VIR_TRISTATE_BOOL_ABSENT: + case VIR_TRISTATE_BOOL_LAST: + if (!address) { + virReportError(VIR_ERR_XML_ERROR, + _("Missing required address attribute in networ= k '%1$s'"), + networkName); + goto cleanup; + } + break; + case VIR_TRISTATE_BOOL_YES: + if (!isIPv4) { + virReportError(VIR_ERR_XML_ERROR, + _("autoaddr=3D'yes' is only supported for IPv4 = in network '%1$s'"), + networkName); + goto cleanup; + } + break; + } + /* validate address, etc. for each family */ if ((def->family =3D=3D NULL) || (STREQ(def->family, "ipv4"))) { if (!(VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET) || @@ -1057,7 +1083,8 @@ virNetworkIPDefParseXML(const char *networkName, goto cleanup; } } else if (STREQ(def->family, "ipv6")) { - if (!VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET6)) { + if (!(VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET6) || + VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_UNSPEC))) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Family 'ipv6' specified for non-IPv6 address= '%1$s' in network '%2$s'"), address, networkName); @@ -1083,11 +1110,11 @@ virNetworkIPDefParseXML(const char *networkName, } =20 if ((dhcp =3D virXPathNode("./dhcp[1]", ctxt)) && - virNetworkDHCPDefParseXML(networkName, dhcp, def) < 0) + virNetworkDHCPDefParseXML(networkName, dhcp, def, isIPv4) < 0) goto cleanup; =20 if (virXPathNode("./tftp[1]", ctxt)) { - if (!VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET)) { + if (!isIPv4) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unsupported element in an IPv6 elemen= t in network '%1$s'"), networkName); @@ -2045,6 +2072,11 @@ virNetworkIPDefFormat(virBuffer *buf, =20 if (def->family) virBufferAsprintf(buf, " family=3D'%s'", def->family); + + if (def->autoaddr !=3D VIR_TRISTATE_BOOL_ABSENT) + virBufferAsprintf(buf, " autoaddr=3D'%s'", + virTristateBoolTypeToString(def->autoaddr)); + if (VIR_SOCKET_ADDR_VALID(&def->address)) { g_autofree char *addr =3D virSocketAddrFormat(&def->address); if (!addr) @@ -2072,7 +2104,14 @@ virNetworkIPDefFormat(virBuffer *buf, virBufferEscapeString(buf, "\n", def->tftproot); } - if ((def->nranges || def->nhosts)) { + + if (!(def->nranges || def->nhosts)) { + /* in case an empty (with no subelements) + * was specified + */ + if (def->dhcp) + virBufferAddLit(buf, "\n"); + } else { size_t i; virBufferAddLit(buf, "\n"); virBufferAdjustIndent(buf, 2); diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index c2a4198abc..d44984e71a 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -158,6 +158,12 @@ struct _virNetworkDNSDef { typedef struct _virNetworkIPDef virNetworkIPDef; struct _virNetworkIPDef { char *family; /* ipv4 or ipv6 - default is ipv4 */ + + /* automatically determine IP address when the network is started. + * default is 'no' + */ + virTristateBool autoaddr; + virSocketAddr address; /* Bridge IP address */ =20 /* One or the other of the following two will be used for a given @@ -171,6 +177,7 @@ struct _virNetworkIPDef { =20 virTristateBool localPTR; =20 + bool dhcp; /* true if there is a element */ size_t nranges; /* Zero or more dhcp ranges */ virNetworkDHCPRangeDef *ranges; =20 diff --git a/src/conf/schemas/network.rng b/src/conf/schemas/network.rng index b7c8551fad..64d4818e96 100644 --- a/src/conf/schemas/network.rng +++ b/src/conf/schemas/network.rng @@ -354,6 +354,11 @@ + + + + + diff --git a/tests/networkxml2xmlin/nat-network-autoaddr.xml b/tests/networ= kxml2xmlin/nat-network-autoaddr.xml new file mode 100644 index 0000000000..71167285e6 --- /dev/null +++ b/tests/networkxml2xmlin/nat-network-autoaddr.xml @@ -0,0 +1,11 @@ + + default + 81ff0d90-c91e-6742-64da-4a736edb9a9b + + + + + + + + diff --git a/tests/networkxml2xmlout/nat-network-autoaddr.xml b/tests/netwo= rkxml2xmlout/nat-network-autoaddr.xml new file mode 100644 index 0000000000..629710175c --- /dev/null +++ b/tests/networkxml2xmlout/nat-network-autoaddr.xml @@ -0,0 +1,11 @@ + + default + 81ff0d90-c91e-6742-64da-4a736edb9a9b + + + + + + + + diff --git a/tests/networkxml2xmltest.c b/tests/networkxml2xmltest.c index 0783d84915..de8abefe42 100644 --- a/tests/networkxml2xmltest.c +++ b/tests/networkxml2xmltest.c @@ -137,6 +137,7 @@ mymain(void) DO_TEST("nat-network-forward-nat-address"); DO_TEST("nat-network-forward-nat-no-address"); DO_TEST("nat-network-mtu"); + DO_TEST("nat-network-autoaddr"); DO_TEST("8021Qbh-net"); DO_TEST("direct-net"); DO_TEST("host-bridge-net"); --=20 2.45.2