• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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