Snap for 4565898 from 868971835f55f7770707c715f402484089d76dba to pi-release

Change-Id: Icdd17a98ff32b25e573748c6053113a99732973f
diff --git a/server/NetdNativeService.cpp b/server/NetdNativeService.cpp
index 65676db..31a87dc 100644
--- a/server/NetdNativeService.cpp
+++ b/server/NetdNativeService.cpp
@@ -487,8 +487,10 @@
         int32_t mode,
         const std::string& sourceAddress,
         const std::string& destinationAddress,
-        int64_t underlyingNetworkHandle,
+        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,
@@ -500,8 +502,8 @@
     ALOGD("ipSecAddSecurityAssociation()");
     return asBinderStatus(gCtls->xfrmCtrl.ipSecAddSecurityAssociation(
               transformId, mode, sourceAddress, destinationAddress,
-              underlyingNetworkHandle,
-              spi,
+              underlyingNetId,
+              spi, markValue, markMask,
               authAlgo, authKey, authTruncBits,
               cryptAlgo, cryptKey, cryptTruncBits,
               aeadAlgo, aeadKey, aeadIcvBits,
@@ -512,7 +514,9 @@
         int32_t transformId,
         const std::string& sourceAddress,
         const std::string& destinationAddress,
-        int32_t spi) {
+        int32_t spi,
+        int32_t markValue,
+        int32_t markMask) {
     // Necessary locking done in IpSecService and kernel
     ENFORCE_PERMISSION(CONNECTIVITY_INTERNAL);
     ALOGD("ipSecDeleteSecurityAssociation()");
@@ -520,7 +524,9 @@
                     transformId,
                     sourceAddress,
                     destinationAddress,
-                    spi));
+                    spi,
+                    markValue,
+                    markMask));
 }
 
 binder::Status NetdNativeService::ipSecApplyTransportModeTransform(
@@ -551,6 +557,122 @@
                     socket));
 }
 
+binder::Status NetdNativeService::ipSecAddSecurityPolicy(
+        int32_t transformId,
+        int32_t direction,
+        const std::string& sourceAddress,
+        const std::string& destinationAddress,
+        int32_t spi,
+        int32_t markValue,
+        int32_t markMask){
+    // Necessary locking done in IpSecService and kernel
+    ENFORCE_PERMISSION(NETWORK_STACK);
+    ALOGD("ipSecAddSecurityPolicy()");
+    return asBinderStatus(gCtls->xfrmCtrl.ipSecAddSecurityPolicy(
+                    transformId,
+                    direction,
+                    sourceAddress,
+                    destinationAddress,
+                    spi,
+                    markValue,
+                    markMask));
+}
+
+binder::Status NetdNativeService::ipSecUpdateSecurityPolicy(
+        int32_t transformId,
+        int32_t direction,
+        const std::string& sourceAddress,
+        const std::string& destinationAddress,
+        int32_t spi,
+        int32_t markValue,
+        int32_t markMask){
+    // Necessary locking done in IpSecService and kernel
+    ENFORCE_PERMISSION(NETWORK_STACK);
+    ALOGD("ipSecAddSecurityPolicy()");
+    return asBinderStatus(gCtls->xfrmCtrl.ipSecUpdateSecurityPolicy(
+                    transformId,
+                    direction,
+                    sourceAddress,
+                    destinationAddress,
+                    spi,
+                    markValue,
+                    markMask));
+}
+
+binder::Status NetdNativeService::ipSecDeleteSecurityPolicy(
+        int32_t transformId,
+        int32_t direction,
+        const std::string& sourceAddress,
+        const std::string& destinationAddress,
+        int32_t markValue,
+        int32_t markMask){
+    // Necessary locking done in IpSecService and kernel
+    ENFORCE_PERMISSION(NETWORK_STACK);
+    ALOGD("ipSecAddSecurityPolicy()");
+    return asBinderStatus(gCtls->xfrmCtrl.ipSecDeleteSecurityPolicy(
+                    transformId,
+                    direction,
+                    sourceAddress,
+                    destinationAddress,
+                    markValue,
+                    markMask));
+}
+
+binder::Status NetdNativeService::addVirtualTunnelInterface(
+        const std::string& deviceName,
+        const std::string& localAddress,
+        const std::string& remoteAddress,
+        int32_t iKey,
+        int32_t oKey) {
+    // Necessary locking done in IpSecService and kernel
+    ENFORCE_PERMISSION(NETWORK_STACK);
+    ALOGD("addVirtualTunnelInterface()");
+    int ret = gCtls->xfrmCtrl.addVirtualTunnelInterface(
+                             deviceName,
+                             localAddress,
+                             remoteAddress,
+                             iKey,
+                             oKey,
+                             false);
+
+    return (ret == 0) ? binder::Status::ok() :
+                        asBinderStatus(netdutils::statusFromErrno(
+                                       ret, "Error in creating virtual tunnel interface."));
+}
+
+binder::Status NetdNativeService::updateVirtualTunnelInterface(
+        const std::string& deviceName,
+        const std::string& localAddress,
+        const std::string& remoteAddress,
+        int32_t iKey,
+        int32_t oKey) {
+    // Necessary locking done in IpSecService and kernel
+    ENFORCE_PERMISSION(NETWORK_STACK);
+    ALOGD("updateVirtualTunnelInterface()");
+    int ret = gCtls->xfrmCtrl.addVirtualTunnelInterface(
+                             deviceName,
+                             localAddress,
+                             remoteAddress,
+                             iKey,
+                             oKey,
+                             true);
+
+    return (ret == 0) ? binder::Status::ok() :
+                        asBinderStatus(netdutils::statusFromErrno(
+                                       ret, "Error in updating virtual tunnel interface."));
+}
+
+binder::Status NetdNativeService::removeVirtualTunnelInterface(const std::string& deviceName) {
+    // Necessary locking done in IpSecService and kernel
+    ENFORCE_PERMISSION(NETWORK_STACK);
+    ALOGD("removeVirtualTunnelInterface()");
+    int ret = gCtls->xfrmCtrl.removeVirtualTunnelInterface(deviceName);
+
+    return (ret == 0) ? binder::Status::ok() :
+                        asBinderStatus(netdutils::statusFromErrno(
+                                       ret, "Error in removing virtual tunnel interface."));
+}
+
 binder::Status NetdNativeService::setIPv6AddrGenMode(const std::string& ifName,
                                                      int32_t mode) {
     ENFORCE_PERMISSION(NETWORK_STACK);
diff --git a/server/NetdNativeService.h b/server/NetdNativeService.h
index cf53c74..2f6cb80 100644
--- a/server/NetdNativeService.h
+++ b/server/NetdNativeService.h
@@ -113,8 +113,10 @@
             int32_t mode,
             const std::string& sourceAddress,
             const std::string& destinationAddress,
-            int64_t underlyingNetworkHandle,
+            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,
@@ -132,7 +134,9 @@
             int32_t transformId,
             const std::string& sourceAddress,
             const std::string& destinationAddress,
-            int32_t spi);
+            int32_t spi,
+            int32_t markValue,
+            int32_t markMask);
 
     binder::Status ipSecApplyTransportModeTransform(
             const android::base::unique_fd& socket,
@@ -145,7 +149,49 @@
     binder::Status ipSecRemoveTransportModeTransform(
             const android::base::unique_fd& socket);
 
+    binder::Status ipSecAddSecurityPolicy(
+            int32_t transformId,
+            int32_t direction,
+            const std::string& sourceAddress,
+            const std::string& destinationAddress,
+            int32_t spi,
+            int32_t markValue,
+            int32_t markMask);
+
+    binder::Status ipSecUpdateSecurityPolicy(
+            int32_t transformId,
+            int32_t direction,
+            const std::string& sourceAddress,
+            const std::string& destinationAddress,
+            int32_t spi,
+            int32_t markValue,
+            int32_t markMask);
+
+    binder::Status ipSecDeleteSecurityPolicy(
+            int32_t transformId,
+            int32_t direction,
+            const std::string& sourceAddress,
+            const std::string& destinationAddress,
+            int32_t markValue,
+            int32_t markMask);
+
     binder::Status trafficCheckBpfStatsEnable(bool* ret) override;
+
+    binder::Status addVirtualTunnelInterface(
+            const std::string& deviceName,
+            const std::string& localAddress,
+            const std::string& remoteAddress,
+            int32_t iKey,
+            int32_t oKey);
+
+    binder::Status updateVirtualTunnelInterface(
+            const std::string& deviceName,
+            const std::string& localAddress,
+            const std::string& remoteAddress,
+            int32_t iKey,
+            int32_t oKey);
+
+    binder::Status removeVirtualTunnelInterface(const std::string& deviceName);
 };
 
 }  // namespace net
