blob: 3e620b15a8ba74d0fd256df78a7d20477dfe4215 [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
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090019#include <iomanip>
20#include <thread>
Hugo Benichi4596d2f2018-01-19 13:36:29 +090021#include <vector>
Hugo Benichi7b314e12018-01-15 21:54:00 +090022
23#include <arpa/inet.h>
24#include <netinet/tcp.h>
25#include <linux/tcp.h>
26
Hugo Benichi8c397672018-01-22 22:06:15 +090027#include "Controllers.h"
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090028#include "DumpWriter.h"
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090029#include "SockDiag.h"
30#include "TcpSocketMonitor.h"
31
Hugo Benichi7b314e12018-01-15 21:54:00 +090032namespace android {
33namespace net {
34
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090035using std::chrono::duration_cast;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090036using std::chrono::steady_clock;
37
Hugo Benichi7b314e12018-01-15 21:54:00 +090038constexpr const char* getTcpStateName(int t) {
39 switch (t) {
40 case TCP_ESTABLISHED:
41 return "ESTABLISHED";
42 case TCP_SYN_SENT:
43 return "SYN-SENT";
44 case TCP_SYN_RECV:
45 return "SYN-RECV";
46 case TCP_FIN_WAIT1:
47 return "FIN-WAIT-1";
48 case TCP_FIN_WAIT2:
49 return "FIN-WAIT-2";
50 case TCP_TIME_WAIT:
51 return "TIME-WAIT";
52 case TCP_CLOSE:
53 return "CLOSE";
54 case TCP_CLOSE_WAIT:
55 return "CLOSE-WAIT";
56 case TCP_LAST_ACK:
57 return "LAST-ACK";
58 case TCP_LISTEN:
59 return "LISTEN";
60 case TCP_CLOSING:
61 return "CLOSING";
62 default:
63 return "UNKNOWN";
64 }
65}
66
Hugo Benichi54bfc7c2018-01-23 14:16:52 +090067// Helper macro for reading fields into struct tcp_info and handling different struct tcp_info
68// versions in the kernel.
Hugo Benichi321b22c2018-01-30 11:37:27 +090069#define TCPINFO_GET(ptr, fld, len, zero) \
70 (((ptr) != nullptr && (offsetof(struct tcp_info, fld) + sizeof((ptr)->fld)) < len) ? \
71 (ptr)->fld : zero)
Hugo Benichi54bfc7c2018-01-23 14:16:52 +090072
Hugo Benichicbaa36b2018-01-17 12:11:43 +090073static void tcpInfoPrint(DumpWriter &dw, Fwmark mark, const struct inet_diag_msg *sockinfo,
Hugo Benichi54bfc7c2018-01-23 14:16:52 +090074 const struct tcp_info *tcpinfo, uint32_t tcpinfoLen) {
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090075 char saddr[INET6_ADDRSTRLEN] = {};
76 char daddr[INET6_ADDRSTRLEN] = {};
77 inet_ntop(sockinfo->idiag_family, &(sockinfo->id.idiag_src), saddr, sizeof(saddr));
78 inet_ntop(sockinfo->idiag_family, &(sockinfo->id.idiag_dst), daddr, sizeof(daddr));
Hugo Benichi7b314e12018-01-15 21:54:00 +090079
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090080 dw.println(
81 "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 +090082 "rtt=%gms sent=%u lost=%u",
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090083 mark.netId,
84 sockinfo->idiag_uid,
85 mark.intValue,
86 saddr,
87 daddr,
88 ntohs(sockinfo->id.idiag_sport),
89 ntohs(sockinfo->id.idiag_dport),
90 getTcpStateName(sockinfo->idiag_state), sockinfo->idiag_state,
Hugo Benichi321b22c2018-01-30 11:37:27 +090091 TCPINFO_GET(tcpinfo, tcpi_rtt, tcpinfoLen, 0) / 1000.0,
92 TCPINFO_GET(tcpinfo, tcpi_segs_out, tcpinfoLen, 0),
93 TCPINFO_GET(tcpinfo, tcpi_lost, tcpinfoLen, 0));
Hugo Benichi7b314e12018-01-15 21:54:00 +090094}
95
96const String16 TcpSocketMonitor::DUMP_KEYWORD = String16("tcp_socket_info");
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090097const milliseconds TcpSocketMonitor::kDefaultPollingInterval = milliseconds(30000);
Hugo Benichi7b314e12018-01-15 21:54:00 +090098
99void TcpSocketMonitor::dump(DumpWriter& dw) {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900100 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900101
Hugo Benichi7b314e12018-01-15 21:54:00 +0900102 dw.println("TcpSocketMonitor");
Erik Klineb31fd692018-06-06 20:50:11 +0900103 ScopedIndent tcpSocketMonitorDetails(dw);
Hugo Benichi7b314e12018-01-15 21:54:00 +0900104
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900105 const auto now = steady_clock::now();
106 const auto d = duration_cast<milliseconds>(now - mLastPoll);
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900107 dw.println("running=%d, suspended=%d, last poll %lld ms ago",
108 mIsRunning, mIsSuspended, d.count());
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900109
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900110 if (!mNetworkStats.empty()) {
111 dw.blankline();
112 dw.println("Network stats:");
Erik Klineb31fd692018-06-06 20:50:11 +0900113 for (const auto& stats : mNetworkStats) {
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900114 if (stats.second.nSockets == 0) {
115 continue;
116 }
Erik Klineb31fd692018-06-06 20:50:11 +0900117 dw.println("netId=%d sent=%d lost=%d rttMs=%gms sentAckDiff=%dms",
118 stats.first,
119 stats.second.sent,
120 stats.second.lost,
121 stats.second.rttUs / 1000.0 / stats.second.nSockets,
122 stats.second.sentAckDiffMs / stats.second.nSockets);
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900123 }
Hugo Benichi7b314e12018-01-15 21:54:00 +0900124 }
125
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900126 if (!mSocketEntries.empty()) {
127 dw.blankline();
128 dw.println("Socket entries:");
Erik Klineb31fd692018-06-06 20:50:11 +0900129 for (const auto& stats : mSocketEntries) {
130 dw.println("netId=%u uid=%u cookie=%llu",
131 stats.second.mark.netId, stats.second.uid, (unsigned long long) stats.first);
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900132 }
133 }
Hugo Benichi7b314e12018-01-15 21:54:00 +0900134
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900135 SockDiag sd;
136 if (sd.open()) {
137 dw.blankline();
138 dw.println("Current socket dump:");
139 const auto tcpInfoReader = [&dw](Fwmark mark, const struct inet_diag_msg *sockinfo,
140 const struct tcp_info *tcpinfo, uint32_t tcpinfoLen) {
141 tcpInfoPrint(dw, mark, sockinfo, tcpinfo, tcpinfoLen);
142 };
143
144 if (int ret = sd.getLiveTcpInfos(tcpInfoReader)) {
145 ALOGE("Failed to dump TCP socket info: %s", strerror(-ret));
146 }
147 } else {
148 ALOGE("Error opening sock diag for dumping TCP socket info");
Hugo Benichi7b314e12018-01-15 21:54:00 +0900149 }
Hugo Benichi7b314e12018-01-15 21:54:00 +0900150}
151
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900152void TcpSocketMonitor::setPollingInterval(milliseconds nextSleepDurationMs) {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900153 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900154
155 mNextSleepDurationMs = nextSleepDurationMs;
156
157 ALOGD("tcpinfo polling interval set to %lld ms", mNextSleepDurationMs.count());
158}
159
160void TcpSocketMonitor::resumePolling() {
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900161 bool wasSuspended;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900162 {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900163 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900164
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900165 wasSuspended = mIsSuspended;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900166 mIsSuspended = false;
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900167 ALOGD("resuming tcpinfo polling (interval=%lldms)", mNextSleepDurationMs.count());
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900168 }
169
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900170 if (wasSuspended) {
171 mCv.notify_all();
172 }
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900173}
174
175void TcpSocketMonitor::suspendPolling() {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900176 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900177
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900178 bool wasSuspended = mIsSuspended;
179 mIsSuspended = true;
180 ALOGD("suspending tcpinfo polling");
181
182 if (!wasSuspended) {
183 mSocketEntries.clear();
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900184 }
185}
186
187void TcpSocketMonitor::poll() {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900188 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900189
190 if (mIsSuspended) {
191 return;
192 }
193
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900194 SockDiag sd;
195 if (!sd.open()) {
196 ALOGE("Error opening sock diag for polling TCP socket info");
197 return;
198 }
199
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900200 const auto now = steady_clock::now();
201 const auto tcpInfoReader = [this, now](Fwmark mark, const struct inet_diag_msg *sockinfo,
202 const struct tcp_info *tcpinfo,
203 uint32_t tcpinfoLen) NO_THREAD_SAFETY_ANALYSIS {
Hugo Benichi54bfc7c2018-01-23 14:16:52 +0900204 if (sockinfo == nullptr || tcpinfo == nullptr || tcpinfoLen == 0 || mark.intValue == 0) {
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900205 return;
206 }
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900207 updateSocketStats(now, mark, sockinfo, tcpinfo, tcpinfoLen);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900208 };
209
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900210 // Reset mNetworkStats
211 mNetworkStats.clear();
212
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900213 if (int ret = sd.getLiveTcpInfos(tcpInfoReader)) {
214 ALOGE("Failed to poll TCP socket info: %s", strerror(-ret));
215 return;
216 }
217
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900218 // Remove any SocketEntry not updated
219 for (auto it = mSocketEntries.cbegin(); it != mSocketEntries.cend();) {
220 if (it->second.lastUpdate < now) {
221 it = mSocketEntries.erase(it);
222 } else {
223 it++;
224 }
225 }
Hugo Benichi8c397672018-01-22 22:06:15 +0900226
227 const auto listener = gCtls->eventReporter.getNetdEventListener();
228 if (listener != nullptr) {
229 std::vector<int> netIds;
230 std::vector<int> sentPackets;
231 std::vector<int> lostPackets;
232 std::vector<int> rtts;
233 std::vector<int> sentAckDiffs;
234 for (auto const& stats : mNetworkStats) {
235 int32_t nSockets = stats.second.nSockets;
236 if (nSockets == 0) {
237 continue;
238 }
239 netIds.push_back(stats.first);
240 sentPackets.push_back(stats.second.sent);
241 lostPackets.push_back(stats.second.lost);
242 rtts.push_back(stats.second.rttUs / nSockets);
243 sentAckDiffs.push_back(stats.second.sentAckDiffMs / nSockets);
244 }
245 listener->onTcpSocketStatsEvent(netIds, sentPackets, lostPackets, rtts, sentAckDiffs);
246 }
247
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900248 mLastPoll = now;
249}
250
251void TcpSocketMonitor::waitForNextPoll() {
252 bool isSuspended;
253 milliseconds nextSleepDurationMs;
254 {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900255 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900256 isSuspended = mIsSuspended;
257 nextSleepDurationMs= mNextSleepDurationMs;
258 }
259
260 std::unique_lock<std::mutex> ul(mLock);
261 if (isSuspended) {
262 mCv.wait(ul);
263 } else {
264 mCv.wait_for(ul, nextSleepDurationMs);
265 }
266}
267
268bool TcpSocketMonitor::isRunning() {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900269 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900270 return mIsRunning;
271}
272
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900273void TcpSocketMonitor::updateSocketStats(time_point now, Fwmark mark,
274 const struct inet_diag_msg *sockinfo,
275 const struct tcp_info *tcpinfo,
276 uint32_t tcpinfoLen) NO_THREAD_SAFETY_ANALYSIS {
Hugo Benichi321b22c2018-01-30 11:37:27 +0900277 int32_t lastAck = TCPINFO_GET(tcpinfo, tcpi_last_ack_recv, tcpinfoLen, 0);
278 int32_t lastSent = TCPINFO_GET(tcpinfo, tcpi_last_data_sent, tcpinfoLen, 0);
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900279 TcpStats diff = {
Hugo Benichi321b22c2018-01-30 11:37:27 +0900280 .sent = TCPINFO_GET(tcpinfo, tcpi_segs_out, tcpinfoLen, 0),
281 .lost = TCPINFO_GET(tcpinfo, tcpi_lost, tcpinfoLen, 0),
282 .rttUs = TCPINFO_GET(tcpinfo, tcpi_rtt, tcpinfoLen, 0),
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900283 .sentAckDiffMs = lastAck - lastSent,
284 .nSockets = 1,
285 };
286
287 {
288 // Update socket stats with the newest entry, computing the diff w.r.t the previous entry.
289 const uint64_t cookie = (static_cast<uint64_t>(sockinfo->id.idiag_cookie[0]) << 32)
290 | static_cast<uint64_t>(sockinfo->id.idiag_cookie[1]);
291 const SocketEntry previous = mSocketEntries[cookie];
292 mSocketEntries[cookie] = {
293 .sent = diff.sent,
294 .lost = diff.lost,
295 .lastUpdate = now,
296 .mark = mark,
297 .uid = sockinfo->idiag_uid,
298 };
299
300 diff.sent -= previous.sent;
301 diff.lost -= previous.lost;
302 }
303
304 {
305 // Aggregate the diff per network id.
306 auto& stats = mNetworkStats[mark.netId];
307 stats.sent += diff.sent;
308 stats.lost += diff.lost;
309 stats.rttUs += diff.rttUs;
310 stats.sentAckDiffMs += diff.sentAckDiffMs;
311 stats.nSockets += diff.nSockets;
312 }
313}
314
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900315TcpSocketMonitor::TcpSocketMonitor() {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900316 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900317
318 mNextSleepDurationMs = kDefaultPollingInterval;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900319 mIsRunning = true;
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900320 mIsSuspended = true;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900321 mPollingThread = std::thread([this] {
Lorenzo Colitti15fc2d32018-01-26 10:07:20 +0900322 (void) this;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900323 while (isRunning()) {
324 poll();
325 waitForNextPoll();
326 }
327 });
328}
329
330TcpSocketMonitor::~TcpSocketMonitor() {
331 {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900332 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900333 mIsRunning = false;
334 mIsSuspended = true;
335 }
336 mCv.notify_all();
337 mPollingThread.join();
338}
339
Hugo Benichi7b314e12018-01-15 21:54:00 +0900340} // namespace net
341} // namespace android