1/* 2 * Copyright (C) 2021-2022 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#include "dhcp_ipv6_client.h" 16 17#include <unistd.h> 18#include <linux/rtnetlink.h> 19#include "dhcp_logger.h" 20 21namespace OHOS { 22namespace DHCP { 23DEFINE_DHCPLOG_DHCP_LABEL("WifiDhcpIpv6Event"); 24 25const int KERNEL_SOCKET_FAMILY = 16; 26const int KERNEL_SOCKET_IFA_FAMILY = 10; 27const int KERNEL_ICMP_TYPE = 134; 28 29void DhcpIpv6Client::setSocketFilter(void* addr) 30{ 31 if (!addr) { 32 DHCP_LOGE("setSocketFilter failed, addr invalid."); 33 return; 34 } 35 struct sockaddr_nl *nladdr = (struct sockaddr_nl*)addr; 36 nladdr->nl_family = KERNEL_SOCKET_FAMILY; 37 nladdr->nl_pid = 0; 38 nladdr->nl_groups = RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE | (1 << (RTNLGRP_ND_USEROPT - 1)); 39} 40 41void DhcpIpv6Client::parseNdUserOptMessage(void* data, int len) 42{ 43 if (!data) { 44 DHCP_LOGE("parseNdUserOptMessage failed, msg invalid."); 45 return; 46 } 47 struct nduseroptmsg *msg = (struct nduseroptmsg *)data; 48 if (msg->nduseropt_opts_len > len) { 49 DHCP_LOGE("invliad len msg->nduseropt_opts_len:%{public}d > len:%{public}d", 50 msg->nduseropt_opts_len, len); 51 return; 52 } 53 int optlen = msg->nduseropt_opts_len; 54 if (msg->nduseropt_family != KERNEL_SOCKET_IFA_FAMILY) { 55 DHCP_LOGE("invliad nduseropt_family:%{public}d", msg->nduseropt_family); 56 return; 57 } 58 if (msg->nduseropt_icmp_type != KERNEL_ICMP_TYPE || msg->nduseropt_icmp_code != 0) { 59 DHCP_LOGE("invliad nduseropt_icmp_type:%{public}d, nduseropt_icmp_type:%{public}d", 60 msg->nduseropt_icmp_type, msg->nduseropt_icmp_code); 61 return; 62 } 63 onIpv6DnsAddEvent((void*)(msg + 1), optlen, msg->nduseropt_ifindex); 64} 65 66void DhcpIpv6Client::parseNDRouteMessage(void* msg) 67{ 68 if (msg == NULL) { 69 return; 70 } 71 struct nlmsghdr *hdrMsg = (struct nlmsghdr*)msg; 72 struct rtmsg* rtMsg = reinterpret_cast<struct rtmsg*>(NLMSG_DATA(hdrMsg)); 73 if (hdrMsg->nlmsg_len < sizeof(struct rtmsg)) { 74 DHCP_LOGE("invliad msglen:%{public}d", hdrMsg->nlmsg_len); 75 return; 76 } 77 if ((rtMsg->rtm_protocol != RTPROT_KERNEL && rtMsg->rtm_protocol != RTPROT_RA) || 78 (rtMsg->rtm_scope != RT_SCOPE_UNIVERSE) || (rtMsg->rtm_type != RTN_UNICAST) || 79 (rtMsg->rtm_src_len != 0) || (rtMsg->rtm_flags & RTM_F_CLONED)) { 80 DHCP_LOGE("invliad arg"); 81 return; 82 } 83 char dst[DHCP_INET6_ADDRSTRLEN] = {0}; 84 char gateway[DHCP_INET6_ADDRSTRLEN] = {0}; 85 int32_t rtmFamily = rtMsg->rtm_family; 86 size_t size = RTM_PAYLOAD(hdrMsg); 87 int ifindex = -1; 88 rtattr *rtaInfo = NULL; 89 for (rtaInfo = RTM_RTA(rtMsg); RTA_OK(rtaInfo, (int)size); rtaInfo = RTA_NEXT(rtaInfo, size)) { 90 switch (rtaInfo->rta_type) { 91 case RTA_GATEWAY: 92 if (GetIpFromS6Address(RTA_DATA(rtaInfo), rtmFamily, gateway, sizeof(gateway)) != 0) { 93 DHCP_LOGE("inet_ntop RTA_GATEWAY failed."); 94 return; 95 } 96 break; 97 case RTA_DST: 98 if (GetIpFromS6Address(RTA_DATA(rtaInfo), rtmFamily, dst, sizeof(dst)) != 0) { 99 DHCP_LOGE("inet_ntop RTA_DST failed."); 100 return; 101 } 102 break; 103 case RTA_OIF: 104 ifindex = *(reinterpret_cast<int32_t*>(RTA_DATA(rtaInfo))); 105 break; 106 default: 107 break; 108 } 109 } 110 onIpv6RouteAddEvent(gateway, dst, ifindex); 111} 112 113void DhcpIpv6Client::parseNewneighMessage(void* msg) 114{ 115 if (!msg) { 116 return; 117 } 118 struct nlmsghdr *nlh = (struct nlmsghdr*)msg; 119 struct ndmsg *ndm = (struct ndmsg *)NLMSG_DATA(nlh); 120 if (!ndm) { 121 return; 122 } 123 if (ndm->ndm_family == KERNEL_SOCKET_IFA_FAMILY && 124 ndm->ndm_state == NUD_REACHABLE) { 125 struct rtattr *rta = RTM_RTA(ndm); 126 int rtl = static_cast<int>(RTM_PAYLOAD(nlh)); 127 while (RTA_OK(rta, rtl)) { 128 if (rta->rta_type == NDA_DST) { 129 struct in6_addr *addr = (struct in6_addr *)RTA_DATA(rta); 130 char gateway[DHCP_INET6_ADDRSTRLEN] = {0}; 131 char dst[DHCP_INET6_ADDRSTRLEN] = {0}; 132 if (GetIpFromS6Address(addr, ndm->ndm_family, gateway, 133 DHCP_INET6_ADDRSTRLEN) != 0) { 134 DHCP_LOGE("inet_ntop routeAddr failed."); 135 return; 136 } 137 onIpv6RouteAddEvent(gateway, dst, ndm->ndm_ifindex); 138 DHCP_LOGD("getIpv6RouteAddr: %{public}s", gateway); 139 break; 140 } 141 rta = RTA_NEXT(rta, rtl); 142 } 143 } 144} 145 146void DhcpIpv6Client::fillRouteData(char* buff, int &len) 147{ 148 if (!buff) { 149 return; 150 } 151 struct nlmsghdr *nlh = (struct nlmsghdr *)buff; 152 nlh->nlmsg_len = NLMSG_SPACE(static_cast<unsigned int>(sizeof(struct ndmsg))); 153 nlh->nlmsg_type = RTM_GETNEIGH; 154 nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; 155 nlh->nlmsg_seq = 1; 156 nlh->nlmsg_pid = static_cast<unsigned int>(getpid()); 157 len = nlh->nlmsg_len; 158} 159 160void DhcpIpv6Client::handleKernelEvent(const uint8_t* data, int len) 161{ 162 if (!data) { 163 DHCP_LOGE("handleKernelEvent failed, data invalid."); 164 return; 165 } 166 if (len < (int32_t)sizeof(struct nlmsghdr)) { 167 DHCP_LOGE("recv kernel data not full continue."); 168 return; 169 } 170 struct nlmsghdr *nlh = (struct nlmsghdr*)data; 171 while (nlh && NLMSG_OK(nlh, len) && nlh->nlmsg_type != NLMSG_DONE) { 172 DHCP_LOGD("handleKernelEvent nlmsg_type:%{public}d.", nlh->nlmsg_type); 173 if (nlh->nlmsg_type == RTM_NEWADDR) { 174 struct ifaddrmsg *ifa = (struct ifaddrmsg*)NLMSG_DATA(nlh); 175 struct rtattr *rth = IFA_RTA(ifa); 176 int rtl = static_cast<int>(IFA_PAYLOAD(nlh)); 177 while (rtl && RTA_OK(rth, rtl)) { 178 if (rth->rta_type != IFA_ADDRESS || ifa->ifa_family != KERNEL_SOCKET_IFA_FAMILY) { 179 rth = RTA_NEXT(rth, rtl); 180 continue; 181 } 182 onIpv6AddressAddEvent(RTA_DATA(rth), ifa->ifa_prefixlen, ifa->ifa_index); 183 rth = RTA_NEXT(rth, rtl); 184 } 185 } else if (nlh->nlmsg_type == RTM_NEWNDUSEROPT) { 186 unsigned int optLen = 0; 187 if ((nlh->nlmsg_len - sizeof(*nlh)) >= 0) { 188 optLen = nlh->nlmsg_len - sizeof(*nlh); 189 } else { 190 return; 191 } 192 struct nduseroptmsg* ndmsg = (struct nduseroptmsg*)NLMSG_DATA(nlh); 193 if (sizeof(*ndmsg) > (size_t)optLen) { 194 DHCP_LOGE("ndoption get invalid length."); 195 continue; 196 } 197 size_t optsize = NLMSG_PAYLOAD(nlh, sizeof(*ndmsg)); 198 parseNdUserOptMessage((void*)ndmsg, optsize); 199 } else if (nlh->nlmsg_type == RTM_NEWROUTE) { 200 parseNDRouteMessage((void*)nlh); 201 } else if (nlh->nlmsg_type == RTM_NEWNEIGH) { 202 parseNewneighMessage((void*)nlh); 203 } 204 nlh = NLMSG_NEXT(nlh, len); 205 } 206} 207} // namespace DHCP 208} // namespace OHOS 209