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;
diff --git a/server/SockDiag.h b/server/SockDiag.h
index 7af8152..c95aa8c 100644
--- a/server/SockDiag.h
+++ b/server/SockDiag.h
@@ -32,6 +32,7 @@
#include "UidRanges.h"
struct inet_diag_msg;
+struct tcp_info;
class SockDiagTest;
namespace android {
@@ -42,10 +43,14 @@
public:
static const int kBufferSize = 4096;
- // Callback function that is called once for every socket in the dump. A return value of true
- // means destroy the socket.
+ // Callback function that is called once for every socket in the sockDestroy dump.
+ // A return value of true means destroy the socket.
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(const struct inet_diag_msg *, const struct tcp_info *)>
+ TcpInfoReader;
+
struct DestroyRequest {
nlmsghdr nlh;
inet_diag_req_v2 req;
@@ -58,6 +63,7 @@
int sendDumpRequest(uint8_t proto, uint8_t family, uint32_t states);
int sendDumpRequest(uint8_t proto, uint8_t family, const char *addrstr);
int readDiagMsg(uint8_t proto, const DestroyFilter& callback);
+ int readDiagMsgWithTcpInfo(const TcpInfoReader& callback);
int sockDestroy(uint8_t proto, const inet_diag_msg *);
// Destroys all sockets on the given IPv4 or IPv6 address.
@@ -72,12 +78,16 @@
int destroySocketsLackingPermission(unsigned netId, Permission permission,
bool excludeLoopback);
+ // Dump struct tcp_info for all "live" (CONNECTED, SYN_SENT, SYN_RECV) TCP sockets.
+ int getLiveTcpInfos(const TcpInfoReader& sockInfoReader);
+
private:
friend class SockDiagTest;
int mSock;
int mWriteSock;
int mSocketsDestroyed;
- int sendDumpRequest(uint8_t proto, uint8_t family, uint32_t states, iovec *iov, int iovcnt);
+ int sendDumpRequest(uint8_t proto, uint8_t family, uint8_t extensions, uint32_t states,
+ iovec *iov, int iovcnt);
int destroySockets(uint8_t proto, int family, const char *addrstr);
int destroyLiveSockets(DestroyFilter destroy, const char *what, iovec *iov, int iovcnt);
bool hasSocks() { return mSock != -1 && mWriteSock != -1; }