blob: ce8df97d8756ba2e099d7e3d77ed82a1fdba2b7c [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.
69#define tcpinfo_get(ptr, fld, len, zero) \
70 (((ptr) != nullptr && offsetof(struct tcp_info, fld) < len) ? (ptr)->fld : zero)
71
Hugo Benichicbaa36b2018-01-17 12:11:43 +090072static void tcpInfoPrint(DumpWriter &dw, Fwmark mark, const struct inet_diag_msg *sockinfo,
Hugo Benichi54bfc7c2018-01-23 14:16:52 +090073 const struct tcp_info *tcpinfo, uint32_t tcpinfoLen) {
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090074 char saddr[INET6_ADDRSTRLEN] = {};
75 char daddr[INET6_ADDRSTRLEN] = {};
76 inet_ntop(sockinfo->idiag_family, &(sockinfo->id.idiag_src), saddr, sizeof(saddr));
77 inet_ntop(sockinfo->idiag_family, &(sockinfo->id.idiag_dst), daddr, sizeof(daddr));
Hugo Benichi7b314e12018-01-15 21:54:00 +090078
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090079 dw.println(
80 "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 +090081 "rtt=%gms sent=%u lost=%u",
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090082 mark.netId,
83 sockinfo->idiag_uid,
84 mark.intValue,
85 saddr,
86 daddr,
87 ntohs(sockinfo->id.idiag_sport),
88 ntohs(sockinfo->id.idiag_dport),
89 getTcpStateName(sockinfo->idiag_state), sockinfo->idiag_state,
Hugo Benichi54bfc7c2018-01-23 14:16:52 +090090 tcpinfo_get(tcpinfo, tcpi_rtt, tcpinfoLen, 0) / 1000.0,
Hugo Benichi4596d2f2018-01-19 13:36:29 +090091 tcpinfo_get(tcpinfo, tcpi_data_segs_out, tcpinfoLen, 0),
92 tcpinfo_get(tcpinfo, tcpi_lost, tcpinfoLen, 0));
Hugo Benichi7b314e12018-01-15 21:54:00 +090093}
94
95const String16 TcpSocketMonitor::DUMP_KEYWORD = String16("tcp_socket_info");
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090096const milliseconds TcpSocketMonitor::kDefaultPollingInterval = milliseconds(30000);
Hugo Benichi7b314e12018-01-15 21:54:00 +090097
98void TcpSocketMonitor::dump(DumpWriter& dw) {
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090099 std::lock_guard<std::mutex> guard(mLock);
100
Hugo Benichi7b314e12018-01-15 21:54:00 +0900101 dw.println("TcpSocketMonitor");
102 dw.incIndent();
103
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900104 const auto now = steady_clock::now();
105 const auto d = duration_cast<milliseconds>(now - mLastPoll);
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900106 dw.println("running=%d, suspended=%d, last poll %lld ms ago",
107 mIsRunning, mIsSuspended, d.count());
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900108
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900109 if (!mNetworkStats.empty()) {
110 dw.blankline();
111 dw.println("Network stats:");
112 for (auto const& stats : mNetworkStats) {
113 if (stats.second.nSockets == 0) {
114 continue;
115 }
116 dw.println("netId=%d sent=%d lost=%d rttMs=%gms sentAckDiff=%gms",
117 stats.first,
118 stats.second.sent,
119 stats.second.lost,
120 stats.second.rttUs / 1000.0 / stats.second.nSockets,
121 stats.second.sentAckDiffMs / stats.second.nSockets);
122 }
Hugo Benichi7b314e12018-01-15 21:54:00 +0900123 }
124
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900125 if (!mSocketEntries.empty()) {
126 dw.blankline();
127 dw.println("Socket entries:");
128 for (auto const& stats : mSocketEntries) {
129 dw.println("netId=%u uid=%u cookie=%ld",
130 stats.second.mark.netId, stats.second.uid, stats.first);
131 }
132 }
Hugo Benichi7b314e12018-01-15 21:54:00 +0900133
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900134 SockDiag sd;
135 if (sd.open()) {
136 dw.blankline();
137 dw.println("Current socket dump:");
138 const auto tcpInfoReader = [&dw](Fwmark mark, const struct inet_diag_msg *sockinfo,
139 const struct tcp_info *tcpinfo, uint32_t tcpinfoLen) {
140 tcpInfoPrint(dw, mark, sockinfo, tcpinfo, tcpinfoLen);
141 };
142
143 if (int ret = sd.getLiveTcpInfos(tcpInfoReader)) {
144 ALOGE("Failed to dump TCP socket info: %s", strerror(-ret));
145 }
146 } else {
147 ALOGE("Error opening sock diag for dumping TCP socket info");
Hugo Benichi7b314e12018-01-15 21:54:00 +0900148 }
149
150 dw.decIndent();
151}
152
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900153void TcpSocketMonitor::setPollingInterval(milliseconds nextSleepDurationMs) {
154 std::lock_guard<std::mutex> guard(mLock);
155
156 mNextSleepDurationMs = nextSleepDurationMs;
157
158 ALOGD("tcpinfo polling interval set to %lld ms", mNextSleepDurationMs.count());
159}
160
161void TcpSocketMonitor::resumePolling() {
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900162 bool wasSuspended;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900163 {
164 std::lock_guard<std::mutex> guard(mLock);
165
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900166 wasSuspended = mIsSuspended;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900167 mIsSuspended = false;
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900168 ALOGD("resuming tcpinfo polling (interval=%lldms)", mNextSleepDurationMs.count());
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900169 }
170
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900171 if (wasSuspended) {
172 mCv.notify_all();
173 }
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900174}
175
176void TcpSocketMonitor::suspendPolling() {
177 std::lock_guard<std::mutex> guard(mLock);
178
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900179 bool wasSuspended = mIsSuspended;
180 mIsSuspended = true;
181 ALOGD("suspending tcpinfo polling");
182
183 if (!wasSuspended) {
184 mSocketEntries.clear();
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900185 }
186}
187
188void TcpSocketMonitor::poll() {
189 std::lock_guard<std::mutex> guard(mLock);
190
191 if (mIsSuspended) {
192 return;
193 }
194
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900195 SockDiag sd;
196 if (!sd.open()) {
197 ALOGE("Error opening sock diag for polling TCP socket info");
198 return;
199 }
200
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900201 const auto now = steady_clock::now();
202 const auto tcpInfoReader = [this, now](Fwmark mark, const struct inet_diag_msg *sockinfo,
203 const struct tcp_info *tcpinfo,
204 uint32_t tcpinfoLen) NO_THREAD_SAFETY_ANALYSIS {
Hugo Benichi54bfc7c2018-01-23 14:16:52 +0900205 if (sockinfo == nullptr || tcpinfo == nullptr || tcpinfoLen == 0 || mark.intValue == 0) {
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900206 return;
207 }
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900208 updateSocketStats(now, mark, sockinfo, tcpinfo, tcpinfoLen);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900209 };
210
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900211 // Reset mNetworkStats
212 mNetworkStats.clear();
213
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900214 if (int ret = sd.getLiveTcpInfos(tcpInfoReader)) {
215 ALOGE("Failed to poll TCP socket info: %s", strerror(-ret));
216 return;
217 }
218
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900219 // Remove any SocketEntry not updated
220 for (auto it = mSocketEntries.cbegin(); it != mSocketEntries.cend();) {
221 if (it->second.lastUpdate < now) {
222 it = mSocketEntries.erase(it);
223 } else {
224 it++;
225 }
226 }
Hugo Benichi8c397672018-01-22 22:06:15 +0900227
228 const auto listener = gCtls->eventReporter.getNetdEventListener();
229 if (listener != nullptr) {
230 std::vector<int> netIds;
231 std::vector<int> sentPackets;
232 std::vector<int> lostPackets;
233 std::vector<int> rtts;
234 std::vector<int> sentAckDiffs;
235 for (auto const& stats : mNetworkStats) {
236 int32_t nSockets = stats.second.nSockets;
237 if (nSockets == 0) {
238 continue;
239 }
240 netIds.push_back(stats.first);
241 sentPackets.push_back(stats.second.sent);
242 lostPackets.push_back(stats.second.lost);
243 rtts.push_back(stats.second.rttUs / nSockets);
244 sentAckDiffs.push_back(stats.second.sentAckDiffMs / nSockets);
245 }
246 listener->onTcpSocketStatsEvent(netIds, sentPackets, lostPackets, rtts, sentAckDiffs);
247 }
248
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900249 mLastPoll = now;
250}
251
252void TcpSocketMonitor::waitForNextPoll() {
253 bool isSuspended;
254 milliseconds nextSleepDurationMs;
255 {
256 std::lock_guard<std::mutex> guard(mLock);
257 isSuspended = mIsSuspended;
258 nextSleepDurationMs= mNextSleepDurationMs;
259 }
260
261 std::unique_lock<std::mutex> ul(mLock);
262 if (isSuspended) {
263 mCv.wait(ul);
264 } else {
265 mCv.wait_for(ul, nextSleepDurationMs);
266 }
267}
268
269bool TcpSocketMonitor::isRunning() {
270 std::lock_guard<std::mutex> guard(mLock);
271 return mIsRunning;
272}
273
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900274void TcpSocketMonitor::updateSocketStats(time_point now, Fwmark mark,
275 const struct inet_diag_msg *sockinfo,
276 const struct tcp_info *tcpinfo,
277 uint32_t tcpinfoLen) NO_THREAD_SAFETY_ANALYSIS {
278 int32_t lastAck = tcpinfo_get(tcpinfo, tcpi_last_ack_recv, tcpinfoLen, 0);
279 int32_t lastSent = tcpinfo_get(tcpinfo, tcpi_last_data_sent, tcpinfoLen, 0);
280 TcpStats diff = {
281 .sent = tcpinfo_get(tcpinfo, tcpi_data_segs_out, tcpinfoLen, 0),
282 .lost = tcpinfo_get(tcpinfo, tcpi_lost, tcpinfoLen, 0),
283 .rttUs = tcpinfo_get(tcpinfo, tcpi_rtt, tcpinfoLen, 0),
284 .sentAckDiffMs = lastAck - lastSent,
285 .nSockets = 1,
286 };
287
288 {
289 // Update socket stats with the newest entry, computing the diff w.r.t the previous entry.
290 const uint64_t cookie = (static_cast<uint64_t>(sockinfo->id.idiag_cookie[0]) << 32)
291 | static_cast<uint64_t>(sockinfo->id.idiag_cookie[1]);
292 const SocketEntry previous = mSocketEntries[cookie];
293 mSocketEntries[cookie] = {
294 .sent = diff.sent,
295 .lost = diff.lost,
296 .lastUpdate = now,
297 .mark = mark,
298 .uid = sockinfo->idiag_uid,
299 };
300
301 diff.sent -= previous.sent;
302 diff.lost -= previous.lost;
303 }
304
305 {
306 // Aggregate the diff per network id.
307 auto& stats = mNetworkStats[mark.netId];
308 stats.sent += diff.sent;
309 stats.lost += diff.lost;
310 stats.rttUs += diff.rttUs;
311 stats.sentAckDiffMs += diff.sentAckDiffMs;
312 stats.nSockets += diff.nSockets;
313 }
314}
315
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900316TcpSocketMonitor::TcpSocketMonitor() {
317 std::lock_guard<std::mutex> guard(mLock);
318
319 mNextSleepDurationMs = kDefaultPollingInterval;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900320 mIsRunning = true;
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900321 mIsSuspended = true;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900322 mPollingThread = std::thread([this] {
Lorenzo Colitti15fc2d32018-01-26 10:07:20 +0900323 (void) this;
324#if 0 // Disable due to crashes in updateSocketStats. b/72512637
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900325 while (isRunning()) {
326 poll();
327 waitForNextPoll();
328 }
Lorenzo Colitti15fc2d32018-01-26 10:07:20 +0900329#endif
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900330 });
331}
332
333TcpSocketMonitor::~TcpSocketMonitor() {
334 {
335 std::lock_guard<std::mutex> guard(mLock);
336 mIsRunning = false;
337 mIsSuspended = true;
338 }
339 mCv.notify_all();
340 mPollingThread.join();
341}
342
Hugo Benichi7b314e12018-01-15 21:54:00 +0900343} // namespace net
344} // namespace android