1 /*
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <unistd.h>
17
18 #include <cstdint>
19 #include <sys/types.h>
20 #include <sys/socket.h>
21 #include <netdb.h>
22 #include <ctime>
23 #include <cerrno>
24 #include <cstdlib>
25 #include <cstring>
26 #include <cmath>
27 #include <vector>
28 #include <algorithm>
29 #include <functional>
30 #include <netinet/ip_icmp.h>
31 #include <poll.h>
32
33 #include "netmanager_base_common_utils.h"
34 #include "net_manager_constants.h"
35 #include "net_probe.h"
36
37 namespace OHOS {
38 namespace NetManagerStandard {
39
40 constexpr int32_t PING_DATA_SIZE = 64;
41 constexpr int32_t MAX_PING_DURATION = 1000;
42
43 constexpr uint8_t ICMPV4_ECHO_REQUEST = 8u;
44 constexpr uint8_t ICMPV6_ECHO_REQUEST = 128u;
45
46 constexpr int32_t MSEC_PER_SECOND = 1000;
47 constexpr int32_t NSEC_PER_MSEC = 1000000;
48
49 constexpr int32_t PERCENTAGE = 100u;
50
51 constexpr uint16_t PING_CHECKSUM_MASK = 255;
52
53 constexpr uint32_t SQUARE = 2;
54
55 #if __BYTE_ORDER == __BIG_ENDIAN
56 #define IS_BIG_ENDIAN 1
57 #else
58 #define IS_BIG_ENDIAN 0
59 #endif
60
PingChecksum(uint16_t * data,int32_t len)61 static uint16_t PingChecksum(uint16_t *data, int32_t len)
62 {
63 uint16_t u = 0;
64 uint16_t d;
65
66 while (len > 0) {
67 d = *data++;
68
69 if (len == 1) {
70 d &= PING_CHECKSUM_MASK << IS_BIG_ENDIAN;
71 }
72
73 u += d;
74 if (d >= u) {
75 u++;
76 }
77
78 len -= sizeof(uint16_t);
79 }
80
81 return u;
82 }
83
Now(void)84 static int64_t Now(void)
85 {
86 struct timespec ts;
87
88 clock_gettime(CLOCK_MONOTONIC, &ts);
89
90 return ts.tv_sec * MSEC_PER_SECOND + ts.tv_nsec / NSEC_PER_MSEC;
91 }
92
SendRequest(int32_t fd,iovec & iov,struct addrinfo * ai,int16_t seq,int64_t time)93 static int32_t SendRequest(int32_t fd, iovec &iov, struct addrinfo *ai, int16_t seq, int64_t time)
94 {
95 struct icmphdr *ih = reinterpret_cast<struct icmphdr*>(iov.iov_base);
96 int64_t *timePtr = nullptr;
97
98 ih->code = 0;
99 ih->type = (ai->ai_family == AF_INET) ? ICMPV4_ECHO_REQUEST : ICMPV6_ECHO_REQUEST;
100 ih->un.echo.sequence = seq;
101 ih->un.echo.id = getpid();
102
103 timePtr = reinterpret_cast<int64_t *>(ih + 1);
104 *timePtr = time;
105
106 ih->checksum = PingChecksum(reinterpret_cast<uint16_t *>(iov.iov_base),
107 static_cast<int32_t>(iov.iov_len));
108 return sendto(fd, iov.iov_base, iov.iov_len, 0, ai->ai_addr, ai->ai_addrlen);
109 }
110
WaitResponse(int32_t fd,iovec & iov,int64_t & respTime,int32_t timeout)111 static int32_t WaitResponse(int32_t fd, iovec &iov, int64_t &respTime, int32_t timeout)
112 {
113 int32_t rc;
114
115 if (timeout >= 0) {
116 struct pollfd pFd;
117
118 pFd.fd = fd;
119 pFd.events = POLLIN;
120 if (poll(&pFd, 1, timeout) <= 0) {
121 return 0;
122 }
123 }
124
125 rc = recv(fd, iov.iov_base, iov.iov_len, 0);
126 if (rc >= static_cast<int32_t>(sizeof(struct icmphdr) + sizeof(int64_t))) {
127 struct icmphdr *ih = reinterpret_cast<struct icmphdr*>(iov.iov_base);
128 int64_t *timePtr = reinterpret_cast<int64_t *>(ih + 1);
129
130 respTime = *timePtr;
131 }
132
133 return rc;
134 }
135
136 /*
137 * When error like no routes or sendto failed occurs during ping operations, function will return success
138 * but with 100% packet loss.
139 */
140 constexpr uint8_t LOSS_RATE_ALL = 100u;
ResetToFullLoss(NetConn_ProbeResultInfo & result,uint32_t duration)141 static int32_t ResetToFullLoss(NetConn_ProbeResultInfo &result, uint32_t duration)
142 {
143 result.lossRate = LOSS_RATE_ALL;
144 result.rtt[NETCONN_RTT_MIN] = static_cast<uint32_t>(duration * MSEC_PER_SECOND);
145 result.rtt[NETCONN_RTT_MAX] = static_cast<uint32_t>(duration * MSEC_PER_SECOND);
146 result.rtt[NETCONN_RTT_AVG] = static_cast<uint32_t>(duration * MSEC_PER_SECOND);
147 result.rtt[NETCONN_RTT_STD] = 0u;
148
149 return 0;
150 }
151
CalcPingRttStd(std::vector<int64_t> & timesTake,uint32_t totalRecv,uint32_t rttAvg)152 static uint32_t CalcPingRttStd(std::vector<int64_t> ×Take, uint32_t totalRecv, uint32_t rttAvg)
153 {
154 int64_t sumDelay = 0;
155
156 for (uint32_t i = 0u; i < totalRecv; ++i) {
157 sumDelay += pow(rttAvg - timesTake[i], SQUARE);
158 }
159
160 return (totalRecv > 0) ? static_cast<uint32_t>(sqrt(static_cast<uint32_t>(sumDelay) / totalRecv)) : 0u;
161 }
162
163 const int64_t SEND_INTERVAL = 1000; /* ms */
164 const int64_t WAIT_INTERVAL = 1000; /* ms */
165
DoPing(int32_t s,struct addrinfo * ai,uint32_t duration,NetConn_ProbeResultInfo & result)166 static int32_t DoPing(int32_t s, struct addrinfo *ai, uint32_t duration, NetConn_ProbeResultInfo &result)
167 {
168 uint8_t buffer[sizeof(struct icmphdr) + PING_DATA_SIZE] = {0}; /* icmp header and data */
169 iovec iov = {reinterpret_cast<void *>(buffer), sizeof(buffer)};
170 std::vector<int64_t> timesTake(duration);
171 uint16_t seq = 0;
172 int64_t timeNextSend = 0; /* ms, initial to 0, send request immediately */
173 int64_t timeWait; /* ms */
174 uint32_t totalSend = 0;
175 uint32_t totalRecv = 0;
176 int64_t sumDelay = 0;
177 int64_t respTime = 0;
178 int32_t rc = 0;
179
180 while (totalSend < duration) {
181 int64_t timeNow = Now();
182 if (timeNextSend < timeNow) {
183 rc = SendRequest(s, iov, ai, seq++, timeNow);
184 if (rc < 0) {
185 continue;
186 }
187
188 totalSend += 1;
189
190 timeNextSend = timeNow + SEND_INTERVAL;
191 timeWait = WAIT_INTERVAL;
192 } else {
193 timeWait = timeNextSend - timeNow;
194 if (timeWait <= 0) {
195 timeWait = 1;
196 }
197 }
198
199 rc = WaitResponse(s, iov, respTime, static_cast<int>(timeWait));
200 if (rc <= 0) {
201 continue;
202 }
203
204 int64_t currentDelay = Now() - respTime;
205 timesTake[totalRecv++] = currentDelay;
206 sumDelay += currentDelay;
207
208 result.rtt[NETCONN_RTT_MAX] = std::max(result.rtt[NETCONN_RTT_MAX], static_cast<uint32_t>(currentDelay));
209 result.rtt[NETCONN_RTT_MIN] = std::min(result.rtt[NETCONN_RTT_MIN], static_cast<uint32_t>(currentDelay));
210 }
211
212 if (totalRecv == 0) {
213 return ResetToFullLoss(result, duration);
214 }
215
216 result.rtt[NETCONN_RTT_AVG] = (totalRecv > 0) ? (static_cast<uint32_t>(sumDelay) / totalRecv) : 0u;
217 result.rtt[NETCONN_RTT_STD] = CalcPingRttStd(timesTake, totalRecv, result.rtt[NETCONN_RTT_AVG]);
218 result.lossRate = static_cast<uint8_t>((totalSend > 0) ? ((totalSend - totalRecv) * PERCENTAGE / totalSend) : 0u);
219
220 return rc;
221 }
222
InitialProbeResult(NetConn_ProbeResultInfo & result)223 static void InitialProbeResult(NetConn_ProbeResultInfo &result)
224 {
225 result.rtt[NETCONN_RTT_MIN] = UINT_MAX;
226 result.rtt[NETCONN_RTT_MAX] = 0u;
227 result.rtt[NETCONN_RTT_AVG] = 0u;
228 result.rtt[NETCONN_RTT_STD] = 0u;
229 result.lossRate = 0u;
230 }
231
QueryProbeResult(std::string & dest,int32_t duration,NetConn_ProbeResultInfo & result)232 int32_t NetProbe::QueryProbeResult(std::string &dest, int32_t duration, NetConn_ProbeResultInfo &result)
233 {
234 struct addrinfo info = {0};
235 struct addrinfo *ai = nullptr;
236 int32_t family = AF_UNSPEC;
237 int32_t rc;
238
239 if ((duration <= 0) || (duration > MAX_PING_DURATION)) {
240 return NETMANAGER_ERR_PARAMETER_ERROR;
241 }
242
243 info.ai_family = family;
244 rc = getaddrinfo(dest.c_str(), nullptr, &info, &ai);
245 if (rc < 0 || ai == nullptr) {
246 (void)ResetToFullLoss(result, duration);
247 return NETMANAGER_SUCCESS;
248 }
249
250 int32_t fd = socket(ai->ai_family, SOCK_DGRAM, (ai->ai_family == AF_INET) ? IPPROTO_ICMP : IPPROTO_ICMPV6);
251 if (fd >= 0) {
252 InitialProbeResult(result);
253
254 rc = DoPing(fd, ai, static_cast<uint32_t>(duration), result);
255 if (rc < 0) {
256 (void)ResetToFullLoss(result, duration);
257 }
258
259 close(fd);
260 } else {
261 (void)ResetToFullLoss(result, duration);
262 }
263
264 freeaddrinfo(ai);
265
266 return NETMANAGER_SUCCESS;
267 }
268
269 }
270 }
271