Add unit test for xfrmcontroller

Bug: 38259578
Test: runtest -x tests/netd_integration_test.cpp
Change-Id: Ie15b7447db8f084313d78f5900ace007e22e533e
diff --git a/server/XfrmControllerTest.cpp b/server/XfrmControllerTest.cpp
new file mode 100644
index 0000000..9bba0c0
--- /dev/null
+++ b/server/XfrmControllerTest.cpp
@@ -0,0 +1,374 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * xfrm_ctrl_test.cpp - unit tests for xfrm controllers.
+ */
+
+#include <cerrno>
+#include <cinttypes>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <set>
+#include <vector>
+
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <gmock/gmock.h>
+#include <ifaddrs.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netlink.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+
+#include "NetdConstants.h"
+#include "NetlinkCommands.h"
+#include "Stopwatch.h"
+#include "XfrmController.h"
+#include "android/net/INetd.h"
+#include "android/net/UidRange.h"
+#include "binder/IServiceManager.h"
+#include "netdutils/MockSyscalls.h"
+#include "netdutils/Netlink.h"
+#include "tun_interface.h"
+
+using android::base::unique_fd;
+using android::netdutils::Fd;
+using android::netdutils::MockSyscalls;
+using android::netdutils::Slice;
+using android::netdutils::Status;
+using android::netdutils::StatusOr;
+using android::netdutils::Syscalls;
+
+using ::testing::DoAll;
+using ::testing::Return;
+using ::testing::SaveArg;
+using ::testing::SetArgPointee;
+using ::testing::_;
+
+/**
+ * Set the memory which is pointed to by the Slice parameter
+ * with the content pointed to by the input Slice value.
+ */
+ACTION_TEMPLATE(SetArgSlice, HAS_1_TEMPLATE_PARAMS(int, k), AND_1_VALUE_PARAMS(value)) {
+    Slice orig = ::testing::get<k>(args);
+    android::netdutils::copy(orig, value);
+}
+
+/** Extract the content of each iovec and put it into a vector. */
+ACTION_TEMPLATE(SaveFlattenedIovecs, HAS_1_TEMPLATE_PARAMS(int, k), AND_1_VALUE_PARAMS(resVec)) {
+    std::vector<iovec> iovs = ::testing::get<k>(args);
+
+    for (const iovec& iov : iovs) {
+        resVec->insert(resVec->end(), reinterpret_cast<uint8_t*>(iov.iov_base),
+                       reinterpret_cast<uint8_t*>(iov.iov_base) + iov.iov_len);
+    }
+}
+
+namespace android {
+namespace net {
+
+static constexpr int DROID_SPI = 0xD1201D;
+static constexpr size_t KEY_LENGTH = 32;
+static constexpr int NLMSG_DEFAULTSIZE = 8192;
+
+struct Policy {
+    xfrm_userpolicy_info info;
+    xfrm_user_tmpl tmpl;
+};
+
+struct NetlinkResponse {
+    nlmsghdr hdr;
+    char buf[NLMSG_DEFAULTSIZE];
+};
+
+class XfrmControllerTest : public ::testing::Test {
+public:
+    MockSyscalls mockSyscalls;
+
+    void SetUp() override { netdutils::sSyscalls.swap(mockSyscalls); }
+};
+
+TEST_F(XfrmControllerTest, TestIpSecAllocateSpi) {
+    int outSpi = 0;
+    XfrmController ctrl;
+
+    NetlinkResponse response = {};
+    response.hdr.nlmsg_type = XFRM_MSG_ALLOCSPI;
+
+    /** It's an injected return result for the sendMessage function to go through */
+    StatusOr<Slice> readStatus(netdutils::makeSlice(response));
+
+    size_t expectMsgLength = NLMSG_HDRLEN + NLMSG_ALIGN(sizeof(xfrm_userspi_info));
+
+    // Set the return to allow the program go through
+    StatusOr<size_t> expectRet(expectMsgLength);
+
+    // A vector to hold the flattened netlink message for nlMsgSlice
+    std::vector<uint8_t> nlMsgBuf;
+    EXPECT_CALL(mockSyscalls, writev(_, _))
+        .WillOnce(DoAll(SaveFlattenedIovecs<1>(&nlMsgBuf), Return(expectRet)));
+    EXPECT_CALL(mockSyscalls, read(_, _))
+        .WillOnce(DoAll(SetArgSlice<1>(netdutils::makeSlice(response)), Return(readStatus)));
+
+    Status res = ctrl.ipSecAllocateSpi(
+        1 /* resourceId */, static_cast<int>(XfrmDirection::OUT), "127.0.0.1" /* local address */,
+        "8.8.8.8" /* remote address */, DROID_SPI /* request spi */, &outSpi);
+
+    EXPECT_EQ(0, res.code());
+    EXPECT_EQ(DROID_SPI, outSpi);
+    EXPECT_EQ(expectMsgLength, nlMsgBuf.size());
+
+    Slice nlMsgSlice = netdutils::makeSlice(nlMsgBuf);
+    nlMsgSlice = drop(nlMsgSlice, NLMSG_HDRLEN);
+
+    xfrm_userspi_info userspi{};
+    netdutils::extract(nlMsgSlice, userspi);
+
+    EXPECT_EQ(AF_INET, userspi.info.sel.family);
+
+    xfrm_address_t saddr;
+    inet_pton(AF_INET, "127.0.0.1", reinterpret_cast<void*>(&saddr));
+    EXPECT_EQ(0, memcmp(&saddr, &userspi.info.saddr, sizeof(xfrm_address_t)));
+
+    xfrm_address_t daddr;
+    inet_pton(AF_INET, "8.8.8.8", reinterpret_cast<void*>(&daddr));
+    EXPECT_EQ(0, memcmp(&daddr, &userspi.info.id.daddr, sizeof(xfrm_address_t)));
+
+    EXPECT_EQ(DROID_SPI, static_cast<int>(userspi.min));
+    EXPECT_EQ(DROID_SPI, static_cast<int>(userspi.max));
+}
+
+TEST_F(XfrmControllerTest, TestIpSecAllocateSpiIpv6) {
+    int outSpi = 0;
+    XfrmController ctrl;
+
+    NetlinkResponse response = {};
+    response.hdr.nlmsg_type = XFRM_MSG_ALLOCSPI;
+
+    /** It's an injected return result for the sendMessage function to go through */
+    StatusOr<Slice> readStatus(netdutils::makeSlice(response));
+
+    size_t expectMsgLength = NLMSG_HDRLEN + NLMSG_ALIGN(sizeof(xfrm_userspi_info));
+    // Set the return to allow the program go through
+    StatusOr<size_t> expectRet(expectMsgLength);
+
+    // A vector to hold the flattened netlink message for nlMsgSlice
+    std::vector<uint8_t> nlMsgBuf;
+    EXPECT_CALL(mockSyscalls, writev(_, _))
+        .WillOnce(DoAll(SaveFlattenedIovecs<1>(&nlMsgBuf), Return(expectRet)));
+    EXPECT_CALL(mockSyscalls, read(_, _))
+        .WillOnce(DoAll(SetArgSlice<1>(netdutils::makeSlice(response)), Return(readStatus)));
+
+    Status res = ctrl.ipSecAllocateSpi(
+        1 /* resourceId */, static_cast<int>(XfrmDirection::OUT), "::1" /* local address */,
+        "2001:4860:4860::8888" /* remote address */, DROID_SPI /* request spi */, &outSpi);
+
+    EXPECT_EQ(0, res.code());
+    EXPECT_EQ(DROID_SPI, outSpi);
+    EXPECT_EQ(expectMsgLength, nlMsgBuf.size());
+
+    Slice nlMsgSlice = netdutils::makeSlice(nlMsgBuf);
+    nlMsgSlice = drop(nlMsgSlice, NLMSG_HDRLEN);
+
+    xfrm_userspi_info userspi{};
+    netdutils::extract(nlMsgSlice, userspi);
+
+    EXPECT_EQ(AF_INET6, userspi.info.sel.family);
+
+    xfrm_address_t saddr;
+    inet_pton(AF_INET6, "::1", reinterpret_cast<void*>(&saddr));
+    EXPECT_EQ(0, memcmp(&saddr, &userspi.info.saddr, sizeof(xfrm_address_t)));
+
+    xfrm_address_t daddr;
+    inet_pton(AF_INET6, "2001:4860:4860::8888", reinterpret_cast<void*>(&daddr));
+    EXPECT_EQ(0, memcmp(&daddr, &userspi.info.id.daddr, sizeof(xfrm_address_t)));
+
+    EXPECT_EQ(DROID_SPI, static_cast<int>(userspi.min));
+    EXPECT_EQ(DROID_SPI, static_cast<int>(userspi.max));
+}
+
+TEST_F(XfrmControllerTest, TestIpSecAddSecurityAssociation) {
+
+    int reqSpi = DROID_SPI;
+    XfrmController ctrl;
+
+    NetlinkResponse response = {};
+    response.hdr.nlmsg_type = XFRM_MSG_ALLOCSPI;
+
+    /** It's an injected return result for the sendMessage function to go through */
+    StatusOr<Slice> readStatus(netdutils::makeSlice(response));
+
+    std::vector<uint8_t> authKey(KEY_LENGTH, 0);
+    std::vector<uint8_t> cryptKey(KEY_LENGTH, 1);
+
+    // Calculate the length of the expected netlink message.
+    size_t expectMsgLength = 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_encap_tmpl));
+    StatusOr<size_t> expectRet(expectMsgLength);
+
+    std::vector<uint8_t> nlMsgBuf;
+    EXPECT_CALL(mockSyscalls, writev(_, _))
+        .WillOnce(DoAll(SaveFlattenedIovecs<1>(&nlMsgBuf), Return(expectRet)));
+    EXPECT_CALL(mockSyscalls, read(_, _))
+        .WillOnce(DoAll(SetArgSlice<1>(netdutils::makeSlice(response)), Return(readStatus)));
+
+    Status res = ctrl.ipSecAddSecurityAssociation(
+        1 /* resourceId */, static_cast<int>(XfrmMode::TUNNEL),
+        static_cast<int>(XfrmDirection::OUT), "127.0.0.1" /* local address */,
+        "8.8.8.8" /* remote address */, 0 /* network handle */, reqSpi,
+        "hmac(sha256)" /* auth algo */, authKey, 0, "cbc(aes)" /* encryption algo */, cryptKey, 0,
+        UDP_ENCAP_ESPINUDP_NON_IKE /* encapType */, 34567 /* local port */,
+        34567 /* remote port */);
+
+    EXPECT_EQ(0, res.code());
+    EXPECT_EQ(expectMsgLength, nlMsgBuf.size());
+
+    Slice nlMsgSlice = netdutils::makeSlice(nlMsgBuf);
+    nlMsgSlice = drop(nlMsgSlice, NLMSG_HDRLEN);
+
+    xfrm_usersa_info usersa{};
+    netdutils::extract(nlMsgSlice, usersa);
+
+    EXPECT_EQ(AF_INET, usersa.family);
+    EXPECT_EQ(1 /* Transform Id*/, static_cast<int>(usersa.reqid));
+    EXPECT_EQ(XFRM_MODE_TUNNEL, usersa.mode);
+    EXPECT_EQ(htonl(DROID_SPI), usersa.id.spi);
+    EXPECT_EQ(IPPROTO_ESP, usersa.id.proto);
+
+    xfrm_address_t saddr{};
+    inet_pton(AF_INET, "127.0.0.1", reinterpret_cast<void*>(&saddr));
+    EXPECT_EQ(0, memcmp(&saddr, &usersa.saddr, sizeof(xfrm_address_t)));
+
+    xfrm_address_t daddr{};
+    inet_pton(AF_INET, "8.8.8.8", reinterpret_cast<void*>(&daddr));
+    EXPECT_EQ(0, memcmp(&daddr, &usersa.id.daddr, sizeof(xfrm_address_t)));
+
+    Slice attr_buf = drop(nlMsgSlice, NLA_ALIGN(sizeof(xfrm_usersa_info)));
+
+    // Extract and check the encryption/authentication algorithm
+    XfrmController::nlattr_algo_crypt encryptAlgo{};
+    XfrmController::nlattr_algo_auth authAlgo{};
+    auto attrHandler = [&encryptAlgo, &authAlgo](const nlattr& attr, const Slice& attr_payload) {
+        Slice buf = attr_payload;
+        if (attr.nla_type == XFRMA_ALG_CRYPT) {
+            encryptAlgo.hdr = attr;
+            netdutils::extract(buf, encryptAlgo.crypt);
+            buf = drop(buf, sizeof(xfrm_algo));
+            netdutils::extract(buf, encryptAlgo.key);
+        } else if (attr.nla_type == XFRMA_ALG_AUTH_TRUNC) {
+            authAlgo.hdr = attr;
+            netdutils::extract(buf, authAlgo.auth);
+            buf = drop(buf, sizeof(xfrm_algo_auth));
+            netdutils::extract(buf, authAlgo.key);
+        }
+    };
+    forEachNetlinkAttribute(attr_buf, attrHandler);
+
+    EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(cryptKey.data()),
+                        reinterpret_cast<void*>(&encryptAlgo.key), KEY_LENGTH));
+    EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(authKey.data()),
+                        reinterpret_cast<void*>(&authAlgo.key), KEY_LENGTH));
+}
+
+TEST_F(XfrmControllerTest, TestIpSecApplyTransportModeTransform) {
+
+    int optlen = 0;
+    const void* optval;
+    XfrmController ctrl;
+
+    struct sockaddr socketaddr;
+    socketaddr.sa_family = AF_INET;
+
+    unique_fd sock(socket(AF_INET, SOCK_STREAM, 0));
+
+    EXPECT_CALL(mockSyscalls, getsockname(_, _, _))
+        .WillOnce(DoAll(SetArgPointee<1>(socketaddr), Return(netdutils::status::ok)));
+
+    EXPECT_CALL(mockSyscalls, setsockopt(_, _, _, _, _))
+        .WillOnce(DoAll(SaveArg<3>(&optval), SaveArg<4>(&optlen), Return(netdutils::status::ok)));
+
+    Status res = ctrl.ipSecApplyTransportModeTransform(
+        sock, 1 /* resourceId */, static_cast<int>(XfrmDirection::OUT),
+        "127.0.0.1" /* local address */, "8.8.8.8" /* remote address */, DROID_SPI);
+
+    EXPECT_EQ(0, res.code());
+    EXPECT_EQ(static_cast<int>(sizeof(Policy)), optlen);
+
+    const Policy* policy = reinterpret_cast<const Policy*>(optval);
+    EXPECT_EQ(1 /* resourceId */, static_cast<int>(policy->tmpl.reqid));
+    EXPECT_EQ(htonl(DROID_SPI), policy->tmpl.id.spi);
+
+    xfrm_address_t saddr{};
+    inet_pton(AF_INET, "127.0.0.1", reinterpret_cast<void*>(&saddr));
+    EXPECT_EQ(0, memcmp(&saddr, &policy->tmpl.saddr, sizeof(xfrm_address_t)));
+
+    xfrm_address_t daddr{};
+    inet_pton(AF_INET, "8.8.8.8", reinterpret_cast<void*>(&daddr));
+    EXPECT_EQ(0, memcmp(&daddr, &policy->tmpl.id.daddr, sizeof(xfrm_address_t)));
+}
+
+TEST_F(XfrmControllerTest, TestIpSecDeleteSecurityAssociation) {
+    XfrmController ctrl;
+    NetlinkResponse response = {};
+    response.hdr.nlmsg_type = XFRM_MSG_ALLOCSPI;
+
+    /** It's an injected return result for the sendMessage function to go through */
+    StatusOr<Slice> readStatus(netdutils::makeSlice(response));
+
+    size_t expectMsgLength = NLMSG_HDRLEN + NLMSG_ALIGN(sizeof(xfrm_usersa_id));
+    // Set the return to allow the program run through
+    StatusOr<size_t> expectRet(expectMsgLength);
+
+    std::vector<uint8_t> nlMsgBuf;
+    EXPECT_CALL(mockSyscalls, writev(_, _))
+        .WillOnce(DoAll(SaveFlattenedIovecs<1>(&nlMsgBuf), Return(expectRet)));
+    EXPECT_CALL(mockSyscalls, read(_, _))
+        .WillOnce(DoAll(SetArgSlice<1>(netdutils::makeSlice(response)), Return(readStatus)));
+
+    Status res = ctrl.ipSecDeleteSecurityAssociation(
+        1 /* resourceId */, static_cast<int>(XfrmDirection::OUT), "127.0.0.1" /* local address */,
+        "8.8.8.8" /* remote address */, DROID_SPI);
+
+    EXPECT_EQ(0, res.code());
+    EXPECT_EQ(expectMsgLength, nlMsgBuf.size());
+
+    Slice nlMsgSlice = netdutils::makeSlice(nlMsgBuf);
+    nlMsgSlice = netdutils::drop(nlMsgSlice, NLMSG_HDRLEN);
+
+    xfrm_usersa_id said{};
+    netdutils::extract(nlMsgSlice, said);
+
+    EXPECT_EQ(htonl(DROID_SPI), said.spi);
+    xfrm_address_t daddr;
+    inet_pton(AF_INET, "8.8.8.8", reinterpret_cast<void*>(&daddr));
+
+    EXPECT_EQ(0, memcmp(&daddr, &said.daddr, sizeof(xfrm_address_t)));
+}
+
+} // namespace net
+} // namespace android