diff --git a/server/SockDiag.cpp b/server/SockDiag.cpp
index d2bf49e..32fec6e 100644
--- a/server/SockDiag.cpp
+++ b/server/SockDiag.cpp
@@ -218,12 +218,14 @@
     NetlinkDumpCallback callback = [tcpInfoReader] (nlmsghdr *nlh) {
         Fwmark mark;
         struct tcp_info *tcpinfo = nullptr;
+        uint32_t tcpinfoLength = 0;
         inet_diag_msg *msg = reinterpret_cast<inet_diag_msg *>(NLMSG_DATA(nlh));
         uint32_t attr_len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*msg));
         struct rtattr *attr = reinterpret_cast<struct rtattr*>(msg+1);
         while (RTA_OK(attr, attr_len)) {
             if (attr->rta_type == INET_DIAG_INFO) {
                 tcpinfo = reinterpret_cast<struct tcp_info*>(RTA_DATA(attr));
+                tcpinfoLength = RTA_PAYLOAD(attr);
             }
             if (attr->rta_type == INET_DIAG_MARK) {
                 mark.intValue = *reinterpret_cast<uint32_t*>(RTA_DATA(attr));
@@ -231,7 +233,7 @@
             attr = RTA_NEXT(attr, attr_len);
         }
 
-        tcpInfoReader(mark, msg, tcpinfo);
+        tcpInfoReader(mark, msg, tcpinfo, tcpinfoLength);
     };
 
     return processNetlinkDump(mSock, callback);
diff --git a/server/SockDiag.h b/server/SockDiag.h
index 36b699d..a44c144 100644
--- a/server/SockDiag.h
+++ b/server/SockDiag.h
@@ -49,8 +49,11 @@
     typedef std::function<bool(uint8_t proto, const inet_diag_msg *)> DestroyFilter;
 
     // Callback function that is called once for every socket in the sockInfo dump.
-    typedef std::function<void(Fwmark mark, const struct inet_diag_msg *, const struct tcp_info *)>
-            TcpInfoReader;
+    // 'tcp_info_length' is the length in bytes of the INET_DIAG_INFO attribute read from Netlink.
+    // Knowing this length is necessary for handling struct tcp_info serialized from different
+    // kernel versions.
+    typedef std::function<void(Fwmark mark, const struct inet_diag_msg *, const struct tcp_info *,
+            uint32_t tcp_info_length)> TcpInfoReader;
 
     struct DestroyRequest {
         nlmsghdr nlh;
diff --git a/server/TcpSocketMonitor.cpp b/server/TcpSocketMonitor.cpp
index 2d53119..a6d71b7 100644
--- a/server/TcpSocketMonitor.cpp
+++ b/server/TcpSocketMonitor.cpp
@@ -18,13 +18,13 @@
 
 #include <iomanip>
 #include <thread>
+#include <vector>
 
 #include <arpa/inet.h>
 #include <netinet/tcp.h>
 #include <linux/tcp.h>
 
 #include "DumpWriter.h"
-#include "Fwmark.h"
 #include "SockDiag.h"
 #include "TcpSocketMonitor.h"
 
@@ -32,7 +32,6 @@
 namespace net {
 
 using std::chrono::duration_cast;
-using std::chrono::milliseconds;
 using std::chrono::steady_clock;
 
 constexpr const char* getTcpStateName(int t) {
@@ -64,8 +63,13 @@
     }
 }
 
+// Helper macro for reading fields into struct tcp_info and handling different struct tcp_info
+// versions in the kernel.
+#define tcpinfo_get(ptr, fld, len, zero) \
+    (((ptr) != nullptr && offsetof(struct tcp_info, fld) < len) ? (ptr)->fld : zero)
+
 static void tcpInfoPrint(DumpWriter &dw, Fwmark mark, const struct inet_diag_msg *sockinfo,
-                         const struct tcp_info *tcpinfo) {
+                         const struct tcp_info *tcpinfo, uint32_t tcpinfoLen) {
     char saddr[INET6_ADDRSTRLEN] = {};
     char daddr[INET6_ADDRSTRLEN] = {};
     inet_ntop(sockinfo->idiag_family, &(sockinfo->id.idiag_src), saddr, sizeof(saddr));
@@ -73,7 +77,7 @@
 
     dw.println(
             "netId=%d uid=%u mark=0x%x saddr=%s daddr=%s sport=%u dport=%u tcp_state=%s(%u) "
-            "rqueue=%u wqueue=%u  rtt=%gms var_rtt=%gms rcv_rtt=%gms unacked=%u snd_cwnd=%u",
+            "rtt=%gms sent=%u lost=%u",
             mark.netId,
             sockinfo->idiag_uid,
             mark.intValue,
@@ -82,13 +86,9 @@
             ntohs(sockinfo->id.idiag_sport),
             ntohs(sockinfo->id.idiag_dport),
             getTcpStateName(sockinfo->idiag_state), sockinfo->idiag_state,
-            sockinfo->idiag_rqueue,
-            sockinfo->idiag_wqueue,
-            tcpinfo != nullptr ? tcpinfo->tcpi_rtt/1000.0 : 0,
-            tcpinfo != nullptr ? tcpinfo->tcpi_rttvar/1000.0 : 0,
-            tcpinfo != nullptr ? tcpinfo->tcpi_rcv_rtt/1000.0 : 0,
-            tcpinfo != nullptr ? tcpinfo->tcpi_unacked : 0,
-            tcpinfo != nullptr ? tcpinfo->tcpi_snd_cwnd : 0);
+            tcpinfo_get(tcpinfo, tcpi_rtt, tcpinfoLen, 0) / 1000.0,
+            tcpinfo_get(tcpinfo, tcpi_data_segs_out, tcpinfoLen, 0),
+            tcpinfo_get(tcpinfo, tcpi_lost, tcpinfoLen, 0));
 }
 
 const String16 TcpSocketMonitor::DUMP_KEYWORD = String16("tcp_socket_info");
@@ -102,22 +102,48 @@
 
     const auto now = steady_clock::now();
     const auto d = duration_cast<milliseconds>(now - mLastPoll);
-    dw.println("last poll %lld ms ago", d.count());
+    dw.println("running=%d, suspended=%d, last poll %lld ms ago",
+            mIsRunning, mIsSuspended, d.count());
 
