File libvirt-util-retry-NLM_F_REQUEST-with-different-values-of-IFLA_EXT_MASK.patch of Package libvirt

From ce71a59c94b93c42ab3ec6365e3ae4439709b3bc Mon Sep 17 00:00:00 2001
Message-Id: <ce71a59c94b93c42ab3ec6365e3ae4439709b3bc.1373271640.git.jdenemar@redhat.com>
From: Laine Stump <laine@redhat.com>
Date: Tue, 26 Mar 2013 14:58:27 -0400
Subject: [PATCH] util: retry NLM_F_REQUEST with different values of
 IFLA_EXT_MASK

https://bugzilla.redhat.com/show_bug.cgi?id=923963

This patch is intended to be applied to RHEL6.x builds *only*, not for
upstream. It should be carried to any new RHEL6.x branch where the
resulting binary might be run on any RHEL kernel from RHEL6.4GA or
earlier.

It resolves the following:

  https://bugzilla.redhat.com/show_bug.cgi?id=923963
  https://bugzilla.redhat.com/show_bug.cgi?id=924557

One of these deals with running RHEL6.4GA libvirt on a RHEL6.3 kernel,
the other with running RHEL6.4.z libvirt on a RHEL6.4GA kernel.

Explanation:

The RHEL6.4 GA kernel incorrectly backported the netlink IFLA_EXT_MASK
attribute from upstream by simply adding it at the end of the
attributes in the anonymous "IFLA_*" enum in
/usr/include/linux/if_link.h. Post-GA, this error was corrected, and a
6.4.z kernel was released. So now there are 3 different "vintages" of
kernel - those without IFLA_EXT_MASK (6.3.x) those with incorrect
IFLA_EXT_MASK (6.4GA) and those with a correct IFLA_EXT_MASK (6.4.z
and beyond).

The problem comes when libvirt built with one vintage of kernel
headers is installed and run on a host that has a different vintage of
kernel - the value/existence of IFLA_EXT_MASK mismatches, resulting in
an EINVAL error in the netlink response message (although I was at one
time told that netlink would ignore unrecognized attributes, this
apparently is not the case, as evidenced by my own experiments as well
as the above bug reports).

The latest kernels do correct one potentially conflicting case - if an
application built using 6.4GA kernel headers is run on a 6.4.z kernel,
the kernel rezognizes the (otherwise unused) incorrect attribute value
and treats it as the correct value. That doesn't help in either of the
cases of the BZes referenced above though (nor in any other
combination except the one specific combination of 6.4GZ application +
6.4.z kernel).

Solution:

First we try NLM_F_REQUEST with an IFLA_EXT_MASK attribute using the
proper upstream value, then (if that fails with EINVAL in the reponse)
try again with the incorrect value used in RHEL6.4GA, and finally if
that fails (again, only with EINVAL in the reponse) we retry with no
IFLA_EXT_MASK attribute at all (which is what works for RHEL6.3, where
IFLA_EXT_MASK isn't in the kernel at all). If all of these fail then
we just report the error and return failure.
---
 src/util/virnetdev.c | 51 ++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 42 insertions(+), 9 deletions(-)

diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c
index 37eb3e9..f6f1794 100644
--- a/src/util/virnetdev.c
+++ b/src/util/virnetdev.c
@@ -1219,6 +1219,26 @@ static struct nla_policy ifla_vf_policy[IFLA_VF_MAX+1] = {
                             .maxlen = sizeof(struct ifla_vf_vlan) },
 };
 
