| /* | 
 |  * | 
 |  * Copyright (C) 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. | 
 |  */ | 
 |  | 
 | #include <random> | 
 | #include <string> | 
 | #include <vector> | 
 |  | 
 | #include <ctype.h> | 
 | #include <errno.h> | 
 | #include <fcntl.h> | 
 | #include <getopt.h> | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 |  | 
 | #define __STDC_FORMAT_MACROS | 
 | #include <inttypes.h> | 
 |  | 
 | #include <arpa/inet.h> | 
 | #include <net/if.h> | 
 | #include <netinet/in.h> | 
 |  | 
 | #include <sys/socket.h> | 
 | #include <sys/stat.h> | 
 | #include <sys/types.h> | 
 | #include <sys/wait.h> | 
 |  | 
 | #include <linux/in.h> | 
 | #include <linux/ipsec.h> | 
 | #include <linux/netlink.h> | 
 | #include <linux/xfrm.h> | 
 |  | 
 | #define LOG_TAG "XfrmController" | 
 | #include <android-base/properties.h> | 
 | #include <android-base/stringprintf.h> | 
 | #include <android-base/strings.h> | 
 | #include <android-base/unique_fd.h> | 
 | #include <android/net/INetd.h> | 
 | #include <cutils/properties.h> | 
 | #include <log/log.h> | 
 | #include <log/log_properties.h> | 
 | #include <logwrap/logwrap.h> | 
 | #include "Fwmark.h" | 
 | #include "InterfaceController.h" | 
 | #include "NetdConstants.h" | 
 | #include "NetlinkCommands.h" | 
 | #include "Permission.h" | 
 | #include "XfrmController.h" | 
 | #include "android-base/stringprintf.h" | 
 | #include "android-base/strings.h" | 
 | #include "android-base/unique_fd.h" | 
 | #include "netdutils/DumpWriter.h" | 
 | #include "netdutils/Fd.h" | 
 | #include "netdutils/Slice.h" | 
 | #include "netdutils/Syscalls.h" | 
 |  | 
 | using android::net::INetd; | 
 | using android::netdutils::DumpWriter; | 
 | using android::netdutils::Fd; | 
 | using android::netdutils::ScopedIndent; | 
 | using android::netdutils::Slice; | 
 | using android::netdutils::Status; | 
 | using android::netdutils::StatusOr; | 
 | using android::netdutils::Syscalls; | 
 |  | 
 | namespace android { | 
 | namespace net { | 
 |  | 
 | // Exposed for testing | 
 | constexpr uint32_t ALGO_MASK_AUTH_ALL = ~0; | 
 | // Exposed for testing | 
 | constexpr uint32_t ALGO_MASK_CRYPT_ALL = ~0; | 
 | // Exposed for testing | 
 | constexpr uint32_t ALGO_MASK_AEAD_ALL = ~0; | 
 | // Exposed for testing | 
 | constexpr uint8_t REPLAY_WINDOW_SIZE = 4; | 
 |  | 
 | namespace { | 
 |  | 
 | constexpr uint32_t RAND_SPI_MIN = 256; | 
 | 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; | 
 | constexpr int LOOPBACK_IFINDEX = 1; | 
 |  | 
 | bool mIsXfrmIntfSupported = false; | 
 |  | 
 | static inline bool isEngBuild() { | 
 |     static const std::string sBuildType = android::base::GetProperty("ro.build.type", "user"); | 
 |     return sBuildType == "eng"; | 
 | } | 
 |  | 
 | #define XFRM_MSG_TRANS(x)                                                                          \ | 
 |     case x:                                                                                        \ | 
 |         return #x; | 
 |  | 
 | const char* xfrmMsgTypeToString(uint16_t msg) { | 
 |     switch (msg) { | 
 |         XFRM_MSG_TRANS(XFRM_MSG_NEWSA) | 
 |         XFRM_MSG_TRANS(XFRM_MSG_DELSA) | 
 |         XFRM_MSG_TRANS(XFRM_MSG_GETSA) | 
 |         XFRM_MSG_TRANS(XFRM_MSG_NEWPOLICY) | 
 |         XFRM_MSG_TRANS(XFRM_MSG_DELPOLICY) | 
 |         XFRM_MSG_TRANS(XFRM_MSG_GETPOLICY) | 
 |         XFRM_MSG_TRANS(XFRM_MSG_ALLOCSPI) | 
 |         XFRM_MSG_TRANS(XFRM_MSG_ACQUIRE) | 
 |         XFRM_MSG_TRANS(XFRM_MSG_EXPIRE) | 
 |         XFRM_MSG_TRANS(XFRM_MSG_UPDPOLICY) | 
 |         XFRM_MSG_TRANS(XFRM_MSG_UPDSA) | 
 |         XFRM_MSG_TRANS(XFRM_MSG_POLEXPIRE) | 
 |         XFRM_MSG_TRANS(XFRM_MSG_FLUSHSA) | 
 |         XFRM_MSG_TRANS(XFRM_MSG_FLUSHPOLICY) | 
 |         XFRM_MSG_TRANS(XFRM_MSG_NEWAE) | 
 |         XFRM_MSG_TRANS(XFRM_MSG_GETAE) | 
 |         XFRM_MSG_TRANS(XFRM_MSG_REPORT) | 
 |         XFRM_MSG_TRANS(XFRM_MSG_MIGRATE) | 
 |         XFRM_MSG_TRANS(XFRM_MSG_NEWSADINFO) | 
 |         XFRM_MSG_TRANS(XFRM_MSG_GETSADINFO) | 
 |         XFRM_MSG_TRANS(XFRM_MSG_GETSPDINFO) | 
 |         XFRM_MSG_TRANS(XFRM_MSG_NEWSPDINFO) | 
 |         XFRM_MSG_TRANS(XFRM_MSG_MAPPING) | 
 |         default: | 
 |             return "XFRM_MSG UNKNOWN"; | 
 |     } | 
 | } | 
 |  | 
 | // actually const but cannot be declared as such for reasons | 
 | uint8_t kPadBytesArray[] = {0, 0, 0}; | 
 | void* kPadBytes = static_cast<void*>(kPadBytesArray); | 
 |  | 
 | #define LOG_HEX(__desc16__, __buf__, __len__)                                                      \ | 
 |     do {                                                                                           \ | 
 |         if (isEngBuild()) {                                                                        \ | 
 |             logHex(__desc16__, __buf__, __len__);                                                  \ | 
 |         }                                                                                          \ | 
 |     } while (0) | 
 |  | 
 | #define LOG_IOV(__iov__)                                                                           \ | 
 |     do {                                                                                           \ | 
 |         if (isEngBuild()) {                                                                        \ | 
 |             logIov(__iov__);                                                                       \ | 
 |         }                                                                                          \ | 
 |     } while (0) | 
 |  | 
 | void logHex(const char* desc16, const char* buf, size_t len) { | 
 |     char* printBuf = new char[len * 2 + 1 + 26]; // len->ascii, +newline, +prefix strlen | 
 |     int offset = 0; | 
 |     if (desc16) { | 
 |         sprintf(printBuf, "{%-16s}", desc16); | 
 |         offset += 18; // prefix string length | 
 |     } | 
 |     sprintf(printBuf + offset, "[%4.4u]: ", (len > 9999) ? 9999 : (unsigned)len); | 
 |     offset += 8; | 
 |  | 
 |     for (uint32_t j = 0; j < (uint32_t)len; j++) { | 
 |         sprintf(&printBuf[j * 2 + offset], "%0.2x", (unsigned char)buf[j]); | 
 |     } | 
 |     ALOGD("%s", printBuf); | 
 |     delete[] printBuf; | 
 | } | 
 |  | 
 | void logIov(const std::vector<iovec>& iov) { | 
 |     for (const iovec& row : iov) { | 
 |         logHex(nullptr, reinterpret_cast<char*>(row.iov_base), row.iov_len); | 
 |     } | 
 | } | 
 |  | 
 | size_t fillNlAttr(__u16 nlaType, size_t valueSize, nlattr* nlAttr) { | 
 |     size_t dataLen = valueSize; | 
 |     int padLength = NLMSG_ALIGN(dataLen) - dataLen; | 
 |     nlAttr->nla_len = (__u16)(dataLen + sizeof(nlattr)); | 
 |     nlAttr->nla_type = nlaType; | 
 |     return padLength; | 
 | } | 
 |  | 
 | size_t fillNlAttrIpAddress(__u16 nlaType, int family, const std::string& value, nlattr* nlAttr, | 
 |                            Slice ipAddress) { | 
 |     inet_pton(family, value.c_str(), ipAddress.base()); | 
 |     return fillNlAttr(nlaType, (family == AF_INET) ? sizeof(in_addr) : sizeof(in6_addr), 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 | 
 | StatusOr<uint16_t> convertStringAddress(const std::string& addr, uint8_t* buffer) { | 
 |     if (inet_pton(AF_INET, addr.c_str(), buffer) == 1) { | 
 |         return AF_INET; | 
 |     } else if (inet_pton(AF_INET6, addr.c_str(), buffer) == 1) { | 
 |         return AF_INET6; | 
 |     } else { | 
 |         return Status(EAFNOSUPPORT); | 
 |     } | 
 | } | 
 |  | 
 | // TODO: Need to consider a way to refer to the sSycalls instance | 
 | inline Syscalls& getSyscallInstance() { return netdutils::sSyscalls.get(); } | 
 |  | 
 | class XfrmSocketImpl : public XfrmSocket { | 
 | private: | 
 |     static constexpr int NLMSG_DEFAULTSIZE = 8192; | 
 |  | 
 |     union NetlinkResponse { | 
 |         nlmsghdr hdr; | 
 |         struct _err_ { | 
 |             nlmsghdr hdr; | 
 |             nlmsgerr err; | 
 |         } err; | 
 |  | 
 |         struct _buf_ { | 
 |             nlmsghdr hdr; | 
 |             char buf[NLMSG_DEFAULTSIZE]; | 
 |         } buf; | 
 |     }; | 
 |  | 
 | public: | 
 |     netdutils::Status open() override { | 
 |         mSock = openNetlinkSocket(NETLINK_XFRM); | 
 |         if (mSock < 0) { | 
 |             ALOGW("Could not get a new socket, line=%d", __LINE__); | 
 |             return netdutils::statusFromErrno(-mSock, "Could not open netlink socket"); | 
 |         } | 
 |  | 
 |         return netdutils::status::ok; | 
 |     } | 
 |  | 
 |     static netdutils::Status validateResponse(NetlinkResponse response, size_t len) { | 
 |         if (len < sizeof(nlmsghdr)) { | 
 |             ALOGW("Invalid response message received over netlink"); | 
 |             return netdutils::statusFromErrno(EBADMSG, "Invalid message"); | 
 |         } | 
 |  | 
 |         switch (response.hdr.nlmsg_type) { | 
 |             case NLMSG_NOOP: | 
 |             case NLMSG_DONE: | 
 |                 return netdutils::status::ok; | 
 |             case NLMSG_OVERRUN: | 
 |                 ALOGD("Netlink request overran kernel buffer"); | 
 |                 return netdutils::statusFromErrno(EBADMSG, "Kernel buffer overrun"); | 
 |             case NLMSG_ERROR: | 
 |                 if (len < sizeof(NetlinkResponse::_err_)) { | 
 |                     ALOGD("Netlink message received malformed error response"); | 
 |                     return netdutils::statusFromErrno(EBADMSG, "Malformed error response"); | 
 |                 } | 
 |                 return netdutils::statusFromErrno( | 
 |                     -response.err.err.error, | 
 |                     "Error netlink message"); // Netlink errors are negative errno. | 
 |             case XFRM_MSG_NEWSA: | 
 |                 break; | 
 |         } | 
 |  | 
 |         if (response.hdr.nlmsg_type < XFRM_MSG_BASE /*== NLMSG_MIN_TYPE*/ || | 
 |             response.hdr.nlmsg_type > XFRM_MSG_MAX) { | 
 |             ALOGD("Netlink message responded with an out-of-range message ID"); | 
 |             return netdutils::statusFromErrno(EBADMSG, "Invalid message ID"); | 
 |         } | 
 |  | 
 |         // TODO Add more message validation here | 
 |         return netdutils::status::ok; | 
 |     } | 
 |  | 
 |     netdutils::Status sendMessage(uint16_t nlMsgType, uint16_t nlMsgFlags, uint16_t nlMsgSeqNum, | 
 |                                   std::vector<iovec>* iovecs) const override { | 
 |         nlmsghdr nlMsg = { | 
 |             .nlmsg_type = nlMsgType, | 
 |             .nlmsg_flags = nlMsgFlags, | 
 |             .nlmsg_seq = nlMsgSeqNum, | 
 |         }; | 
 |  | 
 |         (*iovecs)[0].iov_base = &nlMsg; | 
 |         (*iovecs)[0].iov_len = NLMSG_HDRLEN; | 
 |         for (const iovec& iov : *iovecs) { | 
 |             nlMsg.nlmsg_len += iov.iov_len; | 
 |         } | 
 |  | 
 |         ALOGD("Sending Netlink XFRM Message: %s", xfrmMsgTypeToString(nlMsgType)); | 
 |         LOG_IOV(*iovecs); | 
 |  | 
 |         StatusOr<size_t> writeResult = getSyscallInstance().writev(mSock, *iovecs); | 
 |         if (!isOk(writeResult)) { | 
 |             ALOGE("netlink socket writev failed (%s)", toString(writeResult).c_str()); | 
 |             return writeResult; | 
 |         } | 
 |  | 
 |         if (nlMsg.nlmsg_len != writeResult.value()) { | 
 |             ALOGE("Invalid netlink message length sent %d", static_cast<int>(writeResult.value())); | 
 |             return netdutils::statusFromErrno(EBADMSG, "Invalid message length"); | 
 |         } | 
 |  | 
 |         NetlinkResponse response = {}; | 
 |  | 
 |         StatusOr<Slice> readResult = | 
 |             getSyscallInstance().read(Fd(mSock), netdutils::makeSlice(response)); | 
 |         if (!isOk(readResult)) { | 
 |             ALOGE("netlink response error (%s)", toString(readResult).c_str()); | 
 |             return readResult; | 
 |         } | 
 |  | 
 |         LOG_HEX("netlink msg resp", reinterpret_cast<char*>(readResult.value().base()), | 
 |                 readResult.value().size()); | 
 |  | 
 |         Status validateStatus = validateResponse(response, readResult.value().size()); | 
 |         if (!isOk(validateStatus)) { | 
 |             ALOGE("netlink response contains error (%s)", toString(validateStatus).c_str()); | 
 |         } | 
 |  | 
 |         return validateStatus; | 
 |     } | 
 | }; | 
 |  | 
 | StatusOr<int> convertToXfrmAddr(const std::string& strAddr, xfrm_address_t* xfrmAddr) { | 
 |     if (strAddr.length() == 0) { | 
 |         memset(xfrmAddr, 0, sizeof(*xfrmAddr)); | 
 |         return AF_UNSPEC; | 
 |     } | 
 |  | 
 |     if (inet_pton(AF_INET6, strAddr.c_str(), reinterpret_cast<void*>(xfrmAddr))) { | 
 |         return AF_INET6; | 
 |     } else if (inet_pton(AF_INET, strAddr.c_str(), reinterpret_cast<void*>(xfrmAddr))) { | 
 |         return AF_INET; | 
 |     } else { | 
 |         return netdutils::statusFromErrno(EAFNOSUPPORT, "Invalid address family"); | 
 |     } | 
 | } | 
 |  | 
 | void fillXfrmNlaHdr(nlattr* hdr, uint16_t type, uint16_t len) { | 
 |     hdr->nla_type = type; | 
 |     hdr->nla_len = len; | 
 | } | 
 |  | 
 | void fillXfrmCurLifetimeDefaults(xfrm_lifetime_cur* cur) { | 
 |     memset(reinterpret_cast<char*>(cur), 0, sizeof(*cur)); | 
 | } | 
 | void fillXfrmLifetimeDefaults(xfrm_lifetime_cfg* cfg) { | 
 |     cfg->soft_byte_limit = XFRM_INF; | 
 |     cfg->hard_byte_limit = XFRM_INF; | 
 |     cfg->soft_packet_limit = XFRM_INF; | 
 |     cfg->hard_packet_limit = XFRM_INF; | 
 | } | 
 |  | 
 | /* | 
 |  * Allocate SPIs within an (inclusive) range of min-max. | 
 |  * returns 0 (INVALID_SPI) once the entire range has been parsed. | 
 |  */ | 
 | class RandomSpi { | 
 | public: | 
 |     RandomSpi(int min, int max) : mMin(min) { | 
 |         // Re-seeding should be safe because the seed itself is | 
 |         // sufficiently random and we don't need secure random | 
 |         std::mt19937 rnd = std::mt19937(std::random_device()()); | 
 |         mNext = std::uniform_int_distribution<>(1, INT_MAX)(rnd); | 
 |         mSize = max - min + 1; | 
 |         mCount = mSize; | 
 |     } | 
 |  | 
 |     uint32_t next() { | 
 |         if (!mCount) | 
 |             return 0; | 
 |         mCount--; | 
 |         return (mNext++ % mSize) + mMin; | 
 |     } | 
 |  | 
 | private: | 
 |     uint32_t mNext; | 
 |     uint32_t mSize; | 
 |     uint32_t mMin; | 
 |     uint32_t mCount; | 
 | }; | 
 |  | 
 | } // namespace | 
 |  | 
 | // | 
 | // Begin XfrmController Impl | 
 | // | 
 | // | 
 | XfrmController::XfrmController(void) {} | 
 |  | 
 | // Test-only constructor allowing override of XFRM Interface support checks | 
 | XfrmController::XfrmController(bool xfrmIntfSupport) { | 
 |     mIsXfrmIntfSupported = xfrmIntfSupport; | 
 | } | 
 |  | 
 | netdutils::Status XfrmController::Init() { | 
 |     RETURN_IF_NOT_OK(flushInterfaces()); | 
 |     mIsXfrmIntfSupported = isXfrmIntfSupported(); | 
 |  | 
 |     XfrmSocketImpl sock; | 
 |     RETURN_IF_NOT_OK(sock.open()); | 
 |     RETURN_IF_NOT_OK(flushSaDb(sock)); | 
 |     return flushPolicyDb(sock); | 
 | } | 
 |  | 
 | netdutils::Status XfrmController::flushInterfaces() { | 
 |     const auto& ifaces = InterfaceController::getIfaceNames(); | 
 |     RETURN_IF_NOT_OK(ifaces); | 
 |     const String8 ifPrefix8 = String8(INetd::IPSEC_INTERFACE_PREFIX().string()); | 
 |  | 
 |     for (const std::string& iface : ifaces.value()) { | 
 |         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())) { | 
 |             RETURN_IF_NOT_OK(ipSecRemoveTunnelInterface(iface)); | 
 |         } | 
 |     } | 
 |     return netdutils::status::ok; | 
 | } | 
 |  | 
 | netdutils::Status XfrmController::flushSaDb(const XfrmSocket& s) { | 
 |     struct xfrm_usersa_flush flushUserSa = {.proto = IPSEC_PROTO_ANY}; | 
 |  | 
 |     std::vector<iovec> iov = {{nullptr, 0}, // reserved for the eventual addition of a NLMSG_HDR | 
 |                               {&flushUserSa, sizeof(flushUserSa)}, // xfrm_usersa_flush structure | 
 |                               {kPadBytes, NLMSG_ALIGN(sizeof(flushUserSa)) - sizeof(flushUserSa)}}; | 
 |  | 
 |     return s.sendMessage(XFRM_MSG_FLUSHSA, NETLINK_REQUEST_FLAGS, 0, &iov); | 
 | } | 
 |  | 
 | netdutils::Status XfrmController::flushPolicyDb(const XfrmSocket& s) { | 
 |     std::vector<iovec> iov = {{nullptr, 0}}; // reserved for the eventual addition of a NLMSG_HDR | 
 |     return s.sendMessage(XFRM_MSG_FLUSHPOLICY, NETLINK_REQUEST_FLAGS, 0, &iov); | 
 | } | 
 |  | 
 | bool XfrmController::isXfrmIntfSupported() { | 
 |     const char* IPSEC_TEST_INTF_NAME = "ipsec_test"; | 
 |     const int32_t XFRM_TEST_IF_ID = 0xFFFF; | 
 |  | 
 |     bool errored = false; | 
 |     errored |= | 
 |             ipSecAddXfrmInterface(IPSEC_TEST_INTF_NAME, XFRM_TEST_IF_ID, NETLINK_ROUTE_CREATE_FLAGS) | 
 |                     .code(); | 
 |     errored |= ipSecRemoveTunnelInterface(IPSEC_TEST_INTF_NAME).code(); | 
 |     return !errored; | 
 | } | 
 |  | 
 | netdutils::Status XfrmController::ipSecSetEncapSocketOwner(int socketFd, int newUid, | 
 |                                                            uid_t callerUid) { | 
 |     ALOGD("XfrmController:%s, line=%d", __FUNCTION__, __LINE__); | 
 |  | 
 |     const int fd = socketFd; | 
 |     struct stat info; | 
 |     if (fstat(fd, &info)) { | 
 |         return netdutils::statusFromErrno(errno, "Failed to stat socket file descriptor"); | 
 |     } | 
 |     if (info.st_uid != callerUid) { | 
 |         return netdutils::statusFromErrno(EPERM, "fchown disabled for non-owner calls"); | 
 |     } | 
 |     if (S_ISSOCK(info.st_mode) == 0) { | 
 |         return netdutils::statusFromErrno(EINVAL, "File descriptor was not a socket"); | 
 |     } | 
 |  | 
 |     int optval; | 
 |     socklen_t optlen = sizeof(optval); | 
 |     netdutils::Status status = | 
 |             getSyscallInstance().getsockopt(Fd(fd), IPPROTO_UDP, UDP_ENCAP, &optval, &optlen); | 
 |     if (status != netdutils::status::ok) { | 
 |         return status; | 
 |     } | 
 |     if (optval != UDP_ENCAP_ESPINUDP && optval != UDP_ENCAP_ESPINUDP_NON_IKE) { | 
 |         return netdutils::statusFromErrno(EINVAL, "Socket did not have UDP-encap sockopt set"); | 
 |     } | 
 |     if (fchown(fd, newUid, -1)) { | 
 |         return netdutils::statusFromErrno(errno, "Failed to fchown socket file descriptor"); | 
 |     } | 
 |  | 
 |     return netdutils::status::ok; | 
 | } | 
 |  | 
 | netdutils::Status XfrmController::ipSecAllocateSpi(int32_t transformId, | 
 |                                                    const std::string& sourceAddress, | 
 |                                                    const std::string& destinationAddress, | 
 |                                                    int32_t inSpi, int32_t* outSpi) { | 
 |     ALOGD("XfrmController:%s, line=%d", __FUNCTION__, __LINE__); | 
 |     ALOGD("transformId=%d", transformId); | 
 |     ALOGD("sourceAddress=%s", sourceAddress.c_str()); | 
 |     ALOGD("destinationAddress=%s", destinationAddress.c_str()); | 
 |     ALOGD("inSpi=%0.8x", inSpi); | 
 |  | 
 |     XfrmSaInfo saInfo{}; | 
 |     netdutils::Status ret = fillXfrmCommonInfo(sourceAddress, destinationAddress, INVALID_SPI, 0, 0, | 
 |                                                transformId, 0, &saInfo); | 
 |     if (!isOk(ret)) { | 
 |         return ret; | 
 |     } | 
 |  | 
 |     XfrmSocketImpl sock; | 
 |     netdutils::Status socketStatus = sock.open(); | 
 |     if (!isOk(socketStatus)) { | 
 |         ALOGD("Sock open failed for XFRM, line=%d", __LINE__); | 
 |         return socketStatus; | 
 |     } | 
 |  | 
 |     int minSpi = RAND_SPI_MIN, maxSpi = RAND_SPI_MAX; | 
 |  | 
 |     if (inSpi) | 
 |         minSpi = maxSpi = inSpi; | 
 |  | 
 |     ret = allocateSpi(saInfo, minSpi, maxSpi, reinterpret_cast<uint32_t*>(outSpi), sock); | 
 |     if (!isOk(ret)) { | 
 |         // TODO: May want to return a new Status with a modified status string | 
 |         ALOGD("Failed to Allocate an SPI, line=%d", __LINE__); | 
 |         *outSpi = INVALID_SPI; | 
 |     } | 
 |  | 
 |     return ret; | 
 | } | 
 |  | 
 | netdutils::Status XfrmController::ipSecAddSecurityAssociation( | 
 |         int32_t transformId, int32_t mode, const std::string& sourceAddress, | 
 |         const std::string& destinationAddress, int32_t underlyingNetId, int32_t spi, | 
 |         int32_t markValue, int32_t markMask, const std::string& authAlgo, | 
 |         const std::vector<uint8_t>& authKey, int32_t authTruncBits, const std::string& cryptAlgo, | 
 |         const std::vector<uint8_t>& cryptKey, int32_t cryptTruncBits, const std::string& aeadAlgo, | 
 |         const std::vector<uint8_t>& aeadKey, int32_t aeadIcvBits, int32_t encapType, | 
 |         int32_t encapLocalPort, int32_t encapRemotePort, int32_t xfrmInterfaceId) { | 
 |     ALOGD("XfrmController::%s, line=%d", __FUNCTION__, __LINE__); | 
 |     ALOGD("transformId=%d", transformId); | 
 |     ALOGD("mode=%d", mode); | 
 |     ALOGD("sourceAddress=%s", sourceAddress.c_str()); | 
 |     ALOGD("destinationAddress=%s", destinationAddress.c_str()); | 
 |     ALOGD("underlyingNetworkId=%d", underlyingNetId); | 
 |     ALOGD("spi=%0.8x", spi); | 
 |     ALOGD("markValue=%x", markValue); | 
 |     ALOGD("markMask=%x", markMask); | 
 |     ALOGD("authAlgo=%s", authAlgo.c_str()); | 
 |     ALOGD("authTruncBits=%d", authTruncBits); | 
 |     ALOGD("cryptAlgo=%s", cryptAlgo.c_str()); | 
 |     ALOGD("cryptTruncBits=%d,", cryptTruncBits); | 
 |     ALOGD("aeadAlgo=%s", aeadAlgo.c_str()); | 
 |     ALOGD("aeadIcvBits=%d,", aeadIcvBits); | 
 |     ALOGD("encapType=%d", encapType); | 
 |     ALOGD("encapLocalPort=%d", encapLocalPort); | 
 |     ALOGD("encapRemotePort=%d", encapRemotePort); | 
 |     ALOGD("xfrmInterfaceId=%d", xfrmInterfaceId); | 
 |  | 
 |     XfrmSaInfo saInfo{}; | 
 |     netdutils::Status ret = fillXfrmCommonInfo(sourceAddress, destinationAddress, spi, markValue, | 
 |                                                markMask, transformId, xfrmInterfaceId, &saInfo); | 
 |     if (!isOk(ret)) { | 
 |         return ret; | 
 |     } | 
 |  | 
 |     saInfo.auth = XfrmAlgo{ | 
 |         .name = authAlgo, .key = authKey, .truncLenBits = static_cast<uint16_t>(authTruncBits)}; | 
 |  | 
 |     saInfo.crypt = XfrmAlgo{ | 
 |         .name = cryptAlgo, .key = cryptKey, .truncLenBits = static_cast<uint16_t>(cryptTruncBits)}; | 
 |  | 
 |     saInfo.aead = XfrmAlgo{ | 
 |         .name = aeadAlgo, .key = aeadKey, .truncLenBits = static_cast<uint16_t>(aeadIcvBits)}; | 
 |  | 
 |     switch (static_cast<XfrmMode>(mode)) { | 
 |         case XfrmMode::TRANSPORT: | 
 |         case XfrmMode::TUNNEL: | 
 |             saInfo.mode = static_cast<XfrmMode>(mode); | 
 |             break; | 
 |         default: | 
 |             return netdutils::statusFromErrno(EINVAL, "Invalid xfrm mode"); | 
 |     } | 
 |  | 
 |     XfrmSocketImpl sock; | 
 |     netdutils::Status socketStatus = sock.open(); | 
 |     if (!isOk(socketStatus)) { | 
 |         ALOGD("Sock open failed for XFRM, line=%d", __LINE__); | 
 |         return socketStatus; | 
 |     } | 
 |  | 
 |     switch (static_cast<XfrmEncapType>(encapType)) { | 
 |         case XfrmEncapType::ESPINUDP: | 
 |         case XfrmEncapType::ESPINUDP_NON_IKE: | 
 |             if (saInfo.addrFamily != AF_INET) { | 
 |                 return netdutils::statusFromErrno(EAFNOSUPPORT, "IPv6 encap not supported"); | 
 |             } | 
 |             // The ports are not used on input SAs, so this is OK to be wrong when | 
 |             // direction is ultimately input. | 
 |             saInfo.encap.srcPort = encapLocalPort; | 
 |             saInfo.encap.dstPort = encapRemotePort; | 
 |             [[fallthrough]]; | 
 |         case XfrmEncapType::NONE: | 
 |             saInfo.encap.type = static_cast<XfrmEncapType>(encapType); | 
 |             break; | 
 |         default: | 
 |             return netdutils::statusFromErrno(EINVAL, "Invalid encap type"); | 
 |     } | 
 |  | 
 |     saInfo.netId = underlyingNetId; | 
 |  | 
 |     ret = updateSecurityAssociation(saInfo, sock); | 
 |     if (!isOk(ret)) { | 
 |         ALOGD("Failed updating a Security Association, line=%d", __LINE__); | 
 |     } | 
 |  | 
 |     return ret; | 
 | } | 
 |  | 
 | netdutils::Status XfrmController::ipSecDeleteSecurityAssociation( | 
 |         int32_t transformId, const std::string& sourceAddress, | 
 |         const std::string& destinationAddress, int32_t spi, int32_t markValue, int32_t markMask, | 
 |         int32_t xfrmInterfaceId) { | 
 |     ALOGD("XfrmController:%s, line=%d", __FUNCTION__, __LINE__); | 
 |     ALOGD("transformId=%d", transformId); | 
 |     ALOGD("sourceAddress=%s", sourceAddress.c_str()); | 
 |     ALOGD("destinationAddress=%s", destinationAddress.c_str()); | 
 |     ALOGD("spi=%0.8x", spi); | 
 |     ALOGD("markValue=%x", markValue); | 
 |     ALOGD("markMask=%x", markMask); | 
 |     ALOGD("xfrmInterfaceId=%d", xfrmInterfaceId); | 
 |  | 
 |     XfrmSaInfo saInfo{}; | 
 |     netdutils::Status ret = fillXfrmCommonInfo(sourceAddress, destinationAddress, spi, markValue, | 
 |                                                markMask, transformId, xfrmInterfaceId, &saInfo); | 
 |     if (!isOk(ret)) { | 
 |         return ret; | 
 |     } | 
 |  | 
 |     XfrmSocketImpl sock; | 
 |     netdutils::Status socketStatus = sock.open(); | 
 |     if (!isOk(socketStatus)) { | 
 |         ALOGD("Sock open failed for XFRM, line=%d", __LINE__); | 
 |         return socketStatus; | 
 |     } | 
 |  | 
 |     ret = deleteSecurityAssociation(saInfo, sock); | 
 |     if (!isOk(ret)) { | 
 |         ALOGD("Failed to delete Security Association, line=%d", __LINE__); | 
 |     } | 
 |  | 
 |     return ret; | 
 | } | 
 |  | 
 | netdutils::Status XfrmController::fillXfrmCommonInfo(const std::string& sourceAddress, | 
 |                                                      const std::string& destinationAddress, | 
 |                                                      int32_t spi, int32_t markValue, | 
 |                                                      int32_t markMask, int32_t transformId, | 
 |                                                      int32_t xfrmInterfaceId, | 
 |                                                      XfrmCommonInfo* info) { | 
 |     // Use the addresses to determine the address family and do validation | 
 |     xfrm_address_t sourceXfrmAddr{}, destXfrmAddr{}; | 
 |     StatusOr<int> sourceFamily, destFamily; | 
 |     sourceFamily = convertToXfrmAddr(sourceAddress, &sourceXfrmAddr); | 
 |     destFamily = convertToXfrmAddr(destinationAddress, &destXfrmAddr); | 
 |     if (!isOk(sourceFamily) || !isOk(destFamily)) { | 
 |         return netdutils::statusFromErrno(EINVAL, "Invalid address " + sourceAddress + "/" + | 
 |                                                       destinationAddress); | 
 |     } | 
 |  | 
 |     if (destFamily.value() == AF_UNSPEC || | 
 |         (sourceFamily.value() != AF_UNSPEC && sourceFamily.value() != destFamily.value())) { | 
 |         ALOGD("Invalid or Mismatched Address Families, %d != %d, line=%d", sourceFamily.value(), | 
 |               destFamily.value(), __LINE__); | 
 |         return netdutils::statusFromErrno(EINVAL, "Invalid or mismatched address families"); | 
 |     } | 
 |  | 
 |     info->addrFamily = destFamily.value(); | 
 |  | 
 |     info->dstAddr = destXfrmAddr; | 
 |     info->srcAddr = sourceXfrmAddr; | 
 |  | 
 |     return fillXfrmCommonInfo(spi, markValue, markMask, transformId, xfrmInterfaceId, info); | 
 | } | 
 |  | 
 | netdutils::Status XfrmController::fillXfrmCommonInfo(int32_t spi, int32_t markValue, | 
 |                                                      int32_t markMask, int32_t transformId, | 
 |                                                      int32_t xfrmInterfaceId, | 
 |                                                      XfrmCommonInfo* info) { | 
 |     info->transformId = transformId; | 
 |     info->spi = htonl(spi); | 
 |  | 
 |     if (mIsXfrmIntfSupported) { | 
 |         info->xfrm_if_id = xfrmInterfaceId; | 
 |     } else { | 
 |         info->mark.v = markValue; | 
 |         info->mark.m = markMask; | 
 |     } | 
 |  | 
 |     return netdutils::status::ok; | 
 | } | 
 |  | 
 | netdutils::Status XfrmController::ipSecApplyTransportModeTransform( | 
 |         int socketFd, int32_t transformId, int32_t direction, const std::string& sourceAddress, | 
 |         const std::string& destinationAddress, int32_t spi) { | 
 |     ALOGD("XfrmController::%s, line=%d", __FUNCTION__, __LINE__); | 
 |     ALOGD("transformId=%d", transformId); | 
 |     ALOGD("direction=%d", direction); | 
 |     ALOGD("sourceAddress=%s", sourceAddress.c_str()); | 
 |     ALOGD("destinationAddress=%s", destinationAddress.c_str()); | 
 |     ALOGD("spi=%0.8x", spi); | 
 |  | 
 |     StatusOr<sockaddr_storage> ret = | 
 |             getSyscallInstance().getsockname<sockaddr_storage>(Fd(socketFd)); | 
 |     if (!isOk(ret)) { | 
 |         ALOGE("Failed to get socket info in %s", __FUNCTION__); | 
 |         return ret; | 
 |     } | 
 |     struct sockaddr_storage saddr = ret.value(); | 
 |  | 
 |     XfrmSpInfo spInfo{}; | 
 |     netdutils::Status status = fillXfrmCommonInfo(sourceAddress, destinationAddress, spi, 0, 0, | 
 |                                                   transformId, 0, &spInfo); | 
 |     if (!isOk(status)) { | 
 |         ALOGE("Couldn't build SA ID %s", __FUNCTION__); | 
 |         return status; | 
 |     } | 
 |  | 
 |     spInfo.selAddrFamily = spInfo.addrFamily; | 
 |  | 
 |     // Allow dual stack sockets. Dual stack sockets are guaranteed to never have an AF_INET source | 
 |     // address; the source address would instead be an IPv4-mapped address. Thus, disallow AF_INET | 
 |     // sockets with mismatched address families (All other cases are acceptable). | 
 |     if (saddr.ss_family == AF_INET && spInfo.addrFamily != AF_INET) { | 
 |         ALOGE("IPV4 socket address family(%d) should match IPV4 Transform " | 
 |               "address family(%d)!", | 
 |               saddr.ss_family, spInfo.addrFamily); | 
 |         return netdutils::statusFromErrno(EINVAL, "Mismatched address family"); | 
 |     } | 
 |  | 
 |     struct { | 
 |         xfrm_userpolicy_info info; | 
 |         xfrm_user_tmpl tmpl; | 
 |     } policy{}; | 
 |  | 
 |     fillUserSpInfo(spInfo, static_cast<XfrmDirection>(direction), &policy.info); | 
 |     fillUserTemplate(spInfo, &policy.tmpl); | 
 |  | 
 |     LOG_HEX("XfrmUserPolicy", reinterpret_cast<char*>(&policy), sizeof(policy)); | 
 |  | 
 |     int sockOpt, sockLayer; | 
 |     switch (saddr.ss_family) { | 
 |         case AF_INET: | 
 |             sockOpt = IP_XFRM_POLICY; | 
 |             sockLayer = SOL_IP; | 
 |             break; | 
 |         case AF_INET6: | 
 |             sockOpt = IPV6_XFRM_POLICY; | 
 |             sockLayer = SOL_IPV6; | 
 |             break; | 
 |         default: | 
 |             return netdutils::statusFromErrno(EAFNOSUPPORT, "Invalid address family"); | 
 |     } | 
 |  | 
 |     status = getSyscallInstance().setsockopt(Fd(socketFd), sockLayer, sockOpt, policy); | 
 |     if (!isOk(status)) { | 
 |         ALOGE("Error setting socket option for XFRM! (%s)", toString(status).c_str()); | 
 |     } | 
 |  | 
 |     return status; | 
 | } | 
 |  | 
 | netdutils::Status XfrmController::ipSecRemoveTransportModeTransform(int socketFd) { | 
 |     ALOGD("XfrmController::%s, line=%d", __FUNCTION__, __LINE__); | 
 |  | 
 |     StatusOr<sockaddr_storage> ret = | 
 |             getSyscallInstance().getsockname<sockaddr_storage>(Fd(socketFd)); | 
 |     if (!isOk(ret)) { | 
 |         ALOGE("Failed to get socket info in %s! (%s)", __FUNCTION__, toString(ret).c_str()); | 
 |         return ret; | 
 |     } | 
 |  | 
 |     int sockOpt, sockLayer; | 
 |     switch (ret.value().ss_family) { | 
 |         case AF_INET: | 
 |             sockOpt = IP_XFRM_POLICY; | 
 |             sockLayer = SOL_IP; | 
 |             break; | 
 |         case AF_INET6: | 
 |             sockOpt = IPV6_XFRM_POLICY; | 
 |             sockLayer = SOL_IPV6; | 
 |             break; | 
 |         default: | 
 |             return netdutils::statusFromErrno(EAFNOSUPPORT, "Invalid address family"); | 
 |     } | 
 |  | 
 |     // Kernel will delete the security policy on this socket for both direction | 
 |     // if optval is set to NULL and optlen is set to 0. | 
 |     netdutils::Status status = | 
 |             getSyscallInstance().setsockopt(Fd(socketFd), sockLayer, sockOpt, nullptr, 0); | 
 |     if (!isOk(status)) { | 
 |         ALOGE("Error removing socket option for XFRM! (%s)", toString(status).c_str()); | 
 |     } | 
 |  | 
 |     return status; | 
 | } | 
 |  | 
 | netdutils::Status XfrmController::ipSecAddSecurityPolicy( | 
 |         int32_t transformId, int32_t selAddrFamily, int32_t direction, | 
 |         const std::string& tmplSrcAddress, const std::string& tmplDstAddress, int32_t spi, | 
 |         int32_t markValue, int32_t markMask, int32_t xfrmInterfaceId) { | 
 |     return processSecurityPolicy(transformId, selAddrFamily, direction, tmplSrcAddress, | 
 |                                  tmplDstAddress, spi, markValue, markMask, xfrmInterfaceId, | 
 |                                  XFRM_MSG_NEWPOLICY); | 
 | } | 
 |  | 
 | netdutils::Status XfrmController::ipSecUpdateSecurityPolicy( | 
 |         int32_t transformId, int32_t selAddrFamily, int32_t direction, | 
 |         const std::string& tmplSrcAddress, const std::string& tmplDstAddress, int32_t spi, | 
 |         int32_t markValue, int32_t markMask, int32_t xfrmInterfaceId) { | 
 |     return processSecurityPolicy(transformId, selAddrFamily, direction, tmplSrcAddress, | 
 |                                  tmplDstAddress, spi, markValue, markMask, xfrmInterfaceId, | 
 |                                  XFRM_MSG_UPDPOLICY); | 
 | } | 
 |  | 
 | netdutils::Status XfrmController::ipSecDeleteSecurityPolicy(int32_t transformId, | 
 |                                                             int32_t selAddrFamily, | 
 |                                                             int32_t direction, int32_t markValue, | 
 |                                                             int32_t markMask, | 
 |                                                             int32_t xfrmInterfaceId) { | 
 |     return processSecurityPolicy(transformId, selAddrFamily, direction, "", "", 0, markValue, | 
 |                                  markMask, xfrmInterfaceId, XFRM_MSG_DELPOLICY); | 
 | } | 
 |  | 
 | netdutils::Status XfrmController::processSecurityPolicy( | 
 |         int32_t transformId, int32_t selAddrFamily, int32_t direction, | 
 |         const std::string& tmplSrcAddress, const std::string& tmplDstAddress, int32_t spi, | 
 |         int32_t markValue, int32_t markMask, int32_t xfrmInterfaceId, int32_t msgType) { | 
 |     ALOGD("XfrmController::%s, line=%d", __FUNCTION__, __LINE__); | 
 |     ALOGD("selAddrFamily=%s", selAddrFamily == AF_INET6 ? "AF_INET6" : "AF_INET"); | 
 |     ALOGD("transformId=%d", transformId); | 
 |     ALOGD("direction=%d", direction); | 
 |     ALOGD("tmplSrcAddress=%s", tmplSrcAddress.c_str()); | 
 |     ALOGD("tmplDstAddress=%s", tmplDstAddress.c_str()); | 
 |     ALOGD("spi=%0.8x", spi); | 
 |     ALOGD("markValue=%d", markValue); | 
 |     ALOGD("markMask=%d", markMask); | 
 |     ALOGD("msgType=%d", msgType); | 
 |     ALOGD("xfrmInterfaceId=%d", xfrmInterfaceId); | 
 |  | 
 |     XfrmSpInfo spInfo{}; | 
 |     spInfo.mode = XfrmMode::TUNNEL; | 
 |  | 
 |     XfrmSocketImpl sock; | 
 |     RETURN_IF_NOT_OK(sock.open()); | 
 |  | 
 |     // Set the correct address families. Tunnel mode policies use wildcard selectors, while | 
 |     // templates have addresses set. These may be different address families. This method is called | 
 |     // separately for IPv4 and IPv6 policies, and thus only need to map a single inner address | 
 |     // family to the outer address families. | 
 |     spInfo.selAddrFamily = selAddrFamily; | 
 |  | 
 |     if (msgType == XFRM_MSG_DELPOLICY) { | 
 |         RETURN_IF_NOT_OK(fillXfrmCommonInfo(spi, markValue, markMask, transformId, xfrmInterfaceId, | 
 |                                             &spInfo)); | 
 |  | 
 |         return deleteTunnelModeSecurityPolicy(spInfo, sock, static_cast<XfrmDirection>(direction)); | 
 |     } else { | 
 |         RETURN_IF_NOT_OK(fillXfrmCommonInfo(tmplSrcAddress, tmplDstAddress, spi, markValue, | 
 |                                             markMask, transformId, xfrmInterfaceId, &spInfo)); | 
 |  | 
 |         return updateTunnelModeSecurityPolicy(spInfo, sock, static_cast<XfrmDirection>(direction), | 
 |                                               msgType); | 
 |     } | 
 | } | 
 |  | 
 | void XfrmController::fillXfrmSelector(const int selAddrFamily, xfrm_selector* selector) { | 
 |     selector->family = selAddrFamily; | 
 |     selector->proto = AF_UNSPEC; // TODO: do we need to match the protocol? it's | 
 |                                  // possible via the socket | 
 | } | 
 |  | 
 | netdutils::Status XfrmController::updateSecurityAssociation(const XfrmSaInfo& record, | 
 |                                                             const XfrmSocket& sock) { | 
 |     xfrm_usersa_info usersa{}; | 
 |     nlattr_algo_crypt crypt{}; | 
 |     nlattr_algo_auth auth{}; | 
 |     nlattr_algo_aead aead{}; | 
 |     nlattr_xfrm_mark xfrmmark{}; | 
 |     nlattr_xfrm_output_mark xfrmoutputmark{}; | 
 |     nlattr_encap_tmpl encap{}; | 
 |     nlattr_xfrm_interface_id xfrm_if_id{}; | 
 |  | 
 |     enum { | 
 |         NLMSG_HDR, | 
 |         USERSA, | 
 |         USERSA_PAD, | 
 |         CRYPT, | 
 |         CRYPT_PAD, | 
 |         AUTH, | 
 |         AUTH_PAD, | 
 |         AEAD, | 
 |         AEAD_PAD, | 
 |         MARK, | 
 |         MARK_PAD, | 
 |         OUTPUT_MARK, | 
 |         OUTPUT_MARK_PAD, | 
 |         ENCAP, | 
 |         ENCAP_PAD, | 
 |         INTF_ID, | 
 |         INTF_ID_PAD, | 
 |     }; | 
 |  | 
 |     std::vector<iovec> iov = { | 
 |             {nullptr, 0},          // reserved for the eventual addition of a NLMSG_HDR | 
 |             {&usersa, 0},          // main usersa_info struct | 
 |             {kPadBytes, 0},        // up to NLMSG_ALIGNTO pad bytes of padding | 
 |             {&crypt, 0},           // adjust size if crypt algo is present | 
 |             {kPadBytes, 0},        // up to NLATTR_ALIGNTO pad bytes | 
 |             {&auth, 0},            // adjust size if auth algo is present | 
 |             {kPadBytes, 0},        // up to NLATTR_ALIGNTO pad bytes | 
 |             {&aead, 0},            // adjust size if aead algo is present | 
 |             {kPadBytes, 0},        // up to NLATTR_ALIGNTO pad bytes | 
 |             {&xfrmmark, 0},        // adjust size if xfrm mark is present | 
 |             {kPadBytes, 0},        // up to NLATTR_ALIGNTO pad bytes | 
 |             {&xfrmoutputmark, 0},  // adjust size if xfrm output mark is present | 
 |             {kPadBytes, 0},        // up to NLATTR_ALIGNTO pad bytes | 
 |             {&encap, 0},           // adjust size if encapsulating | 
 |             {kPadBytes, 0},        // up to NLATTR_ALIGNTO pad bytes | 
 |             {&xfrm_if_id, 0},      // adjust size if interface ID is present | 
 |             {kPadBytes, 0},        // up to NLATTR_ALIGNTO pad bytes | 
 |     }; | 
 |  | 
 |     if (!record.aead.name.empty() && (!record.auth.name.empty() || !record.crypt.name.empty())) { | 
 |         return netdutils::statusFromErrno(EINVAL, "Invalid xfrm algo selection; AEAD is mutually " | 
 |                                                   "exclusive with both Authentication and " | 
 |                                                   "Encryption"); | 
 |     } | 
 |  | 
 |     if (record.aead.key.size() > MAX_KEY_LENGTH || record.auth.key.size() > MAX_KEY_LENGTH || | 
 |         record.crypt.key.size() > MAX_KEY_LENGTH) { | 
 |         return netdutils::statusFromErrno(EINVAL, "Key length invalid; exceeds MAX_KEY_LENGTH"); | 
 |     } | 
 |  | 
 |     if (record.mode != XfrmMode::TUNNEL && | 
 |         (record.xfrm_if_id != 0 || record.netId != 0 || record.mark.v != 0 || record.mark.m != 0)) { | 
 |         return netdutils::statusFromErrno(EINVAL, | 
 |                                           "xfrm_if_id, mark and netid parameters invalid " | 
 |                                           "for non tunnel-mode transform"); | 
 |     } else if (record.mode == XfrmMode::TUNNEL && !mIsXfrmIntfSupported && record.xfrm_if_id != 0) { | 
 |         return netdutils::statusFromErrno(EINVAL, "xfrm_if_id set for VTI Security Association"); | 
 |     } | 
 |  | 
 |     int len; | 
 |     len = iov[USERSA].iov_len = fillUserSaInfo(record, &usersa); | 
 |     iov[USERSA_PAD].iov_len = NLMSG_ALIGN(len) - len; | 
 |  | 
 |     len = iov[CRYPT].iov_len = fillNlAttrXfrmAlgoEnc(record.crypt, &crypt); | 
 |     iov[CRYPT_PAD].iov_len = NLA_ALIGN(len) - len; | 
 |  | 
 |     len = iov[AUTH].iov_len = fillNlAttrXfrmAlgoAuth(record.auth, &auth); | 
 |     iov[AUTH_PAD].iov_len = NLA_ALIGN(len) - len; | 
 |  | 
 |     len = iov[AEAD].iov_len = fillNlAttrXfrmAlgoAead(record.aead, &aead); | 
 |     iov[AEAD_PAD].iov_len = NLA_ALIGN(len) - len; | 
 |  | 
 |     len = iov[MARK].iov_len = fillNlAttrXfrmMark(record, &xfrmmark); | 
 |     iov[MARK_PAD].iov_len = NLA_ALIGN(len) - len; | 
 |  | 
 |     len = iov[OUTPUT_MARK].iov_len = fillNlAttrXfrmOutputMark(record.netId, &xfrmoutputmark); | 
 |     iov[OUTPUT_MARK_PAD].iov_len = NLA_ALIGN(len) - len; | 
 |  | 
 |     len = iov[ENCAP].iov_len = fillNlAttrXfrmEncapTmpl(record, &encap); | 
 |     iov[ENCAP_PAD].iov_len = NLA_ALIGN(len) - len; | 
 |  | 
 |     len = iov[INTF_ID].iov_len = fillNlAttrXfrmIntfId(record.xfrm_if_id, &xfrm_if_id); | 
 |     iov[INTF_ID_PAD].iov_len = NLA_ALIGN(len) - len; | 
 |  | 
 |     return sock.sendMessage(XFRM_MSG_UPDSA, NETLINK_REQUEST_FLAGS, 0, &iov); | 
 | } | 
 |  | 
 | int XfrmController::fillNlAttrXfrmAlgoEnc(const XfrmAlgo& inAlgo, nlattr_algo_crypt* algo) { | 
 |     if (inAlgo.name.empty()) { // Do not fill anything if algorithm not provided | 
 |         return 0; | 
 |     } | 
 |  | 
 |     int len = NLA_HDRLEN + sizeof(xfrm_algo); | 
 |     // Kernel always changes last char to null terminator; no safety checks needed. | 
 |     strncpy(algo->crypt.alg_name, inAlgo.name.c_str(), sizeof(algo->crypt.alg_name)); | 
 |     algo->crypt.alg_key_len = inAlgo.key.size() * 8; // bits | 
 |     memcpy(algo->key, &inAlgo.key[0], inAlgo.key.size()); | 
 |     len += inAlgo.key.size(); | 
 |     fillXfrmNlaHdr(&algo->hdr, XFRMA_ALG_CRYPT, len); | 
 |     return len; | 
 | } | 
 |  | 
 | int XfrmController::fillNlAttrXfrmAlgoAuth(const XfrmAlgo& inAlgo, nlattr_algo_auth* algo) { | 
 |     if (inAlgo.name.empty()) { // Do not fill anything if algorithm not provided | 
 |         return 0; | 
 |     } | 
 |  | 
 |     int len = NLA_HDRLEN + sizeof(xfrm_algo_auth); | 
 |     // Kernel always changes last char to null terminator; no safety checks needed. | 
 |     strncpy(algo->auth.alg_name, inAlgo.name.c_str(), sizeof(algo->auth.alg_name)); | 
 |     algo->auth.alg_key_len = inAlgo.key.size() * 8; // bits | 
 |  | 
 |     // This is the extra field for ALG_AUTH_TRUNC | 
 |     algo->auth.alg_trunc_len = inAlgo.truncLenBits; | 
 |  | 
 |     memcpy(algo->key, &inAlgo.key[0], inAlgo.key.size()); | 
 |     len += inAlgo.key.size(); | 
 |  | 
 |     fillXfrmNlaHdr(&algo->hdr, XFRMA_ALG_AUTH_TRUNC, len); | 
 |     return len; | 
 | } | 
 |  | 
 | int XfrmController::fillNlAttrXfrmAlgoAead(const XfrmAlgo& inAlgo, nlattr_algo_aead* algo) { | 
 |     if (inAlgo.name.empty()) { // Do not fill anything if algorithm not provided | 
 |         return 0; | 
 |     } | 
 |  | 
 |     int len = NLA_HDRLEN + sizeof(xfrm_algo_aead); | 
 |     // Kernel always changes last char to null terminator; no safety checks needed. | 
 |     strncpy(algo->aead.alg_name, inAlgo.name.c_str(), sizeof(algo->aead.alg_name)); | 
 |     algo->aead.alg_key_len = inAlgo.key.size() * 8; // bits | 
 |  | 
 |     // This is the extra field for ALG_AEAD. ICV length is the same as truncation length | 
 |     // for any AEAD algorithm. | 
 |     algo->aead.alg_icv_len = inAlgo.truncLenBits; | 
 |  | 
 |     memcpy(algo->key, &inAlgo.key[0], inAlgo.key.size()); | 
 |     len += inAlgo.key.size(); | 
 |  | 
 |     fillXfrmNlaHdr(&algo->hdr, XFRMA_ALG_AEAD, len); | 
 |     return len; | 
 | } | 
 |  | 
 | int XfrmController::fillNlAttrXfrmEncapTmpl(const XfrmSaInfo& record, nlattr_encap_tmpl* tmpl) { | 
 |     if (record.encap.type == XfrmEncapType::NONE) { | 
 |         return 0; | 
 |     } | 
 |  | 
 |     int len = NLA_HDRLEN + sizeof(xfrm_encap_tmpl); | 
 |     tmpl->tmpl.encap_type = static_cast<uint16_t>(record.encap.type); | 
 |     tmpl->tmpl.encap_sport = htons(record.encap.srcPort); | 
 |     tmpl->tmpl.encap_dport = htons(record.encap.dstPort); | 
 |     fillXfrmNlaHdr(&tmpl->hdr, XFRMA_ENCAP, len); | 
 |     return len; | 
 | } | 
 |  | 
 | int XfrmController::fillUserSaInfo(const XfrmSaInfo& record, xfrm_usersa_info* usersa) { | 
 |     // Use AF_UNSPEC for all SAs. In transport mode, kernel picks selector family based on | 
 |     // usersa->family, while in tunnel mode, the XFRM_STATE_AF_UNSPEC flag allows dual-stack SAs. | 
 |     fillXfrmSelector(AF_UNSPEC, &usersa->sel); | 
 |  | 
 |     usersa->id.proto = IPPROTO_ESP; | 
 |     usersa->id.spi = record.spi; | 
 |     usersa->id.daddr = record.dstAddr; | 
 |  | 
 |     usersa->saddr = record.srcAddr; | 
 |  | 
 |     fillXfrmLifetimeDefaults(&usersa->lft); | 
 |     fillXfrmCurLifetimeDefaults(&usersa->curlft); | 
 |     memset(&usersa->stats, 0, sizeof(usersa->stats)); // leave stats zeroed out | 
 |     usersa->reqid = record.transformId; | 
 |     usersa->family = record.addrFamily; | 
 |     usersa->mode = static_cast<uint8_t>(record.mode); | 
 |     usersa->replay_window = REPLAY_WINDOW_SIZE; | 
 |  | 
 |     if (record.mode == XfrmMode::TRANSPORT) { | 
 |         usersa->flags = 0; // TODO: should we actually set flags, XFRM_SA_XFLAG_DONT_ENCAP_DSCP? | 
 |     } else { | 
 |         usersa->flags = XFRM_STATE_AF_UNSPEC; | 
 |     } | 
 |  | 
 |     return sizeof(*usersa); | 
 | } | 
 |  | 
 | int XfrmController::fillUserSaId(const XfrmCommonInfo& record, xfrm_usersa_id* said) { | 
 |     said->daddr = record.dstAddr; | 
 |     said->spi = record.spi; | 
 |     said->family = record.addrFamily; | 
 |     said->proto = IPPROTO_ESP; | 
 |  | 
 |     return sizeof(*said); | 
 | } | 
 |  | 
 | netdutils::Status XfrmController::deleteSecurityAssociation(const XfrmCommonInfo& record, | 
 |                                                             const XfrmSocket& sock) { | 
 |     xfrm_usersa_id said{}; | 
 |     nlattr_xfrm_mark xfrmmark{}; | 
 |     nlattr_xfrm_interface_id xfrm_if_id{}; | 
 |  | 
 |     enum { NLMSG_HDR, USERSAID, USERSAID_PAD, MARK, MARK_PAD, INTF_ID, INTF_ID_PAD }; | 
 |  | 
 |     std::vector<iovec> iov = { | 
 |             {nullptr, 0},      // reserved for the eventual addition of a NLMSG_HDR | 
 |             {&said, 0},        // main usersa_info struct | 
 |             {kPadBytes, 0},    // up to NLMSG_ALIGNTO pad bytes of padding | 
 |             {&xfrmmark, 0},    // adjust size if xfrm mark is present | 
 |             {kPadBytes, 0},    // up to NLATTR_ALIGNTO pad bytes | 
 |             {&xfrm_if_id, 0},  // adjust size if interface ID is present | 
 |             {kPadBytes, 0},    // up to NLATTR_ALIGNTO pad bytes | 
 |     }; | 
 |  | 
 |     int len; | 
 |     len = iov[USERSAID].iov_len = fillUserSaId(record, &said); | 
 |     iov[USERSAID_PAD].iov_len = NLMSG_ALIGN(len) - len; | 
 |  | 
 |     len = iov[MARK].iov_len = fillNlAttrXfrmMark(record, &xfrmmark); | 
 |     iov[MARK_PAD].iov_len = NLA_ALIGN(len) - len; | 
 |  | 
 |     len = iov[INTF_ID].iov_len = fillNlAttrXfrmIntfId(record.xfrm_if_id, &xfrm_if_id); | 
 |     iov[INTF_ID_PAD].iov_len = NLA_ALIGN(len) - len; | 
 |  | 
 |     return sock.sendMessage(XFRM_MSG_DELSA, NETLINK_REQUEST_FLAGS, 0, &iov); | 
 | } | 
 |  | 
 | netdutils::Status XfrmController::allocateSpi(const XfrmSaInfo& record, uint32_t minSpi, | 
 |                                               uint32_t maxSpi, uint32_t* outSpi, | 
 |                                               const XfrmSocket& sock) { | 
 |     xfrm_userspi_info spiInfo{}; | 
 |  | 
 |     enum { NLMSG_HDR, USERSAID, USERSAID_PAD }; | 
 |  | 
 |     std::vector<iovec> iov = { | 
 |         {nullptr, 0},      // reserved for the eventual addition of a NLMSG_HDR | 
 |         {&spiInfo, 0},  // main userspi_info struct | 
 |         {kPadBytes, 0}, // up to NLMSG_ALIGNTO pad bytes of padding | 
 |     }; | 
 |  | 
 |     int len; | 
 |     if (fillUserSaInfo(record, &spiInfo.info) == 0) { | 
 |         ALOGE("Failed to fill transport SA Info"); | 
 |     } | 
 |  | 
 |     len = iov[USERSAID].iov_len = sizeof(spiInfo); | 
 |     iov[USERSAID_PAD].iov_len = NLMSG_ALIGN(len) - len; | 
 |  | 
 |     RandomSpi spiGen = RandomSpi(minSpi, maxSpi); | 
 |     int spi; | 
 |     netdutils::Status ret; | 
 |     while ((spi = spiGen.next()) != INVALID_SPI) { | 
 |         spiInfo.min = spi; | 
 |         spiInfo.max = spi; | 
 |         ret = sock.sendMessage(XFRM_MSG_ALLOCSPI, NETLINK_REQUEST_FLAGS, 0, &iov); | 
 |  | 
 |         /* If the SPI is in use, we'll get ENOENT */ | 
 |         if (netdutils::equalToErrno(ret, ENOENT)) | 
 |             continue; | 
 |  | 
 |         if (isOk(ret)) { | 
 |             *outSpi = spi; | 
 |             ALOGD("Allocated an SPI: %x", *outSpi); | 
 |         } else { | 
 |             *outSpi = INVALID_SPI; | 
 |             ALOGE("SPI Allocation Failed with error %d", ret.code()); | 
 |         } | 
 |  | 
 |         return ret; | 
 |     } | 
 |  | 
 |     // Should always be -ENOENT if we get here | 
 |     return ret; | 
 | } | 
 |  | 
 | netdutils::Status XfrmController::updateTunnelModeSecurityPolicy(const XfrmSpInfo& record, | 
 |                                                                  const XfrmSocket& sock, | 
 |                                                                  XfrmDirection direction, | 
 |                                                                  uint16_t msgType) { | 
 |     xfrm_userpolicy_info userpolicy{}; | 
 |     nlattr_user_tmpl usertmpl{}; | 
 |     nlattr_xfrm_mark xfrmmark{}; | 
 |     nlattr_xfrm_interface_id xfrm_if_id{}; | 
 |  | 
 |     enum { | 
 |         NLMSG_HDR, | 
 |         USERPOLICY, | 
 |         USERPOLICY_PAD, | 
 |         USERTMPL, | 
 |         USERTMPL_PAD, | 
 |         MARK, | 
 |         MARK_PAD, | 
 |         INTF_ID, | 
 |         INTF_ID_PAD, | 
 |     }; | 
 |  | 
 |     std::vector<iovec> iov = { | 
 |             {nullptr, 0},      // reserved for the eventual addition of a NLMSG_HDR | 
 |             {&userpolicy, 0},  // main xfrm_userpolicy_info struct | 
 |             {kPadBytes, 0},    // up to NLMSG_ALIGNTO pad bytes of padding | 
 |             {&usertmpl, 0},    // adjust size if xfrm_user_tmpl struct is present | 
 |             {kPadBytes, 0},    // up to NLATTR_ALIGNTO pad bytes | 
 |             {&xfrmmark, 0},    // adjust size if xfrm mark is present | 
 |             {kPadBytes, 0},    // up to NLATTR_ALIGNTO pad bytes | 
 |             {&xfrm_if_id, 0},  // adjust size if interface ID is present | 
 |             {kPadBytes, 0},    // up to NLATTR_ALIGNTO pad bytes | 
 |     }; | 
 |  | 
 |     int len; | 
 |     len = iov[USERPOLICY].iov_len = fillUserSpInfo(record, direction, &userpolicy); | 
 |     iov[USERPOLICY_PAD].iov_len = NLMSG_ALIGN(len) - len; | 
 |  | 
 |     len = iov[USERTMPL].iov_len = fillNlAttrUserTemplate(record, &usertmpl); | 
 |     iov[USERTMPL_PAD].iov_len = NLA_ALIGN(len) - len; | 
 |  | 
 |     len = iov[MARK].iov_len = fillNlAttrXfrmMark(record, &xfrmmark); | 
 |     iov[MARK_PAD].iov_len = NLA_ALIGN(len) - len; | 
 |  | 
 |     len = iov[INTF_ID].iov_len = fillNlAttrXfrmIntfId(record.xfrm_if_id, &xfrm_if_id); | 
 |     iov[INTF_ID_PAD].iov_len = NLA_ALIGN(len) - len; | 
 |  | 
 |     return sock.sendMessage(msgType, NETLINK_REQUEST_FLAGS, 0, &iov); | 
 | } | 
 |  | 
 | netdutils::Status XfrmController::deleteTunnelModeSecurityPolicy(const XfrmSpInfo& record, | 
 |                                                                  const XfrmSocket& sock, | 
 |                                                                  XfrmDirection direction) { | 
 |     xfrm_userpolicy_id policyid{}; | 
 |     nlattr_xfrm_mark xfrmmark{}; | 
 |     nlattr_xfrm_interface_id xfrm_if_id{}; | 
 |  | 
 |     enum { | 
 |         NLMSG_HDR, | 
 |         USERPOLICYID, | 
 |         USERPOLICYID_PAD, | 
 |         MARK, | 
 |         MARK_PAD, | 
 |         INTF_ID, | 
 |         INTF_ID_PAD, | 
 |     }; | 
 |  | 
 |     std::vector<iovec> iov = { | 
 |             {nullptr, 0},      // reserved for the eventual addition of a NLMSG_HDR | 
 |             {&policyid, 0},    // main xfrm_userpolicy_id struct | 
 |             {kPadBytes, 0},    // up to NLMSG_ALIGNTO pad bytes of padding | 
 |             {&xfrmmark, 0},    // adjust size if xfrm mark is present | 
 |             {kPadBytes, 0},    // up to NLATTR_ALIGNTO pad bytes | 
 |             {&xfrm_if_id, 0},  // adjust size if interface ID is present | 
 |             {kPadBytes, 0},    // up to NLATTR_ALIGNTO pad bytes | 
 |     }; | 
 |  | 
 |     int len = iov[USERPOLICYID].iov_len = fillUserPolicyId(record, direction, &policyid); | 
 |     iov[USERPOLICYID_PAD].iov_len = NLMSG_ALIGN(len) - len; | 
 |  | 
 |     len = iov[MARK].iov_len = fillNlAttrXfrmMark(record, &xfrmmark); | 
 |     iov[MARK_PAD].iov_len = NLA_ALIGN(len) - len; | 
 |  | 
 |     len = iov[INTF_ID].iov_len = fillNlAttrXfrmIntfId(record.xfrm_if_id, &xfrm_if_id); | 
 |     iov[INTF_ID_PAD].iov_len = NLA_ALIGN(len) - len; | 
 |  | 
 |     return sock.sendMessage(XFRM_MSG_DELPOLICY, NETLINK_REQUEST_FLAGS, 0, &iov); | 
 | } | 
 |  | 
 | int XfrmController::fillUserSpInfo(const XfrmSpInfo& record, XfrmDirection direction, | 
 |                                    xfrm_userpolicy_info* usersp) { | 
 |     fillXfrmSelector(record.selAddrFamily, &usersp->sel); | 
 |     fillXfrmLifetimeDefaults(&usersp->lft); | 
 |     fillXfrmCurLifetimeDefaults(&usersp->curlft); | 
 |     /* if (index) index & 0x3 == dir -- must be true | 
 |      * xfrm_user.c:verify_newpolicy_info() */ | 
 |     usersp->index = 0; | 
 |     usersp->dir = static_cast<uint8_t>(direction); | 
 |     usersp->action = XFRM_POLICY_ALLOW; | 
 |     usersp->flags = XFRM_POLICY_LOCALOK; | 
 |     usersp->share = XFRM_SHARE_UNIQUE; | 
 |     return sizeof(*usersp); | 
 | } | 
 |  | 
 | int XfrmController::fillUserTemplate(const XfrmSpInfo& record, xfrm_user_tmpl* tmpl) { | 
 |     tmpl->id.daddr = record.dstAddr; | 
 |     tmpl->id.spi = record.spi; | 
 |     tmpl->id.proto = IPPROTO_ESP; | 
 |  | 
 |     tmpl->family = record.addrFamily; | 
 |     tmpl->saddr = record.srcAddr; | 
 |     tmpl->reqid = record.transformId; | 
 |     tmpl->mode = static_cast<uint8_t>(record.mode); | 
 |     tmpl->share = XFRM_SHARE_UNIQUE; | 
 |     tmpl->optional = 0; // if this is true, then a failed state lookup will be considered OK: | 
 |                         // http://lxr.free-electrons.com/source/net/xfrm/xfrm_policy.c#L1492 | 
 |     tmpl->aalgos = ALGO_MASK_AUTH_ALL;  // TODO: if there's a bitmask somewhere of | 
 |                                         // algos, we should find it and apply it. | 
 |                                         // I can't find one. | 
 |     tmpl->ealgos = ALGO_MASK_CRYPT_ALL; // TODO: if there's a bitmask somewhere... | 
 |     return sizeof(xfrm_user_tmpl*); | 
 | } | 
 |  | 
 | int XfrmController::fillNlAttrUserTemplate(const XfrmSpInfo& record, nlattr_user_tmpl* tmpl) { | 
 |     fillUserTemplate(record, &tmpl->tmpl); | 
 |  | 
 |     int len = NLA_HDRLEN + sizeof(xfrm_user_tmpl); | 
 |     fillXfrmNlaHdr(&tmpl->hdr, XFRMA_TMPL, len); | 
 |     return len; | 
 | } | 
 |  | 
 | int XfrmController::fillNlAttrXfrmMark(const XfrmCommonInfo& record, nlattr_xfrm_mark* mark) { | 
 |     // Do not set if we were not given a mark | 
 |     if (record.mark.v == 0 && record.mark.m == 0) { | 
 |         return 0; | 
 |     } | 
 |  | 
 |     mark->mark.v = record.mark.v; // set to 0 if it's not used | 
 |     mark->mark.m = record.mark.m; // set to 0 if it's not used | 
 |     int len = NLA_HDRLEN + sizeof(xfrm_mark); | 
 |     fillXfrmNlaHdr(&mark->hdr, XFRMA_MARK, len); | 
 |     return len; | 
 | } | 
 |  | 
 | // This function sets the output mark (or set-mark in newer kernels) to that of the underlying | 
 | // Network's netid. This allows outbound IPsec Tunnel mode packets to be correctly directed to a | 
 | // preselected underlying Network. Packet as marked as protected from VPNs and have a network | 
 | // explicitly selected to prevent interference or routing loops. Also set permission flag to | 
 | // PERMISSION_SYSTEM to ensure we can use background/restricted networks. Permission to use | 
 | // restricted networks is checked in IpSecService. | 
 | int XfrmController::fillNlAttrXfrmOutputMark(const __u32 underlyingNetId, | 
 |                                              nlattr_xfrm_output_mark* output_mark) { | 
 |     // Do not set if we were not given an output mark | 
 |     if (underlyingNetId == 0) { | 
 |         return 0; | 
 |     } | 
 |  | 
 |     Fwmark fwmark; | 
 |     fwmark.netId = underlyingNetId; | 
 |  | 
 |     // TODO: Rework this to more accurately follow the underlying network | 
 |     fwmark.permission = PERMISSION_SYSTEM; | 
 |     fwmark.explicitlySelected = true; | 
 |     fwmark.protectedFromVpn = true; | 
 |     output_mark->outputMark = fwmark.intValue; | 
 |  | 
 |     int len = NLA_HDRLEN + sizeof(__u32); | 
 |     fillXfrmNlaHdr(&output_mark->hdr, XFRMA_OUTPUT_MARK, len); | 
 |     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 | 
 |     fillXfrmSelector(record.selAddrFamily, &usersp->sel); | 
 |     usersp->dir = static_cast<uint8_t>(direction); | 
 |     return sizeof(*usersp); | 
 | } | 
 |  | 
 | netdutils::Status XfrmController::ipSecAddTunnelInterface(const std::string& deviceName, | 
 |                                                           const std::string& localAddress, | 
 |                                                           const std::string& remoteAddress, | 
 |                                                           int32_t ikey, int32_t okey, | 
 |                                                           int32_t interfaceId, bool isUpdate) { | 
 |     ALOGD("XfrmController::%s, line=%d", __FUNCTION__, __LINE__); | 
 |     ALOGD("deviceName=%s", deviceName.c_str()); | 
 |     ALOGD("localAddress=%s", localAddress.c_str()); | 
 |     ALOGD("remoteAddress=%s", remoteAddress.c_str()); | 
 |     ALOGD("ikey=%0.8x", ikey); | 
 |     ALOGD("okey=%0.8x", okey); | 
 |     ALOGD("interfaceId=%0.8x", interfaceId); | 
 |     ALOGD("isUpdate=%d", isUpdate); | 
 |  | 
 |     uint16_t flags = isUpdate ? NETLINK_REQUEST_FLAGS : NETLINK_ROUTE_CREATE_FLAGS; | 
 |  | 
 |     if (mIsXfrmIntfSupported) { | 
 |         return ipSecAddXfrmInterface(deviceName, interfaceId, flags); | 
 |     } else { | 
 |         return ipSecAddVirtualTunnelInterface(deviceName, localAddress, remoteAddress, ikey, okey, | 
 |                                               flags); | 
 |     } | 
 | } | 
 |  | 
 | netdutils::Status XfrmController::ipSecAddXfrmInterface(const std::string& deviceName, | 
 |                                                         int32_t interfaceId, uint16_t flags) { | 
 |     ALOGD("XfrmController::%s, line=%d", __FUNCTION__, __LINE__); | 
 |  | 
 |     if (deviceName.empty()) { | 
 |         return netdutils::statusFromErrno(EINVAL, "XFRM Interface deviceName empty"); | 
 |     } | 
 |  | 
 |     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, | 
 |                                          }, | 
 |                                  //   Always use LOOPBACK_IFINDEX, since we use output marks for | 
 |                                  //   route lookup instead. The use case of having a Network with | 
 |                                  //   loopback in it is unsupported in tunnel mode. | 
 |                                  .xfrmLink = static_cast<uint32_t>(LOOPBACK_IFINDEX), | 
 |  | 
 |                                  .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); | 
 |     RETURN_IF_NOT_OK(statusOrRemoteFam); | 
 |  | 
 |     uint8_t locAddr[sizeof(in6_addr)]; | 
 |     StatusOr<uint16_t> statusOrLocalFam = convertStringAddress(localAddress, locAddr); | 
 |     RETURN_IF_NOT_OK(statusOrLocalFam); | 
 |  | 
 |     if (statusOrLocalFam.value() != statusOrRemoteFam.value()) { | 
 |         return netdutils::statusFromErrno(EINVAL, "Local and remote address families do not match"); | 
 |     } | 
 |  | 
 |     uint16_t family = statusOrLocalFam.value(); | 
 |  | 
 |     ifinfomsg ifInfoMsg{}; | 
 |  | 
 |     // Construct IFLA_IFNAME | 
 |     nlattr iflaIfName; | 
 |     char iflaIfNameStrValue[deviceName.length() + 1]; | 
 |     size_t iflaIfNameLength = | 
 |         strlcpy(iflaIfNameStrValue, deviceName.c_str(), sizeof(iflaIfNameStrValue)); | 
 |     size_t iflaIfNamePad = fillNlAttr(IFLA_IFNAME, iflaIfNameLength, &iflaIfName); | 
 |  | 
 |     // Construct IFLA_INFO_KIND | 
 |     // Constants "vti6" and "vti" enable the kernel to call different code paths, | 
 |     // (ip_tunnel.c, ip6_tunnel), based on the family. | 
 |     const std::string infoKindValue = (family == AF_INET6) ? INFO_KIND_VTI6 : INFO_KIND_VTI; | 
 |     nlattr iflaIfInfoKind; | 
 |     char infoKindValueStrValue[infoKindValue.length() + 1]; | 
 |     size_t iflaIfInfoKindLength = | 
 |         strlcpy(infoKindValueStrValue, infoKindValue.c_str(), sizeof(infoKindValueStrValue)); | 
 |     size_t iflaIfInfoKindPad = fillNlAttr(IFLA_INFO_KIND, iflaIfInfoKindLength, &iflaIfInfoKind); | 
 |  | 
 |     // Construct IFLA_VTI_LOCAL | 
 |     nlattr iflaVtiLocal; | 
 |     uint8_t binaryLocalAddress[sizeof(in6_addr)]; | 
 |     size_t iflaVtiLocalPad = | 
 |         fillNlAttrIpAddress(IFLA_VTI_LOCAL, family, localAddress, &iflaVtiLocal, | 
 |                             netdutils::makeSlice(binaryLocalAddress)); | 
 |  | 
 |     // Construct IFLA_VTI_REMOTE | 
 |     nlattr iflaVtiRemote; | 
 |     uint8_t binaryRemoteAddress[sizeof(in6_addr)]; | 
 |     size_t iflaVtiRemotePad = | 
 |         fillNlAttrIpAddress(IFLA_VTI_REMOTE, family, remoteAddress, &iflaVtiRemote, | 
 |                             netdutils::makeSlice(binaryRemoteAddress)); | 
 |  | 
 |     // Construct IFLA_VTI_OKEY | 
 |     nlattr_payload_u32 iflaVtiIKey; | 
 |     size_t iflaVtiIKeyPad = fillNlAttrU32(IFLA_VTI_IKEY, htonl(ikey), &iflaVtiIKey); | 
 |  | 
 |     // Construct IFLA_VTI_IKEY | 
 |     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.hdr.nla_len + iflaVtiIKeyPad + | 
 |                                     iflaVtiOKey.hdr.nla_len + iflaVtiOKeyPad; | 
 |  | 
 |     // Construct IFLA_INFO_DATA | 
 |     nlattr iflaInfoData; | 
 |     size_t iflaInfoDataPad = fillNlAttr(IFLA_INFO_DATA, iflaInfoDataPayloadLength, &iflaInfoData); | 
 |  | 
 |     // Construct IFLA_LINKINFO | 
 |     nlattr iflaLinkInfo; | 
 |     size_t iflaLinkInfoPad = fillNlAttr(IFLA_LINKINFO, | 
 |                                         iflaInfoData.nla_len + iflaInfoDataPad + | 
 |                                             iflaIfInfoKind.nla_len + iflaIfInfoKindPad, | 
 |                                         &iflaLinkInfo); | 
 |  | 
 |     iovec iov[] = { | 
 |             {nullptr, 0}, | 
 |             {&ifInfoMsg, sizeof(ifInfoMsg)}, | 
 |  | 
 |             {&iflaIfName, sizeof(iflaIfName)}, | 
 |             {iflaIfNameStrValue, iflaIfNameLength}, | 
 |             {&PADDING_BUFFER, iflaIfNamePad}, | 
 |  | 
 |             {&iflaLinkInfo, sizeof(iflaLinkInfo)}, | 
 |  | 
 |             {&iflaIfInfoKind, sizeof(iflaIfInfoKind)}, | 
 |             {infoKindValueStrValue, iflaIfInfoKindLength}, | 
 |             {&PADDING_BUFFER, iflaIfInfoKindPad}, | 
 |  | 
 |             {&iflaInfoData, sizeof(iflaInfoData)}, | 
 |  | 
 |             {&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}, | 
 |  | 
 |             {&iflaVtiIKey, iflaVtiIKey.hdr.nla_len}, | 
 |             {&PADDING_BUFFER, iflaVtiIKeyPad}, | 
 |  | 
 |             {&iflaVtiOKey, iflaVtiOKey.hdr.nla_len}, | 
 |             {&PADDING_BUFFER, iflaVtiOKeyPad}, | 
 |  | 
 |             {&PADDING_BUFFER, iflaInfoDataPad}, | 
 |  | 
 |             {&PADDING_BUFFER, iflaLinkInfoPad}, | 
 |     }; | 
 |  | 
 |     // sendNetlinkRequest returns -errno | 
 |     int ret = -1 * sendNetlinkRequest(RTM_NEWLINK, flags, iov, ARRAY_SIZE(iov), nullptr); | 
 |     return netdutils::statusFromErrno(ret, "Failed to add/update virtual tunnel interface"); | 
 | } | 
 |  | 
 | 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 netdutils::statusFromErrno(EINVAL, "Required parameter not provided"); | 
 |     } | 
 |  | 
 |     uint8_t PADDING_BUFFER[] = {0, 0, 0, 0}; | 
 |  | 
 |     ifinfomsg ifInfoMsg{}; | 
 |     nlattr iflaIfName; | 
 |     char iflaIfNameStrValue[deviceName.length() + 1]; | 
 |     size_t iflaIfNameLength = | 
 |         strlcpy(iflaIfNameStrValue, deviceName.c_str(), sizeof(iflaIfNameStrValue)); | 
 |     size_t iflaIfNamePad = fillNlAttr(IFLA_IFNAME, iflaIfNameLength, &iflaIfName); | 
 |  | 
 |     iovec iov[] = { | 
 |         {nullptr, 0}, | 
 |         {&ifInfoMsg, sizeof(ifInfoMsg)}, | 
 |  | 
 |         {&iflaIfName, sizeof(iflaIfName)}, | 
 |         {iflaIfNameStrValue, iflaIfNameLength}, | 
 |         {&PADDING_BUFFER, iflaIfNamePad}, | 
 |     }; | 
 |  | 
 |     uint16_t action = RTM_DELLINK; | 
 |     uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; | 
 |  | 
 |     // sendNetlinkRequest returns -errno | 
 |     int ret = -1 * sendNetlinkRequest(action, flags, iov, ARRAY_SIZE(iov), nullptr); | 
 |     return netdutils::statusFromErrno(ret, "Error in deleting IpSec interface " + deviceName); | 
 | } | 
 |  | 
 | void XfrmController::dump(DumpWriter& dw) { | 
 |     ScopedIndent indentForXfrmController(dw); | 
 |     dw.println("XfrmController"); | 
 |  | 
 |     ScopedIndent indentForXfrmISupport(dw); | 
 |     dw.println("XFRM-I support: %d", mIsXfrmIntfSupported); | 
 | } | 
 |  | 
 | } // namespace net | 
 | } // namespace android |