blob: 5478e5d7c9a71d8baecdf443ed3a88f06485363c [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 "DumpWriter.h"
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090030#include "SockDiag.h"
31#include "TcpSocketMonitor.h"
32
Hugo Benichi7b314e12018-01-15 21:54:00 +090033namespace android {
34namespace net {
35
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090036using std::chrono::duration_cast;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090037using std::chrono::steady_clock;
38
Hugo Benichi7b314e12018-01-15 21:54:00 +090039constexpr const char* getTcpStateName(int t) {
40 switch (t) {
41 case TCP_ESTABLISHED:
42 return "ESTABLISHED";
43 case TCP_SYN_SENT:
44 return "SYN-SENT";
45 case TCP_SYN_RECV:
46 return "SYN-RECV";
47 case TCP_FIN_WAIT1:
48 return "FIN-WAIT-1";
49 case TCP_FIN_WAIT2:
50 return "FIN-WAIT-2";
51 case TCP_TIME_WAIT:
52 return "TIME-WAIT";
53 case TCP_CLOSE:
54 return "CLOSE";
55 case TCP_CLOSE_WAIT:
56 return "CLOSE-WAIT";
57 case TCP_LAST_ACK:
58 return "LAST-ACK";
59 case TCP_LISTEN:
60 return "LISTEN";
61 case TCP_CLOSING:
62 return "CLOSING";
63 default:
64 return "UNKNOWN";
65 }
66}
67
Hugo Benichi54bfc7c2018-01-23 14:16:52 +090068// Helper macro for reading fields into struct tcp_info and handling different struct tcp_info
69// versions in the kernel.
Hugo Benichi321b22c2018-01-30 11:37:27 +090070#define TCPINFO_GET(ptr, fld, len, zero) \
71 (((ptr) != nullptr && (offsetof(struct tcp_info, fld) + sizeof((ptr)->fld)) < len) ? \
72 (ptr)->fld : zero)
Hugo Benichi54bfc7c2018-01-23 14:16:52 +090073
Hugo Benichicbaa36b2018-01-17 12:11:43 +090074static void tcpInfoPrint(DumpWriter &dw, Fwmark mark, const struct inet_diag_msg *sockinfo,
Hugo Benichi54bfc7c2018-01-23 14:16:52 +090075 const struct tcp_info *tcpinfo, uint32_t tcpinfoLen) {
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090076 char saddr[INET6_ADDRSTRLEN] = {};
77 char daddr[INET6_ADDRSTRLEN] = {};
78 inet_ntop(sockinfo->idiag_family, &(sockinfo->id.idiag_src), saddr, sizeof(saddr));
79 inet_ntop(sockinfo->idiag_family, &(sockinfo->id.idiag_dst), daddr, sizeof(daddr));
Hugo Benichi7b314e12018-01-15 21:54:00 +090080
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090081 dw.println(
82 "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 +090083 "rtt=%gms sent=%u lost=%u",
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090084 mark.netId,
85 sockinfo->idiag_uid,
86 mark.intValue,
87 saddr,
88 daddr,
89 ntohs(sockinfo->id.idiag_sport),
90 ntohs(sockinfo->id.idiag_dport),
91 getTcpStateName(sockinfo->idiag_state), sockinfo->idiag_state,
Hugo Benichi321b22c2018-01-30 11:37:27 +090092 TCPINFO_GET(tcpinfo, tcpi_rtt, tcpinfoLen, 0) / 1000.0,
93 TCPINFO_GET(tcpinfo, tcpi_segs_out, tcpinfoLen, 0),
94 TCPINFO_GET(tcpinfo, tcpi_lost, tcpinfoLen, 0));
Hugo Benichi7b314e12018-01-15 21:54:00 +090095}
96
97const String16 TcpSocketMonitor::DUMP_KEYWORD = String16("tcp_socket_info");
Hugo Benichia9e3c5d2018-01-18 10:33:22 +090098const milliseconds TcpSocketMonitor::kDefaultPollingInterval = milliseconds(30000);
Hugo Benichi7b314e12018-01-15 21:54:00 +090099
100void TcpSocketMonitor::dump(DumpWriter& dw) {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900101 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900102
Hugo Benichi7b314e12018-01-15 21:54:00 +0900103 dw.println("TcpSocketMonitor");
Erik Klineb31fd692018-06-06 20:50:11 +0900104 ScopedIndent tcpSocketMonitorDetails(dw);
Hugo Benichi7b314e12018-01-15 21:54:00 +0900105
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900106 const auto now = steady_clock::now();
107 const auto d = duration_cast<milliseconds>(now - mLastPoll);
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900108 dw.println("running=%d, suspended=%d, last poll %lld ms ago",
109 mIsRunning, mIsSuspended, d.count());
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900110
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900111 if (!mNetworkStats.empty()) {
112 dw.blankline();
113 dw.println("Network stats:");
Bernie Innocenti6e686962018-09-12 17:43:01 +0900114 for (const std::pair<const uint32_t, TcpStats>& stats : mNetworkStats) {
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900115 if (stats.second.nSockets == 0) {
116 continue;
117 }
Erik Klineb31fd692018-06-06 20:50:11 +0900118 dw.println("netId=%d sent=%d lost=%d rttMs=%gms sentAckDiff=%dms",
119 stats.first,
120 stats.second.sent,
121 stats.second.lost,
122 stats.second.rttUs / 1000.0 / stats.second.nSockets,
123 stats.second.sentAckDiffMs / stats.second.nSockets);
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900124 }
Hugo Benichi7b314e12018-01-15 21:54:00 +0900125 }
126
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900127 if (!mSocketEntries.empty()) {
128 dw.blankline();
129 dw.println("Socket entries:");
Bernie Innocenti6e686962018-09-12 17:43:01 +0900130 for (const std::pair<const uint64_t, SocketEntry>& stats : mSocketEntries) {
Bernie Innocenti1bcc25a2018-08-10 17:15:00 +0900131 dw.println("netId=%u uid=%u cookie=%" PRIu64, stats.second.mark.netId, stats.second.uid,
132 stats.first);
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900133 }
134 }
Hugo Benichi7b314e12018-01-15 21:54:00 +0900135
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900136 SockDiag sd;
137 if (sd.open()) {
138 dw.blankline();
139 dw.println("Current socket dump:");
140 const auto tcpInfoReader = [&dw](Fwmark mark, const struct inet_diag_msg *sockinfo,
141 const struct tcp_info *tcpinfo, uint32_t tcpinfoLen) {
142 tcpInfoPrint(dw, mark, sockinfo, tcpinfo, tcpinfoLen);
143 };
144
145 if (int ret = sd.getLiveTcpInfos(tcpInfoReader)) {
146 ALOGE("Failed to dump TCP socket info: %s", strerror(-ret));
147 }
148 } else {
149 ALOGE("Error opening sock diag for dumping TCP socket info");
Hugo Benichi7b314e12018-01-15 21:54:00 +0900150 }
Hugo Benichi7b314e12018-01-15 21:54:00 +0900151}
152
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900153void TcpSocketMonitor::setPollingInterval(milliseconds nextSleepDurationMs) {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900154 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900155
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 {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900164 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900165
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() {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900177 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900178
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() {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900189 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900190
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 {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900256 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900257 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() {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900270 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900271 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 {
Hugo Benichi321b22c2018-01-30 11:37:27 +0900278 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);
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900280 TcpStats diff = {
Hugo Benichi321b22c2018-01-30 11:37:27 +0900281 .sent = TCPINFO_GET(tcpinfo, tcpi_segs_out, tcpinfoLen, 0),
282 .lost = TCPINFO_GET(tcpinfo, tcpi_lost, tcpinfoLen, 0),
283 .rttUs = TCPINFO_GET(tcpinfo, tcpi_rtt, tcpinfoLen, 0),
Hugo Benichi4596d2f2018-01-19 13:36:29 +0900284 .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() {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900317 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900318
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;
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900324 while (isRunning()) {
325 poll();
326 waitForNextPoll();
327 }
328 });
329}
330
331TcpSocketMonitor::~TcpSocketMonitor() {
332 {
Bernie Innocentiabf8a342018-08-10 15:17:16 +0900333 std::lock_guard guard(mLock);
Hugo Benichia9e3c5d2018-01-18 10:33:22 +0900334 mIsRunning = false;
335 mIsSuspended = true;
336 }
337 mCv.notify_all();
338 mPollingThread.join();
339}
340
Hugo Benichi7b314e12018-01-15 21:54:00 +0900341} // namespace net
342} // namespace android