From nobody Wed Feb 11 10:17:16 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.25 as permitted sender) client-ip=209.132.183.25; envelope-from=libvir-list-bounces@redhat.com; helo=mx4-phx2.redhat.com; Authentication-Results: mx.zoho.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.25 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; Return-Path: Received: from mx4-phx2.redhat.com (mx4-phx2.redhat.com [209.132.183.25]) by mx.zohomail.com with SMTPS id 1489589490875963.1840354312313; Wed, 15 Mar 2017 07:51:30 -0700 (PDT) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by mx4-phx2.redhat.com (8.13.8/8.13.8) with ESMTP id v2FElIpB025775; Wed, 15 Mar 2017 10:47:18 -0400 Received: from smtp.corp.redhat.com (int-mx16.intmail.prod.int.phx2.redhat.com [10.5.11.28]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id v2FElFxV008376 for ; Wed, 15 Mar 2017 10:47:15 -0400 Received: by smtp.corp.redhat.com (Postfix) id 21D752D5C7; Wed, 15 Mar 2017 14:47:15 +0000 (UTC) Received: from mx1.redhat.com (ext-mx04.extmail.prod.ext.phx2.redhat.com [10.5.110.28]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 1A4562D5C4 for ; Wed, 15 Mar 2017 14:47:15 +0000 (UTC) Received: from smtp.nue.novell.com (smtp.nue.novell.com [195.135.221.5]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 1C6D38046C for ; Wed, 15 Mar 2017 14:47:13 +0000 (UTC) Received: from laptop.vms (mhy71-2-88-167-63-197.fbx.proxad.net [88.167.63.197]) by smtp.nue.novell.com with ESMTP (TLS encrypted); Wed, 15 Mar 2017 15:47:10 +0100 DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 1C6D38046C Authentication-Results: ext-mx04.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=suse.com Authentication-Results: ext-mx04.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=cbosdonnat@suse.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com 1C6D38046C From: =?UTF-8?q?C=C3=A9dric=20Bosdonnat?= To: libvir-list@redhat.com Date: Wed, 15 Mar 2017 15:45:51 +0100 Message-Id: <20170315144551.18249-6-cbosdonnat@suse.com> In-Reply-To: <20170315144551.18249-1-cbosdonnat@suse.com> References: <20170315144551.18249-1-cbosdonnat@suse.com> X-Greylist: Sender passed SPF test, Sender IP whitelisted by DNSRBL, ACL 203 matched, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.28]); Wed, 15 Mar 2017 14:47:13 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.28]); Wed, 15 Mar 2017 14:47:13 +0000 (UTC) for IP:'195.135.221.5' DOMAIN:'smtp.nue.novell.com' HELO:'smtp.nue.novell.com' FROM:'cbosdonnat@suse.com' RCPT:'' X-RedHat-Spam-Score: -1.501 (BAYES_50, RCVD_IN_DNSWL_MED, SPF_PASS) 195.135.221.5 smtp.nue.novell.com 195.135.221.5 smtp.nue.novell.com X-Scanned-By: MIMEDefang 2.78 on 10.5.110.28 X-Scanned-By: MIMEDefang 2.74 on 10.5.11.28 X-loop: libvir-list@redhat.com Cc: =?UTF-8?q?C=C3=A9dric=20Bosdonnat?= , Laine Stump Subject: [libvirt] [PATCH v2 5/5] network: check accept_ra before enabling ipv6 forwarding X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" When enabling IPv6 on all interfaces, we may get the host Router Advertisement routes discarded. To avoid this, the user needs to set accept_ra to 2 for the interfaces with such routes. See https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt on this topic. To avoid user mistakenly losing routes on their hosts, check accept_ra values before enabling IPv6 forwarding. If a RA route is detected, but neither the corresponding device nor global accept_ra is set to 2, the network will fail to start. --- src/libvirt_private.syms | 1 + src/network/bridge_driver.c | 16 +++-- src/util/virnetdevip.c | 158 ++++++++++++++++++++++++++++++++++++++++= ++++ src/util/virnetdevip.h | 1 + 4 files changed, 171 insertions(+), 5 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 0fe88c3fa..ec6553520 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2056,6 +2056,7 @@ virNetDevBridgeSetVlanFiltering; virNetDevIPAddrAdd; virNetDevIPAddrDel; virNetDevIPAddrGet; +virNetDevIPCheckIPv6Forwarding; virNetDevIPInfoAddToDev; virNetDevIPInfoClear; virNetDevIPRouteAdd; diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 3f6561055..d02cd19f9 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -61,6 +61,7 @@ #include "virlog.h" #include "virdnsmasq.h" #include "configmake.h" +#include "virnetlink.h" #include "virnetdev.h" #include "virnetdevip.h" #include "virnetdevbridge.h" @@ -2377,11 +2378,16 @@ networkStartNetworkVirtual(virNetworkDriverStatePtr= driver, } =20 /* If forward.type !=3D NONE, turn on global IP forwarding */ - if (network->def->forward.type !=3D VIR_NETWORK_FORWARD_NONE && - networkEnableIPForwarding(v4present, v6present) < 0) { - virReportSystemError(errno, "%s", - _("failed to enable IP forwarding")); - goto err3; + if (network->def->forward.type !=3D VIR_NETWORK_FORWARD_NONE) { + if (!virNetDevIPCheckIPv6Forwarding()) + goto err3; /* Precise error message already provided */ + + + if (networkEnableIPForwarding(v4present, v6present) < 0) { + virReportSystemError(errno, "%s", + _("failed to enable IP forwarding")); + goto err3; + } } =20 =20 diff --git a/src/util/virnetdevip.c b/src/util/virnetdevip.c index 42fbba1eb..a4d382427 100644 --- a/src/util/virnetdevip.c +++ b/src/util/virnetdevip.c @@ -508,6 +508,158 @@ virNetDevIPWaitDadFinish(virSocketAddrPtr *addrs, siz= e_t count) return ret; } =20 +static int +virNetDevIPGetAcceptRA(const char *ifname) +{ + char *path =3D NULL; + char *buf =3D NULL; + char *suffix; + int accept_ra =3D -1; + + if (virAsprintf(&path, "/proc/sys/net/ipv6/conf/%s/accept_ra", + ifname ? ifname : "all") < 0) + goto cleanup; + + if ((virFileReadAll(path, 512, &buf) < 0) || + (virStrToLong_i(buf, &suffix, 10, &accept_ra) < 0)) + goto cleanup; + + cleanup: + VIR_FREE(path); + VIR_FREE(buf); + + return accept_ra; +} + +struct virNetDevIPCheckIPv6ForwardingData { + bool hasRARoutes; + + /* Devices with conflicting accept_ra */ + char **devices; + size_t ndevices; +}; + +static int +virNetDevIPCheckIPv6ForwardingCallback(const struct nlmsghdr *resp, + void *opaque) +{ + struct rtmsg *rtmsg =3D NLMSG_DATA(resp); + int accept_ra =3D -1; + struct rtattr *rta; + char *ifname =3D NULL; + struct virNetDevIPCheckIPv6ForwardingData *data =3D opaque; + int ret =3D 0; + int len =3D RTM_PAYLOAD(resp); + int oif =3D -1; + + /* Ignore messages other than route ones */ + if (resp->nlmsg_type !=3D RTM_NEWROUTE) + return ret; + + /* Extract a few attributes */ + for (rta =3D RTM_RTA(rtmsg); RTA_OK(rta, len); rta =3D RTA_NEXT(rta, l= en)) { + switch (rta->rta_type) { + case RTA_OIF: + oif =3D *(int *)RTA_DATA(rta); + + if (!(ifname =3D virNetDevGetName(oif))) + goto error; + break; + } + } + + /* No need to do anything else for non RA routes */ + if (rtmsg->rtm_protocol !=3D RTPROT_RA) + goto cleanup; + + data->hasRARoutes =3D true; + + /* Check the accept_ra value for the interface */ + accept_ra =3D virNetDevIPGetAcceptRA(ifname); + VIR_DEBUG("Checking route for device %s, accept_ra: %d", ifname, accep= t_ra); + + if (accept_ra !=3D 2 && VIR_APPEND_ELEMENT(data->devices, data->ndevic= es, ifname) < 0) + goto error; + + cleanup: + VIR_FREE(ifname); + return ret; + + error: + ret =3D -1; + goto cleanup; +} + +bool +virNetDevIPCheckIPv6Forwarding(void) +{ + struct nl_msg *nlmsg =3D NULL; + bool valid =3D false; + struct rtgenmsg genmsg; + size_t i; + struct virNetDevIPCheckIPv6ForwardingData data =3D { + .hasRARoutes =3D false, + .devices =3D NULL, + .ndevices =3D 0 + }; + + + /* Prepare the request message */ + if (!(nlmsg =3D nlmsg_alloc_simple(RTM_GETROUTE, + NLM_F_REQUEST | NLM_F_DUMP))) { + virReportOOMError(); + goto cleanup; + } + + memset(&genmsg, 0, sizeof(genmsg)); + genmsg.rtgen_family =3D AF_INET6; + + if (nlmsg_append(nlmsg, &genmsg, sizeof(genmsg), NLMSG_ALIGNTO) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("allocated netlink buffer is too small")); + goto cleanup; + } + + /* Send the request and loop over the responses */ + if (virNetlinkDumpCommand(nlmsg, virNetDevIPCheckIPv6ForwardingCallbac= k, + 0, 0, NETLINK_ROUTE, 0, &data) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Failed to loop over IPv6 routes")); + goto cleanup; + } + + valid =3D !data.hasRARoutes || data.ndevices =3D=3D 0; + + /* Check the global accept_ra if at least one isn't set on a + per-device basis */ + if (!valid && data.hasRARoutes) { + int accept_ra =3D virNetDevIPGetAcceptRA(NULL); + valid =3D accept_ra =3D=3D 2; + VIR_DEBUG("Checked global accept_ra: %d", accept_ra); + } + + if (!valid) { + virBuffer buf =3D VIR_BUFFER_INITIALIZER; + for (i =3D 0; i < data.ndevices; i++) { + virBufferAdd(&buf, data.devices[i], -1); + if (i < data.ndevices - 1) + virBufferAddLit(&buf, ", "); + } + + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Check the host setup: enabling IPv6 forwarding w= ith " + "RA routes without accept_ra set to 2 is likely t= o cause " + "routes loss. Interfaces to look at: %s"), + virBufferCurrentContent(&buf)); + virBufferFreeAndReset(&buf); + } + + cleanup: + nlmsg_free(nlmsg); + for (i =3D 0; i < data.ndevices; i++) + VIR_FREE(data.devices[i]); + return valid; +} =20 #else /* defined(__linux__) && defined(HAVE_LIBNL) */ =20 @@ -655,6 +807,12 @@ virNetDevIPWaitDadFinish(virSocketAddrPtr *addrs ATTRI= BUTE_UNUSED, return -1; } =20 +bool +virNetDevIPCheckIPv6Forwarding(void) +{ + VIR_WARN("built without libnl: unable to check if IPv6 forwarding can = be safely enabled"); + return true; +} =20 #endif /* defined(__linux__) && defined(HAVE_LIBNL) */ =20 diff --git a/src/util/virnetdevip.h b/src/util/virnetdevip.h index b7abdf94d..cc466ca25 100644 --- a/src/util/virnetdevip.h +++ b/src/util/virnetdevip.h @@ -83,6 +83,7 @@ int virNetDevIPAddrGet(const char *ifname, virSocketAddrP= tr addr) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; int virNetDevIPWaitDadFinish(virSocketAddrPtr *addrs, size_t count) ATTRIBUTE_NONNULL(1); +bool virNetDevIPCheckIPv6Forwarding(void); =20 /* virNetDevIPRoute object */ void virNetDevIPRouteFree(virNetDevIPRoutePtr def); --=20 2.11.0 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list