Add XFRM-I support to XfrmController

This patch adds support for creating and managing XFRM interfaces,
adding xfrm_if_id parameters to all relevant netlink calls.

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

Bug: 77856928
Test: Xfrm, Binder tests updated, passing
Change-Id: I09869e6a0000384c9c4d0aef1de4d5434c33374a
diff --git a/server/XfrmControllerTest.cpp b/server/XfrmControllerTest.cpp
index 5539dab..be819d8 100644
--- a/server/XfrmControllerTest.cpp
+++ b/server/XfrmControllerTest.cpp
@@ -103,7 +103,8 @@
 static constexpr int DROID_SPI = 0xD1201D;
 static constexpr size_t KEY_LENGTH = 32;
 static constexpr int NLMSG_DEFAULTSIZE = 8192;
-static constexpr uint16_t TEST_XFRM_OUTPUT_MARK = 0x512;
+static constexpr uint32_t TEST_XFRM_UNDERLYING_NET = 0x512;
+static constexpr uint32_t TEST_XFRM_IF_ID = 0x1234;
 static constexpr uint32_t TEST_XFRM_MARK = 0x123;
 static constexpr uint32_t TEST_XFRM_MASK = 0xFFFFFFFF;
 
@@ -277,10 +278,16 @@
 
     // Calculate the length of the expected netlink message.
     size_t expectedMsgLength =
-        NLMSG_HDRLEN + NLMSG_ALIGN(sizeof(xfrm_usersa_info)) +
-        NLA_ALIGN(offsetof(XfrmController::nlattr_algo_crypt, key) + KEY_LENGTH) +
-        NLA_ALIGN(offsetof(XfrmController::nlattr_algo_auth, key) + KEY_LENGTH) +
-        NLA_ALIGN(sizeof(XfrmController::nlattr_xfrm_mark));
+            NLMSG_HDRLEN + NLMSG_ALIGN(sizeof(xfrm_usersa_info)) +
+            NLA_ALIGN(offsetof(XfrmController::nlattr_algo_crypt, key) + KEY_LENGTH) +
+            NLA_ALIGN(offsetof(XfrmController::nlattr_algo_auth, key) + KEY_LENGTH) +
+            NLA_ALIGN(sizeof(XfrmController::nlattr_xfrm_mark));
+
+    uint32_t testIfId = 0;
+    if (mode == XfrmMode::TUNNEL) {
+        expectedMsgLength += NLA_ALIGN(sizeof(XfrmController::nlattr_xfrm_interface_id));
+        testIfId = TEST_XFRM_IF_ID;
+    }
 
     if (underlying_netid) {
         expectedMsgLength += NLA_ALIGN(sizeof(XfrmController::nlattr_xfrm_output_mark));
@@ -294,12 +301,13 @@
 
     XfrmController ctrl;
     Status res = ctrl.ipSecAddSecurityAssociation(
-        1 /* resourceId */, static_cast<int>(mode), localAddr, remoteAddr,
-        underlying_netid /* underlying netid */, DROID_SPI, TEST_XFRM_MARK /* mark */,
-        TEST_XFRM_MASK /* mask */, "hmac(sha256)" /* auth algo */,
-        authKey, 128 /* auth trunc length */, "cbc(aes)" /* encryption algo */,
-        cryptKey, 0 /* crypt trunc length? */, "" /* AEAD algo */, {}, 0,
-        static_cast<int>(XfrmEncapType::NONE), 0 /* local port */, 0 /* remote port */);
+            1 /* resourceId */, static_cast<int>(mode), localAddr, remoteAddr,
+            underlying_netid /* underlying netid */, DROID_SPI, TEST_XFRM_MARK /* mark */,
+            TEST_XFRM_MASK /* mask */, "hmac(sha256)" /* auth algo */, authKey,
+            128 /* auth trunc length */, "cbc(aes)" /* encryption algo */, cryptKey,
+            0 /* crypt trunc length? */, "" /* AEAD algo */, {}, 0,
+            static_cast<int>(XfrmEncapType::NONE), 0 /* local port */, 0 /* remote port */,
+            testIfId /* xfrm_if_id */);
 
     EXPECT_TRUE(isOk(res)) << res;
     EXPECT_EQ(expectedMsgLength, nlMsgBuf.size());
