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