blob: 358e4c0417d57f1f32a9c5432c241638deda234f [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) {
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900100 std::lock_guard<std::mutex> guard(mLock);
101
Hugo Benichi7b314e12018-01-15 21:54:00 +0900102 dw.println("TcpSocketMonitor");
103 dw.incIndent();
104
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:");
113 for (auto const& stats : mNetworkStats) {
114 if (stats.second.nSockets == 0) {
115 continue;
116 }
117 dw.println("netId=%d sent=%d lost=%d rttMs=%gms sentAckDiff=%gms",
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);
123 }
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:");
129 for (auto const& stats : mSocketEntries) {
130 dw.println("netId=%u uid=%u cookie=%ld",
131 stats.second.mark.netId, stats.second.uid, stats.first);
132 }
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 }
150
151 dw.decIndent();
152}
153
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900154void TcpSocketMonitor::setPollingInterval(milliseconds nextSleepDurationMs) {
155 std::lock_guard<std::mutex> guard(mLock);
156
157 mNextSleepDurationMs = nextSleepDurationMs;
158
159 ALOGD("tcpinfo polling interval set to %lld ms", mNextSleepDurationMs.count());
160}
161
162void TcpSocketMonitor::resumePolling() {
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900163 bool wasSuspended;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900164 {
165 std::lock_guard<std::mutex> guard(mLock);
166
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900167 wasSuspended = mIsSuspended;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900168 mIsSuspended = false;
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900169 ALOGD("resuming tcpinfo polling (interval=%lldms)", mNextSleepDurationMs.count());
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900170 }
171
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900172 if (wasSuspended) {
173 mCv.notify_all();
174 }
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900175}
176
177void TcpSocketMonitor::suspendPolling() {
178 std::lock_guard<std::mutex> guard(mLock);
179
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900180 bool wasSuspended = mIsSuspended;
181 mIsSuspended = true;
182 ALOGD("suspending tcpinfo polling");
183
184 if (!wasSuspended) {
185 mSocketEntries.clear();
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900186 }
187}
188
189void TcpSocketMonitor::poll() {
190 std::lock_guard<std::mutex> guard(mLock);
191
192 if (mIsSuspended) {
193 return;
194 }
195
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900196 SockDiag sd;
197 if (!sd.open()) {
198 ALOGE("Error opening sock diag for polling TCP socket info");
199 return;
200 }
201
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900202 const auto now = steady_clock::now();
203 const auto tcpInfoReader = [this, now](Fwmark mark, const struct inet_diag_msg *sockinfo,
204 const struct tcp_info *tcpinfo,
205 uint32_t tcpinfoLen) NO_THREAD_SAFETY_ANALYSIS {
Hugo Benichi54bfc7c2018-01-23 14:16:52 +0900206 if (sockinfo == nullptr || tcpinfo == nullptr || tcpinfoLen == 0 || mark.intValue == 0) {
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900207 return;
208 }
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900209 updateSocketStats(now, mark, sockinfo, tcpinfo, tcpinfoLen);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900210 };
211
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900212 // Reset mNetworkStats
213 mNetworkStats.clear();
214
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900215 if (int ret = sd.getLiveTcpInfos(tcpInfoReader)) {
216 ALOGE("Failed to poll TCP socket info: %s", strerror(-ret));
217 return;
218 }
219
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900220 // Remove any SocketEntry not updated
221 for (auto it = mSocketEntries.cbegin(); it != mSocketEntries.cend();) {
222 if (it->second.lastUpdate < now) {
223 it = mSocketEntries.erase(it);
224 } else {
225 it++;
226 }
227 }
Hugo Benichi8c397672018-01-22 22:06:15 +0900228
229 const auto listener = gCtls->eventReporter.getNetdEventListener();
230 if (listener != nullptr) {
231 std::vector<int> netIds;
232 std::vector<int> sentPackets;
233 std::vector<int> lostPackets;
234 std::vector<int> rtts;
235 std::vector<int> sentAckDiffs;
236 for (auto const& stats : mNetworkStats) {
237 int32_t nSockets = stats.second.nSockets;
238 if (nSockets == 0) {
239 continue;
240 }
241 netIds.push_back(stats.first);
242 sentPackets.push_back(stats.second.sent);
243 lostPackets.push_back(stats.second.lost);
244 rtts.push_back(stats.second.rttUs / nSockets);
245 sentAckDiffs.push_back(stats.second.sentAckDiffMs / nSockets);
246 }
247 listener->onTcpSocketStatsEvent(netIds, sentPackets, lostPackets, rtts, sentAckDiffs);
248 }
249
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900250 mLastPoll = now;
251}
252
253void TcpSocketMonitor::waitForNextPoll() {
254 bool isSuspended;
255 milliseconds nextSleepDurationMs;
256 {
257 std::lock_guard<std::mutex> guard(mLock);
258 isSuspended = mIsSuspended;
259 nextSleepDurationMs= mNextSleepDurationMs;
260 }
261
262 std::unique_lock<std::mutex> ul(mLock);
263 if (isSuspended) {
264 mCv.wait(ul);
265 } else {
266 mCv.wait_for(ul, nextSleepDurationMs);
267 }
268}
269
270bool TcpSocketMonitor::isRunning() {
271 std::lock_guard<std::mutex> guard(mLock);
272 return mIsRunning;
273}
274
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900275void TcpSocketMonitor::updateSocketStats(time_point now, Fwmark mark,
276 const struct inet_diag_msg *sockinfo,
277 const struct tcp_info *tcpinfo,
278 uint32_t tcpinfoLen) NO_THREAD_SAFETY_ANALYSIS {
Hugo Benichi321b22c2018-01-30 11:37:27 +0900279 int32_t lastAck = TCPINFO_GET(tcpinfo, tcpi_last_ack_recv, tcpinfoLen, 0);
280 int32_t lastSent = TCPINFO_GET(tcpinfo, tcpi_last_data_sent, tcpinfoLen, 0);
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900281 TcpStats diff = {
Hugo Benichi321b22c2018-01-30 11:37:27 +0900282 .sent = TCPINFO_GET(tcpinfo, tcpi_segs_out, tcpinfoLen, 0),
283 .lost = TCPINFO_GET(tcpinfo, tcpi_lost, tcpinfoLen, 0),
284 .rttUs = TCPINFO_GET(tcpinfo, tcpi_rtt, tcpinfoLen, 0),
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900285 .sentAckDiffMs = lastAck - lastSent,
286 .nSockets = 1,
287 };
288
289 {
290 // Update socket stats with the newest entry, computing the diff w.r.t the previous entry.
291 const uint64_t cookie = (static_cast<uint64_t>(sockinfo->id.idiag_cookie[0]) << 32)
292 | static_cast<uint64_t>(sockinfo->id.idiag_cookie[1]);
293 const SocketEntry previous = mSocketEntries[cookie];
294 mSocketEntries[cookie] = {
295 .sent = diff.sent,
296 .lost = diff.lost,
297 .lastUpdate = now,
298 .mark = mark,
299 .uid = sockinfo->idiag_uid,
300 };
301
302 diff.sent -= previous.sent;
303 diff.lost -= previous.lost;
304 }
305
306 {
307 // Aggregate the diff per network id.
308 auto& stats = mNetworkStats[mark.netId];
309 stats.sent += diff.sent;
310 stats.lost += diff.lost;
311 stats.rttUs += diff.rttUs;
312 stats.sentAckDiffMs += diff.sentAckDiffMs;
313 stats.nSockets += diff.nSockets;
314 }
315}
316
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900317TcpSocketMonitor::TcpSocketMonitor() {
318 std::lock_guard<std::mutex> guard(mLock);
319
320 mNextSleepDurationMs = kDefaultPollingInterval;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900321 mIsRunning = true;
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900322 mIsSuspended = true;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900323 mPollingThread = std::thread([this] {
Lorenzo Colitti15fc2d32018-01-26 10:07:20 +0900324 (void) this;
325#if 0 // Disable due to crashes in updateSocketStats. b/72512637
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900326 while (isRunning()) {
327 poll();
328 waitForNextPoll();
329 }
Lorenzo Colitti15fc2d32018-01-26 10:07:20 +0900330#endif
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900331 });
332}
333
334TcpSocketMonitor::~TcpSocketMonitor() {
335 {
336 std::lock_guard<std::mutex> guard(mLock);
337 mIsRunning = false;
338 mIsSuspended = true;
339 }
340 mCv.notify_all();
341 mPollingThread.join();
342}
343
Hugo Benichi7b314e12018-01-15 21:54:00 +0900344} // namespace net
345} // namespace android