-    SockDiag sd;
-    if (!sd.open()) {
-        ALOGE("Error opening sock diag for dumping TCP socket info");
-        return;
+    if (!mNetworkStats.empty()) {
+        dw.blankline();
+        dw.println("Network stats:");
+        for (auto const& stats : mNetworkStats) {
+            if (stats.second.nSockets == 0) {
+                continue;
+            }
+            dw.println("netId=%d sent=%d lost=%d rttMs=%gms sentAckDiff=%gms",
+                    stats.first,
+                    stats.second.sent,
+                    stats.second.lost,
+                    stats.second.rttUs / 1000.0 / stats.second.nSockets,
+                    stats.second.sentAckDiffMs / stats.second.nSockets);
+        }
     }
 
-    const auto tcpInfoReader = [&dw](Fwmark mark, const struct inet_diag_msg *sockinfo,
-                                     const struct tcp_info *tcpinfo) {
-        tcpInfoPrint(dw, mark, sockinfo, tcpinfo);
-    };
+    if (!mSocketEntries.empty()) {
+        dw.blankline();
+        dw.println("Socket entries:");
+        for (auto const& stats : mSocketEntries) {
+            dw.println("netId=%u uid=%u cookie=%ld",
+                    stats.second.mark.netId, stats.second.uid, stats.first);
+        }
+    }
 
-    if (int ret = sd.getLiveTcpInfos(tcpInfoReader)) {
-        ALOGE("Failed to dump TCP socket info: %s", strerror(-ret));
-        return;
+    SockDiag sd;
+    if (sd.open()) {
+        dw.blankline();
+        dw.println("Current socket dump:");
+        const auto tcpInfoReader = [&dw](Fwmark mark, const struct inet_diag_msg *sockinfo,
+                                         const struct tcp_info *tcpinfo, uint32_t tcpinfoLen) {
+            tcpInfoPrint(dw, mark, sockinfo, tcpinfo, tcpinfoLen);
+        };
+
+        if (int ret = sd.getLiveTcpInfos(tcpInfoReader)) {
+            ALOGE("Failed to dump TCP socket info: %s", strerror(-ret));
+        }
+    } else {
+        ALOGE("Error opening sock diag for dumping TCP socket info");
     }
 
     dw.decIndent();
@@ -132,28 +158,29 @@
 }
 
 void TcpSocketMonitor::resumePolling() {
+    bool wasSuspended;
     {
         std::lock_guard<std::mutex> guard(mLock);
 
-        if (!mIsSuspended) {
-            return;
-        }
-
+        wasSuspended = mIsSuspended;
         mIsSuspended = false;
-
-        ALOGD("resuming tcpinfo polling with polling interval set to %lld ms",
-                mNextSleepDurationMs.count());
+        ALOGD("resuming tcpinfo polling (interval=%lldms)", mNextSleepDurationMs.count());
     }
 
-    mCv.notify_all();
+    if (wasSuspended) {
+        mCv.notify_all();
+    }
 }
 
 void TcpSocketMonitor::suspendPolling() {
     std::lock_guard<std::mutex> guard(mLock);
 
-    if (!mIsSuspended) {
-        ALOGD("suspending tcpinfo polling");
-        mIsSuspended = true;
+    bool wasSuspended = mIsSuspended;
+    mIsSuspended = true;
+    ALOGD("suspending tcpinfo polling");
+
+    if (!wasSuspended) {
+        mSocketEntries.clear();
     }
 }
 
@@ -164,28 +191,38 @@
         return;
     }
 
-    const auto now = steady_clock::now();
-
     SockDiag sd;
     if (!sd.open()) {
         ALOGE("Error opening sock diag for polling TCP socket info");
         return;
     }
 
-    const auto tcpInfoReader = [](Fwmark mark, const struct inet_diag_msg *sockinfo,
-                                const struct tcp_info *tcpinfo) {
-        if (sockinfo == nullptr || tcpinfo == nullptr || mark.intValue == 0) {
+    const auto now = steady_clock::now();
+    const auto tcpInfoReader = [this, now](Fwmark mark, const struct inet_diag_msg *sockinfo,
+                                           const struct tcp_info *tcpinfo,
+                                           uint32_t tcpinfoLen) NO_THREAD_SAFETY_ANALYSIS {
+        if (sockinfo == nullptr || tcpinfo == nullptr || tcpinfoLen == 0 || mark.intValue == 0) {
             return;
         }
-
-        // TODO: process socket stats
+        updateSocketStats(now, mark, sockinfo, tcpinfo, tcpinfoLen);
     };
 
+    // Reset mNetworkStats
+    mNetworkStats.clear();
+
     if (int ret = sd.getLiveTcpInfos(tcpInfoReader)) {
         ALOGE("Failed to poll TCP socket info: %s", strerror(-ret));
         return;
     }
 
+    // Remove any SocketEntry not updated
+    for (auto it = mSocketEntries.cbegin(); it != mSocketEntries.cend();) {
+        if (it->second.lastUpdate < now) {
+            it = mSocketEntries.erase(it);
+        } else {
+            it++;
+        }
+    }
     mLastPoll = now;
 }
 
@@ -211,12 +248,54 @@
     return mIsRunning;
 }
 
+void TcpSocketMonitor::updateSocketStats(time_point now, Fwmark mark,
+                                         const struct inet_diag_msg *sockinfo,
+                                         const struct tcp_info *tcpinfo,
+                                         uint32_t tcpinfoLen) NO_THREAD_SAFETY_ANALYSIS {
+    int32_t lastAck = tcpinfo_get(tcpinfo, tcpi_last_ack_recv, tcpinfoLen, 0);
+    int32_t lastSent = tcpinfo_get(tcpinfo, tcpi_last_data_sent, tcpinfoLen, 0);
+    TcpStats diff = {
+        .sent = tcpinfo_get(tcpinfo, tcpi_data_segs_out, tcpinfoLen, 0),
+        .lost = tcpinfo_get(tcpinfo, tcpi_lost, tcpinfoLen, 0),
+        .rttUs = tcpinfo_get(tcpinfo, tcpi_rtt, tcpinfoLen, 0),
+        .sentAckDiffMs = lastAck - lastSent,
+        .nSockets = 1,
+    };
+
+    {
+        // Update socket stats with the newest entry, computing the diff w.r.t the previous entry.
+        const uint64_t cookie = (static_cast<uint64_t>(sockinfo->id.idiag_cookie[0]) << 32)
+                | static_cast<uint64_t>(sockinfo->id.idiag_cookie[1]);
+        const SocketEntry previous = mSocketEntries[cookie];
+        mSocketEntries[cookie] = {
+            .sent = diff.sent,
+            .lost = diff.lost,
+            .lastUpdate = now,
+            .mark = mark,
+            .uid = sockinfo->idiag_uid,
+        };
+
+        diff.sent -= previous.sent;
+        diff.lost -= previous.lost;
+    }
+
+    {
+        // Aggregate the diff per network id.
+        auto& stats = mNetworkStats[mark.netId];
+        stats.sent += diff.sent;
+        stats.lost += diff.lost;
+        stats.rttUs += diff.rttUs;
+        stats.sentAckDiffMs += diff.sentAckDiffMs;
+        stats.nSockets += diff.nSockets;
+    }
+}
+
 TcpSocketMonitor::TcpSocketMonitor() {
     std::lock_guard<std::mutex> guard(mLock);
 
     mNextSleepDurationMs = kDefaultPollingInterval;
-    mIsSuspended = true;
     mIsRunning = true;
+    mIsSuspended = true;
     mPollingThread = std::thread([this] {
         while (isRunning()) {
             poll();
diff --git a/server/TcpSocketMonitor.h b/server/TcpSocketMonitor.h
index ec39a13..f408ff0 100644
--- a/server/TcpSocketMonitor.h
+++ b/server/TcpSocketMonitor.h
@@ -20,25 +20,62 @@
 #include <chrono>
 #include <condition_variable>
 #include <mutex>
+#include <unordered_map>
 
 #include <android-base/thread_annotations.h>
 #include "utils/String16.h"
 
+#include "Fwmark.h"
+
 namespace android {
 namespace net {
 
+using std::chrono::milliseconds;
+
 class DumpWriter;
 
 class TcpSocketMonitor {
   public:
+    using time_point = std::chrono::time_point<std::chrono::steady_clock>;
+
     static const String16 DUMP_KEYWORD;
-    static const std::chrono::milliseconds kDefaultPollingInterval;
+    static const milliseconds kDefaultPollingInterval;
+
+    // A subset of fields found in struct inet_diag_msg and struct tcp_info.
+    struct TcpStats {
+        // Number of packets sent. Tracks struct tcp_sock data_segs_out.
+        // Not available on 3.18 kernels.
+        uint32_t sent;
+        // Number of packets lost. Tracks struct tcp_sock lost_out.
+        uint32_t lost;
+        // Smoothed round trip time. Tracks struct tcp_sock srtt_us.
+        uint32_t rttUs;
+        // Milliseconds difference between the last packet sent and last ack received.
+        int32_t sentAckDiffMs;
+        // Number of socket stats aggregated in this TcpStats entry.
+        int32_t nSockets;
+    };
+
+    // Socket metadata used for computing TcpStats diff across sock_diag dumps.
+    struct SocketEntry {
+        // Number of packets sent. Tracks struct tcp_sock data_segs_out.
+        // Not available on 3.18 kernels.
+        uint32_t sent;
+        // Number of packets lost. Tracks struct tcp_sock lost_out.
+        uint32_t lost;
+        // Last update timestamp for that socket.
+        time_point lastUpdate;
+        // Socket mark.
+        Fwmark mark;
+        // The uid owning the socket.
+        uint32_t uid;
+    };
 
     TcpSocketMonitor();
     ~TcpSocketMonitor();
 
     void dump(DumpWriter& dw);
-    void setPollingInterval(std::chrono::milliseconds duration);
+    void setPollingInterval(milliseconds duration);
     void resumePolling();
     void suspendPolling();
 
@@ -46,21 +83,32 @@
     void poll();
     void waitForNextPoll();
     bool isRunning();
+    void updateSocketStats(time_point now, Fwmark mark, const struct inet_diag_msg *sockinfo,
+                           const struct tcp_info *tcpinfo, uint32_t tcpinfoLen) REQUIRES(mLock);
 
     // Lock guarding all reads and writes to member variables.
     std::mutex mLock;
     // Used by the polling thread for sleeping between poll operations.
     std::condition_variable mCv;
+    // The thread that polls sock_diag continously.
+    std::thread mPollingThread;
     // The duration of a sleep between polls. Can be updated by the instance owner for dynamically
     // adjusting the polling rate.
-    std::chrono::milliseconds mNextSleepDurationMs GUARDED_BY(mLock);
+    milliseconds mNextSleepDurationMs GUARDED_BY(mLock);
     // The time of the last successful poll operation.
-    std::chrono::time_point<std::chrono::steady_clock> mLastPoll GUARDED_BY(mLock);
+    time_point mLastPoll GUARDED_BY(mLock);
     // True if the polling thread should sleep until notified.
     bool mIsSuspended GUARDED_BY(mLock);
     // True while the polling thread should poll.
     bool mIsRunning GUARDED_BY(mLock);
-    std::thread mPollingThread;
+    // Map of SocketEntry structs keyed by socket cookie. This map tracks per-socket data needed for
+    // computing diffs between sock_diag dumps. Entries for closed sockets are continously cleaned
+    // after every dump operation based on timestamps of last updates.
+    std::unordered_map<uint64_t, SocketEntry> mSocketEntries GUARDED_BY(mLock);
+    // Map of TcpStats entries aggregated per network and keyed per network id.
+    // This map tracks per-network data for a single sock_diag dump and is cleared before every dump
+    // operation.
+    std::unordered_map<uint32_t, TcpStats> mNetworkStats GUARDED_BY(mLock);
 };
 
 }  // namespace net
diff --git a/server/XfrmController.cpp b/server/XfrmController.cpp
index 03f2fe3..cc2c305 100644
--- a/server/XfrmController.cpp
+++ b/server/XfrmController.cpp
@@ -158,6 +158,36 @@
     }
 }
 
+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, int32_t value, nlattr* nlAttr, uint32_t* u32Value) {
+    *u32Value = htonl(value);
+    return fillNlAttr(nlaType, sizeof((*u32Value)), nlAttr);
+}
+
+// returns the address family, placing the string in the provided buffer
+StatusOr<uint16_t> convertStringAddress(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(); }
 
@@ -385,7 +415,7 @@
 
     XfrmSaInfo saInfo{};
     netdutils::Status ret =
-        fillXfrmId(sourceAddress, destinationAddress, INVALID_SPI, transformId, &saInfo);
+        fillXfrmId(sourceAddress, destinationAddress, INVALID_SPI, 0, 0, transformId, &saInfo);
     if (!isOk(ret)) {
         return ret;
     }
@@ -414,18 +444,20 @@
 
 netdutils::Status XfrmController::ipSecAddSecurityAssociation(
     int32_t transformId, int32_t mode, const std::string& sourceAddress,
-    const std::string& destinationAddress, int64_t underlyingNetworkHandle, int32_t spi,
-    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) {
+    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) {
     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("underlyingNetworkHandle=%" PRIx64, underlyingNetworkHandle);
+    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());
@@ -437,8 +469,8 @@
     ALOGD("encapRemotePort=%d", encapRemotePort);
 
     XfrmSaInfo saInfo{};
-    netdutils::Status ret =
-        fillXfrmId(sourceAddress, destinationAddress, spi, transformId, &saInfo);
+    netdutils::Status ret = fillXfrmId(sourceAddress, destinationAddress, spi, markValue, markMask,
+                                       transformId, &saInfo);
     if (!isOk(ret)) {
         return ret;
     }
@@ -486,6 +518,8 @@
             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__);
@@ -494,18 +528,20 @@
     return ret;
 }
 
-netdutils::Status
-XfrmController::ipSecDeleteSecurityAssociation(int32_t transformId,
-                                               const std::string& sourceAddress,
-                                               const std::string& destinationAddress, int32_t spi) {
+netdutils::Status XfrmController::ipSecDeleteSecurityAssociation(
+    int32_t transformId, const std::string& sourceAddress, const std::string& destinationAddress,
+    int32_t spi, int32_t markValue, int32_t markMask) {
     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);
 
     XfrmId saId{};
-    netdutils::Status ret = fillXfrmId(sourceAddress, destinationAddress, spi, transformId, &saId);
+    netdutils::Status ret =
+        fillXfrmId(sourceAddress, destinationAddress, spi, markValue, markMask, transformId, &saId);
     if (!isOk(ret)) {
         return ret;
     }
@@ -527,10 +563,13 @@
 
 netdutils::Status XfrmController::fillXfrmId(const std::string& sourceAddress,
                                              const std::string& destinationAddress, int32_t spi,
+                                             int32_t markValue, int32_t markMask,
                                              int32_t transformId, XfrmId* xfrmId) {
     // Fill the straightforward fields first
     xfrmId->transformId = transformId;
     xfrmId->spi = htonl(spi);
+    xfrmId->mark.v = markValue;
+    xfrmId->mark.m = markMask;
 
     // Use the addresses to determine the address family and do validation
     xfrm_address_t sourceXfrmAddr{}, destXfrmAddr{};
@@ -575,7 +614,7 @@
 
     XfrmSaInfo saInfo{};
     netdutils::Status status =
-        fillXfrmId(sourceAddress, destinationAddress, spi, transformId, &saInfo);
+        fillXfrmId(sourceAddress, destinationAddress, spi, 0, 0, transformId, &saInfo);
     if (!isOk(status)) {
         ALOGE("Couldn't build SA ID %s", __FUNCTION__);
         return status;
@@ -655,11 +694,68 @@
     return status;
 }
 
-void XfrmController::fillTransportModeSelector(const XfrmSaInfo& record, xfrm_selector* selector) {
+netdutils::Status XfrmController::ipSecAddSecurityPolicy(int32_t transformId, int32_t direction,
+                                                         const std::string& localAddress,
+                                                         const std::string& remoteAddress,
+                                                         int32_t spi, int32_t markValue,
+                                                         int32_t markMask) {
+    return processSecurityPolicy(transformId, direction, localAddress, remoteAddress, spi,
+                                 markValue, markMask, XFRM_MSG_NEWPOLICY);
+}
+
+netdutils::Status XfrmController::ipSecUpdateSecurityPolicy(int32_t transformId, int32_t direction,
+                                                            const std::string& localAddress,
+                                                            const std::string& remoteAddress,
+                                                            int32_t spi, int32_t markValue,
+                                                            int32_t markMask) {
+    return processSecurityPolicy(transformId, direction, localAddress, remoteAddress, spi,
+                                 markValue, markMask, XFRM_MSG_UPDPOLICY);
+}
+
+netdutils::Status XfrmController::ipSecDeleteSecurityPolicy(int32_t transformId, int32_t direction,
+                                                            const std::string& localAddress,
+                                                            const std::string& remoteAddress,
+                                                            int32_t markValue, int32_t markMask) {
+    return processSecurityPolicy(transformId, direction, localAddress, remoteAddress, 0, markValue,
+                                 markMask, XFRM_MSG_DELPOLICY);
+}
+
+netdutils::Status XfrmController::processSecurityPolicy(int32_t transformId, int32_t direction,
+                                                        const std::string& localAddress,
+                                                        const std::string& remoteAddress,
+                                                        int32_t spi, int32_t markValue,
+                                                        int32_t markMask, int32_t msgType) {
+    ALOGD("XfrmController::%s, line=%d", __FUNCTION__, __LINE__);
+    ALOGD("transformId=%d", transformId);
+    ALOGD("direction=%d", direction);
+    ALOGD("localAddress=%s", localAddress.c_str());
+    ALOGD("remoteAddress=%s", remoteAddress.c_str());
+    ALOGD("spi=%0.8x", spi);
+    ALOGD("markValue=%d", markValue);
+    ALOGD("markMask=%d", markMask);
+    ALOGD("msgType=%d", msgType);
+
+    XfrmSaInfo saInfo{};
+    saInfo.mode = XfrmMode::TUNNEL;
+
+    XfrmSocketImpl sock;
+    RETURN_IF_NOT_OK(sock.open());
+
+    RETURN_IF_NOT_OK(
+        fillXfrmId(localAddress, remoteAddress, spi, markValue, markMask, transformId, &saInfo));
+
+    if (msgType == XFRM_MSG_DELPOLICY) {
+        return deleteTunnelModeSecurityPolicy(saInfo, sock, static_cast<XfrmDirection>(direction));
+    } else {
+        return updateTunnelModeSecurityPolicy(saInfo, sock, static_cast<XfrmDirection>(direction),
+                                              msgType);
+    }
+}
+
+void XfrmController::fillXfrmSelector(const XfrmSaInfo& record, xfrm_selector* selector) {
     selector->family = record.addrFamily;
-    selector->proto = AF_UNSPEC;      // TODO: do we need to match the protocol? it's
-                                      // possible via the socket
-    selector->ifindex = record.netId; // TODO : still need to sort this out
+    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,
@@ -668,6 +764,8 @@
     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{};
 
     enum {
@@ -680,22 +778,30 @@
         AUTH_PAD,
         AEAD,
         AEAD_PAD,
+        MARK,
+        MARK_PAD,
+        OUTPUT_MARK,
+        OUTPUT_MARK_PAD,
         ENCAP,
-        ENCAP_PAD
+        ENCAP_PAD,
     };
 
     std::vector<iovec> iov = {
-        {NULL, 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
-        {&encap, 0},    // adjust size if encapsulating
-        {kPadBytes, 0}, // up to NLATTR_ALIGNTO pad bytes
+        {NULL, 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
     };
 
     if (!record.aead.name.empty() && (!record.auth.name.empty() || !record.crypt.name.empty())) {
@@ -722,8 +828,15 @@
     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;
+
     return sock.sendMessage(XFRM_MSG_UPDSA, NETLINK_REQUEST_FLAGS, 0, &iov);
 }
 
@@ -797,7 +910,7 @@
 }
 
 int XfrmController::fillUserSaInfo(const XfrmSaInfo& record, xfrm_usersa_info* usersa) {
-    fillTransportModeSelector(record, &usersa->sel);
+    fillXfrmSelector(record, &usersa->sel);
 
     usersa->id.proto = IPPROTO_ESP;
     usersa->id.spi = record.spi;
@@ -834,19 +947,25 @@
 netdutils::Status XfrmController::deleteSecurityAssociation(const XfrmId& record,
                                                             const XfrmSocket& sock) {
     xfrm_usersa_id said{};
+    nlattr_xfrm_mark xfrmmark{};
 
-    enum { NLMSG_HDR, USERSAID, USERSAID_PAD };
+    enum { NLMSG_HDR, USERSAID, USERSAID_PAD, MARK, MARK_PAD };
 
     std::vector<iovec> iov = {
         {NULL, 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
     };
 
     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;
+
     return sock.sendMessage(XFRM_MSG_DELSA, NETLINK_REQUEST_FLAGS, 0, &iov);
 }
 
@@ -898,9 +1017,81 @@
     return ret;
 }
 
+netdutils::Status XfrmController::updateTunnelModeSecurityPolicy(const XfrmSaInfo& record,
+                                                                 const XfrmSocket& sock,
+                                                                 XfrmDirection direction,
+                                                                 uint16_t msgType) {
+    xfrm_userpolicy_info userpolicy{};
+    nlattr_user_tmpl usertmpl{};
+    nlattr_xfrm_mark xfrmmark{};
+
+    enum {
+        NLMSG_HDR,
+        USERPOLICY,
+        USERPOLICY_PAD,
+        USERTMPL,
+        USERTMPL_PAD,
+        MARK,
+        MARK_PAD,
+    };
+
+    std::vector<iovec> iov = {
+        {NULL, 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
+    };
+
+    int len;
+    len = iov[USERPOLICY].iov_len = fillTransportModeUserSpInfo(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;
+
+    return sock.sendMessage(msgType, NETLINK_REQUEST_FLAGS, 0, &iov);
+}
+
+netdutils::Status XfrmController::deleteTunnelModeSecurityPolicy(const XfrmSaInfo& record,
+                                                                 const XfrmSocket& sock,
+                                                                 XfrmDirection direction) {
+    xfrm_userpolicy_id policyid{};
+    nlattr_xfrm_mark xfrmmark{};
+
+    enum {
+        NLMSG_HDR,
+        USERPOLICYID,
+        USERPOLICYID_PAD,
+        MARK,
+        MARK_PAD,
+    };
+
+    std::vector<iovec> iov = {
+        {NULL, 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
+    };
+
+    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;
+
+    return sock.sendMessage(XFRM_MSG_DELPOLICY, NETLINK_REQUEST_FLAGS, 0, &iov);
+}
+
 int XfrmController::fillTransportModeUserSpInfo(const XfrmSaInfo& record, XfrmDirection direction,
                                                 xfrm_userpolicy_info* usersp) {
-    fillTransportModeSelector(record, &usersp->sel);
+    fillXfrmSelector(record, &usersp->sel);
     fillXfrmLifetimeDefaults(&usersp->lft);
     fillXfrmCurLifetimeDefaults(&usersp->curlft);
     /* if (index) index & 0x3 == dir -- must be true
@@ -929,7 +1120,231 @@
                                         // 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 0;
+    return sizeof(xfrm_user_tmpl*);
+}
+
+int XfrmController::fillNlAttrUserTemplate(const XfrmSaInfo& 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 XfrmId& record, nlattr_xfrm_mark* mark) {
+    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;
+}
+
+int XfrmController::fillNlAttrXfrmOutputMark(const __u32 output_mark_value,
+                                             nlattr_xfrm_output_mark* output_mark) {
+    // Do not set if we were not given an output mark
+    if (output_mark_value == 0) {
+        return 0;
+    }
+
+    output_mark->outputMark = output_mark_value;
+    int len = NLA_HDRLEN + sizeof(__u32);
+    fillXfrmNlaHdr(&output_mark->hdr, XFRMA_OUTPUT_MARK, len);
+    return len;
+}
+
+int XfrmController::fillUserPolicyId(const XfrmSaInfo& record, XfrmDirection direction,
+                                     xfrm_userpolicy_id* usersp) {
+    // For DELPOLICY, when index is absent, selector is needed to match the policy
+    fillXfrmSelector(record, &usersp->sel);
+    usersp->dir = static_cast<uint8_t>(direction);
+    return sizeof(*usersp);
+}
+
+int XfrmController::addVirtualTunnelInterface(const std::string& deviceName,
+                                              const std::string& localAddress,
+                                              const std::string& remoteAddress, int32_t ikey,
+                                              int32_t okey, bool isUpdate) {
+    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("isUpdate=%d", isUpdate);
+
+    if (deviceName.empty() || localAddress.empty() || remoteAddress.empty()) {
+        return EINVAL;
+    }
+
+    const char* INFO_KIND_VTI6 = "vti6";
+    const char* INFO_KIND_VTI = "vti";
+    uint8_t PADDING_BUFFER[] = {0, 0, 0, 0};
+
+    // Find address family.
+    uint8_t remAddr[sizeof(in6_addr)];
+
+    StatusOr<uint16_t> statusOrRemoteFam = convertStringAddress(remoteAddress, remAddr);
+    if (!isOk(statusOrRemoteFam)) {
+        return statusOrRemoteFam.status().code();
+    }
+
+    uint8_t locAddr[sizeof(in6_addr)];
+    StatusOr<uint16_t> statusOrLocalFam = convertStringAddress(localAddress, locAddr);
+    if (!isOk(statusOrLocalFam)) {
+        return statusOrLocalFam.status().code();
+    }
+
+    if (statusOrLocalFam.value() != statusOrRemoteFam.value()) {
+        return EINVAL;
+    }
+
+    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 iflaVtiIKey;
+    uint32_t iKeyValue;
+    size_t iflaVtiIKeyPad = fillNlAttrU32(IFLA_VTI_IKEY, ikey, &iflaVtiIKey, &iKeyValue);
+
+    // Construct IFLA_VTI_IKEY
+    nlattr iflaVtiOKey;
+    uint32_t oKeyValue;
+    size_t iflaVtiOKeyPad = fillNlAttrU32(IFLA_VTI_OKEY, okey, &iflaVtiOKey, &oKeyValue);
+
+    int iflaInfoDataPayloadLength = iflaVtiLocal.nla_len + iflaVtiLocalPad + iflaVtiRemote.nla_len +
+                                    iflaVtiRemotePad + iflaVtiIKey.nla_len + iflaVtiIKeyPad +
+                                    iflaVtiOKey.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[] = {
+        {NULL, 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, sizeof(iflaVtiIKey)},
+        {&iKeyValue, sizeof(iKeyValue)},
+        {&PADDING_BUFFER, iflaVtiIKeyPad},
+
+        {&iflaVtiOKey, sizeof(iflaVtiOKey)},
+        {&oKeyValue, sizeof(oKeyValue)},
+        {&PADDING_BUFFER, iflaVtiOKeyPad},
+
+        {&PADDING_BUFFER, iflaInfoDataPad},
+
+        {&PADDING_BUFFER, iflaLinkInfoPad},
+    };
+
+    uint16_t action = RTM_NEWLINK;
+    uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
+
+    if (!isUpdate) {
+        flags |= NLM_F_EXCL | NLM_F_CREATE;
+    }
+
+    int ret = sendNetlinkRequest(action, flags, iov, ARRAY_SIZE(iov), nullptr);
+    if (ret) {
+        ALOGE("Error in %s virtual tunnel interface. Error Code: %d",
+              isUpdate ? "updating" : "adding", ret);
+    }
+    return ret;
+}
+
+int XfrmController::removeVirtualTunnelInterface(const std::string& deviceName) {
+    ALOGD("XfrmController::%s, line=%d", __FUNCTION__, __LINE__);
+    ALOGD("deviceName=%s", deviceName.c_str());
+
+    if (deviceName.empty()) {
+        return EINVAL;
+    }
+
+    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[] = {
+        {NULL, 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;
+
+    int ret = sendNetlinkRequest(action, flags, iov, ARRAY_SIZE(iov), nullptr);
+    if (ret) {
+        ALOGE("Error in removing virtual tunnel interface %s. Error Code: %d", iflaIfNameStrValue,
+              ret);
+    }
+    return ret;
 }
 
 } // namespace net
diff --git a/server/XfrmController.h b/server/XfrmController.h
index ee42dec..298fae6 100644
--- a/server/XfrmController.h
+++ b/server/XfrmController.h
@@ -22,12 +22,15 @@
 #include <string>
 #include <utility> // for pair
 
+#include <linux/if_link.h>
+#include <linux/if_tunnel.h>
 #include <linux/netlink.h>
 #include <linux/udp.h>
 #include <linux/xfrm.h>
 #include <sysutils/SocketClient.h>
 
 #include "NetdConstants.h"
+#include "netdutils/Slice.h"
 #include "netdutils/Status.h"
 
 namespace android {
@@ -106,6 +109,7 @@
     int addrFamily;  // AF_INET or AF_INET6
     int transformId; // requestId
     int spi;
+    xfrm_mark mark;
 };
 
 struct XfrmSaInfo : XfrmId {
@@ -130,15 +134,18 @@
 
     netdutils::Status ipSecAddSecurityAssociation(
         int32_t transformId, int32_t mode, const std::string& sourceAddress,
-        const std::string& destinationAddress, int64_t underlyingNetworkHandle, int32_t spi,
-        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);
+        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);
 
     netdutils::Status ipSecDeleteSecurityAssociation(int32_t transformId,
-                                                     const std::string& localAddress,
-                                                     const std::string& remoteAddress, int32_t spi);
+                                                     const std::string& sourceAddress,
+                                                     const std::string& destinationAddress,
+                                                     int32_t spi, int32_t markValue,
+                                                     int32_t markMask);
 
     netdutils::Status ipSecApplyTransportModeTransform(const android::base::unique_fd& socket,
                                                        int32_t transformId, int32_t direction,
@@ -148,6 +155,28 @@
 
     netdutils::Status ipSecRemoveTransportModeTransform(const android::base::unique_fd& socket);
 
+    netdutils::Status ipSecAddSecurityPolicy(int32_t transformId, int32_t direction,
+                                             const std::string& sourceAddress,
+                                             const std::string& destinationAddress, int32_t spi,
+                                             int32_t markValue, int32_t markMask);
+
+    netdutils::Status ipSecUpdateSecurityPolicy(int32_t transformId, int32_t direction,
+                                                const std::string& sourceAddress,
+                                                const std::string& destinationAddress, int32_t spi,
+                                                int32_t markValue, int32_t markMask);
+
+    netdutils::Status ipSecDeleteSecurityPolicy(int32_t transformId, int32_t direction,
+                                                const std::string& sourceAddress,
+                                                const std::string& destinationAddress,
+                                                int32_t markValue, int32_t markMask);
+
+    int addVirtualTunnelInterface(const std::string& deviceName,
+                                  const std::string& localAddress,
+                                  const std::string& remoteAddress,
+                                  int32_t ikey, int32_t okey, bool isUpdate);
+
+    int removeVirtualTunnelInterface(const std::string& deviceName);
+
     // Some XFRM netlink attributes comprise a header, a struct, and some data
     // after the struct. We wrap all of those in one struct for easier
     // marshalling. The structs below must be ABI compatible with the kernel and
@@ -194,6 +223,20 @@
         xfrm_encap_tmpl tmpl;
     };
 
+    // Container for the content of an XFRMA_MARK netlink attribute.
+    // Exposed for testing
+    struct nlattr_xfrm_mark {
+        nlattr hdr;
+        xfrm_mark mark;
+    };
+
+    // Container for the content of an XFRMA_OUTPUT_MARK netlink attribute.
+    // Exposed for testing
+    struct nlattr_xfrm_output_mark {
+        nlattr hdr;
+        __u32 outputMark;
+    };
+
 private:
 /*
  * Below is a redefinition of the xfrm_usersa_info struct that is part
@@ -238,7 +281,8 @@
     // helper function for filling in the XfrmId (and XfrmSaInfo) structure
     static netdutils::Status fillXfrmId(const std::string& sourceAddress,
                                         const std::string& destinationAddress, int32_t spi,
-                                        int32_t transformId, XfrmId* xfrmId);
+                                        int32_t markValue, int32_t markMask, int32_t transformId,
+                                        XfrmId* xfrmId);
 
     // Top level functions for managing a Transport Mode Transform
     static netdutils::Status addTransportModeTransform(const XfrmSaInfo& record);
@@ -246,7 +290,7 @@
 
     // TODO(messagerefactor): FACTOR OUT ALL MESSAGE BUILDING CODE BELOW HERE
     // Shared between SA and SP
-    static void fillTransportModeSelector(const XfrmSaInfo& record, xfrm_selector* selector);
+    static void fillXfrmSelector(const XfrmSaInfo& record, xfrm_selector* selector);
 
     // Shared between Transport and Tunnel Mode
     static int fillNlAttrXfrmAlgoEnc(const XfrmAlgo& in_algo, nlattr_algo_crypt* algo);
@@ -264,12 +308,31 @@
                                                        const XfrmSocket& sock);
     static int fillUserSaId(const XfrmId& record, xfrm_usersa_id* said);
     static int fillUserTemplate(const XfrmSaInfo& record, xfrm_user_tmpl* tmpl);
+
     static int fillTransportModeUserSpInfo(const XfrmSaInfo& record, XfrmDirection direction,
                                            xfrm_userpolicy_info* usersp);
+    static int fillNlAttrUserTemplate(const XfrmSaInfo& record, nlattr_user_tmpl* tmpl);
+    static int fillUserPolicyId(const XfrmSaInfo& record, XfrmDirection direction,
+                                xfrm_userpolicy_id* policy_id);
+    static int fillNlAttrXfrmMark(const XfrmId& record, nlattr_xfrm_mark* mark);
+    static int fillNlAttrXfrmOutputMark(const __u32 output_mark_value,
+                                        nlattr_xfrm_output_mark* output_mark);
 
     static netdutils::Status allocateSpi(const XfrmSaInfo& record, uint32_t minSpi, uint32_t maxSpi,
                                          uint32_t* outSpi, const XfrmSocket& sock);
 
+    static netdutils::Status processSecurityPolicy(int32_t transformId, int32_t direction,
+                                                   const std::string& localAddress,
+                                                   const std::string& remoteAddress,
+                                                   int32_t spi, int32_t markValue,
+                                                   int32_t markMask, int32_t msgType);
+    static netdutils::Status updateTunnelModeSecurityPolicy(const XfrmSaInfo& record,
+                                                            const XfrmSocket& sock,
+                                                            XfrmDirection direction,
+                                                            uint16_t msgType);
+    static netdutils::Status deleteTunnelModeSecurityPolicy(const XfrmSaInfo& record,
+                                                            const XfrmSocket& sock,
+                                                            XfrmDirection direction);
     // END TODO(messagerefactor)
 };
 
diff --git a/server/XfrmControllerTest.cpp b/server/XfrmControllerTest.cpp
index 4c70fc6..5715a6b 100644
--- a/server/XfrmControllerTest.cpp
+++ b/server/XfrmControllerTest.cpp
@@ -102,6 +102,9 @@
 static constexpr int DROID_SPI = 0xD1201D;
 static constexpr size_t KEY_LENGTH = 32;
 static constexpr int NLMSG_DEFAULTSIZE = 8192;
+static constexpr uint32_t TEST_XFRM_OUTPUT_MARK = 0x512;
+static constexpr uint32_t TEST_XFRM_MARK = 0x123;
+static constexpr uint32_t TEST_XFRM_MASK = 0xFFFFFFFF;
 
 static constexpr char LOCALHOST_V4[] = "127.0.0.1";
 static constexpr char LOCALHOST_V6[] = "::1";
@@ -261,7 +264,7 @@
 }
 
 void testIpSecAddSecurityAssociation(int version, const MockSyscalls& mockSyscalls,
-                                     const XfrmMode& mode) {
+                                     const XfrmMode& mode, __u32 underlying_netid) {
     const int family = (version == 6) ? AF_INET6 : AF_INET;
     const std::string localAddr = (version == 6) ? LOCALHOST_V6 : LOCALHOST_V4;
     const std::string remoteAddr = (version == 6) ? TEST_ADDR_V6 : TEST_ADDR_V4;
@@ -277,7 +280,12 @@
     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(offsetof(XfrmController::nlattr_algo_auth, key) + KEY_LENGTH) +
+        NLA_ALIGN(sizeof(XfrmController::nlattr_xfrm_mark));
+
+    if (underlying_netid) {
+        expectedMsgLength += NLA_ALIGN(sizeof(XfrmController::nlattr_xfrm_output_mark));
+    }
 
     std::vector<uint8_t> nlMsgBuf;
     EXPECT_CALL(mockSyscalls, writev(_, _))
@@ -288,9 +296,10 @@
     XfrmController ctrl;
     Status res = ctrl.ipSecAddSecurityAssociation(
         1 /* resourceId */, static_cast<int>(mode), localAddr, remoteAddr,
-        0 /* underlying network */, DROID_SPI, "hmac(sha256)" /* auth algo */,
-        authKey, 128 /* auth trunc length */, "cbc(aes)" /* encryption algo */, cryptKey,
-        0 /* crypt trunc length? */, "" /* AEAD algo */, {}, 0,
+        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 */);
 
     EXPECT_TRUE(isOk(res)) << res;
@@ -324,7 +333,10 @@
     // 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) {
+    XfrmController::nlattr_xfrm_mark mark{};
+    XfrmController::nlattr_xfrm_output_mark outputmark{};
+    auto attrHandler = [&encryptAlgo, &authAlgo, &mark, &outputmark](const nlattr& attr,
+                                                                     const Slice& attr_payload) {
         Slice buf = attr_payload;
         if (attr.nla_type == XFRMA_ALG_CRYPT) {
             encryptAlgo.hdr = attr;
@@ -336,6 +348,12 @@
             netdutils::extract(buf, authAlgo.auth);
             buf = drop(buf, sizeof(xfrm_algo_auth));
             netdutils::extract(buf, authAlgo.key);
+        } else if (attr.nla_type == XFRMA_MARK) {
+            mark.hdr = attr;
+            netdutils::extract(buf, mark.mark);
+        } else if (attr.nla_type == XFRMA_OUTPUT_MARK) {
+            mark.hdr = attr;
+            netdutils::extract(buf, outputmark.outputMark);
         } else {
             FAIL() << "Unexpected nlattr type: " << attr.nla_type;
         }
@@ -347,16 +365,27 @@
                         reinterpret_cast<void*>(&encryptAlgo.key), KEY_LENGTH));
     EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(authKey.data()),
                         reinterpret_cast<void*>(&authAlgo.key), KEY_LENGTH));
+    EXPECT_EQ(TEST_XFRM_MARK, mark.mark.v);
+    EXPECT_EQ(TEST_XFRM_MASK, mark.mark.m);
+    if (underlying_netid) {
+        EXPECT_EQ(TEST_XFRM_OUTPUT_MARK, outputmark.outputMark);
+    }
 }
 
 TEST_P(XfrmControllerParameterizedTest, TestTransportModeIpSecAddSecurityAssociation) {
     const int version = GetParam();
-    testIpSecAddSecurityAssociation(version, mockSyscalls, XfrmMode::TRANSPORT);
+    testIpSecAddSecurityAssociation(version, mockSyscalls, XfrmMode::TRANSPORT, 0);
 }
 
 TEST_P(XfrmControllerParameterizedTest, TestTunnelModeIpSecAddSecurityAssociation) {
     const int version = GetParam();
-    testIpSecAddSecurityAssociation(version, mockSyscalls, XfrmMode::TUNNEL);
+    testIpSecAddSecurityAssociation(version, mockSyscalls, XfrmMode::TUNNEL, 0);
+}
+
+TEST_P(XfrmControllerParameterizedTest, TestTunnelModeIpSecAddSecurityAssociationWithOutputMark) {
+    const int version = GetParam();
+    testIpSecAddSecurityAssociation(version, mockSyscalls, XfrmMode::TUNNEL,
+                                    TEST_XFRM_OUTPUT_MARK);
 }
 
 TEST_F(XfrmControllerTest, TestIpSecAddSecurityAssociationIPv4Encap) {
@@ -371,7 +400,7 @@
     XfrmController ctrl;
     Status res = ctrl.ipSecAddSecurityAssociation(
         1, static_cast<int>(XfrmMode::TRANSPORT),
-        LOCALHOST_V6, TEST_ADDR_V6, 0, DROID_SPI, "hmac(sha256)", {}, 128, "cbc(aes)",
+        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);
 
     EXPECT_FALSE(isOk(res)) << "IPv6 UDP encap not rejected";
@@ -471,10 +500,11 @@
     const std::string remoteAddr = (version == 6) ? TEST_ADDR_V6 : TEST_ADDR_V4;
 
     NetlinkResponse response{};
-    response.hdr.nlmsg_type = XFRM_MSG_ALLOCSPI;
+    response.hdr.nlmsg_type = XFRM_MSG_DELSA;
     Slice responseSlice = netdutils::makeSlice(response);
 
-    size_t expectedMsgLength = NLMSG_HDRLEN + NLMSG_ALIGN(sizeof(xfrm_usersa_id));
+    size_t expectedMsgLength = NLMSG_HDRLEN + NLMSG_ALIGN(sizeof(xfrm_usersa_id)) +
+                               NLA_ALIGN(sizeof(XfrmController::nlattr_xfrm_mark));
 
     std::vector<uint8_t> nlMsgBuf;
     EXPECT_CALL(mockSyscalls, writev(_, _))
@@ -483,8 +513,8 @@
         .WillOnce(DoAll(SetArgSlice<1>(responseSlice), Return(responseSlice)));
 
     XfrmController ctrl;
-    Status res = ctrl.ipSecDeleteSecurityAssociation(
-        1 /* resourceId */, localAddr, remoteAddr, DROID_SPI);
+    Status res = ctrl.ipSecDeleteSecurityAssociation(1 /* resourceId */, localAddr, remoteAddr,
+                                                     DROID_SPI, TEST_XFRM_MARK, TEST_XFRM_MASK);
 
     EXPECT_TRUE(isOk(res)) << res;
     EXPECT_EQ(expectedMsgLength, nlMsgBuf.size());
@@ -499,5 +529,149 @@
     expectAddressEquals(family, remoteAddr, said.daddr);
 }
 
+TEST_P(XfrmControllerParameterizedTest, TestIpSecAddSecurityPolicy) {
+    const int version = GetParam();
+    const int family = (version == 6) ? AF_INET6 : AF_INET;
+    const std::string localAddr = (version == 6) ? LOCALHOST_V6 : LOCALHOST_V4;
+    const std::string remoteAddr = (version == 6) ? TEST_ADDR_V6 : TEST_ADDR_V4;
+
+    NetlinkResponse response{};
+    response.hdr.nlmsg_type = XFRM_MSG_NEWPOLICY;
+    Slice responseSlice = netdutils::makeSlice(response);
+
+    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));
+
+    std::vector<uint8_t> nlMsgBuf;
+    EXPECT_CALL(mockSyscalls, writev(_, _))
+        .WillOnce(DoAll(SaveFlattenedIovecs<1>(&nlMsgBuf), Return(expectedMsgLength)));
+    EXPECT_CALL(mockSyscalls, read(_, _))
+        .WillOnce(DoAll(SetArgSlice<1>(responseSlice), Return(responseSlice)));
+
+    XfrmController ctrl;
+    Status res = ctrl.ipSecAddSecurityPolicy(
+        1 /* resourceId */, static_cast<int>(XfrmDirection::OUT), localAddr, remoteAddr,
+        0 /* SPI */, TEST_XFRM_MARK, TEST_XFRM_MASK);
+
+    EXPECT_TRUE(isOk(res)) << res;
+    EXPECT_EQ(expectedMsgLength, nlMsgBuf.size());
+
+    Slice nlMsgSlice = netdutils::makeSlice(nlMsgBuf);
+    nlmsghdr hdr;
+    netdutils::extract(nlMsgSlice, hdr);
+    EXPECT_EQ(XFRM_MSG_NEWPOLICY, hdr.nlmsg_type);
+
+    nlMsgSlice = netdutils::drop(nlMsgSlice, NLMSG_HDRLEN);
+    xfrm_userpolicy_info userpolicy{};
+    netdutils::extract(nlMsgSlice, userpolicy);
+
+    EXPECT_EQ(static_cast<uint8_t>(XfrmDirection::OUT), userpolicy.dir);
+
+    // Drop the user policy info.
+    Slice attr_buf = drop(nlMsgSlice, NLA_ALIGN(sizeof(xfrm_userpolicy_info)));
+
+    // 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) {
+        Slice buf = attr_payload;
+        if (attr.nla_type == XFRMA_TMPL) {
+            usertmpl.hdr = attr;
+            netdutils::extract(buf, usertmpl.tmpl);
+        } else if (attr.nla_type == XFRMA_MARK) {
+            mark.hdr = attr;
+            netdutils::extract(buf, mark.mark);
+        } else {
+            FAIL() << "Unexpected nlattr type: " << attr.nla_type;
+        }
+    };
+    forEachNetlinkAttribute(attr_buf, attrHandler);
+
+    expectAddressEquals(family, remoteAddr, usertmpl.tmpl.id.daddr);
+    EXPECT_EQ(TEST_XFRM_MARK, mark.mark.v);
+    EXPECT_EQ(TEST_XFRM_MASK, mark.mark.m);
+
+}
+
+TEST_P(XfrmControllerParameterizedTest, TestIpSecUpdateSecurityPolicy) {
+    const int version = GetParam();
+    const std::string localAddr = (version == 6) ? LOCALHOST_V6 : LOCALHOST_V4;
+    const std::string remoteAddr = (version == 6) ? TEST_ADDR_V6 : TEST_ADDR_V4;
+
+    NetlinkResponse response{};
+    response.hdr.nlmsg_type = XFRM_MSG_UPDPOLICY;
+    Slice responseSlice = netdutils::makeSlice(response);
+
+    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));
+
+    std::vector<uint8_t> nlMsgBuf;
+    EXPECT_CALL(mockSyscalls, writev(_, _))
+        .WillOnce(DoAll(SaveFlattenedIovecs<1>(&nlMsgBuf), Return(expectedMsgLength)));
+    EXPECT_CALL(mockSyscalls, read(_, _))
+        .WillOnce(DoAll(SetArgSlice<1>(responseSlice), Return(responseSlice)));
+
+    XfrmController ctrl;
+    Status res = ctrl.ipSecUpdateSecurityPolicy(
+        1 /* resourceId */, static_cast<int>(XfrmDirection::OUT), localAddr, remoteAddr,
+        0 /* SPI */, 0 /* Mark */, 0 /* Mask */);
+
+    EXPECT_TRUE(isOk(res)) << res;
+    EXPECT_EQ(expectedMsgLength, nlMsgBuf.size());
+
+    Slice nlMsgSlice = netdutils::makeSlice(nlMsgBuf);
+    nlmsghdr hdr;
+    netdutils::extract(nlMsgSlice, hdr);
+    EXPECT_EQ(XFRM_MSG_UPDPOLICY, hdr.nlmsg_type);
+}
+
+TEST_P(XfrmControllerParameterizedTest, TestIpSecDeleteSecurityPolicy) {
+    const int version = GetParam();
+    const std::string localAddr = (version == 6) ? LOCALHOST_V6 : LOCALHOST_V4;
+    const std::string remoteAddr = (version == 6) ? TEST_ADDR_V6 : TEST_ADDR_V4;
+
+    NetlinkResponse response{};
+    response.hdr.nlmsg_type = XFRM_MSG_DELPOLICY;
+    Slice responseSlice = netdutils::makeSlice(response);
+
+    size_t expectedMsgLength = NLMSG_HDRLEN + NLMSG_ALIGN(sizeof(xfrm_userpolicy_id)) +
+                               NLMSG_ALIGN(sizeof(XfrmController::nlattr_xfrm_mark));
+
+    std::vector<uint8_t> nlMsgBuf;
+    EXPECT_CALL(mockSyscalls, writev(_, _))
+        .WillOnce(DoAll(SaveFlattenedIovecs<1>(&nlMsgBuf), Return(expectedMsgLength)));
+    EXPECT_CALL(mockSyscalls, read(_, _))
+        .WillOnce(DoAll(SetArgSlice<1>(responseSlice), Return(responseSlice)));
+
+    XfrmController ctrl;
+    Status res = ctrl.ipSecDeleteSecurityPolicy(
+        1 /* resourceId */, static_cast<int>(XfrmDirection::OUT), localAddr, remoteAddr,
+        TEST_XFRM_MARK, TEST_XFRM_MASK);
+
+    EXPECT_TRUE(isOk(res)) << res;
+    EXPECT_EQ(expectedMsgLength, nlMsgBuf.size());
+
+    Slice nlMsgSlice = netdutils::makeSlice(nlMsgBuf);
+    nlMsgSlice = netdutils::drop(nlMsgSlice, NLMSG_HDRLEN);
+
+    xfrm_userpolicy_id policyid{};
+    netdutils::extract(nlMsgSlice, policyid);
+
+    EXPECT_EQ(static_cast<uint8_t>(XfrmDirection::OUT), policyid.dir);
+
+    // 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);
+    EXPECT_EQ(TEST_XFRM_MARK, mark.mark.v);
+    EXPECT_EQ(TEST_XFRM_MASK, mark.mark.m);
+
+}
+
+// TODO: Add tests for VTIs, ensuring that we are sending the correct data over netlink.
+
 } // namespace net
 } // namespace android