@@ -334,8 +342,9 @@
     XfrmController::nlattr_algo_auth authAlgo{};
     XfrmController::nlattr_xfrm_mark mark{};
     XfrmController::nlattr_xfrm_output_mark outputmark{};
-    auto attrHandler = [&encryptAlgo, &authAlgo, &mark, &outputmark](const nlattr& attr,
-                                                                     const Slice& attr_payload) {
+    XfrmController::nlattr_xfrm_interface_id xfrm_if_id{};
+    auto attrHandler = [&encryptAlgo, &authAlgo, &mark, &outputmark, &xfrm_if_id](
+                               const nlattr& attr, const Slice& attr_payload) {
         Slice buf = attr_payload;
         if (attr.nla_type == XFRMA_ALG_CRYPT) {
             encryptAlgo.hdr = attr;
@@ -353,6 +362,9 @@
         } else if (attr.nla_type == XFRMA_OUTPUT_MARK) {
             mark.hdr = attr;
             netdutils::extract(buf, outputmark.outputMark);
+        } else if (attr.nla_type == XFRMA_IF_ID) {
+            xfrm_if_id.hdr = attr;
+            netdutils::extract(buf, xfrm_if_id.if_id);
         } else {
             FAIL() << "Unexpected nlattr type: " << attr.nla_type;
         }
@@ -366,6 +378,8 @@
                         reinterpret_cast<void*>(&authAlgo.key), KEY_LENGTH));
     EXPECT_EQ(TEST_XFRM_MARK, mark.mark.v);
     EXPECT_EQ(TEST_XFRM_MASK, mark.mark.m);
+    EXPECT_EQ(testIfId, xfrm_if_id.if_id);
+
     if (underlying_netid) {
         Fwmark fwmark;
         fwmark.intValue = outputmark.outputMark;
@@ -389,7 +403,7 @@
 TEST_P(XfrmControllerParameterizedTest, TestTunnelModeIpSecAddSecurityAssociationWithOutputMark) {
     const int version = GetParam();
     testIpSecAddSecurityAssociation(version, mockSyscalls, XfrmMode::TUNNEL,
-                                    TEST_XFRM_OUTPUT_MARK);
+                                    TEST_XFRM_UNDERLYING_NET);
 }
 
 TEST_F(XfrmControllerTest, TestIpSecAddSecurityAssociationIPv4Encap) {
@@ -403,9 +417,9 @@
 
     XfrmController ctrl;
     Status res = ctrl.ipSecAddSecurityAssociation(
-        1, static_cast<int>(XfrmMode::TRANSPORT),
-        LOCALHOST_V6, TEST_ADDR_V6, 0, DROID_SPI, 0, 0, "hmac(sha256)", {}, 128, "cbc(aes)",
-        {}, 0, "", {}, 0, static_cast<int>(XfrmEncapType::ESPINUDP_NON_IKE), 0, 0);
+            1, static_cast<int>(XfrmMode::TRANSPORT), LOCALHOST_V6, TEST_ADDR_V6, 0, DROID_SPI, 0,
+            0, "hmac(sha256)", {}, 128, "cbc(aes)", {}, 0, "", {}, 0,
+            static_cast<int>(XfrmEncapType::ESPINUDP_NON_IKE), 0, 0, 0);
 
     EXPECT_FALSE(isOk(res)) << "IPv6 UDP encap not rejected";
 }
@@ -508,7 +522,8 @@
     Slice responseSlice = netdutils::makeSlice(response);
 
     size_t expectedMsgLength = NLMSG_HDRLEN + NLMSG_ALIGN(sizeof(xfrm_usersa_id)) +
-                               NLA_ALIGN(sizeof(XfrmController::nlattr_xfrm_mark));
+                               NLA_ALIGN(sizeof(XfrmController::nlattr_xfrm_mark)) +
+                               NLA_ALIGN(sizeof(XfrmController::nlattr_xfrm_interface_id));
 
     std::vector<uint8_t> nlMsgBuf;
     EXPECT_CALL(mockSyscalls, writev(_, _))
