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 : zero)
76
tcpInfoPrint(DumpWriter & dw,Fwmark mark,const struct inet_diag_msg * sockinfo,const struct tcp_info * tcpinfo,uint32_t tcpinfoLen)77 static void tcpInfoPrint(DumpWriter &dw, Fwmark mark, const struct inet_diag_msg *sockinfo,
78 const struct tcp_info *tcpinfo, uint32_t tcpinfoLen) {
79 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));
83
84 dw.println(
85 "netId=%d uid=%u mark=0x%x saddr=%s daddr=%s sport=%u dport=%u tcp_state=%s(%u) "
86 "rtt=%gms sent=%u lost=%u",
87 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,
95 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));
98 }
99
100 const String16 TcpSocketMonitor::DUMP_KEYWORD = String16("tcp_socket_info");
101 const milliseconds TcpSocketMonitor::kDefaultPollingInterval = milliseconds(30000);
102
dump(DumpWriter & dw)103 void TcpSocketMonitor::dump(DumpWriter& dw) {
104 std::lock_guard guard(mLock);
105
106 dw.println("TcpSocketMonitor");
107 ScopedIndent tcpSocketMonitorDetails(dw);
108
109 const auto now = steady_clock::now();
110 const auto d = duration_cast<milliseconds>(now - mLastPoll);
111 dw.println("running=%d, suspended=%d, last poll %lld ms ago",
112 mIsRunning, mIsSuspended, d.count());
113
114 if (!mNetworkStats.empty()) {
115 dw.blankline();
116 dw.println("Network stats:");
117 for (const std::pair<const uint32_t, TcpStats>& stats : mNetworkStats) {
118 if (stats.second.nSockets == 0) {
119 continue;
120 }
121 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);
127 }
128 }
129
130 if (!mSocketEntries.empty()) {
131 dw.blankline();
132 dw.println("Socket entries:");
133 for (const std::pair<const uint64_t, SocketEntry>& stats : mSocketEntries) {
134 dw.println("netId=%u uid=%u cookie=%" PRIu64, stats.second.mark.netId, stats.second.uid,
135 stats.first);
136 }
137 }
138
139 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");
153 }
154 }
155
setPollingInterval(milliseconds nextSleepDurationMs)156 void TcpSocketMonitor::setPollingInterval(milliseconds nextSleepDurationMs) {
157 std::lock_guard guard(mLock);
158
159 mNextSleepDurationMs = nextSleepDurationMs;
160
161 ALOGD("tcpinfo polling interval set to %lld ms", mNextSleepDurationMs.count());
162 }
163
resumePolling()164 void TcpSocketMonitor::resumePolling() {
165 bool wasSuspended;
166 {
167 std::lock_guard guard(mLock);
168
169 wasSuspended = mIsSuspended;
170 mIsSuspended = false;
171 ALOGD("resuming tcpinfo polling (interval=%lldms)", mNextSleepDurationMs.count());
172 }
173
174 if (wasSuspended) {
175 mCv.notify_all();
176 }
177 }
178
suspendPolling()179 void TcpSocketMonitor::suspendPolling() {
180 std::lock_guard guard(mLock);
181
182 bool wasSuspended = mIsSuspended;
183 mIsSuspended = true;
184 ALOGD("suspending tcpinfo polling");
185
186 if (!wasSuspended) {
187 mSocketEntries.clear();
188 }
189 }
190
poll()191 void TcpSocketMonitor::poll() {
192 std::lock_guard guard(mLock);
193
194 if (mIsSuspended) {
195 return;
196 }
197
198 SockDiag sd;
199 if (!sd.open()) {
200 ALOGE("Error opening sock diag for polling TCP socket info");
201 return;
202 }
203
204 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 {
208 if (sockinfo == nullptr || tcpinfo == nullptr || tcpinfoLen == 0 || mark.intValue == 0) {
209 return;
210 }
211 updateSocketStats(now, mark, sockinfo, tcpinfo, tcpinfoLen);
212 };
213
214 // Reset mNetworkStats
215 mNetworkStats.clear();
216
217 if (int ret = sd.getLiveTcpInfos(tcpInfoReader)) {
218 ALOGE("Failed to poll TCP socket info: %s", strerror(-ret));
219 return;
220 }
221
222 // 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 }
230
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
252 mLastPoll = now;
253 }
254
waitForNextPoll()255 void TcpSocketMonitor::waitForNextPoll() {
256 bool isSuspended;
257 milliseconds nextSleepDurationMs;
258 {
259 std::lock_guard guard(mLock);
260 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
isRunning()272 bool TcpSocketMonitor::isRunning() {
273 std::lock_guard guard(mLock);
274 return mIsRunning;
275 }
276
updateSocketStats(time_point now,Fwmark mark,const struct inet_diag_msg * sockinfo,const struct tcp_info * tcpinfo,uint32_t tcpinfoLen)277 void 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 {
281 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);
283 TcpStats diff = {
284 .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),
287 .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
TcpSocketMonitor()319 TcpSocketMonitor::TcpSocketMonitor() {
320 std::lock_guard guard(mLock);
321
322 mNextSleepDurationMs = kDefaultPollingInterval;
323 mIsRunning = true;
324 mIsSuspended = true;
325 mPollingThread = std::thread([this] {
326 (void) this;
327 while (isRunning()) {
328 poll();
329 waitForNextPoll();
330 }
331 });
332 }
333
~TcpSocketMonitor()334 TcpSocketMonitor::~TcpSocketMonitor() {
335 {
336 std::lock_guard guard(mLock);
337 mIsRunning = false;
338 mIsSuspended = true;
339 }
340 mCv.notify_all();
341 mPollingThread.join();
342 }
343
344 } // namespace net
345 } // namespace android
346