diff --git a/server/binder/android/net/INetd.aidl b/server/binder/android/net/INetd.aidl
index c61c08a..5c9e8a4 100644
--- a/server/binder/android/net/INetd.aidl
+++ b/server/binder/android/net/INetd.aidl
@@ -345,8 +345,10 @@
     * @param mode either Transport or Tunnel mode
     * @param sourceAddress InetAddress as string for the sending endpoint
     * @param destinationAddress InetAddress as string for the receiving endpoint
-    * @param underlyingNetworkHandle the networkHandle of the network to which the SA is applied
+    * @param underlyingNetId the netId of the network to which the SA is applied
     * @param spi a 32-bit unique ID allocated to the user
+    * @param markValue a 32-bit unique ID chosen by the user
+    * @param markMask a 32-bit mask chosen by the user
     * @param authAlgo a string identifying the authentication algorithm to be used
     * @param authKey a byte array containing the authentication key
     * @param authTruncBits the truncation length of the MAC produced by the authentication algorithm
@@ -366,8 +368,10 @@
             int mode,
             in @utf8InCpp String sourceAddress,
             in @utf8InCpp String destinationAddress,
-            long underlyingNetworkHandle,
+            int underlyingNetId,
             int spi,
+            int markValue,
+            int markMask,
             in @utf8InCpp String authAlgo, in byte[] authKey, in int authTruncBits,
             in @utf8InCpp String cryptAlgo, in byte[] cryptKey, in int cryptTruncBits,
             in @utf8InCpp String aeadAlgo, in byte[] aeadKey, in int aeadIcvBits,