@@ -518,7 +533,8 @@
 
     XfrmController ctrl;
     Status res = ctrl.ipSecDeleteSecurityAssociation(1 /* resourceId */, localAddr, remoteAddr,
-                                                     DROID_SPI, TEST_XFRM_MARK, TEST_XFRM_MASK);
+                                                     DROID_SPI, TEST_XFRM_MARK, TEST_XFRM_MASK,
+                                                     TEST_XFRM_IF_ID);
 
     EXPECT_TRUE(isOk(res)) << res;
     EXPECT_EQ(expectedMsgLength, nlMsgBuf.size());
@@ -526,11 +542,24 @@
     Slice nlMsgSlice = netdutils::makeSlice(nlMsgBuf);
     nlMsgSlice = netdutils::drop(nlMsgSlice, NLMSG_HDRLEN);
 
+    // Extract and check the usersa_id
     xfrm_usersa_id said{};
     netdutils::extract(nlMsgSlice, said);
-
+    nlMsgSlice = drop(nlMsgSlice, sizeof(xfrm_usersa_id));
     EXPECT_EQ(htonl(DROID_SPI), said.spi);
     expectAddressEquals(family, remoteAddr, said.daddr);
+
+    // Extract and check the mark.
+    XfrmController::nlattr_xfrm_mark mark{};
+    netdutils::extract(nlMsgSlice, mark);
+    nlMsgSlice = drop(nlMsgSlice, sizeof(XfrmController::nlattr_xfrm_mark));
+    EXPECT_EQ(TEST_XFRM_MARK, mark.mark.v);
+    EXPECT_EQ(TEST_XFRM_MASK, mark.mark.m);
+
+    // Extract and check the interface id.
+    XfrmController::nlattr_xfrm_interface_id xfrm_if_id{};
+    netdutils::extract(nlMsgSlice, xfrm_if_id);
+    EXPECT_EQ(TEST_XFRM_IF_ID, xfrm_if_id.if_id);
 }
 
 TEST_P(XfrmControllerParameterizedTest, TestIpSecAddSecurityPolicy) {
@@ -545,7 +574,8 @@
 
     size_t expectedMsgLength = NLMSG_HDRLEN + NLMSG_ALIGN(sizeof(xfrm_userpolicy_info)) +
                                NLMSG_ALIGN(sizeof(XfrmController::nlattr_user_tmpl)) +
-                               NLMSG_ALIGN(sizeof(XfrmController::nlattr_xfrm_mark));
+                               NLMSG_ALIGN(sizeof(XfrmController::nlattr_xfrm_mark)) +
+                               NLMSG_ALIGN(sizeof(XfrmController::nlattr_xfrm_interface_id));
 
     std::vector<uint8_t> nlMsgBuf;
     EXPECT_CALL(mockSyscalls, writev(_, _))
@@ -556,7 +586,7 @@
     XfrmController ctrl;
     Status res = ctrl.ipSecAddSecurityPolicy(
             1 /* resourceId */, family, static_cast<int>(XfrmDirection::OUT), localAddr, remoteAddr,
-            0 /* SPI */, TEST_XFRM_MARK, TEST_XFRM_MASK);
+            0 /* SPI */, TEST_XFRM_MARK, TEST_XFRM_MASK, TEST_XFRM_IF_ID);
 
     EXPECT_TRUE(isOk(res)) << res;
     EXPECT_EQ(expectedMsgLength, nlMsgBuf.size());
@@ -578,7 +608,9 @@
     // Extract and check the user tmpl and mark.
     XfrmController::nlattr_user_tmpl usertmpl{};
     XfrmController::nlattr_xfrm_mark mark{};
