From nobody Mon Feb 9 15:09:43 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.39 as permitted sender) client-ip=209.132.183.39; envelope-from=libvir-list-bounces@redhat.com; helo=mx6-phx2.redhat.com; Authentication-Results: mx.zoho.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.39 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; Return-Path: Received: from mx6-phx2.redhat.com (mx6-phx2.redhat.com [209.132.183.39]) by mx.zohomail.com with SMTPS id 1488553533239255.96391472475136; Fri, 3 Mar 2017 07:05:33 -0800 (PST) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by mx6-phx2.redhat.com (8.14.4/8.14.4) with ESMTP id v23F2EPk013608; Fri, 3 Mar 2017 10:02:14 -0500 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 v23F0XLa029264 for ; Fri, 3 Mar 2017 10:00:33 -0500 Received: by smtp.corp.redhat.com (Postfix) id DB6212D5ED; Fri, 3 Mar 2017 15:00:33 +0000 (UTC) Received: from mx1.redhat.com (ext-mx08.extmail.prod.ext.phx2.redhat.com [10.5.110.32]) by smtp.corp.redhat.com (Postfix) with ESMTPS id D07862D5D2 for ; Fri, 3 Mar 2017 15:00:33 +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 91FEBC057FA4 for ; Fri, 3 Mar 2017 15:00:30 +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); Fri, 03 Mar 2017 16:00:27 +0100 From: =?UTF-8?q?C=C3=A9dric=20Bosdonnat?= To: libvir-list@redhat.com Date: Fri, 3 Mar 2017 16:00:22 +0100 Message-Id: <20170303150022.2524-5-cbosdonnat@suse.com> In-Reply-To: <20170303150022.2524-1-cbosdonnat@suse.com> References: <20170303150022.2524-1-cbosdonnat@suse.com> X-Greylist: Sender passed SPF test, Sender IP whitelisted by DNSRBL, ACL 202 matched, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.32]); Fri, 03 Mar 2017 15:00:31 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.32]); Fri, 03 Mar 2017 15:00:31 +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.32 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?= Subject: [libvirt] [PATCH 4/4] 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 loosing 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/network/bridge_driver.c | 178 ++++++++++++++++++++++++++++++++++++++++= ++-- 1 file changed, 173 insertions(+), 5 deletions(-) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 3f6561055..1ac837f7f 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" @@ -2067,6 +2068,168 @@ networkReloadFirewallRules(virNetworkDriverStatePtr= driver) NULL); } =20 +static int +networkGetAcceptRA(const char *ifname) +{ + char *path =3D NULL; + char *buf =3D NULL; + char *suffix; + int accept_ra =3D -1; + + if (virAsprintf(&path, SYSCTL_PATH "/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; +} + +#if defined(__linux__) && defined(HAVE_LIBNL) +struct networkIPv6CheckData { + bool hasRARoutes; + + /* Devices with conflicting accept_ra */ + char **devices; + size_t ndevices; +}; + +static int +networkCheckIPv6ForwardingCallback(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; + char name[IFNAMSIZ]; + struct networkIPv6CheckData *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; + + memset(&name, 0, sizeof(name)); + + /* 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 (!if_indextoname(oif, name) || VIR_STRDUP(ifname, name) < 0) + VIR_DEBUG("Failed to convert OIF to a device name"); + 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 networkGetAcceptRA(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) + ret =3D -1; + + cleanup: + VIR_FREE(ifname); + return ret; +} + +static bool +networkCheckIPv6Forwarding(void) +{ + struct nl_msg *nlmsg =3D NULL; + bool valid =3D false; + struct rtgenmsg genmsg; + size_t i; + struct networkIPv6CheckData 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, networkCheckIPv6ForwardingCallback, 0= , 0, + NETLINK_ROUTE, 0, &data) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Failed to loop over 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 networkGetAcceptRA(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; +} + +#else /* defined(__linux__) && defined(HAVE_LIBNL) */ + +static bool +networkCheckIPv6Forwarding(void) +{ + VIR_WARN("built without libnl: unable to check if IPv6 forwarding can = be safely enabled"); + return true; +} +#endif /* defined(__linux__) && defined(HAVE_LIBNL) */ + /* Enable IP Forwarding. Return 0 for success, -1 for failure. */ static int networkEnableIPForwarding(bool enableIPv4, bool enableIPv6) @@ -2377,11 +2540,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 (!networkCheckIPv6Forwarding()) + goto err3; /* Precise error message already provided */ + + + if (networkEnableIPForwarding(v4present, v6present) < 0) { + virReportSystemError(errno, "%s", + _("failed to enable IP forwarding")); + goto err3; + } } =20 =20 --=20 2.11.0 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list