Add the number of struct tcp_info bytes read from Netlink

This patch adds to the SockDiag TcpInfoReader callback the number of
bytes read from Netlink when parsing a struct tcp_info attribute with
attribute id INET_DIAG_INFO.

On different kernels, struct tcp_info will have different fields and
different sizes when serialized. A generic sock_diag struct tcp_info
handler can use the attribute byte size to distinguish between different
fields and safely read data inside struct tcp_info.

Bug: 64147860
Test: tested manually on sailfish with 3.18 kernel,
      using $ adb shell dumpsys netd tcp_socket_info
Change-Id: I45da9ed787dc7f0c4873ce1132b5f8094bcffd0a
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..4a0a217 100644
--- a/server/TcpSocketMonitor.cpp
+++ b/server/TcpSocketMonitor.cpp
@@ -64,8 +64,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 +78,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",
+            "rqueue=%u wqueue=%u rtt=%gms var_rtt=%gms rcv_rtt=%gms unacked=%u snd_cwnd=%u",
             mark.netId,
             sockinfo->idiag_uid,
             mark.intValue,
@@ -84,11 +89,11 @@
             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_rttvar, tcpinfoLen, 0) / 1000.0,
+            tcpinfo_get(tcpinfo, tcpi_rcv_rtt, tcpinfoLen, 0) / 1000.0,
+            tcpinfo_get(tcpinfo, tcpi_unacked, tcpinfoLen, 0),
+            tcpinfo_get(tcpinfo, tcpi_data_segs_out, tcpinfoLen, 0));
 }
 
 const String16 TcpSocketMonitor::DUMP_KEYWORD = String16("tcp_socket_info");
@@ -111,8 +116,8 @@
     }
 
     const auto tcpInfoReader = [&dw](Fwmark mark, const struct inet_diag_msg *sockinfo,
-                                     const struct tcp_info *tcpinfo) {
-        tcpInfoPrint(dw, mark, sockinfo, tcpinfo);
+                                     const struct tcp_info *tcpinfo, uint32_t tcpinfoLen) {
+        tcpInfoPrint(dw, mark, sockinfo, tcpinfo, tcpinfoLen);
     };
 
     if (int ret = sd.getLiveTcpInfos(tcpInfoReader)) {
@@ -173,8 +178,8 @@
     }
 
     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 struct tcp_info *tcpinfo, uint32_t tcpinfoLen) {
+        if (sockinfo == nullptr || tcpinfo == nullptr || tcpinfoLen == 0 || mark.intValue == 0) {
             return;
         }