1 /*
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
19 #include <chrono>
20 #include <cinttypes>
21 #include <thread>
22 #include <vector>
23
24 #include <arpa/inet.h>
25 #include <netinet/tcp.h>
26 #include <linux/tcp.h>
27
28 #include "Controllers.h"
29 #include "SockDiag.h"
30 #include "TcpSocketMonitor.h"
31 #include "netdutils/DumpWriter.h"
32
33 using android::netdutils::DumpWriter;
34 using android::netdutils::ScopedIndent;
35
36 namespace android {
37 namespace net {
38
39 using std::chrono::duration_cast;
40 using std::chrono::steady_clock;
41
getTcpStateName(int t)42 constexpr 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
71 // Helper macro for reading fields into struct tcp_info and handling different struct tcp_info
72 // versions in the kernel.
73 #define TCPINFO_GET(ptr, fld, len, zero) \
74 (((ptr) != nullptr && (offsetof(struct tcp_info, fld) + sizeof((ptr)->fld)) < (len)) \
75 ? (ptr)->fld \
76 : (zero))
77
tcpInfoPrint(DumpWriter & dw,Fwmark mark,const struct inet_diag_msg * sockinfo,const struct tcp_info * tcpinfo,uint32_t tcpinfoLen)78 static void tcpInfoPrint(DumpWriter &dw, Fwmark mark, const struct inet_diag_msg *sockinfo,
79 const struct tcp_info *tcpinfo, uint32_t tcpinfoLen) {
80 char saddr[INET6_ADDRSTRLEN] = {};
81 char daddr[INET6_ADDRSTRLEN] = {};
82 inet_ntop(sockinfo->idiag_family, &(sockinfo->id.idiag_src), saddr, sizeof(saddr));
83 inet_ntop(sockinfo->idiag_family, &(sockinfo->id.idiag_dst), daddr, sizeof(daddr));
84
85 dw.println(
86 "netId=%d uid=%u mark=0x%x saddr=%s daddr=%s sport=%u dport=%u tcp_state=%s(%u) "
87 "rtt=%gms sent=%u lost=%u",
88 mark.netId,
89 sockinfo->idiag_uid,
90 mark.intValue,
91 saddr,
92 daddr,
93 ntohs(sockinfo->id.idiag_sport),
94 ntohs(sockinfo->id.idiag_dport),
95 getTcpStateName(sockinfo->idiag_state), sockinfo->idiag_state,
96 TCPINFO_GET(tcpinfo, tcpi_rtt, tcpinfoLen, 0) / 1000.0,
97 TCPINFO_GET(tcpinfo, tcpi_segs_out, tcpinfoLen, 0),
98 TCPINFO_GET(tcpinfo, tcpi_lost, tcpinfoLen, 0));
99 }
100
101 const String16 TcpSocketMonitor::DUMP_KEYWORD = String16("tcp_socket_info");
102 const milliseconds TcpSocketMonitor::kDefaultPollingInterval = milliseconds(30000);
103
dump(DumpWriter & dw)104 void TcpSocketMonitor::dump(DumpWriter& dw) {
105 std::lock_guard guard(mLock);
106
107 dw.println("TcpSocketMonitor");
108 ScopedIndent tcpSocketMonitorDetails(dw);
109
110 const auto now = steady_clock::now();
111 const auto d = duration_cast<milliseconds>(now - mLastPoll);
112 dw.println("running=%d, suspended=%d, last poll %lld ms ago",
113 mIsRunning, mIsSuspended, d.count());
114
115 if (!mNetworkStats.empty()) {
116 dw.blankline();
117 dw.println("Network stats:");
118 for (const std::pair<const uint32_t, TcpStats>& stats : mNetworkStats) {
119 if (stats.second.nSockets == 0) {
120 continue;
121 }
122 dw.println("netId=%d sent=%d lost=%d rttMs=%gms sentAckDiff=%dms",
123 stats.first,
124 stats.second.sent,
125 stats.second.lost,
126 stats.second.rttUs / 1000.0 / stats.second.nSockets,
127 stats.second.sentAckDiffMs / stats.second.nSockets);
128 }
129 }
130
131 if (!mSocketEntries.empty()) {
132 dw.blankline();
133 dw.println("Socket entries:");
134 for (const std::pair<const uint64_t, SocketEntry>& stats : mSocketEntries) {
135 dw.println("netId=%u uid=%u cookie=%" PRIu64, stats.second.mark.netId, stats.second.uid,
136 stats.first);
137 }
138 }
139
140 SockDiag sd;
141 if (sd.open()) {
142 dw.blankline();
143 dw.println("Current socket dump:");
144 const auto tcpInfoReader = [&dw](Fwmark mark, const struct inet_diag_msg *sockinfo,
145 const struct tcp_info *tcpinfo, uint32_t tcpinfoLen) {
146 tcpInfoPrint(dw, mark, sockinfo, tcpinfo, tcpinfoLen);
147 };
148
149 if (int ret = sd.getLiveTcpInfos(tcpInfoReader)) {
150 ALOGE("Failed to dump TCP socket info: %s", strerror(-ret));
151 }
152 } else {
153 ALOGE("Error opening sock diag for dumping TCP socket info");
154 }
155 }
156
setPollingInterval(milliseconds nextSleepDurationMs)157 void TcpSocketMonitor::setPollingInterval(milliseconds nextSleepDurationMs) {
158 std::lock_guard guard(mLock);
159
160 mNextSleepDurationMs = nextSleepDurationMs;
161
162 ALOGD("tcpinfo polling interval set to %lld ms", mNextSleepDurationMs.count());
163 }
164
resumePolling()165 void TcpSocketMonitor::resumePolling() {
166 bool wasSuspended;
167 {
168 std::lock_guard guard(mLock);
169
170 wasSuspended = mIsSuspended;
171 mIsSuspended = false;
172 ALOGD("resuming tcpinfo polling (interval=%lldms)", mNextSleepDurationMs.count());
173 }
174
175 if (wasSuspended) {
176 mCv.notify_all();
177 }
178 }
179
suspendPolling()180 void TcpSocketMonitor::suspendPolling() {
181 std::lock_guard guard(mLock);
182
183 bool wasSuspended = mIsSuspended;
184 mIsSuspended = true;
185 ALOGD("suspending tcpinfo polling");
186
187 if (!wasSuspended) {
188 mSocketEntries.clear();
189 }
190 }
191
poll()192 void TcpSocketMonitor::poll() {
193 std::lock_guard guard(mLock);
194
195 if (mIsSuspended) {
196 return;
197 }
198
199 SockDiag sd;
200 if (!sd.open()) {
201 ALOGE("Error opening sock diag for polling TCP socket info");
202 return;
203 }
204
205 const auto now = steady_clock::now();
206 const auto tcpInfoReader = [this, now](Fwmark mark, const struct inet_diag_msg *sockinfo,
207 const struct tcp_info *tcpinfo,
208 uint32_t tcpinfoLen) NO_THREAD_SAFETY_ANALYSIS {
209 if (sockinfo == nullptr || tcpinfo == nullptr || tcpinfoLen == 0 || mark.intValue == 0) {
210 return;
211 }
212 updateSocketStats(now, mark, sockinfo, tcpinfo, tcpinfoLen);
213 };
214
215 // Reset mNetworkStats
216 mNetworkStats.clear();
217
218 if (int ret = sd.getLiveTcpInfos(tcpInfoReader)) {
219 ALOGE("Failed to poll TCP socket info: %s", strerror(-ret));
220 return;
221 }
222
223 // Remove any SocketEntry not updated
224 for (auto it = mSocketEntries.cbegin(); it != mSocketEntries.cend();) {
225 if (it->second.lastUpdate < now) {
226 it = mSocketEntries.erase(it);
227 } else {
228 it++;
229 }
230 }
231
232 const auto listener = gCtls->eventReporter.getNetdEventListener();
233 if (listener != nullptr) {
234 std::vector<int> netIds;
235 std::vector<int> sentPackets;
236 std::vector<int> lostPackets;
237 std::vector<int> rtts;
238 std::vector<int> sentAckDiffs;
239 for (auto const& stats : mNetworkStats) {
240 int32_t nSockets = stats.second.nSockets;
241 if (nSockets == 0) {
242 continue;
243 }
244 netIds.push_back(stats.first);
245 sentPackets.push_back(stats.second.sent);
246 lostPackets.push_back(stats.second.lost);
247 rtts.push_back(stats.second.rttUs / nSockets);
248 sentAckDiffs.push_back(stats.second.sentAckDiffMs / nSockets);
249 }
250 listener->onTcpSocketStatsEvent(netIds, sentPackets, lostPackets, rtts, sentAckDiffs);
251 }
252
253 mLastPoll = now;
254 }
255
waitForNextPoll()256 void TcpSocketMonitor::waitForNextPoll() {
257 bool isSuspended;
258 milliseconds nextSleepDurationMs;
259 {
260 std::lock_guard guard(mLock);
261 isSuspended = mIsSuspended;
262 nextSleepDurationMs= mNextSleepDurationMs;
263 }
264
265 std::unique_lock<std::mutex> ul(mLock);
266 if (isSuspended) {
267 mCv.wait(ul);
268 } else {
269 mCv.wait_for(ul, nextSleepDurationMs);
270 }
271 }
272
isRunning()273 bool TcpSocketMonitor::isRunning() {
274 std::lock_guard guard(mLock);
275 return mIsRunning;
276 }
277
updateSocketStats(time_point now,Fwmark mark,const struct inet_diag_msg * sockinfo,const struct tcp_info * tcpinfo,uint32_t tcpinfoLen)278 void TcpSocketMonitor::updateSocketStats(time_point now, Fwmark mark,
279 const struct inet_diag_msg *sockinfo,
280 const struct tcp_info *tcpinfo,
281 uint32_t tcpinfoLen) NO_THREAD_SAFETY_ANALYSIS {
282 int32_t lastAck = TCPINFO_GET(tcpinfo, tcpi_last_ack_recv, tcpinfoLen, 0);
283 int32_t lastSent = TCPINFO_GET(tcpinfo, tcpi_last_data_sent, tcpinfoLen, 0);
284 TcpStats diff = {
285 .sent = TCPINFO_GET(tcpinfo, tcpi_segs_out, tcpinfoLen, 0),
286 .lost = TCPINFO_GET(tcpinfo, tcpi_lost, tcpinfoLen, 0),
287 .rttUs = TCPINFO_GET(tcpinfo, tcpi_rtt, tcpinfoLen, 0),
288 .sentAckDiffMs = lastAck - lastSent,
289 .nSockets = 1,
290 };
291
292 {
293 // Update socket stats with the newest entry, computing the diff w.r.t the previous entry.
294 const uint64_t cookie = (static_cast<uint64_t>(sockinfo->id.idiag_cookie[0]) << 32)
295 | static_cast<uint64_t>(sockinfo->id.idiag_cookie[1]);
296 const SocketEntry previous = mSocketEntries[cookie];
297 mSocketEntries[cookie] = {
298 .sent = diff.sent,
299 .lost = diff.lost,
300 .lastUpdate = now,
301 .mark = mark,
302 .uid = sockinfo->idiag_uid,
303 };
304
305 diff.sent -= previous.sent;
306 diff.lost -= previous.lost;
307 }
308
309 {
310 // Aggregate the diff per network id.
311 auto& stats = mNetworkStats[mark.netId];
312 stats.sent += diff.sent;
313 stats.lost += diff.lost;
314 stats.rttUs += diff.rttUs;
315 stats.sentAckDiffMs += diff.sentAckDiffMs;
316 stats.nSockets += diff.nSockets;
317 }
318 }
319
TcpSocketMonitor()320 TcpSocketMonitor::TcpSocketMonitor() {
321 std::lock_guard guard(mLock);
322
323 mNextSleepDurationMs = kDefaultPollingInterval;
324 mIsRunning = true;
325 mIsSuspended = true;
326 mPollingThread = std::thread([this] {
327 (void) this;
328 while (isRunning()) {
329 poll();
330 waitForNextPoll();
331 }
332 });
333 }
334
~TcpSocketMonitor()335 TcpSocketMonitor::~TcpSocketMonitor() {
336 {
337 std::lock_guard guard(mLock);
338 mIsRunning = false;
339 mIsSuspended = true;
340 }
341 mCv.notify_all();
342 mPollingThread.join();
343 }
344
345 } // namespace net
346 } // namespace android
347