@@ -382,12 +386,16 @@
     * @param sourceAddress InetAddress as string for the sending endpoint
     * @param destinationAddress InetAddress as string for the receiving endpoint
     * @param spi a requested 32-bit unique ID allocated to the user
+    * @param markValue a 32-bit unique ID chosen by the user
+    * @param markMask a 32-bit mask chosen by the user
     */
     void ipSecDeleteSecurityAssociation(
             int transformId,
             in @utf8InCpp String sourceAddress,
             in @utf8InCpp String destinationAddress,
-            int spi);
+            int spi,
+            int markValue,
+            int markMask);
 
    /**
     * Apply a previously created SA to a specified socket, starting IPsec on that socket
@@ -417,6 +425,103 @@
             in FileDescriptor socket);
 
    /**
+    * Adds an IPsec global policy.
+    *
+    * @param transformId a unique identifier for allocated resources
+    * @param direction DIRECTION_IN or DIRECTION_OUT
+    * @param sourceAddress InetAddress as string for the sending endpoint
+    * @param destinationAddress InetAddress as string for the receiving endpoint
+    * @param spi a 32-bit unique ID allocated to the user
+    * @param markValue a 32-bit unique ID chosen by the user
+    * @param markMask a 32-bit mask chosen by the user
+    */
+    void ipSecAddSecurityPolicy(
+            int transformId,
+            int direction,
+            in @utf8InCpp String sourceAddress,
+            in @utf8InCpp String destinationAddress,
+            int spi,
+            int markValue,
+            int markMask);
+
+   /**
+    * Updates an IPsec global policy.
+    *
+    * @param transformId a unique identifier for allocated resources
+    * @param direction DIRECTION_IN or DIRECTION_OUT
+    * @param sourceAddress InetAddress as string for the sending endpoint
+    * @param destinationAddress InetAddress as string for the receiving endpoint
+    * @param spi a 32-bit unique ID allocated to the user
+    * @param markValue a 32-bit unique ID chosen by the user
+    * @param markMask a 32-bit mask chosen by the user
+    */
+    void ipSecUpdateSecurityPolicy(
+            int transformId,
+            int direction,
+            in @utf8InCpp String sourceAddress,
+            in @utf8InCpp String destinationAddress,
+            int spi,
+            int markValue,
+            int markMask);
+
+   /**
+    * Deletes an IPsec global policy.
+    *
+    * @param transformId a unique identifier for allocated resources
+    * @param direction DIRECTION_IN or DIRECTION_OUT
+    * @param sourceAddress InetAddress as string for the sending endpoint
+    * @param destinationAddress InetAddress as string for the receiving endpoint
+    * @param markValue a 32-bit unique ID chosen by the user
+    * @param markMask a 32-bit mask chosen by the user
+    */
+    void ipSecDeleteSecurityPolicy(
+            int transformId,
+            int direction,
+            in @utf8InCpp String sourceAddress,
+            in @utf8InCpp String destinationAddress,
+            int markValue,
+            int markMask);
+
+   /**
+    * Add a Virtual Tunnel Interface.
+    *
+    * @param devName a unique identifier that represents the name of the device
+    * @param localAddress InetAddress as string for the local endpoint
+    * @param remoteAddress InetAddress as string for the remote endpoint
+    * @param iKey, to match Policies and SAs for input packets.
+    * @param oKey, to match Policies and SAs for output packets.
+    */
+    void addVirtualTunnelInterface(
+            in @utf8InCpp String deviceName,
+            in @utf8InCpp String localAddress,
+            in @utf8InCpp String remoteAddress,
+            int iKey,
+            int oKey);
+
+   /**
+    * Update a Virtual Tunnel Interface.
+    *
+    * @param devName a unique identifier that represents the name of the device
+    * @param localAddress InetAddress as string for the local endpoint
+    * @param remoteAddress InetAddress as string for the remote endpoint
+    * @param iKey, to match Policies and SAs for input packets.
+    * @param oKey, to match Policies and SAs for output packets.
+    */
+    void updateVirtualTunnelInterface(
+            in @utf8InCpp String deviceName,
+            in @utf8InCpp String localAddress,
+            in @utf8InCpp String remoteAddress,
+            int iKey,
+            int oKey);
+
+   /**
+    * Removes a Virtual Tunnel Interface.
+    *
+    * @param devName a unique identifier that represents the name of the device
+    */
+    void removeVirtualTunnelInterface(in @utf8InCpp String deviceName);
+
+   /**
     * Request notification of wakeup packets arriving on an interface. Notifications will be
     * delivered to INetdEventListener.onWakeupEvent().
     *
diff --git a/tests/binder_test.cpp b/tests/binder_test.cpp
index fd2d0d5..6f87745 100644
--- a/tests/binder_test.cpp
+++ b/tests/binder_test.cpp
@@ -243,6 +243,40 @@
     EXPECT_EQ(false, ret);
 }
 
+TEST_F(BinderTest, TestVirtualTunnelInterface) {
+    static const struct TestData {
+        const std::string& family;
+        const std::string& deviceName;
+        const std::string& localAddress;
+        const std::string& remoteAddress;
+        int32_t iKey;
+        int32_t oKey;
+    } kTestData[] = {
+        { "IPV4", "test_vti", "127.0.0.1", "8.8.8.8", 0x1234 + 53, 0x1234 + 53 },
+        { "IPV6", "test_vti6", "::1", "2001:4860:4860::8888", 0x1234 + 50, 0x1234 + 50 },
+    };
+
+    for (unsigned int i = 0; i < arraysize(kTestData); i++) {
+        const auto &td = kTestData[i];
+
+        binder::Status status;
+
+        // Create Virtual Tunnel Interface.
+        status = mNetd->addVirtualTunnelInterface(td.deviceName, td.localAddress,
+                                                  td.remoteAddress, td.iKey, td.oKey);
+        EXPECT_TRUE(status.isOk()) << td.family << status.exceptionMessage();
+
+        // Update Virtual Tunnel Interface.
+        status = mNetd->updateVirtualTunnelInterface(td.deviceName, td.localAddress,
+                                                     td.remoteAddress, td.iKey, td.oKey);
+        EXPECT_TRUE(status.isOk()) << td.family << status.exceptionMessage();
+
+        // Remove Virtual Tunnel Interface.
+        status = mNetd->removeVirtualTunnelInterface(td.deviceName);
+        EXPECT_TRUE(status.isOk()) << td.family << status.exceptionMessage();
+    }
+}
+
 static int bandwidthDataSaverEnabled(const char *binary) {
     std::vector<std::string> lines = listIptablesRule(binary, "bw_data_saver");