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;