blob: a6d71b7076e6596c8ea4b8c939fe9597cb231044 [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 Benichia9e3c5d2018-01-18 10:33:22 +090027#include "DumpWriter.h"
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090028#include "SockDiag.h"
29#include "TcpSocketMonitor.h"
30
Hugo Benichi7b314e12018-01-15 21:54:00 +090031namespace android {
32namespace net {
33
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090034using std::chrono::duration_cast;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090035using std::chrono::steady_clock;
36
Hugo Benichi7b314e12018-01-15 21:54:00 +090037constexpr const char* getTcpStateName(int t) {
38 switch (t) {
39 case TCP_ESTABLISHED:
40 return "ESTABLISHED";
41 case TCP_SYN_SENT:
42 return "SYN-SENT";
43 case TCP_SYN_RECV:
44 return "SYN-RECV";
45 case TCP_FIN_WAIT1:
46 return "FIN-WAIT-1";
47 case TCP_FIN_WAIT2:
48 return "FIN-WAIT-2";
49 case TCP_TIME_WAIT:
50 return "TIME-WAIT";
51 case TCP_CLOSE:
52 return "CLOSE";
53 case TCP_CLOSE_WAIT:
54 return "CLOSE-WAIT";
55 case TCP_LAST_ACK:
56 return "LAST-ACK";
57 case TCP_LISTEN:
58 return "LISTEN";
59 case TCP_CLOSING:
60 return "CLOSING";
61 default:
62 return "UNKNOWN";
63 }
64}
65
Hugo Benichi54bfc7c2018-01-23 14:16:52 +090066// Helper macro for reading fields into struct tcp_info and handling different struct tcp_info
67// versions in the kernel.
68#define tcpinfo_get(ptr, fld, len, zero) \
69 (((ptr) != nullptr && offsetof(struct tcp_info, fld) < len) ? (ptr)->fld : zero)
70
Hugo Benichicbaa36b2018-01-17 12:11:43 +090071static void tcpInfoPrint(DumpWriter &dw, Fwmark mark, const struct inet_diag_msg *sockinfo,
Hugo Benichi54bfc7c2018-01-23 14:16:52 +090072 const struct tcp_info *tcpinfo, uint32_t tcpinfoLen) {
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090073 char saddr[INET6_ADDRSTRLEN] = {};
74 char daddr[INET6_ADDRSTRLEN] = {};
75 inet_ntop(sockinfo->idiag_family, &(sockinfo->id.idiag_src), saddr, sizeof(saddr));
76 inet_ntop(sockinfo->idiag_family, &(sockinfo->id.idiag_dst), daddr, sizeof(daddr));
Hugo Benichi7b314e12018-01-15 21:54:00 +090077
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090078 dw.println(
79 "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 +090080 "rtt=%gms sent=%u lost=%u",
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090081 mark.netId,
82 sockinfo->idiag_uid,
83 mark.intValue,
84 saddr,
85 daddr,
86 ntohs(sockinfo->id.idiag_sport),
87 ntohs(sockinfo->id.idiag_dport),
88 getTcpStateName(sockinfo->idiag_state), sockinfo->idiag_state,
Hugo Benichi54bfc7c2018-01-23 14:16:52 +090089 tcpinfo_get(tcpinfo, tcpi_rtt, tcpinfoLen, 0) / 1000.0,
Hugo Benichi4596d2f2018-01-19 13:36:29 +090090 tcpinfo_get(tcpinfo, tcpi_data_segs_out, tcpinfoLen, 0),
91 tcpinfo_get(tcpinfo, tcpi_lost, tcpinfoLen, 0));
Hugo Benichi7b314e12018-01-15 21:54:00 +090092}
93
94const String16 TcpSocketMonitor::DUMP_KEYWORD = String16("tcp_socket_info");
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090095const milliseconds TcpSocketMonitor::kDefaultPollingInterval = milliseconds(30000);
Hugo Benichi7b314e12018-01-15 21:54:00 +090096
97void TcpSocketMonitor::dump(DumpWriter& dw) {
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090098 std::lock_guard<std::mutex> guard(mLock);
99
Hugo Benichi7b314e12018-01-15 21:54:00 +0900100 dw.println("TcpSocketMonitor");
101 dw.incIndent();
102
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900103 const auto now = steady_clock::now();
104 const auto d = duration_cast<milliseconds>(now - mLastPoll);
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900105 dw.println("running=%d, suspended=%d, last poll %lld ms ago",
106 mIsRunning, mIsSuspended, d.count());
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900107
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900108 if (!mNetworkStats.empty()) {
109 dw.blankline();
110 dw.println("Network stats:");
111 for (auto const& stats : mNetworkStats) {
112 if (stats.second.nSockets == 0) {
113 continue;
114 }
115 dw.println("netId=%d sent=%d lost=%d rttMs=%gms sentAckDiff=%gms",
116 stats.first,
117 stats.second.sent,
118 stats.second.lost,
119 stats.second.rttUs / 1000.0 / stats.second.nSockets,
120 stats.second.sentAckDiffMs / stats.second.nSockets);
121 }
Hugo Benichi7b314e12018-01-15 21:54:00 +0900122 }
123
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900124 if (!mSocketEntries.empty()) {
125 dw.blankline();
126 dw.println("Socket entries:");
127 for (auto const& stats : mSocketEntries) {
128 dw.println("netId=%u uid=%u cookie=%ld",
129 stats.second.mark.netId, stats.second.uid, stats.first);
130 }
131 }
Hugo Benichi7b314e12018-01-15 21:54:00 +0900132
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900133 SockDiag sd;
134 if (sd.open()) {
135 dw.blankline();
136 dw.println("Current socket dump:");
137 const auto tcpInfoReader = [&dw](Fwmark mark, const struct inet_diag_msg *sockinfo,
138 const struct tcp_info *tcpinfo, uint32_t tcpinfoLen) {
139 tcpInfoPrint(dw, mark, sockinfo, tcpinfo, tcpinfoLen);
140 };
141
142 if (int ret = sd.getLiveTcpInfos(tcpInfoReader)) {
143 ALOGE("Failed to dump TCP socket info: %s", strerror(-ret));
144 }
145 } else {
146 ALOGE("Error opening sock diag for dumping TCP socket info");
Hugo Benichi7b314e12018-01-15 21:54:00 +0900147 }
148
149 dw.decIndent();
150}
151
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900152void TcpSocketMonitor::setPollingInterval(milliseconds nextSleepDurationMs) {
153 std::lock_guard<std::mutex> guard(mLock);
154
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 {
163 std::lock_guard<std::mutex> guard(mLock);
164
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() {
176 std::lock_guard<std::mutex> guard(mLock);
177
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() {
188 std::lock_guard<std::mutex> guard(mLock);
189
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 Benichia9e3c5d2018-01-18 10:33:22 +0900226 mLastPoll = now;
227}
228
229void TcpSocketMonitor::waitForNextPoll() {
230 bool isSuspended;
231 milliseconds nextSleepDurationMs;
232 {
233 std::lock_guard<std::mutex> guard(mLock);
234 isSuspended = mIsSuspended;
235 nextSleepDurationMs= mNextSleepDurationMs;
236 }
237
238 std::unique_lock<std::mutex> ul(mLock);
239 if (isSuspended) {
240 mCv.wait(ul);
241 } else {
242 mCv.wait_for(ul, nextSleepDurationMs);
243 }
244}
245
246bool TcpSocketMonitor::isRunning() {
247 std::lock_guard<std::mutex> guard(mLock);
248 return mIsRunning;
249}
250
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900251void TcpSocketMonitor::updateSocketStats(time_point now, Fwmark mark,
252 const struct inet_diag_msg *sockinfo,
253 const struct tcp_info *tcpinfo,
254 uint32_t tcpinfoLen) NO_THREAD_SAFETY_ANALYSIS {
255 int32_t lastAck = tcpinfo_get(tcpinfo, tcpi_last_ack_recv, tcpinfoLen, 0);
256 int32_t lastSent = tcpinfo_get(tcpinfo, tcpi_last_data_sent, tcpinfoLen, 0);
257 TcpStats diff = {
258 .sent = tcpinfo_get(tcpinfo, tcpi_data_segs_out, tcpinfoLen, 0),
259 .lost = tcpinfo_get(tcpinfo, tcpi_lost, tcpinfoLen, 0),
260 .rttUs = tcpinfo_get(tcpinfo, tcpi_rtt, tcpinfoLen, 0),
261 .sentAckDiffMs = lastAck - lastSent,
262 .nSockets = 1,
263 };
264
265 {
266 // Update socket stats with the newest entry, computing the diff w.r.t the previous entry.
267 const uint64_t cookie = (static_cast<uint64_t>(sockinfo->id.idiag_cookie[0]) << 32)
268 | static_cast<uint64_t>(sockinfo->id.idiag_cookie[1]);
269 const SocketEntry previous = mSocketEntries[cookie];
270 mSocketEntries[cookie] = {
271 .sent = diff.sent,
272 .lost = diff.lost,
273 .lastUpdate = now,
274 .mark = mark,
275 .uid = sockinfo->idiag_uid,
276 };
277
278 diff.sent -= previous.sent;
279 diff.lost -= previous.lost;
280 }
281
282 {
283 // Aggregate the diff per network id.
284 auto& stats = mNetworkStats[mark.netId];
285 stats.sent += diff.sent;
286 stats.lost += diff.lost;
287 stats.rttUs += diff.rttUs;
288 stats.sentAckDiffMs += diff.sentAckDiffMs;
289 stats.nSockets += diff.nSockets;
290 }
291}
292
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900293TcpSocketMonitor::TcpSocketMonitor() {
294 std::lock_guard<std::mutex> guard(mLock);
295
296 mNextSleepDurationMs = kDefaultPollingInterval;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900297 mIsRunning = true;
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900298 mIsSuspended = true;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900299 mPollingThread = std::thread([this] {
300 while (isRunning()) {
301 poll();
302 waitForNextPoll();
303 }
304 });
305}
306
307TcpSocketMonitor::~TcpSocketMonitor() {
308 {
309 std::lock_guard<std::mutex> guard(mLock);
310 mIsRunning = false;
311 mIsSuspended = true;
312 }
313 mCv.notify_all();
314 mPollingThread.join();
315}
316
Hugo Benichi7b314e12018-01-15 21:54:00 +0900317} // namespace net
318} // namespace android