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 }