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