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;
}