blob: c18fe92163721be75c9171198d9ca7574fcc78ee [file] [log] [blame]
Hugo Benichi7b314e12018-01-15 21:54:00 +09001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "TcpSocketMonitor"
18
Bernie Innocenti1bcc25a2018-08-10 17:15:00 +090019#include <chrono>
20#include <cinttypes>
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090021#include <thread>
Hugo Benichi4596d2f2018-01-19 13:36:29 +090022#include <vector>
Hugo Benichi7b314e12018-01-15 21:54:00 +090023
24#include <arpa/inet.h>
25#include <netinet/tcp.h>
26#include <linux/tcp.h>
27
Hugo Benichi8c397672018-01-22 22:06:15 +090028#include "Controllers.h"
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090029#include "SockDiag.h"
30#include "TcpSocketMonitor.h"
Luke Huangb257d612019-03-14 21:19:13 +080031#include "netdutils/DumpWriter.h"
32
33using android::netdutils::DumpWriter;
34using android::netdutils::ScopedIndent;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090035
Hugo Benichi7b314e12018-01-15 21:54:00 +090036namespace android {
37namespace net {
38
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090039using std::chrono::duration_cast;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090040using std::chrono::steady_clock;
41
Hugo Benichi7b314e12018-01-15 21:54:00 +090042constexpr const char* getTcpStateName(int t) {
43 switch (t) {
44 case TCP_ESTABLISHED:
45 return "ESTABLISHED";
46 case TCP_SYN_SENT:
47 return "SYN-SENT";
48 case TCP_SYN_RECV:
49 return "SYN-RECV";
50 case TCP_FIN_WAIT1:
51 return "FIN-WAIT-1";
52 case TCP_FIN_WAIT2:
53 return "FIN-WAIT-2";
54 case TCP_TIME_WAIT:
55 return "TIME-WAIT";
56 case TCP_CLOSE:
57 return "CLOSE";
58 case TCP_CLOSE_WAIT:
59 return "CLOSE-WAIT";
60 case TCP_LAST_ACK:
61 return "LAST-ACK";
62 case TCP_LISTEN:
63 return "LISTEN";
64 case TCP_CLOSING:
65 return "CLOSING";
66 default:
67 return "UNKNOWN";
68 }
69}
70
Hugo Benichi54bfc7c2018-01-23 14:16:52 +090071// Helper macro for reading fields into struct tcp_info and handling different struct tcp_info
72// versions in the kernel.
Maciej Żenczykowski02074642020-04-02 09:19:46 +000073#define TCPINFO_GET(ptr, fld, len, zero) \
74 (((ptr) != nullptr && (offsetof(struct tcp_info, fld) + sizeof((ptr)->fld)) < (len)) \
75 ? (ptr)->fld \
76 : (zero))
Hugo Benichi54bfc7c2018-01-23 14:16:52 +090077
Hugo Benichicbaa36b2018-01-17 12:11:43 +090078static void tcpInfoPrint(DumpWriter &dw, Fwmark mark, const struct inet_diag_msg *sockinfo,
Hugo Benichi54bfc7c2018-01-23 14:16:52 +090079 const struct tcp_info *tcpinfo, uint32_t tcpinfoLen) {
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090080 char saddr[INET6_ADDRSTRLEN] = {};
81 char daddr[INET6_ADDRSTRLEN] = {};
82 inet_ntop(sockinfo->idiag_family, &(sockinfo->id.idiag_src), saddr, sizeof(saddr));
83 inet_ntop(sockinfo->idiag_family, &(sockinfo->id.idiag_dst), daddr, sizeof(daddr));
Hugo Benichi7b314e12018-01-15 21:54:00 +090084
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090085 dw.println(
86 "netId=%d uid=%u mark=0x%x saddr=%s daddr=%s sport=%u dport=%u tcp_state=%s(%u) "
Hugo Benichi4596d2f2018-01-19 13:36:29 +090087 "rtt=%gms sent=%u lost=%u",
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090088 mark.netId,
89 sockinfo->idiag_uid,
90 mark.intValue,
91 saddr,
92 daddr,
93 ntohs(sockinfo->id.idiag_sport),
94 ntohs(sockinfo->id.idiag_dport),
95 getTcpStateName(sockinfo->idiag_state), sockinfo->idiag_state,
Hugo Benichi321b22c2018-01-30 11:37:27 +090096 TCPINFO_GET(tcpinfo, tcpi_rtt, tcpinfoLen, 0) / 1000.0,
97 TCPINFO_GET(tcpinfo, tcpi_segs_out, tcpinfoLen, 0),
98 TCPINFO_GET(tcpinfo, tcpi_lost, tcpinfoLen, 0));
Hugo Benichi7b314e12018-01-15 21:54:00 +090099}
100
101const String16 TcpSocketMonitor::DUMP_KEYWORD = String16("tcp_socket_info");
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900102const milliseconds TcpSocketMonitor::kDefaultPollingInterval = milliseconds(30000);
Hugo Benichi7b314e12018-01-15 21:54:00 +0900103
104void TcpSocketMonitor::dump(DumpWriter& dw) {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900105 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900106
Hugo Benichi7b314e12018-01-15 21:54:00 +0900107 dw.println("TcpSocketMonitor");
Erik Klineb31fd692018-06-06 20:50:11 +0900108 ScopedIndent tcpSocketMonitorDetails(dw);
Hugo Benichi7b314e12018-01-15 21:54:00 +0900109
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900110 const auto now = steady_clock::now();
111 const auto d = duration_cast<milliseconds>(now - mLastPoll);
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900112 dw.println("running=%d, suspended=%d, last poll %lld ms ago",
113 mIsRunning, mIsSuspended, d.count());
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900114
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900115 if (!mNetworkStats.empty()) {
116 dw.blankline();
117 dw.println("Network stats:");
Bernie Innocenti6e686962018-09-12 17:43:01 +0900118 for (const std::pair<const uint32_t, TcpStats>& stats : mNetworkStats) {
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900119 if (stats.second.nSockets == 0) {
120 continue;
121 }
Erik Klineb31fd692018-06-06 20:50:11 +0900122 dw.println("netId=%d sent=%d lost=%d rttMs=%gms sentAckDiff=%dms",
123 stats.first,
124 stats.second.sent,
125 stats.second.lost,
126 stats.second.rttUs / 1000.0 / stats.second.nSockets,
127 stats.second.sentAckDiffMs / stats.second.nSockets);
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900128 }
Hugo Benichi7b314e12018-01-15 21:54:00 +0900129 }
130
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900131 if (!mSocketEntries.empty()) {
132 dw.blankline();
133 dw.println("Socket entries:");
Bernie Innocenti6e686962018-09-12 17:43:01 +0900134 for (const std::pair<const uint64_t, SocketEntry>& stats : mSocketEntries) {
Bernie Innocenti1bcc25a2018-08-10 17:15:00 +0900135 dw.println("netId=%u uid=%u cookie=%" PRIu64, stats.second.mark.netId, stats.second.uid,
136 stats.first);
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900137 }
138 }
Hugo Benichi7b314e12018-01-15 21:54:00 +0900139
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900140 SockDiag sd;
141 if (sd.open()) {
142 dw.blankline();
143 dw.println("Current socket dump:");
144 const auto tcpInfoReader = [&dw](Fwmark mark, const struct inet_diag_msg *sockinfo,
145 const struct tcp_info *tcpinfo, uint32_t tcpinfoLen) {
146 tcpInfoPrint(dw, mark, sockinfo, tcpinfo, tcpinfoLen);
147 };
148
149 if (int ret = sd.getLiveTcpInfos(tcpInfoReader)) {
150 ALOGE("Failed to dump TCP socket info: %s", strerror(-ret));
151 }
152 } else {
153 ALOGE("Error opening sock diag for dumping TCP socket info");
Hugo Benichi7b314e12018-01-15 21:54:00 +0900154 }
Hugo Benichi7b314e12018-01-15 21:54:00 +0900155}
156
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900157void TcpSocketMonitor::setPollingInterval(milliseconds nextSleepDurationMs) {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900158 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900159
160 mNextSleepDurationMs = nextSleepDurationMs;
161
162 ALOGD("tcpinfo polling interval set to %lld ms", mNextSleepDurationMs.count());
163}
164
165void TcpSocketMonitor::resumePolling() {
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900166 bool wasSuspended;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900167 {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900168 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900169
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900170 wasSuspended = mIsSuspended;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900171 mIsSuspended = false;
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900172 ALOGD("resuming tcpinfo polling (interval=%lldms)", mNextSleepDurationMs.count());
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900173 }
174
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900175 if (wasSuspended) {
176 mCv.notify_all();
177 }
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900178}
179
180void TcpSocketMonitor::suspendPolling() {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900181 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900182
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900183 bool wasSuspended = mIsSuspended;
184 mIsSuspended = true;
185 ALOGD("suspending tcpinfo polling");
186
187 if (!wasSuspended) {
188 mSocketEntries.clear();
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900189 }
190}
191
192void TcpSocketMonitor::poll() {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900193 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900194
195 if (mIsSuspended) {
196 return;
197 }
198
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900199 SockDiag sd;
200 if (!sd.open()) {
201 ALOGE("Error opening sock diag for polling TCP socket info");
202 return;
203 }
204
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900205 const auto now = steady_clock::now();
206 const auto tcpInfoReader = [this, now](Fwmark mark, const struct inet_diag_msg *sockinfo,
207 const struct tcp_info *tcpinfo,
208 uint32_t tcpinfoLen) NO_THREAD_SAFETY_ANALYSIS {
Hugo Benichi54bfc7c2018-01-23 14:16:52 +0900209 if (sockinfo == nullptr || tcpinfo == nullptr || tcpinfoLen == 0 || mark.intValue == 0) {
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900210 return;
211 }
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900212 updateSocketStats(now, mark, sockinfo, tcpinfo, tcpinfoLen);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900213 };
214
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900215 // Reset mNetworkStats
216 mNetworkStats.clear();
217
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900218 if (int ret = sd.getLiveTcpInfos(tcpInfoReader)) {
219 ALOGE("Failed to poll TCP socket info: %s", strerror(-ret));
220 return;
221 }
222
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900223 // Remove any SocketEntry not updated
224 for (auto it = mSocketEntries.cbegin(); it != mSocketEntries.cend();) {
225 if (it->second.lastUpdate < now) {
226 it = mSocketEntries.erase(it);
227 } else {
228 it++;
229 }
230 }
Hugo Benichi8c397672018-01-22 22:06:15 +0900231
232 const auto listener = gCtls->eventReporter.getNetdEventListener();
233 if (listener != nullptr) {
234 std::vector<int> netIds;
235 std::vector<int> sentPackets;
236 std::vector<int> lostPackets;
237 std::vector<int> rtts;
238 std::vector<int> sentAckDiffs;
239 for (auto const& stats : mNetworkStats) {
240 int32_t nSockets = stats.second.nSockets;
241 if (nSockets == 0) {
242 continue;
243 }
244 netIds.push_back(stats.first);
245 sentPackets.push_back(stats.second.sent);
246 lostPackets.push_back(stats.second.lost);
247 rtts.push_back(stats.second.rttUs / nSockets);
248 sentAckDiffs.push_back(stats.second.sentAckDiffMs / nSockets);
249 }
250 listener->onTcpSocketStatsEvent(netIds, sentPackets, lostPackets, rtts, sentAckDiffs);
251 }
252
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900253 mLastPoll = now;
254}
255
256void TcpSocketMonitor::waitForNextPoll() {
257 bool isSuspended;
258 milliseconds nextSleepDurationMs;
259 {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900260 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900261 isSuspended = mIsSuspended;
262 nextSleepDurationMs= mNextSleepDurationMs;
263 }
264
265 std::unique_lock<std::mutex> ul(mLock);
266 if (isSuspended) {
267 mCv.wait(ul);
268 } else {
269 mCv.wait_for(ul, nextSleepDurationMs);
270 }
271}
272
273bool TcpSocketMonitor::isRunning() {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900274 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900275 return mIsRunning;
276}
277
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900278void TcpSocketMonitor::updateSocketStats(time_point now, Fwmark mark,
279 const struct inet_diag_msg *sockinfo,
280 const struct tcp_info *tcpinfo,
281 uint32_t tcpinfoLen) NO_THREAD_SAFETY_ANALYSIS {
Hugo Benichi321b22c2018-01-30 11:37:27 +0900282 int32_t lastAck = TCPINFO_GET(tcpinfo, tcpi_last_ack_recv, tcpinfoLen, 0);
283 int32_t lastSent = TCPINFO_GET(tcpinfo, tcpi_last_data_sent, tcpinfoLen, 0);
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900284 TcpStats diff = {
Hugo Benichi321b22c2018-01-30 11:37:27 +0900285 .sent = TCPINFO_GET(tcpinfo, tcpi_segs_out, tcpinfoLen, 0),
286 .lost = TCPINFO_GET(tcpinfo, tcpi_lost, tcpinfoLen, 0),
287 .rttUs = TCPINFO_GET(tcpinfo, tcpi_rtt, tcpinfoLen, 0),
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900288 .sentAckDiffMs = lastAck - lastSent,
289 .nSockets = 1,
290 };
291
292 {
293 // Update socket stats with the newest entry, computing the diff w.r.t the previous entry.
294 const uint64_t cookie = (static_cast<uint64_t>(sockinfo->id.idiag_cookie[0]) << 32)
295 | static_cast<uint64_t>(sockinfo->id.idiag_cookie[1]);
296 const SocketEntry previous = mSocketEntries[cookie];
297 mSocketEntries[cookie] = {
298 .sent = diff.sent,
299 .lost = diff.lost,
300 .lastUpdate = now,
301 .mark = mark,
302 .uid = sockinfo->idiag_uid,
303 };
304
305 diff.sent -= previous.sent;
306 diff.lost -= previous.lost;
307 }
308
309 {
310 // Aggregate the diff per network id.
311 auto& stats = mNetworkStats[mark.netId];
312 stats.sent += diff.sent;
313 stats.lost += diff.lost;
314 stats.rttUs += diff.rttUs;
315 stats.sentAckDiffMs += diff.sentAckDiffMs;
316 stats.nSockets += diff.nSockets;
317 }
318}
319
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900320TcpSocketMonitor::TcpSocketMonitor() {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900321 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900322
323 mNextSleepDurationMs = kDefaultPollingInterval;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900324 mIsRunning = true;
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900325 mIsSuspended = true;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900326 mPollingThread = std::thread([this] {
Lorenzo Colitti15fc2d32018-01-26 10:07:20 +0900327 (void) this;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900328 while (isRunning()) {
329 poll();
330 waitForNextPoll();
331 }
332 });
333}
334
335TcpSocketMonitor::~TcpSocketMonitor() {
336 {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900337 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900338 mIsRunning = false;
339 mIsSuspended = true;
340 }
341 mCv.notify_all();
342 mPollingThread.join();
343}
344
Hugo Benichi7b314e12018-01-15 21:54:00 +0900345} // namespace net
346} // namespace android