Add XFRM Interface creation logic

This change adds logic to create XFRM interfaces.
AddVirtualTunnelInterface and RemoveVirtualTunnelInterface also have
been renamed to the more standard ipSec* naming scheme, as well as
returning a Status instead of integers as part of extracting common
code.

This patch should change no functionality.

This is part of a patch set to enable XFRM-I support, with automatic
fallbacks to VTI in XfrmController (1/3)

Bug: 77856928
Test: Binder, unit tests passing on devices with, and without XFRM-I
Change-Id: I403b01f7817715117faa270277db67ba40bbf6dd
diff --git a/server/XfrmController.cpp b/server/XfrmController.cpp
index c27342e..18181e0 100644
--- a/server/XfrmController.cpp
+++ b/server/XfrmController.cpp
@@ -89,6 +89,10 @@
 constexpr uint32_t RAND_SPI_MAX = 0xFFFFFFFE;
 
 constexpr uint32_t INVALID_SPI = 0;
+constexpr const char* INFO_KIND_VTI = "vti";
+constexpr const char* INFO_KIND_VTI6 = "vti6";
+constexpr const char* INFO_KIND_XFRMI = "xfrm";
+constexpr int INFO_KIND_MAX_LEN = 8;
 
 static inline bool isEngBuild() {
     static const std::string sBuildType = android::base::GetProperty("ro.build.type", "user");
@@ -184,9 +188,9 @@
     return fillNlAttr(nlaType, (family == AF_INET) ? sizeof(in_addr) : sizeof(in6_addr), nlAttr);
 }
 
