Fix ABI incompatibility for Netlink XFRM on Fugu

Fugu is compiled with an x86 userspace and an x86_64
kernel. This means that there is no guarantee of ABI
compatibility between the kernel and userspace. The
xfrm_usersa_info struct is one such place where the
compatibility happens to not exist due to struct
alignment differences. This CL patches the
xfrm_usersa_info struct to match the kernel's 64-bit
alignment in at least the case of x86 vs x86_64.

Bug: 37252170
Test: CTS - IpSecManagerTest passes
Change-Id: Ic08a75d543f92f7fa5e0cf8b4277de12464fd406
diff --git a/server/Android.mk b/server/Android.mk
index a0041b0..9a3fa99 100644
--- a/server/Android.mk
+++ b/server/Android.mk
@@ -52,7 +52,13 @@
 LOCAL_MODULE := netd
 
 # Bug: http://b/29823425 Disable -Wvarargs for Clang update to r271374
-LOCAL_CPPFLAGS +=  -Wno-varargs
+LOCAL_CPPFLAGS +=  -Wno-varargs \
+
+ifeq ($(TARGET_ARCH), x86)
+ifneq ($(TARGET_PRODUCT), gce_x86_phone)
+        LOCAL_CPPFLAGS += -D NETLINK_COMPAT32
+endif
+endif
 
 LOCAL_INIT_RC := netd.rc
 
diff --git a/server/XfrmController.cpp b/server/XfrmController.cpp
index e2cef00..f1e0e23 100644
--- a/server/XfrmController.cpp
+++ b/server/XfrmController.cpp
@@ -209,7 +209,7 @@
         };
 
         iov[0].iov_base = &nlMsg;
-        iov[0].iov_len = sizeof(nlMsg);
+        iov[0].iov_len = NLMSG_HDRLEN;
         for (int i = 0; i < iovLen; ++i) {
             nlMsg.nlmsg_len += iov[i].iov_len;
         }
diff --git a/server/XfrmController.h b/server/XfrmController.h
index 6e75830..0f2c95a 100644
--- a/server/XfrmController.h
+++ b/server/XfrmController.h
@@ -121,6 +121,46 @@
 
     static constexpr size_t MAX_ALGO_LENGTH = 128;
 
+/*
+ * Below is a redefinition of the xfrm_usersa_info struct that is part
+ * of the Linux uapi <linux/xfrm.h> to align the structures to a 64-bit
+ * boundary.
+ */
+#ifdef NETLINK_COMPAT32
+    // Shadow the kernel definition of xfrm_usersa_info with a 64-bit aligned version
+    struct xfrm_usersa_info : ::xfrm_usersa_info {
+    } __attribute__((aligned(8)));
+    // Shadow the kernel's version, using the aligned version of xfrm_usersa_info
+    struct xfrm_userspi_info {
+        struct xfrm_usersa_info info;
+        __u32 min;
+        __u32 max;
+    };
+
+    /*
+     * Anyone who encounters a failure when sending netlink messages should look here
+     * first. Hitting the static_assert() below should be a strong hint that Android
+     * IPsec will probably not work with your current settings.
+     *
+     * Again, experimentally determined, the "flags" field should be the first byte in
+     * the final word of the xfrm_usersa_info struct. The check validates the size of
+     * the padding to be 7.
+     *
+     * This padding is verified to be correct on gcc/x86_64 kernel, and clang/x86 userspace.
+     */
+    static_assert(sizeof(::xfrm_usersa_info) % 8 != 0, "struct xfrm_usersa_info has changed "
+                                                       "alignment. Please consider whether this "
+                                                       "patch is needed.");
+    static_assert(sizeof(xfrm_usersa_info) - offsetof(xfrm_usersa_info, flags) == 8,
+                  "struct xfrm_usersa_info probably misaligned with kernel struct.");
+    static_assert(sizeof(xfrm_usersa_info) % 8 == 0, "struct xfrm_usersa_info_t is not 64-bit  "
+                                                     "aligned. Please consider whether this patch "
+                                                     "is needed.");
+    static_assert(sizeof(::xfrm_userspi_info) - sizeof(::xfrm_usersa_info) ==
+                      sizeof(xfrm_userspi_info) - sizeof(xfrm_usersa_info),
+                  "struct xfrm_userspi_info has changed and does not match the kernel struct.");
+#endif
+
     struct nlattr_algo_crypt {
         nlattr hdr;
         xfrm_algo crypt;