-    auto attrHandler = [&usertmpl, &mark](const nlattr& attr, const Slice& attr_payload) {
+    XfrmController::nlattr_xfrm_interface_id xfrm_if_id{};
+    auto attrHandler = [&usertmpl, &mark, &xfrm_if_id](const nlattr& attr,
+                                                       const Slice& attr_payload) {
         Slice buf = attr_payload;
         if (attr.nla_type == XFRMA_TMPL) {
             usertmpl.hdr = attr;
@@ -586,6 +618,9 @@
         } else if (attr.nla_type == XFRMA_MARK) {
             mark.hdr = attr;
             netdutils::extract(buf, mark.mark);
+        } else if (attr.nla_type == XFRMA_IF_ID) {
+            mark.hdr = attr;
+            netdutils::extract(buf, xfrm_if_id.if_id);
         } else {
             FAIL() << "Unexpected nlattr type: " << attr.nla_type;
         }
@@ -595,7 +630,7 @@
     expectAddressEquals(family, remoteAddr, usertmpl.tmpl.id.daddr);
     EXPECT_EQ(TEST_XFRM_MARK, mark.mark.v);
     EXPECT_EQ(TEST_XFRM_MASK, mark.mark.m);
-
+    EXPECT_EQ(TEST_XFRM_IF_ID, xfrm_if_id.if_id);
 }
 
 TEST_P(XfrmControllerParameterizedTest, TestIpSecUpdateSecurityPolicy) {
@@ -610,7 +645,8 @@
 
     size_t expectedMsgLength = NLMSG_HDRLEN + NLMSG_ALIGN(sizeof(xfrm_userpolicy_info)) +
                                NLMSG_ALIGN(sizeof(XfrmController::nlattr_user_tmpl)) +
-                               NLMSG_ALIGN(sizeof(XfrmController::nlattr_xfrm_mark));
+                               NLMSG_ALIGN(sizeof(XfrmController::nlattr_xfrm_mark)) +
+                               NLMSG_ALIGN(sizeof(XfrmController::nlattr_xfrm_interface_id));
 
     std::vector<uint8_t> nlMsgBuf;
     EXPECT_CALL(mockSyscalls, writev(_, _))
@@ -621,7 +657,7 @@
     XfrmController ctrl;
     Status res = ctrl.ipSecUpdateSecurityPolicy(
             1 /* resourceId */, family, static_cast<int>(XfrmDirection::OUT), localAddr, remoteAddr,
-            0 /* SPI */, 0 /* Mark */, 0 /* Mask */);
+            0 /* SPI */, 0 /* Mark */, 0 /* Mask */, TEST_XFRM_IF_ID /* xfrm_if_id */);
 
     EXPECT_TRUE(isOk(res)) << res;
     EXPECT_EQ(expectedMsgLength, nlMsgBuf.size());
@@ -643,7 +679,8 @@
     Slice responseSlice = netdutils::makeSlice(response);
 
     size_t expectedMsgLength = NLMSG_HDRLEN + NLMSG_ALIGN(sizeof(xfrm_userpolicy_id)) +
-                               NLMSG_ALIGN(sizeof(XfrmController::nlattr_xfrm_mark));
+                               NLMSG_ALIGN(sizeof(XfrmController::nlattr_xfrm_mark)) +
+                               NLMSG_ALIGN(sizeof(XfrmController::nlattr_xfrm_interface_id));
 
     std::vector<uint8_t> nlMsgBuf;
     EXPECT_CALL(mockSyscalls, writev(_, _))
@@ -654,7 +691,7 @@
     XfrmController ctrl;
     Status res = ctrl.ipSecDeleteSecurityPolicy(1 /* resourceId */, family,
                                                 static_cast<int>(XfrmDirection::OUT),
-                                                TEST_XFRM_MARK, TEST_XFRM_MASK);
+                                                TEST_XFRM_MARK, TEST_XFRM_MASK, TEST_XFRM_IF_ID);
 
     EXPECT_TRUE(isOk(res)) << res;
     EXPECT_EQ(expectedMsgLength, nlMsgBuf.size());
@@ -669,12 +706,18 @@
 
     // Drop the user policy id.
     nlMsgSlice = drop(nlMsgSlice, NLA_ALIGN(sizeof(xfrm_userpolicy_id)));
+
     // Extract and check the mark.
     XfrmController::nlattr_xfrm_mark mark{};
     netdutils::extract(nlMsgSlice, mark);
+    nlMsgSlice = drop(nlMsgSlice, sizeof(XfrmController::nlattr_xfrm_mark));
     EXPECT_EQ(TEST_XFRM_MARK, mark.mark.v);
     EXPECT_EQ(TEST_XFRM_MASK, mark.mark.m);
 
+    // Extract and check the interface id.
+    XfrmController::nlattr_xfrm_interface_id xfrm_if_id{};
+    netdutils::extract(nlMsgSlice, xfrm_if_id);
+    EXPECT_EQ(TEST_XFRM_IF_ID, xfrm_if_id.if_id);
 }
 
 // TODO: Add tests for VTIs, ensuring that we are sending the correct data over netlink.