+/* The RHEL6.4 GA kernel incorrectly backported the netlink
+ * IFLA_EXT_MASK attribute from upstream by simply adding it at the
+ * end of the attributes in the anonymous "IFLA_*" enum in
+ * /usr/include/linux/if_link.h. Post-release, this error was
+ * corrected. Because of this, we must try our NLM_F_REQUEST first
+ * with an attribute using the proper upstream value, then (if that
+ * fails) with the incorrect value used in RHEL6.4GA, and finally if
+ * that fails we should retry with no IFLA_EXT_MASK attribute at all
+ * (which is what works for RHEL6.3, where IFLA_EXT_MASK isn't in
+ * the kernel at all).
+ */
+
+#define VIR_IFLA_EXT_MASK_RHEL64   26 /* erroneous value used in RHEL6.4 GA */
+#define VIR_IFLA_EXT_MASK_UPSTREAM 29 /* actual value in upstream if_link.h */
+
+/* Also define this locally so we can build even on systems with old
+ * kernel headers.
+ */
+ #define VIR_RTEXT_FILTER_VF (1 << 0)
+
 /**
  * virNetDevLinkDump:
  *
@@ -1251,6 +1271,8 @@ virNetDevLinkDump(const char *ifname, int ifindex,
         .ifi_family = AF_UNSPEC,
         .ifi_index  = ifindex
     };
+    int ifla_ext_mask_attr = VIR_IFLA_EXT_MASK_UPSTREAM;
+    uint32_t ifla_ext_mask_val = VIR_RTEXT_FILTER_VF;
     unsigned int recvbuflen;
     struct nl_msg *nl_msg;
 
@@ -1261,6 +1283,7 @@ virNetDevLinkDump(const char *ifname, int ifindex,
 
     ifinfo.ifi_index = ifindex;
 
+retry:
     nl_msg = nlmsg_alloc_simple(RTM_GETLINK, NLM_F_REQUEST);
     if (!nl_msg) {
         virReportOOMError();
@@ -1275,20 +1298,15 @@ virNetDevLinkDump(const char *ifname, int ifindex,
             goto buffer_too_small;
     }
 
-# ifdef RTEXT_FILTER_VF
     /* if this filter exists in the kernel's netlink implementation,
      * we need to set it, otherwise the response message will not
      * contain the IFLA_VFINFO_LIST that we're looking for.
      */
-    {
-        uint32_t ifla_ext_mask = RTEXT_FILTER_VF;
-
-        if (nla_put(nl_msg, IFLA_EXT_MASK,
-                    sizeof(ifla_ext_mask), &ifla_ext_mask) < 0) {
-            goto buffer_too_small;
-        }
+    if (ifla_ext_mask_attr &&
+        nla_put(nl_msg, ifla_ext_mask_attr,
+                sizeof(ifla_ext_mask_val), &ifla_ext_mask_val) < 0) {
+        goto buffer_too_small;
     }
-# endif
 
     if (virNetlinkCommand(nl_msg, recvbuf, &recvbuflen,
                           src_pid, dst_pid, NETLINK_ROUTE, 0) < 0)
@@ -1305,6 +1323,21 @@ virNetDevLinkDump(const char *ifname, int ifindex,
         if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
             goto malformed_resp;
 
+        if (-err->error == EINVAL && ifla_ext_mask_attr) {
+            /* If we had put an IFLA_EXT_MASK attribute into the
+             * request, EINVAL almost surely means that the value we
+             * used for IFLA_EXT_MASK didn't match what the kernel was
+             * looking for, so we cleanup, switch to the next
+             * possibility, and try again. The final time we will
+             * retry with no IFLA_EXT_MASK attribute at all (signaled
+             * by setting ifla_ext_mask_attr = 0).
+             */
+            VIR_FREE(*recvbuf);
+            nlmsg_free(nl_msg);
+            ifla_ext_mask_attr = (ifla_ext_mask_attr == VIR_IFLA_EXT_MASK_UPSTREAM
+                                  ? VIR_IFLA_EXT_MASK_RHEL64 : 0);
+            goto retry;
+        }
         if (err->error) {
             virReportSystemError(-err->error,
                                  _("error dumping %s (%d) interface"),
-- 
1.8.2.1

openSUSE Build Service is sponsored by