-size_t fillNlAttrU32(__u16 nlaType, int32_t value, nlattr* nlAttr, uint32_t* u32Value) {
-    *u32Value = htonl(value);
-    return fillNlAttr(nlaType, sizeof((*u32Value)), nlAttr);
+size_t fillNlAttrU32(__u16 nlaType, uint32_t value, XfrmController::nlattr_payload_u32* nlAttr) {
+    nlAttr->value = value;
+    return fillNlAttr(nlaType, sizeof(value), &nlAttr->hdr);
 }
 
 // returns the address family, placing the string in the provided buffer
@@ -396,12 +400,10 @@
     const String8 ifPrefix8 = String8(INetd::IPSEC_INTERFACE_PREFIX().string());
 
     for (const std::string& iface : ifaces.value()) {
-        int status = 0;
+        netdutils::Status status;
         // Look for the reserved interface prefix, which must be in the name at position 0
-        if (android::base::StartsWith(iface.c_str(), ifPrefix8.c_str()) &&
-            (status = removeVirtualTunnelInterface(iface)) < 0) {
-            ALOGE("Failed to delete ipsec tunnel %s.", iface.c_str());
-            return netdutils::statusFromErrno(status, "Failed to remove ipsec tunnel.");
+        if (android::base::StartsWith(iface.c_str(), ifPrefix8.c_str())) {
+            RETURN_IF_NOT_OK(ipSecRemoveTunnelInterface(iface));
         }
     }
     return netdutils::status::ok;
@@ -1241,6 +1243,19 @@
     return len;
 }
 
+int XfrmController::fillNlAttrXfrmIntfId(const uint32_t intfIdValue,
+                                         nlattr_xfrm_interface_id* intf_id) {
+    // Do not set if we were not given an interface id
+    if (intfIdValue == 0) {
+        return 0;
+    }
+
+    intf_id->if_id = intfIdValue;
+    int len = NLA_HDRLEN + sizeof(__u32);
+    fillXfrmNlaHdr(&intf_id->hdr, XFRMA_IF_ID, len);
+    return len;
+}
+
 int XfrmController::fillUserPolicyId(const XfrmSpInfo& record, XfrmDirection direction,
                                      xfrm_userpolicy_id* usersp) {
     // For DELPOLICY, when index is absent, selector is needed to match the policy
@@ -1249,10 +1264,11 @@
     return sizeof(*usersp);
 }
 
-int XfrmController::addVirtualTunnelInterface(const std::string& deviceName,
-                                              const std::string& localAddress,
-                                              const std::string& remoteAddress, int32_t ikey,
-                                              int32_t okey, bool isUpdate) {
+netdutils::Status XfrmController::ipSecAddTunnelInterface(const std::string& deviceName,
+                                                          const std::string& localAddress,
+                                                          const std::string& remoteAddress,
+                                                          int32_t ikey, int32_t okey,
+                                                          bool isUpdate) {
     ALOGD("XfrmController::%s, line=%d", __FUNCTION__, __LINE__);
     ALOGD("deviceName=%s", deviceName.c_str());
     ALOGD("localAddress=%s", localAddress.c_str());
@@ -1261,30 +1277,124 @@
     ALOGD("okey=%0.8x", okey);
     ALOGD("isUpdate=%d", isUpdate);
 
-    if (deviceName.empty() || localAddress.empty() || remoteAddress.empty()) {
-        return EINVAL;
+    uint16_t flags = isUpdate ? NETLINK_REQUEST_FLAGS : NETLINK_ROUTE_CREATE_FLAGS;
+
+    return ipSecAddVirtualTunnelInterface(deviceName, localAddress, remoteAddress, ikey, okey,
+                                          flags);
+}
+
+netdutils::Status XfrmController::ipSecAddXfrmInterface(const std::string& deviceName,
+                                                        int32_t underlyingInterface,
+                                                        int32_t interfaceId, uint16_t flags) {
+    ALOGD("XfrmController::%s, line=%d", __FUNCTION__, __LINE__);
+
+    if (deviceName.empty()) {
+        return netdutils::statusFromErrno(EINVAL, "XFRM Interface deviceName empty");
     }
 
-    const char* INFO_KIND_VTI6 = "vti6";
-    const char* INFO_KIND_VTI = "vti";
+    ifinfomsg ifInfoMsg{};
+
+    struct XfrmIntfCreateReq {
+        nlattr ifNameNla;
+        char ifName[IFNAMSIZ];  // Already aligned
+
+        nlattr linkInfoNla;
+        struct LinkInfo {
+            nlattr infoKindNla;
+            char infoKind[INFO_KIND_MAX_LEN];  // Already aligned
+
+            nlattr infoDataNla;
+            struct InfoData {
+                nlattr xfrmLinkNla;
+                uint32_t xfrmLink;
+
+                nlattr xfrmIfIdNla;
+                uint32_t xfrmIfId;
+            } infoData;  // Already aligned
+
+        } linkInfo;  // Already aligned
+    } xfrmIntfCreateReq{
+            .ifNameNla =
+                    {
+                            .nla_len = RTA_LENGTH(IFNAMSIZ),
+                            .nla_type = IFLA_IFNAME,
+                    },
+            // Update .ifName via strlcpy
+
+            .linkInfoNla =
+                    {
+                            .nla_len = RTA_LENGTH(sizeof(XfrmIntfCreateReq::LinkInfo)),
+                            .nla_type = IFLA_LINKINFO,
+                    },
+            .linkInfo = {.infoKindNla =
+                                 {
+                                         .nla_len = RTA_LENGTH(INFO_KIND_MAX_LEN),
+                                         .nla_type = IFLA_INFO_KIND,
+                                 },
+                         // Update .infoKind via strlcpy
+
+                         .infoDataNla =
+                                 {
+                                         .nla_len = RTA_LENGTH(
+                                                 sizeof(XfrmIntfCreateReq::LinkInfo::InfoData)),
+                                         .nla_type = IFLA_INFO_DATA,
+                                 },
+                         .infoData = {
+                                 .xfrmLinkNla =
+                                         {
+                                                 .nla_len = RTA_LENGTH(sizeof(uint32_t)),
+                                                 .nla_type = IFLA_XFRM_LINK,
+                                         },
+                                 .xfrmLink = static_cast<uint32_t>(underlyingInterface),
+
+                                 .xfrmIfIdNla =
+                                         {
+                                                 .nla_len = RTA_LENGTH(sizeof(uint32_t)),
+                                                 .nla_type = IFLA_XFRM_IF_ID,
+                                         },
+                                 .xfrmIfId = static_cast<uint32_t>(interfaceId),
+                         }}};
+
+    strlcpy(xfrmIntfCreateReq.ifName, deviceName.c_str(), IFNAMSIZ);
+    strlcpy(xfrmIntfCreateReq.linkInfo.infoKind, INFO_KIND_XFRMI, INFO_KIND_MAX_LEN);
+
+    iovec iov[] = {
+            {NULL, 0},  // reserved for the eventual addition of a NLMSG_HDR
+            {&ifInfoMsg, sizeof(ifInfoMsg)},
+
+            {&xfrmIntfCreateReq, sizeof(xfrmIntfCreateReq)},
+    };
+
+    // sendNetlinkRequest returns -errno
+    int ret = -sendNetlinkRequest(RTM_NEWLINK, flags, iov, ARRAY_SIZE(iov), nullptr);
+    return netdutils::statusFromErrno(ret, "Add/update xfrm interface");
+}
+
+netdutils::Status XfrmController::ipSecAddVirtualTunnelInterface(const std::string& deviceName,
+                                                                 const std::string& localAddress,
+                                                                 const std::string& remoteAddress,
+                                                                 int32_t ikey, int32_t okey,
+                                                                 uint16_t flags) {
+    ALOGD("XfrmController::%s, line=%d", __FUNCTION__, __LINE__);
+
+    if (deviceName.empty() || localAddress.empty() || remoteAddress.empty()) {
+        return netdutils::statusFromErrno(EINVAL, "Required VTI creation parameter not provided");
+    }
+
     uint8_t PADDING_BUFFER[] = {0, 0, 0, 0};
 
     // Find address family.
     uint8_t remAddr[sizeof(in6_addr)];
 
     StatusOr<uint16_t> statusOrRemoteFam = convertStringAddress(remoteAddress, remAddr);
-    if (!isOk(statusOrRemoteFam)) {
-        return statusOrRemoteFam.status().code();
-    }
+    RETURN_IF_NOT_OK(statusOrRemoteFam);
 
     uint8_t locAddr[sizeof(in6_addr)];
     StatusOr<uint16_t> statusOrLocalFam = convertStringAddress(localAddress, locAddr);
-    if (!isOk(statusOrLocalFam)) {
-        return statusOrLocalFam.status().code();
-    }
+    RETURN_IF_NOT_OK(statusOrLocalFam);
 
     if (statusOrLocalFam.value() != statusOrRemoteFam.value()) {
-        return EINVAL;
+        return netdutils::statusFromErrno(EINVAL, "Local and remote address families do not match");
     }
 
     uint16_t family = statusOrLocalFam.value();
@@ -1323,18 +1433,16 @@
                             netdutils::makeSlice(binaryRemoteAddress));
 
     // Construct IFLA_VTI_OKEY
-    nlattr iflaVtiIKey;
-    uint32_t iKeyValue;
-    size_t iflaVtiIKeyPad = fillNlAttrU32(IFLA_VTI_IKEY, ikey, &iflaVtiIKey, &iKeyValue);
+    nlattr_payload_u32 iflaVtiIKey;
+    size_t iflaVtiIKeyPad = fillNlAttrU32(IFLA_VTI_IKEY, htonl(ikey), &iflaVtiIKey);
 
     // Construct IFLA_VTI_IKEY
-    nlattr iflaVtiOKey;
-    uint32_t oKeyValue;
-    size_t iflaVtiOKeyPad = fillNlAttrU32(IFLA_VTI_OKEY, okey, &iflaVtiOKey, &oKeyValue);
+    nlattr_payload_u32 iflaVtiOKey;
+    size_t iflaVtiOKeyPad = fillNlAttrU32(IFLA_VTI_OKEY, htonl(okey), &iflaVtiOKey);
 
     int iflaInfoDataPayloadLength = iflaVtiLocal.nla_len + iflaVtiLocalPad + iflaVtiRemote.nla_len +
-                                    iflaVtiRemotePad + iflaVtiIKey.nla_len + iflaVtiIKeyPad +
-                                    iflaVtiOKey.nla_len + iflaVtiOKeyPad;
+                                    iflaVtiRemotePad + iflaVtiIKey.hdr.nla_len + iflaVtiIKeyPad +
+                                    iflaVtiOKey.hdr.nla_len + iflaVtiOKeyPad;
 
     // Construct IFLA_INFO_DATA
     nlattr iflaInfoData;
@@ -1348,64 +1456,51 @@
                                         &iflaLinkInfo);
 
     iovec iov[] = {
-        {nullptr, 0},
-        {&ifInfoMsg, sizeof(ifInfoMsg)},
+            {nullptr, 0},
+            {&ifInfoMsg, sizeof(ifInfoMsg)},
 
-        {&iflaIfName, sizeof(iflaIfName)},
-        {iflaIfNameStrValue, iflaIfNameLength},
-        {&PADDING_BUFFER, iflaIfNamePad},
+            {&iflaIfName, sizeof(iflaIfName)},
+            {iflaIfNameStrValue, iflaIfNameLength},
+            {&PADDING_BUFFER, iflaIfNamePad},
 
-        {&iflaLinkInfo, sizeof(iflaLinkInfo)},
+            {&iflaLinkInfo, sizeof(iflaLinkInfo)},
 
-        {&iflaIfInfoKind, sizeof(iflaIfInfoKind)},
-        {infoKindValueStrValue, iflaIfInfoKindLength},
-        {&PADDING_BUFFER, iflaIfInfoKindPad},
+            {&iflaIfInfoKind, sizeof(iflaIfInfoKind)},
+            {infoKindValueStrValue, iflaIfInfoKindLength},
+            {&PADDING_BUFFER, iflaIfInfoKindPad},
 
-        {&iflaInfoData, sizeof(iflaInfoData)},
+            {&iflaInfoData, sizeof(iflaInfoData)},
 
-        {&iflaVtiLocal, sizeof(iflaVtiLocal)},
-        {&binaryLocalAddress, (family == AF_INET) ? sizeof(in_addr) : sizeof(in6_addr)},
-        {&PADDING_BUFFER, iflaVtiLocalPad},
+            {&iflaVtiLocal, sizeof(iflaVtiLocal)},
+            {&binaryLocalAddress, (family == AF_INET) ? sizeof(in_addr) : sizeof(in6_addr)},
+            {&PADDING_BUFFER, iflaVtiLocalPad},
 
-        {&iflaVtiRemote, sizeof(iflaVtiRemote)},
-        {&binaryRemoteAddress, (family == AF_INET) ? sizeof(in_addr) : sizeof(in6_addr)},
-        {&PADDING_BUFFER, iflaVtiRemotePad},
+            {&iflaVtiRemote, sizeof(iflaVtiRemote)},
+            {&binaryRemoteAddress, (family == AF_INET) ? sizeof(in_addr) : sizeof(in6_addr)},
+            {&PADDING_BUFFER, iflaVtiRemotePad},
 
-        {&iflaVtiIKey, sizeof(iflaVtiIKey)},
-        {&iKeyValue, sizeof(iKeyValue)},
-        {&PADDING_BUFFER, iflaVtiIKeyPad},
+            {&iflaVtiIKey, iflaVtiIKey.hdr.nla_len},
+            {&PADDING_BUFFER, iflaVtiIKeyPad},
 
-        {&iflaVtiOKey, sizeof(iflaVtiOKey)},
-        {&oKeyValue, sizeof(oKeyValue)},
-        {&PADDING_BUFFER, iflaVtiOKeyPad},
+            {&iflaVtiOKey, iflaVtiOKey.hdr.nla_len},
+            {&PADDING_BUFFER, iflaVtiOKeyPad},
 
-        {&PADDING_BUFFER, iflaInfoDataPad},
+            {&PADDING_BUFFER, iflaInfoDataPad},
 
-        {&PADDING_BUFFER, iflaLinkInfoPad},
+            {&PADDING_BUFFER, iflaLinkInfoPad},
     };
 
-    uint16_t action = RTM_NEWLINK;
-    uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
-
-    if (!isUpdate) {
-        flags |= NLM_F_EXCL | NLM_F_CREATE;
-    }
-
     // sendNetlinkRequest returns -errno
-    int ret = -1 * sendNetlinkRequest(action, flags, iov, ARRAY_SIZE(iov), nullptr);
-    if (ret) {
-        ALOGE("Error in %s virtual tunnel interface. Error Code: %d",
-              isUpdate ? "updating" : "adding", ret);
-    }
-    return ret;
+    int ret = -1 * sendNetlinkRequest(RTM_NEWLINK, flags, iov, ARRAY_SIZE(iov), nullptr);
+    return netdutils::statusFromErrno(ret, "Failed to add/update virtual tunnel interface");
 }
 
-int XfrmController::removeVirtualTunnelInterface(const std::string& deviceName) {
+netdutils::Status XfrmController::ipSecRemoveTunnelInterface(const std::string& deviceName) {
     ALOGD("XfrmController::%s, line=%d", __FUNCTION__, __LINE__);
     ALOGD("deviceName=%s", deviceName.c_str());
 
     if (deviceName.empty()) {
-        return EINVAL;
+        return netdutils::statusFromErrno(EINVAL, "Required parameter not provided");
     }
 
     uint8_t PADDING_BUFFER[] = {0, 0, 0, 0};
@@ -1431,11 +1526,7 @@
 
     // sendNetlinkRequest returns -errno
     int ret = -1 * sendNetlinkRequest(action, flags, iov, ARRAY_SIZE(iov), nullptr);
-    if (ret) {
-        ALOGE("Error in removing virtual tunnel interface %s. Error Code: %d", iflaIfNameStrValue,
-              ret);
-    }
-    return ret;
+    return netdutils::statusFromErrno(ret, "Error in deleting IpSec interface " + deviceName);
 }
 
 } // namespace net