1518678f8Sopenharmony_ci/* 2518678f8Sopenharmony_ci * Copyright (C) 2021-2022 Huawei Device Co., Ltd. 3518678f8Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 4518678f8Sopenharmony_ci * you may not use this file except in compliance with the License. 5518678f8Sopenharmony_ci * You may obtain a copy of the License at 6518678f8Sopenharmony_ci * 7518678f8Sopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 8518678f8Sopenharmony_ci * 9518678f8Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software 10518678f8Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 11518678f8Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12518678f8Sopenharmony_ci * See the License for the specific language governing permissions and 13518678f8Sopenharmony_ci * limitations under the License. 14518678f8Sopenharmony_ci */ 15518678f8Sopenharmony_ci#include "dhcp_ipv6_client.h" 16518678f8Sopenharmony_ci 17518678f8Sopenharmony_ci#include <unistd.h> 18518678f8Sopenharmony_ci#include <linux/rtnetlink.h> 19518678f8Sopenharmony_ci#include "dhcp_logger.h" 20518678f8Sopenharmony_ci 21518678f8Sopenharmony_cinamespace OHOS { 22518678f8Sopenharmony_cinamespace DHCP { 23518678f8Sopenharmony_ciDEFINE_DHCPLOG_DHCP_LABEL("WifiDhcpIpv6Event"); 24518678f8Sopenharmony_ci 25518678f8Sopenharmony_ciconst int KERNEL_SOCKET_FAMILY = 16; 26518678f8Sopenharmony_ciconst int KERNEL_SOCKET_IFA_FAMILY = 10; 27518678f8Sopenharmony_ciconst int KERNEL_ICMP_TYPE = 134; 28518678f8Sopenharmony_ci 29518678f8Sopenharmony_civoid DhcpIpv6Client::setSocketFilter(void* addr) 30518678f8Sopenharmony_ci{ 31518678f8Sopenharmony_ci if (!addr) { 32518678f8Sopenharmony_ci DHCP_LOGE("setSocketFilter failed, addr invalid."); 33518678f8Sopenharmony_ci return; 34518678f8Sopenharmony_ci } 35518678f8Sopenharmony_ci struct sockaddr_nl *nladdr = (struct sockaddr_nl*)addr; 36518678f8Sopenharmony_ci nladdr->nl_family = KERNEL_SOCKET_FAMILY; 37518678f8Sopenharmony_ci nladdr->nl_pid = 0; 38518678f8Sopenharmony_ci nladdr->nl_groups = RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE | (1 << (RTNLGRP_ND_USEROPT - 1)); 39518678f8Sopenharmony_ci} 40518678f8Sopenharmony_ci 41518678f8Sopenharmony_civoid DhcpIpv6Client::parseNdUserOptMessage(void* data, int len) 42518678f8Sopenharmony_ci{ 43518678f8Sopenharmony_ci if (!data) { 44518678f8Sopenharmony_ci DHCP_LOGE("parseNdUserOptMessage failed, msg invalid."); 45518678f8Sopenharmony_ci return; 46518678f8Sopenharmony_ci } 47518678f8Sopenharmony_ci struct nduseroptmsg *msg = (struct nduseroptmsg *)data; 48518678f8Sopenharmony_ci if (msg->nduseropt_opts_len > len) { 49518678f8Sopenharmony_ci DHCP_LOGE("invliad len msg->nduseropt_opts_len:%{public}d > len:%{public}d", 50518678f8Sopenharmony_ci msg->nduseropt_opts_len, len); 51518678f8Sopenharmony_ci return; 52518678f8Sopenharmony_ci } 53518678f8Sopenharmony_ci int optlen = msg->nduseropt_opts_len; 54518678f8Sopenharmony_ci if (msg->nduseropt_family != KERNEL_SOCKET_IFA_FAMILY) { 55518678f8Sopenharmony_ci DHCP_LOGE("invliad nduseropt_family:%{public}d", msg->nduseropt_family); 56518678f8Sopenharmony_ci return; 57518678f8Sopenharmony_ci } 58518678f8Sopenharmony_ci if (msg->nduseropt_icmp_type != KERNEL_ICMP_TYPE || msg->nduseropt_icmp_code != 0) { 59518678f8Sopenharmony_ci DHCP_LOGE("invliad nduseropt_icmp_type:%{public}d, nduseropt_icmp_type:%{public}d", 60518678f8Sopenharmony_ci msg->nduseropt_icmp_type, msg->nduseropt_icmp_code); 61518678f8Sopenharmony_ci return; 62518678f8Sopenharmony_ci } 63518678f8Sopenharmony_ci onIpv6DnsAddEvent((void*)(msg + 1), optlen, msg->nduseropt_ifindex); 64518678f8Sopenharmony_ci} 65518678f8Sopenharmony_ci 66518678f8Sopenharmony_civoid DhcpIpv6Client::parseNDRouteMessage(void* msg) 67518678f8Sopenharmony_ci{ 68518678f8Sopenharmony_ci if (msg == NULL) { 69518678f8Sopenharmony_ci return; 70518678f8Sopenharmony_ci } 71518678f8Sopenharmony_ci struct nlmsghdr *hdrMsg = (struct nlmsghdr*)msg; 72518678f8Sopenharmony_ci struct rtmsg* rtMsg = reinterpret_cast<struct rtmsg*>(NLMSG_DATA(hdrMsg)); 73518678f8Sopenharmony_ci if (hdrMsg->nlmsg_len < sizeof(struct rtmsg)) { 74518678f8Sopenharmony_ci DHCP_LOGE("invliad msglen:%{public}d", hdrMsg->nlmsg_len); 75518678f8Sopenharmony_ci return; 76518678f8Sopenharmony_ci } 77518678f8Sopenharmony_ci if ((rtMsg->rtm_protocol != RTPROT_KERNEL && rtMsg->rtm_protocol != RTPROT_RA) || 78518678f8Sopenharmony_ci (rtMsg->rtm_scope != RT_SCOPE_UNIVERSE) || (rtMsg->rtm_type != RTN_UNICAST) || 79518678f8Sopenharmony_ci (rtMsg->rtm_src_len != 0) || (rtMsg->rtm_flags & RTM_F_CLONED)) { 80518678f8Sopenharmony_ci DHCP_LOGE("invliad arg"); 81518678f8Sopenharmony_ci return; 82518678f8Sopenharmony_ci } 83518678f8Sopenharmony_ci char dst[DHCP_INET6_ADDRSTRLEN] = {0}; 84518678f8Sopenharmony_ci char gateway[DHCP_INET6_ADDRSTRLEN] = {0}; 85518678f8Sopenharmony_ci int32_t rtmFamily = rtMsg->rtm_family; 86518678f8Sopenharmony_ci size_t size = RTM_PAYLOAD(hdrMsg); 87518678f8Sopenharmony_ci int ifindex = -1; 88518678f8Sopenharmony_ci rtattr *rtaInfo = NULL; 89518678f8Sopenharmony_ci for (rtaInfo = RTM_RTA(rtMsg); RTA_OK(rtaInfo, (int)size); rtaInfo = RTA_NEXT(rtaInfo, size)) { 90518678f8Sopenharmony_ci switch (rtaInfo->rta_type) { 91518678f8Sopenharmony_ci case RTA_GATEWAY: 92518678f8Sopenharmony_ci if (GetIpFromS6Address(RTA_DATA(rtaInfo), rtmFamily, gateway, sizeof(gateway)) != 0) { 93518678f8Sopenharmony_ci DHCP_LOGE("inet_ntop RTA_GATEWAY failed."); 94518678f8Sopenharmony_ci return; 95518678f8Sopenharmony_ci } 96518678f8Sopenharmony_ci break; 97518678f8Sopenharmony_ci case RTA_DST: 98518678f8Sopenharmony_ci if (GetIpFromS6Address(RTA_DATA(rtaInfo), rtmFamily, dst, sizeof(dst)) != 0) { 99518678f8Sopenharmony_ci DHCP_LOGE("inet_ntop RTA_DST failed."); 100518678f8Sopenharmony_ci return; 101518678f8Sopenharmony_ci } 102518678f8Sopenharmony_ci break; 103518678f8Sopenharmony_ci case RTA_OIF: 104518678f8Sopenharmony_ci ifindex = *(reinterpret_cast<int32_t*>(RTA_DATA(rtaInfo))); 105518678f8Sopenharmony_ci break; 106518678f8Sopenharmony_ci default: 107518678f8Sopenharmony_ci break; 108518678f8Sopenharmony_ci } 109518678f8Sopenharmony_ci } 110518678f8Sopenharmony_ci onIpv6RouteAddEvent(gateway, dst, ifindex); 111518678f8Sopenharmony_ci} 112518678f8Sopenharmony_ci 113518678f8Sopenharmony_civoid DhcpIpv6Client::parseNewneighMessage(void* msg) 114518678f8Sopenharmony_ci{ 115518678f8Sopenharmony_ci if (!msg) { 116518678f8Sopenharmony_ci return; 117518678f8Sopenharmony_ci } 118518678f8Sopenharmony_ci struct nlmsghdr *nlh = (struct nlmsghdr*)msg; 119518678f8Sopenharmony_ci struct ndmsg *ndm = (struct ndmsg *)NLMSG_DATA(nlh); 120518678f8Sopenharmony_ci if (!ndm) { 121518678f8Sopenharmony_ci return; 122518678f8Sopenharmony_ci } 123518678f8Sopenharmony_ci if (ndm->ndm_family == KERNEL_SOCKET_IFA_FAMILY && 124518678f8Sopenharmony_ci ndm->ndm_state == NUD_REACHABLE) { 125518678f8Sopenharmony_ci struct rtattr *rta = RTM_RTA(ndm); 126518678f8Sopenharmony_ci int rtl = static_cast<int>(RTM_PAYLOAD(nlh)); 127518678f8Sopenharmony_ci while (RTA_OK(rta, rtl)) { 128518678f8Sopenharmony_ci if (rta->rta_type == NDA_DST) { 129518678f8Sopenharmony_ci struct in6_addr *addr = (struct in6_addr *)RTA_DATA(rta); 130518678f8Sopenharmony_ci char gateway[DHCP_INET6_ADDRSTRLEN] = {0}; 131518678f8Sopenharmony_ci char dst[DHCP_INET6_ADDRSTRLEN] = {0}; 132518678f8Sopenharmony_ci if (GetIpFromS6Address(addr, ndm->ndm_family, gateway, 133518678f8Sopenharmony_ci DHCP_INET6_ADDRSTRLEN) != 0) { 134518678f8Sopenharmony_ci DHCP_LOGE("inet_ntop routeAddr failed."); 135518678f8Sopenharmony_ci return; 136518678f8Sopenharmony_ci } 137518678f8Sopenharmony_ci onIpv6RouteAddEvent(gateway, dst, ndm->ndm_ifindex); 138518678f8Sopenharmony_ci DHCP_LOGD("getIpv6RouteAddr: %{public}s", gateway); 139518678f8Sopenharmony_ci break; 140518678f8Sopenharmony_ci } 141518678f8Sopenharmony_ci rta = RTA_NEXT(rta, rtl); 142518678f8Sopenharmony_ci } 143518678f8Sopenharmony_ci } 144518678f8Sopenharmony_ci} 145518678f8Sopenharmony_ci 146518678f8Sopenharmony_civoid DhcpIpv6Client::fillRouteData(char* buff, int &len) 147518678f8Sopenharmony_ci{ 148518678f8Sopenharmony_ci if (!buff) { 149518678f8Sopenharmony_ci return; 150518678f8Sopenharmony_ci } 151518678f8Sopenharmony_ci struct nlmsghdr *nlh = (struct nlmsghdr *)buff; 152518678f8Sopenharmony_ci nlh->nlmsg_len = NLMSG_SPACE(static_cast<unsigned int>(sizeof(struct ndmsg))); 153518678f8Sopenharmony_ci nlh->nlmsg_type = RTM_GETNEIGH; 154518678f8Sopenharmony_ci nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; 155518678f8Sopenharmony_ci nlh->nlmsg_seq = 1; 156518678f8Sopenharmony_ci nlh->nlmsg_pid = static_cast<unsigned int>(getpid()); 157518678f8Sopenharmony_ci len = nlh->nlmsg_len; 158518678f8Sopenharmony_ci} 159518678f8Sopenharmony_ci 160518678f8Sopenharmony_civoid DhcpIpv6Client::handleKernelEvent(const uint8_t* data, int len) 161518678f8Sopenharmony_ci{ 162518678f8Sopenharmony_ci if (!data) { 163518678f8Sopenharmony_ci DHCP_LOGE("handleKernelEvent failed, data invalid."); 164518678f8Sopenharmony_ci return; 165518678f8Sopenharmony_ci } 166518678f8Sopenharmony_ci if (len < (int32_t)sizeof(struct nlmsghdr)) { 167518678f8Sopenharmony_ci DHCP_LOGE("recv kernel data not full continue."); 168518678f8Sopenharmony_ci return; 169518678f8Sopenharmony_ci } 170518678f8Sopenharmony_ci struct nlmsghdr *nlh = (struct nlmsghdr*)data; 171518678f8Sopenharmony_ci while (nlh && NLMSG_OK(nlh, len) && nlh->nlmsg_type != NLMSG_DONE) { 172518678f8Sopenharmony_ci DHCP_LOGD("handleKernelEvent nlmsg_type:%{public}d.", nlh->nlmsg_type); 173518678f8Sopenharmony_ci if (nlh->nlmsg_type == RTM_NEWADDR) { 174518678f8Sopenharmony_ci struct ifaddrmsg *ifa = (struct ifaddrmsg*)NLMSG_DATA(nlh); 175518678f8Sopenharmony_ci struct rtattr *rth = IFA_RTA(ifa); 176518678f8Sopenharmony_ci int rtl = static_cast<int>(IFA_PAYLOAD(nlh)); 177518678f8Sopenharmony_ci while (rtl && RTA_OK(rth, rtl)) { 178518678f8Sopenharmony_ci if (rth->rta_type != IFA_ADDRESS || ifa->ifa_family != KERNEL_SOCKET_IFA_FAMILY) { 179518678f8Sopenharmony_ci rth = RTA_NEXT(rth, rtl); 180518678f8Sopenharmony_ci continue; 181518678f8Sopenharmony_ci } 182518678f8Sopenharmony_ci onIpv6AddressAddEvent(RTA_DATA(rth), ifa->ifa_prefixlen, ifa->ifa_index); 183518678f8Sopenharmony_ci rth = RTA_NEXT(rth, rtl); 184518678f8Sopenharmony_ci } 185518678f8Sopenharmony_ci } else if (nlh->nlmsg_type == RTM_NEWNDUSEROPT) { 186518678f8Sopenharmony_ci unsigned int optLen = 0; 187518678f8Sopenharmony_ci if ((nlh->nlmsg_len - sizeof(*nlh)) >= 0) { 188518678f8Sopenharmony_ci optLen = nlh->nlmsg_len - sizeof(*nlh); 189518678f8Sopenharmony_ci } else { 190518678f8Sopenharmony_ci return; 191518678f8Sopenharmony_ci } 192518678f8Sopenharmony_ci struct nduseroptmsg* ndmsg = (struct nduseroptmsg*)NLMSG_DATA(nlh); 193518678f8Sopenharmony_ci if (sizeof(*ndmsg) > (size_t)optLen) { 194518678f8Sopenharmony_ci DHCP_LOGE("ndoption get invalid length."); 195518678f8Sopenharmony_ci continue; 196518678f8Sopenharmony_ci } 197518678f8Sopenharmony_ci size_t optsize = NLMSG_PAYLOAD(nlh, sizeof(*ndmsg)); 198518678f8Sopenharmony_ci parseNdUserOptMessage((void*)ndmsg, optsize); 199518678f8Sopenharmony_ci } else if (nlh->nlmsg_type == RTM_NEWROUTE) { 200518678f8Sopenharmony_ci parseNDRouteMessage((void*)nlh); 201518678f8Sopenharmony_ci } else if (nlh->nlmsg_type == RTM_NEWNEIGH) { 202518678f8Sopenharmony_ci parseNewneighMessage((void*)nlh); 203518678f8Sopenharmony_ci } 204518678f8Sopenharmony_ci nlh = NLMSG_NEXT(nlh, len); 205518678f8Sopenharmony_ci } 206518678f8Sopenharmony_ci} 207518678f8Sopenharmony_ci} // namespace DHCP 208518678f8Sopenharmony_ci} // namespace OHOS 209