blob: f4b505d5ab86582bf5f5ae4a9f1f163307dfbf53 [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.
Hugo Benichi321b22c2018-01-30 11:37:27 +090073#define TCPINFO_GET(ptr, fld, len, zero) \
74 (((ptr) != nullptr && (offsetof(struct tcp_info, fld) + sizeof((ptr)->fld)) < len) ? \
75 (ptr)->fld : zero)
Hugo Benichi54bfc7c2018-01-23 14:16:52 +090076
Hugo Benichicbaa36b2018-01-17 12:11:43 +090077static void tcpInfoPrint(DumpWriter &dw, Fwmark mark, const struct inet_diag_msg *sockinfo,
Hugo Benichi54bfc7c2018-01-23 14:16:52 +090078 const struct tcp_info *tcpinfo, uint32_t tcpinfoLen) {
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090079 char saddr[INET6_ADDRSTRLEN] = {};
80 char daddr[INET6_ADDRSTRLEN] = {};
81 inet_ntop(sockinfo->idiag_family, &(sockinfo->id.idiag_src), saddr, sizeof(saddr));
82 inet_ntop(sockinfo->idiag_family, &(sockinfo->id.idiag_dst), daddr, sizeof(daddr));
Hugo Benichi7b314e12018-01-15 21:54:00 +090083
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090084 dw.println(
85 "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 +090086 "rtt=%gms sent=%u lost=%u",
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090087 mark.netId,
88 sockinfo->idiag_uid,
89 mark.intValue,
90 saddr,
91 daddr,
92 ntohs(sockinfo->id.idiag_sport),
93 ntohs(sockinfo->id.idiag_dport),
94 getTcpStateName(sockinfo->idiag_state), sockinfo->idiag_state,
Hugo Benichi321b22c2018-01-30 11:37:27 +090095 TCPINFO_GET(tcpinfo, tcpi_rtt, tcpinfoLen, 0) / 1000.0,
96 TCPINFO_GET(tcpinfo, tcpi_segs_out, tcpinfoLen, 0),
97 TCPINFO_GET(tcpinfo, tcpi_lost, tcpinfoLen, 0));
Hugo Benichi7b314e12018-01-15 21:54:00 +090098}
99
100const String16 TcpSocketMonitor::DUMP_KEYWORD = String16("tcp_socket_info");
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900101const milliseconds TcpSocketMonitor::kDefaultPollingInterval = milliseconds(30000);
Hugo Benichi7b314e12018-01-15 21:54:00 +0900102
103void TcpSocketMonitor::dump(DumpWriter& dw) {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900104 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900105
Hugo Benichi7b314e12018-01-15 21:54:00 +0900106 dw.println("TcpSocketMonitor");
Erik Klineb31fd692018-06-06 20:50:11 +0900107 ScopedIndent tcpSocketMonitorDetails(dw);
Hugo Benichi7b314e12018-01-15 21:54:00 +0900108
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900109 const auto now = steady_clock::now();
110 const auto d = duration_cast<milliseconds>(now - mLastPoll);
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900111 dw.println("running=%d, suspended=%d, last poll %lld ms ago",
112 mIsRunning, mIsSuspended, d.count());
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900113
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900114 if (!mNetworkStats.empty()) {
115 dw.blankline();
116 dw.println("Network stats:");
Bernie Innocenti6e686962018-09-12 17:43:01 +0900117 for (const std::pair<const uint32_t, TcpStats>& stats : mNetworkStats) {
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900118 if (stats.second.nSockets == 0) {
119 continue;
120 }
Erik Klineb31fd692018-06-06 20:50:11 +0900121 dw.println("netId=%d sent=%d lost=%d rttMs=%gms sentAckDiff=%dms",
122 stats.first,
123 stats.second.sent,
124 stats.second.lost,
125 stats.second.rttUs / 1000.0 / stats.second.nSockets,
126 stats.second.sentAckDiffMs / stats.second.nSockets);
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900127 }
Hugo Benichi7b314e12018-01-15 21:54:00 +0900128 }
129
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900130 if (!mSocketEntries.empty()) {
131 dw.blankline();
132 dw.println("Socket entries:");
Bernie Innocenti6e686962018-09-12 17:43:01 +0900133 for (const std::pair<const uint64_t, SocketEntry>& stats : mSocketEntries) {
Bernie Innocenti1bcc25a2018-08-10 17:15:00 +0900134 dw.println("netId=%u uid=%u cookie=%" PRIu64, stats.second.mark.netId, stats.second.uid,
135 stats.first);
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900136 }
137 }
Hugo Benichi7b314e12018-01-15 21:54:00 +0900138
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900139 SockDiag sd;
140 if (sd.open()) {
141 dw.blankline();
142 dw.println("Current socket dump:");
143 const auto tcpInfoReader = [&dw](Fwmark mark, const struct inet_diag_msg *sockinfo,
144 const struct tcp_info *tcpinfo, uint32_t tcpinfoLen) {
145 tcpInfoPrint(dw, mark, sockinfo, tcpinfo, tcpinfoLen);
146 };
147
148 if (int ret = sd.getLiveTcpInfos(tcpInfoReader)) {
149 ALOGE("Failed to dump TCP socket info: %s", strerror(-ret));
150 }
151 } else {
152 ALOGE("Error opening sock diag for dumping TCP socket info");
Hugo Benichi7b314e12018-01-15 21:54:00 +0900153 }
Hugo Benichi7b314e12018-01-15 21:54:00 +0900154}
155
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900156void TcpSocketMonitor::setPollingInterval(milliseconds nextSleepDurationMs) {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900157 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900158
159 mNextSleepDurationMs = nextSleepDurationMs;
160
161 ALOGD("tcpinfo polling interval set to %lld ms", mNextSleepDurationMs.count());
162}
163
164void TcpSocketMonitor::resumePolling() {
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900165 bool wasSuspended;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900166 {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900167 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900168
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900169 wasSuspended = mIsSuspended;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900170 mIsSuspended = false;
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900171 ALOGD("resuming tcpinfo polling (interval=%lldms)", mNextSleepDurationMs.count());
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900172 }
173
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900174 if (wasSuspended) {
175 mCv.notify_all();
176 }
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900177}
178
179void TcpSocketMonitor::suspendPolling() {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900180 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900181
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900182 bool wasSuspended = mIsSuspended;
183 mIsSuspended = true;
184 ALOGD("suspending tcpinfo polling");
185
186 if (!wasSuspended) {
187 mSocketEntries.clear();
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900188 }
189}
190
191void TcpSocketMonitor::poll() {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900192 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900193
194 if (mIsSuspended) {
195 return;
196 }
197
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900198 SockDiag sd;
199 if (!sd.open()) {
200 ALOGE("Error opening sock diag for polling TCP socket info");
201 return;
202 }
203
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900204 const auto now = steady_clock::now();
205 const auto tcpInfoReader = [this, now](Fwmark mark, const struct inet_diag_msg *sockinfo,
206 const struct tcp_info *tcpinfo,
207 uint32_t tcpinfoLen) NO_THREAD_SAFETY_ANALYSIS {
Hugo Benichi54bfc7c2018-01-23 14:16:52 +0900208 if (sockinfo == nullptr || tcpinfo == nullptr || tcpinfoLen == 0 || mark.intValue == 0) {
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900209 return;
210 }
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900211 updateSocketStats(now, mark, sockinfo, tcpinfo, tcpinfoLen);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900212 };
213
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900214 // Reset mNetworkStats
215 mNetworkStats.clear();
216
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900217 if (int ret = sd.getLiveTcpInfos(tcpInfoReader)) {
218 ALOGE("Failed to poll TCP socket info: %s", strerror(-ret));
219 return;
220 }
221
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900222 // Remove any SocketEntry not updated
223 for (auto it = mSocketEntries.cbegin(); it != mSocketEntries.cend();) {
224 if (it->second.lastUpdate < now) {
225 it = mSocketEntries.erase(it);
226 } else {
227 it++;
228 }
229 }
Hugo Benichi8c397672018-01-22 22:06:15 +0900230
231 const auto listener = gCtls->eventReporter.getNetdEventListener();
232 if (listener != nullptr) {
233 std::vector<int> netIds;
234 std::vector<int> sentPackets;
235 std::vector<int> lostPackets;
236 std::vector<int> rtts;
237 std::vector<int> sentAckDiffs;
238 for (auto const& stats : mNetworkStats) {
239 int32_t nSockets = stats.second.nSockets;
240 if (nSockets == 0) {
241 continue;
242 }
243 netIds.push_back(stats.first);
244 sentPackets.push_back(stats.second.sent);
245 lostPackets.push_back(stats.second.lost);
246 rtts.push_back(stats.second.rttUs / nSockets);
247 sentAckDiffs.push_back(stats.second.sentAckDiffMs / nSockets);
248 }
249 listener->onTcpSocketStatsEvent(netIds, sentPackets, lostPackets, rtts, sentAckDiffs);
250 }
251
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900252 mLastPoll = now;
253}
254
255void TcpSocketMonitor::waitForNextPoll() {
256 bool isSuspended;
257 milliseconds nextSleepDurationMs;
258 {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900259 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900260 isSuspended = mIsSuspended;
261 nextSleepDurationMs= mNextSleepDurationMs;
262 }
263
264 std::unique_lock<std::mutex> ul(mLock);
265 if (isSuspended) {
266 mCv.wait(ul);
267 } else {
268 mCv.wait_for(ul, nextSleepDurationMs);
269 }
270}
271
272bool TcpSocketMonitor::isRunning() {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900273 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900274 return mIsRunning;
275}
276
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900277void TcpSocketMonitor::updateSocketStats(time_point now, Fwmark mark,
278 const struct inet_diag_msg *sockinfo,
279 const struct tcp_info *tcpinfo,
280 uint32_t tcpinfoLen) NO_THREAD_SAFETY_ANALYSIS {
Hugo Benichi321b22c2018-01-30 11:37:27 +0900281 int32_t lastAck = TCPINFO_GET(tcpinfo, tcpi_last_ack_recv, tcpinfoLen, 0);
282 int32_t lastSent = TCPINFO_GET(tcpinfo, tcpi_last_data_sent, tcpinfoLen, 0);
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900283 TcpStats diff = {
Hugo Benichi321b22c2018-01-30 11:37:27 +0900284 .sent = TCPINFO_GET(tcpinfo, tcpi_segs_out, tcpinfoLen, 0),
285 .lost = TCPINFO_GET(tcpinfo, tcpi_lost, tcpinfoLen, 0),
286 .rttUs = TCPINFO_GET(tcpinfo, tcpi_rtt, tcpinfoLen, 0),
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900287 .sentAckDiffMs = lastAck - lastSent,
288 .nSockets = 1,
289 };
290
291 {
292 // Update socket stats with the newest entry, computing the diff w.r.t the previous entry.
293 const uint64_t cookie = (static_cast<uint64_t>(sockinfo->id.idiag_cookie[0]) << 32)
294 | static_cast<uint64_t>(sockinfo->id.idiag_cookie[1]);
295 const SocketEntry previous = mSocketEntries[cookie];
296 mSocketEntries[cookie] = {
297 .sent = diff.sent,
298 .lost = diff.lost,
299 .lastUpdate = now,
300 .mark = mark,
301 .uid = sockinfo->idiag_uid,
302 };
303
304 diff.sent -= previous.sent;
305 diff.lost -= previous.lost;
306 }
307
308 {
309 // Aggregate the diff per network id.
310 auto& stats = mNetworkStats[mark.netId];
311 stats.sent += diff.sent;
312 stats.lost += diff.lost;
313 stats.rttUs += diff.rttUs;
314 stats.sentAckDiffMs += diff.sentAckDiffMs;
315 stats.nSockets += diff.nSockets;
316 }
317}
318
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900319TcpSocketMonitor::TcpSocketMonitor() {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900320 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900321
322 mNextSleepDurationMs = kDefaultPollingInterval;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900323 mIsRunning = true;
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900324 mIsSuspended = true;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900325 mPollingThread = std::thread([this] {
Lorenzo Colitti15fc2d32018-01-26 10:07:20 +0900326 (void) this;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900327 while (isRunning()) {
328 poll();
329 waitForNextPoll();
330 }
331 });
332}
333
334TcpSocketMonitor::~TcpSocketMonitor() {
335 {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900336 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900337 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