TCP socket monitoring: minimum SockDiag primitives

This patch adds a getSocketsInfo() method on the SockDiag class for
dumping the struct tcp_info of all IPv4 and IPv6 sockets on the system,
using the sock_diag netlink interface.

Bug: 64147860
Test: - manual tests usint dumpsys command from follow-up patch
      - TODO: netd unit tests
Change-Id: I3ad1726b4fde005c7b9506af96ed7bd79f527316
diff --git a/server/SockDiag.cpp b/server/SockDiag.cpp
index 99e5bac..2f55e90 100644
--- a/server/SockDiag.cpp
+++ b/server/SockDiag.cpp
@@ -93,7 +93,7 @@
     return true;
 }
 
-int SockDiag::sendDumpRequest(uint8_t proto, uint8_t family, uint32_t states,
+int SockDiag::sendDumpRequest(uint8_t proto, uint8_t family, uint8_t extensions, uint32_t states,
                               iovec *iov, int iovcnt) {
     struct {
         nlmsghdr nlh;
@@ -106,6 +106,7 @@
         .req = {
             .sdiag_family = family,
             .sdiag_protocol = proto,
+            .idiag_ext = extensions,
             .idiag_states = states,
         },
     };
@@ -129,7 +130,7 @@
     iovec iov[] = {
         { nullptr, 0 },
     };
-    return sendDumpRequest(proto, family, states, iov, ARRAY_SIZE(iov));
+    return sendDumpRequest(proto, family, 0, states, iov, ARRAY_SIZE(iov));
 }
 
 int SockDiag::sendDumpRequest(uint8_t proto, uint8_t family, const char *addrstr) {
@@ -200,7 +201,7 @@
     };
 
     uint32_t states = ~(1 << TCP_TIME_WAIT);
-    return sendDumpRequest(proto, family, states, iov, ARRAY_SIZE(iov));
+    return sendDumpRequest(proto, family, 0, states, iov, ARRAY_SIZE(iov));
 }
 
 int SockDiag::readDiagMsg(uint8_t proto, const SockDiag::DestroyFilter& shouldDestroy) {
@@ -214,6 +215,26 @@
     return processNetlinkDump(mSock, callback);
 }
 
+int SockDiag::readDiagMsgWithTcpInfo(const TcpInfoReader& tcpInfoReader) {
+    NetlinkDumpCallback callback = [tcpInfoReader] (nlmsghdr *nlh) {
+        struct tcp_info *tcpinfo = nullptr;
+        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));
+                break;
+            }
+            attr = RTA_NEXT(attr, attr_len);
+        }
+
+        tcpInfoReader(msg, tcpinfo);
+    };
+
+    return processNetlinkDump(mSock, callback);
+}
+
 // Determines whether a socket is a loopback socket. Does not check socket state.
 bool SockDiag::isLoopbackSocket(const inet_diag_msg *msg) {
     switch (msg->idiag_family) {
@@ -302,12 +323,12 @@
 
 int SockDiag::destroyLiveSockets(DestroyFilter destroyFilter, const char *what,
                                  iovec *iov, int iovcnt) {
-    int proto = IPPROTO_TCP;
+    const int proto = IPPROTO_TCP;
+    const uint32_t states = (1 << TCP_ESTABLISHED) | (1 << TCP_SYN_SENT) | (1 << TCP_SYN_RECV);
 
     for (const int family : {AF_INET, AF_INET6}) {
         const char *familyName = (family == AF_INET) ? "IPv4" : "IPv6";
-        uint32_t states = (1 << TCP_ESTABLISHED) | (1 << TCP_SYN_SENT) | (1 << TCP_SYN_RECV);
-        if (int ret = sendDumpRequest(proto, family, states, iov, iovcnt)) {
+        if (int ret = sendDumpRequest(proto, family, 0, states, iov, iovcnt)) {
             ALOGE("Failed to dump %s sockets for %s: %s", familyName, what, strerror(-ret));
             return ret;
         }
@@ -320,6 +341,30 @@
     return 0;
 }
 
+int SockDiag::getLiveTcpInfos(const TcpInfoReader& tcpInfoReader) {
+    const int proto = IPPROTO_TCP;
+    const uint32_t states = (1 << TCP_ESTABLISHED) | (1 << TCP_SYN_SENT) | (1 << TCP_SYN_RECV);
+    const uint8_t extensions = (1 << INET_DIAG_MEMINFO); // flag for dumping struct tcp_info.
+
+    iovec iov[] = {
+        { nullptr, 0 },
+    };
+
+    for (const int family : {AF_INET, AF_INET6}) {
+        const char *familyName = (family == AF_INET) ? "IPv4" : "IPv6";
+        if (int ret = sendDumpRequest(proto, family, extensions, states, iov, ARRAY_SIZE(iov))) {
+            ALOGE("Failed to dump %s sockets struct tcp_info: %s", familyName, strerror(-ret));
+            return ret;
+        }
+        if (int ret = readDiagMsgWithTcpInfo(tcpInfoReader)) {
+            ALOGE("Failed to read %s sockets struct tcp_info: %s", familyName, strerror(-ret));
+            return ret;
+        }
+    }
+
+    return 0;
+}
+
 int SockDiag::destroySockets(uint8_t proto, const uid_t uid, bool excludeLoopback) {
     mSocketsDestroyed = 0;
     Stopwatch s;