1 /*
2  * Copyright (C) 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 "dhcp_arp_checker.h"
17 
18 #include <cerrno>
19 #include <chrono>
20 #include <fcntl.h>
21 #include <net/if_arp.h>
22 #include <net/if.h>
23 #include <netpacket/packet.h>
24 #include <poll.h>
25 #include <sys/socket.h>
26 #include <unistd.h>
27 
28 #include "securec.h"
29 #include "dhcp_common_utils.h"
30 #include "dhcp_logger.h"
31 
32 namespace OHOS {
33 namespace DHCP {
34 DEFINE_DHCPLOG_DHCP_LABEL("DhcpArpChecker");
35 constexpr int32_t MAX_LENGTH = 1500;
36 constexpr int32_t OPT_SUCC = 0;
37 constexpr int32_t OPT_FAIL = -1;
38 
DhcpArpChecker()39 DhcpArpChecker::DhcpArpChecker() : m_isSocketCreated(false), m_socketFd(-1), m_ifaceIndex(0), m_protocol(0)
40 {
41     DHCP_LOGD("DhcpArpChecker()");
42 }
43 
~DhcpArpChecker()44 DhcpArpChecker::~DhcpArpChecker()
45 {
46     DHCP_LOGI("~DhcpArpChecker()");
47     Stop();
48 }
49 
Start(std::string & ifname,std::string & hwAddr,std::string & senderIp,std::string & targetIp)50 bool DhcpArpChecker::Start(std::string& ifname, std::string& hwAddr, std::string& senderIp, std::string& targetIp)
51 {
52     if (m_isSocketCreated) {
53         Stop();
54     }
55     uint8_t mac[ETH_ALEN + sizeof(uint32_t)];
56     if (sscanf_s(hwAddr.c_str(), "%02x:%02x:%02x:%02x:%02x:%02x",
57         &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) != ETH_ALEN) {  // mac address
58         DHCP_LOGE("invalid hwAddr:%{private}s", hwAddr.c_str());
59         if (memset_s(mac, sizeof(mac), 0, sizeof(mac)) != EOK) {
60             DHCP_LOGE("ArpChecker memset fail");
61         }
62     }
63 
64     if (CreateSocket(ifname.c_str(), ETH_P_ARP) != 0) {
65         DHCP_LOGE("DhcpArpChecker CreateSocket failed");
66         m_isSocketCreated = false;
67         return false;
68     }
69     inet_aton(senderIp.c_str(), &m_localIpAddr);
70     if (memcpy_s(m_localMacAddr, ETH_ALEN, mac, ETH_ALEN) != EOK) {
71         DHCP_LOGE("DhcpArpChecker memcpy fail");
72         return false;
73     }
74     if (memset_s(m_l2Broadcast, ETH_ALEN, 0xFF, ETH_ALEN) != EOK) {
75         DHCP_LOGE("DhcpArpChecker memset fail");
76         return false;
77     }
78     inet_aton(targetIp.c_str(), &m_targetIpAddr);
79     m_isSocketCreated = true;
80     return true;
81 }
82 
Stop()83 void DhcpArpChecker::Stop()
84 {
85     if (!m_isSocketCreated) {
86         return;
87     }
88     CloseSocket();
89     m_isSocketCreated = false;
90 }
91 
SetArpPacket(ArpPacket & arpPacket,bool isFillSenderIp)92 bool DhcpArpChecker::SetArpPacket(ArpPacket& arpPacket, bool isFillSenderIp)
93 {
94     arpPacket.ar_hrd = htons(ARPHRD_ETHER);
95     arpPacket.ar_pro = htons(ETH_P_IP);
96     arpPacket.ar_hln = ETH_ALEN;
97     arpPacket.ar_pln = IPV4_ALEN;
98     arpPacket.ar_op = htons(ARPOP_REQUEST);
99     if (memcpy_s(arpPacket.ar_sha, ETH_ALEN, m_localMacAddr, ETH_ALEN) != EOK) {
100         DHCP_LOGE("DoArpCheck memcpy fail");
101         return false;
102     }
103     if (isFillSenderIp) {
104         if (memcpy_s(arpPacket.ar_spa, IPV4_ALEN, &m_localIpAddr, sizeof(m_localIpAddr)) != EOK) {
105             DHCP_LOGE("DoArpCheck memcpy fail");
106             return false;
107         }
108     } else {
109         if (memset_s(arpPacket.ar_spa, IPV4_ALEN, 0, IPV4_ALEN) != EOK) {
110             DHCP_LOGE("DoArpCheck memset fail");
111             return false;
112         }
113     }
114     if (memset_s(arpPacket.ar_tha, ETH_ALEN, 0, ETH_ALEN) != EOK) {
115         DHCP_LOGE("DoArpCheck memset fail");
116         return false;
117     }
118     if (memcpy_s(arpPacket.ar_tpa, IPV4_ALEN, &m_targetIpAddr, sizeof(m_targetIpAddr)) != EOK) {
119         DHCP_LOGE("DoArpCheck memcpy fail");
120         return false;
121     }
122     return true;
123 }
124 
DoArpCheck(int32_t timeoutMillis,bool isFillSenderIp,uint64_t & timeCost)125 bool DhcpArpChecker::DoArpCheck(int32_t timeoutMillis, bool isFillSenderIp, uint64_t &timeCost)
126 {
127     if (!m_isSocketCreated) {
128         DHCP_LOGE("DoArpCheck failed, socket not created");
129         return false;
130     }
131     struct ArpPacket arpPacket;
132     if (!SetArpPacket(arpPacket, isFillSenderIp)) {
133         DHCP_LOGE("SetArpPacket failed");
134         return false;
135     }
136 
137     if (SendData(reinterpret_cast<uint8_t *>(&arpPacket), sizeof(arpPacket), m_l2Broadcast) != 0) {
138         return false;
139     }
140     timeCost = 0;
141     int32_t readLen = 0;
142     int64_t elapsed = 0;
143     int32_t leftMillis = timeoutMillis;
144     uint8_t recvBuff[MAX_LENGTH];
145     std::chrono::steady_clock::time_point startTime = std::chrono::steady_clock::now();
146     while (leftMillis > 0) {
147         readLen = RecvData(recvBuff, sizeof(recvBuff), leftMillis);
148         if (readLen >= static_cast<int32_t>(sizeof(struct ArpPacket))) {
149             struct ArpPacket *respPacket = reinterpret_cast<struct ArpPacket*>(recvBuff);
150             if (ntohs(respPacket->ar_hrd) == ARPHRD_ETHER &&
151                 ntohs(respPacket->ar_pro) == ETH_P_IP &&
152                 respPacket->ar_hln == ETH_ALEN &&
153                 respPacket->ar_pln == IPV4_ALEN &&
154                 ntohs(respPacket->ar_op) == ARPOP_REPLY &&
155                 memcmp(respPacket->ar_sha, m_localMacAddr, ETH_ALEN) != 0 &&
156                 memcmp(respPacket->ar_spa, &m_targetIpAddr, IPV4_ALEN) == 0) {
157                 std::chrono::steady_clock::time_point current = std::chrono::steady_clock::now();
158                 timeCost = static_cast<uint64_t>(
159                     std::chrono::duration_cast<std::chrono::milliseconds>(current - startTime).count());
160                 DHCP_LOGI("doArp return true");
161                 return true;
162             }
163         } else if (readLen < 0) {
164             DHCP_LOGE("readLen < 0, stop arp");
165             return false;
166         }
167         std::chrono::steady_clock::time_point current = std::chrono::steady_clock::now();
168         elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(current - startTime).count();
169         leftMillis -= static_cast<int32_t>(elapsed);
170     }
171     DHCP_LOGI("doArp return false");
172     return false;
173 }
174 
GetGwMacAddrList(int32_t timeoutMillis,bool isFillSenderIp,std::vector<std::string> & gwMacLists)175 void DhcpArpChecker::GetGwMacAddrList(int32_t timeoutMillis, bool isFillSenderIp, std::vector<std::string>& gwMacLists)
176 {
177     gwMacLists.clear();
178     if (!m_isSocketCreated) {
179         DHCP_LOGE("GetGwMacAddrList failed, socket not created");
180         return;
181     }
182     struct ArpPacket arpPacket;
183     if (!SetArpPacket(arpPacket, isFillSenderIp)) {
184         DHCP_LOGE("GetGwMacAddrList SetArpPacket failed");
185         return;
186     }
187 
188     if (SendData(reinterpret_cast<uint8_t *>(&arpPacket), sizeof(arpPacket), m_l2Broadcast) != 0) {
189         DHCP_LOGE("GetGwMacAddrList SendData failed");
190         return;
191     }
192     int32_t readLen = 0;
193     int32_t leftMillis = timeoutMillis;
194     uint8_t recvBuff[MAX_LENGTH];
195     std::chrono::steady_clock::time_point startTime = std::chrono::steady_clock::now();
196     while (leftMillis > 0) {
197         readLen = RecvData(recvBuff, sizeof(recvBuff), leftMillis);
198         if (readLen >= static_cast<int32_t>(sizeof(struct ArpPacket))) {
199             struct ArpPacket *respPacket = reinterpret_cast<struct ArpPacket*>(recvBuff);
200             if (ntohs(respPacket->ar_hrd) == ARPHRD_ETHER &&
201                 ntohs(respPacket->ar_pro) == ETH_P_IP &&
202                 respPacket->ar_hln == ETH_ALEN &&
203                 respPacket->ar_pln == IPV4_ALEN &&
204                 ntohs(respPacket->ar_op) == ARPOP_REPLY &&
205                 memcmp(respPacket->ar_sha, m_localMacAddr, ETH_ALEN) != 0 &&
206                 memcmp(respPacket->ar_spa, &m_targetIpAddr, IPV4_ALEN) == 0) {
207                 std::string gwMacAddr = MacArray2Str(respPacket->ar_sha, ETH_ALEN);
208                 SaveGwMacAddr(gwMacAddr, gwMacLists);
209             }
210         }
211         std::chrono::steady_clock::time_point current = std::chrono::steady_clock::now();
212         int64_t elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(current - startTime).count();
213         leftMillis -= static_cast<int32_t>(elapsed);
214     }
215 }
216 
SaveGwMacAddr(std::string gwMacAddr,std::vector<std::string> & gwMacLists)217 void DhcpArpChecker::SaveGwMacAddr(std::string gwMacAddr, std::vector<std::string>& gwMacLists)
218 {
219     auto it = std::find(gwMacLists.begin(), gwMacLists.end(), gwMacAddr);
220     if (!gwMacAddr.empty() && (it == gwMacLists.end())) {
221         gwMacLists.push_back(gwMacAddr);
222     }
223 }
224 
CreateSocket(const char * iface,uint16_t protocol)225 int32_t DhcpArpChecker::CreateSocket(const char *iface, uint16_t protocol)
226 {
227     if (iface == nullptr) {
228         DHCP_LOGE("iface is null");
229         return OPT_FAIL;
230     }
231 
232     int32_t ifaceIndex = static_cast<int32_t>(if_nametoindex(iface));
233     if (ifaceIndex == 0) {
234         DHCP_LOGE("get iface index fail: %{public}s", iface);
235         return OPT_FAIL;
236     }
237     if (ifaceIndex > INTEGER_MAX) {
238         DHCP_LOGE("ifaceIndex > max interger, fail:%{public}s ifaceIndex:%{public}d", iface, ifaceIndex);
239         return OPT_FAIL;
240     }
241     int32_t socketFd = socket(PF_PACKET, SOCK_DGRAM, htons(protocol));
242     if (socketFd < 0) {
243         DHCP_LOGE("create socket fail");
244         return OPT_FAIL;
245     }
246 
247     if (SetNonBlock(socketFd)) {
248         DHCP_LOGE("set non block fail");
249         (void)close(socketFd);
250         return OPT_FAIL;
251     }
252 
253     struct sockaddr_ll rawAddr;
254     rawAddr.sll_ifindex = ifaceIndex;
255     rawAddr.sll_protocol = htons(protocol);
256     rawAddr.sll_family = AF_PACKET;
257 
258     int32_t ret = bind(socketFd, reinterpret_cast<struct sockaddr *>(&rawAddr), sizeof(rawAddr));
259     if (ret != 0) {
260         DHCP_LOGE("bind fail");
261         (void)close(socketFd);
262         return OPT_FAIL;
263     }
264     m_socketFd = socketFd;
265     m_ifaceIndex = ifaceIndex;
266     m_protocol = protocol;
267     return OPT_SUCC;
268 }
269 
SendData(uint8_t * buff,int32_t count,uint8_t * destHwaddr)270 int32_t DhcpArpChecker::SendData(uint8_t *buff, int32_t count, uint8_t *destHwaddr)
271 {
272     if (buff == nullptr || destHwaddr == nullptr) {
273         DHCP_LOGE("buff or dest hwaddr is null");
274         return OPT_FAIL;
275     }
276 
277     if (m_socketFd < 0 || m_ifaceIndex == 0) {
278         DHCP_LOGE("invalid socket fd");
279         return OPT_FAIL;
280     }
281 
282     struct sockaddr_ll rawAddr;
283     (void)memset_s(&rawAddr, sizeof(rawAddr), 0, sizeof(rawAddr));
284     rawAddr.sll_ifindex = m_ifaceIndex;
285     rawAddr.sll_protocol = htons(m_protocol);
286     rawAddr.sll_family = AF_PACKET;
287     if (memcpy_s(rawAddr.sll_addr, sizeof(rawAddr.sll_addr), destHwaddr, ETH_ALEN) != EOK) {
288         DHCP_LOGE("Send: memcpy fail");
289         return OPT_FAIL;
290     }
291 
292     int32_t ret;
293     do {
294         ret = sendto(m_socketFd, buff, count, 0, reinterpret_cast<struct sockaddr *>(&rawAddr), sizeof(rawAddr));
295         if (ret == -1) {
296             DHCP_LOGE("Send: sendto fail");
297             if (errno != EINTR) {
298                 break;
299             }
300         }
301     } while (ret == -1);
302     return ret > 0 ? OPT_SUCC : OPT_FAIL;
303 }
304 
RecvData(uint8_t * buff,int32_t count,int32_t timeoutMillis)305 int32_t DhcpArpChecker::RecvData(uint8_t *buff, int32_t count, int32_t timeoutMillis)
306 {
307     if (m_socketFd < 0) {
308         DHCP_LOGE("invalid socket fd");
309         return -1;
310     }
311 
312     pollfd fds[1];
313     fds[0].fd = m_socketFd;
314     fds[0].events = POLLIN;
315     if (poll(fds, 1, timeoutMillis) <= 0) {
316         return 0;
317     }
318 
319     int32_t nBytes;
320     do {
321         nBytes = read(m_socketFd, buff, count);
322         if (nBytes == -1) {
323             if (errno != EINTR) {
324                 break;
325             }
326         }
327     } while (nBytes == -1);
328 
329     return nBytes < 0 ? 0 : nBytes;
330 }
331 
CloseSocket(void)332 int32_t DhcpArpChecker::CloseSocket(void)
333 {
334     int32_t ret = OPT_FAIL;
335 
336     if (m_socketFd >= 0) {
337         ret = close(m_socketFd);
338         if (ret != OPT_SUCC) {
339             DHCP_LOGE("close fail.");
340         }
341     }
342     m_socketFd = -1;
343     m_ifaceIndex = 0;
344     m_protocol = 0;
345     return ret;
346 }
347 
SetNonBlock(int32_t fd)348 bool DhcpArpChecker::SetNonBlock(int32_t fd)
349 {
350     int32_t ret = fcntl(fd, F_GETFL);
351     if (ret < 0) {
352         return false;
353     }
354 
355     uint32_t flags = (static_cast<uint32_t>(ret) | O_NONBLOCK);
356     return fcntl(fd, F_SETFL, flags);
357 }
358 }
359 }