From nobody Sat Nov 23 17:34:45 2024 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 1723051266255624.5947079748856; Wed, 7 Aug 2024 10:21:06 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 34F5C12BF; Wed, 7 Aug 2024 13:21:05 -0400 (EDT) Received: from lists.libvirt.org (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id 5AABCCEB; Wed, 7 Aug 2024 13:16:21 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 996) id EADA6C2F; Wed, 7 Aug 2024 13:16:10 -0400 (EDT) Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.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 4602CC90 for ; Wed, 7 Aug 2024 13:16:10 -0400 (EDT) Received: from mx-prod-mc-03.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-408-IinXWEifM4GVSIw5HvtZwQ-1; Wed, 07 Aug 2024 13:16:08 -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-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id A093C1945106 for ; Wed, 7 Aug 2024 17:16:07 +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 2C2971956052 for ; Wed, 7 Aug 2024 17:16:07 +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_H4,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=1723050970; 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=EQ6aT6IwPOhclKaIyKEzwrfS7RIOrc2Ey7AXlFyL4Tg=; b=cXqWUKCf2xviTJWoSwZJOxDRnX3GPyMNDAZejtqZT74rtkJIMiY1+2Pr9/p2rA0+eJCA8v u1NlogZoQB01A1IWHUz8/WMhXhcwaZODhaneHkiRmSTJrOicVuWZ/ZfHSEi3zr03BLf02c wTe/m6bb0npgi3Tg9TZ+gOd/fWxPUGg= X-MC-Unique: IinXWEifM4GVSIw5HvtZwQ-1 From: Laine Stump To: devel@lists.libvirt.org Subject: [PATCH 4/7] network: turn on autoaddr selection in bridge driver Date: Wed, 7 Aug 2024 13:16:00 -0400 Message-ID: <20240807171603.218784-5-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: TQCO4AC5M7T4KMGQTRBBS3TW4OC4INJJ X-Message-ID-Hash: TQCO4AC5M7T4KMGQTRBBS3TW4OC4INJJ 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: 1723051267674116600 Content-Type: text/plain; charset="utf-8"; x-default="true" When a network has an element, the network driver will look for an "unused" subnet to assign to the network. The IP address of the network bridge will then be the .1 of that network, and any DHCP range, DHCP static hosts, and tftp boot server will be changed to the new network (while retaining the same host bits). * the range of subnets (and their prefix) have a hostwide configuration in /etc/libvirt/network.conf ("autoaddr_*"). * if the network config has a element with no specified, then a range will automatically be added that starts at ${net}.2 and ends at ${net}.254 (adjusted for prefix - this is example is only if the prefix is 24). * If a network has autoaddr=3D"yes" and also has an IP address defined, then the network driver will check that IP address first, and then start looking for a new address if that initial try fails. * If a new subnet is selected, this change of IP address will be saved to the network's persistent config (along with associated changes to other elements that need to be on the same subnet). This way the next time the network is started, it will attempt to use the same subnet that it had used the previous time. Signed-off-by: Laine Stump --- src/network/bridge_driver.c | 137 ++++++++++++++++++++++++++++++++++-- 1 file changed, 133 insertions(+), 4 deletions(-) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index b8e752f20d..cceeb5d941 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -1901,19 +1901,78 @@ networkAddRouteToBridge(virNetworkObj *obj, /* XXX: This function can be a lot more exhaustive, there are certainly * other scenarios where we can ruin host network connectivity. * XXX: Using a proper library is preferred over parsing /proc + * + * check for any host route colliding exactly with each requested IPv4 + * address/prefix, If a collision is found and autoaddr isn't set for + * the network, immediately return an error. + + * if autoaddr *is* set, iteratively try subnets using the auto_addr* + * info from the system config (in /etc/libvirt/network.conf) until an + * "unused" subnet is found, then use that subnet, and rewrite the + * persistent network config so that next time we'll start trying + * using the same address we got this time. + * + * If all subnets in the configured autoaddr range are already in use, + * then return an error. */ static int -networkCheckIPAddrCollision(virNetworkDef *def, GHashTable *routes) +networkCheckIPAddrCollision(virNetworkDriverState *driver, + virNetworkObj *obj, + GHashTable *routes) { + g_autoptr(virNetworkDriverConfig) cfg =3D virNetworkDriverGetConfig(dr= iver); + uint32_t nextNet =3D ntohl(cfg->autoaddrStart.data.inet4.sin_addr.s_ad= dr); /* next subnet to check */ + uint32_t lastNet =3D ntohl(cfg->autoaddrEnd.data.inet4.sin_addr.s_addr= ); + virNetworkDef *def =3D virNetworkObjGetDef(obj); size_t i; virNetworkIPDef *ipdef; =20 /* check all IPv4 addresses of the network for conflict with * existing routes */ - for (i =3D 0; (ipdef =3D virNetworkDefGetIPByIndex(def, AF_INET, i)); = i++) { + for (i =3D 0; (ipdef =3D virNetworkDefGetIPByIndex(def, AF_UNSPEC, i))= ; i++) { + virSocketAddr ip =3D ipdef->address; /* don't disturb the original= (yet) */ unsigned int prefix =3D virNetworkIPDefPrefix(ipdef); - const char *iface =3D networkSysRoutesTableFind(routes, &ipdef->ad= dress, prefix); + const char *iface; + bool done =3D false; + uint32_t updateNet =3D 0; /* new subnet in *host* byte order (or 0= if no change) */ + + if (VIR_SOCKET_ADDR_IS_FAMILY(&ip, AF_INET6)) + continue; /* autoaddr isn't used on IPv6 addresses */ + + do { + if (VIR_SOCKET_ADDR_IS_FAMILY(&ip, AF_INET)) + iface =3D networkSysRoutesTableFind(routes, &ip, prefix); + else + iface =3D "unset"; /* could be anything !NULL, just need t= o take upcoming else clause */ + + if (!iface || !ipdef->autoaddr) { + + done =3D true; + + } else { + g_autofree char *addrStr =3D NULL; + + if (nextNet > lastNet) { + /* we've tried all of the subnets and they're all used= , so give up */ + virReportError(VIR_ERR_INTERNAL_ERROR, + _("could not find an unused subnet for = autoaddr network '%1$s'"), + def->name); + return -1; + } + + /* this is what we'll try next time through the loop */ + updateNet =3D nextNet; + virSocketAddrSetIPv4Addr(&ip, updateNet + 1); /* + 1 for h= ost address */ + prefix =3D cfg->autoaddrPrefix; + + addrStr =3D virSocketAddrFormat(&ip); + VIR_INFO("autoaddr trying %s/%u", NULLSTR(addrStr), prefix= ); + + /* get ready for the next "new" request (the loop *after* = the next) */ + nextNet =3D ((updateNet >> (32 - prefix)) + 1) << (32 - pr= efix); + } + } while (!done); =20 if (iface) { g_autofree char *addrStr =3D virSocketAddrFormat(&ipdef->addre= ss); @@ -1923,6 +1982,76 @@ networkCheckIPAddrCollision(virNetworkDef *def, GHas= hTable *routes) NULLSTR(addrStr), prefix, iface); return -1; } + + if (updateNet) { + /* address was changed via autoaddr */ + g_autofree char *addrStr =3D virSocketAddrFormat(&ip); + + VIR_INFO("autoaddr success: %s/%u", NULLSTR(addrStr), prefix); + + /* put the newly found unused IP/prefix in the NetworkDef */ + ipdef->address =3D ip; + ipdef->prefix =3D prefix; + memset(&ipdef->netmask, 0, sizeof(ipdef->netmask)); /*in case = original had netmask */ + + /* if there is a element also add/update the DHCP + * range, tftp bootserver, and, static hosts (they will + * all have the network part of their address changed, but + * the host bits will remain the same. + */ + + if (ipdef->dhcp) { + size_t j; + uint32_t tmp; + /* in *host* order initially, since we need to use if for = some arithmetic */ + uint32_t hostmask =3D ~(0xffffffff << (32 - prefix)); + + + if (!ipdef->nranges) { + /* if there isn't any address range in the config, + * we need to setup at least one + */ + virNetworkDHCPRangeDef range =3D { 0 }; + + /* just prime them with the host bits for .2 and + * .254 (adjusted for prefix) + */ + virSocketAddrSetIPv4Addr(&range.addr.start, updateNet = + 2); + virSocketAddrSetIPv4Addr(&range.addr.end, updateNet + = hostmask - 1); + VIR_APPEND_ELEMENT(ipdef->ranges, ipdef->nranges, rang= e); + } + + updateNet =3D htonl(updateNet); + hostmask =3D htonl(hostmask); + + for (j =3D 0; j < ipdef->nranges; j++) { + tmp =3D (ipdef->ranges[j].addr.start.data.inet4.sin_ad= dr.s_addr & hostmask) | updateNet; + virSocketAddrSetIPv4AddrNetOrder(&ipdef->ranges[j].add= r.start, tmp); + + tmp =3D (ipdef->ranges[j].addr.end.data.inet4.sin_addr= .s_addr & hostmask) | updateNet; + virSocketAddrSetIPv4AddrNetOrder(&ipdef->ranges[j].add= r.end, tmp); + } + + for (j =3D 0; j < ipdef->nhosts; j++) { + tmp =3D (ipdef->hosts[j].ip.data.inet4.sin_addr.s_addr= & hostmask) | updateNet; + virSocketAddrSetIPv4AddrNetOrder(&ipdef->hosts[j].ip, = tmp); + } + + if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->bootserver, AF_INET)= ) { + tmp =3D (ipdef->bootserver.data.inet4.sin_addr.s_addr = & hostmask) | updateNet; + virSocketAddrSetIPv4AddrNetOrder(&ipdef->bootserver, t= mp); + } + } + + /* if this network is persistent also update on disk (so + * that the discovered address is tried first the next + * time this network is started) + */ + if (virNetworkObjIsPersistent(obj) + && virNetworkSaveConfig(cfg->networkConfigDir, def, driver= ->xmlopt) < 0) { + VIR_WARN("couldn't update autoaddr in network %1$s", def->= name); + } + } } =20 return 0; @@ -1977,7 +2106,7 @@ networkStartNetworkVirtual(virNetworkDriverState *dri= ver, =20 if (routes) { /* Check to see if any network IP collides with an existing route = on the host */ - if (networkCheckIPAddrCollision(def, routes) < 0) + if (networkCheckIPAddrCollision(driver, obj, routes) < 0) return -1; =20 /* also check for requested routes that collide with existing rout= es */ --=20 2.45.2