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 
21 namespace OHOS {
22 namespace DHCP {
23 DEFINE_DHCPLOG_DHCP_LABEL("WifiDhcpIpv6Event");
24 
25 const int KERNEL_SOCKET_FAMILY = 16;
26 const int KERNEL_SOCKET_IFA_FAMILY = 10;
27 const int KERNEL_ICMP_TYPE = 134;
28 
setSocketFilter(void * addr)29 void 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 
parseNdUserOptMessage(void * data,int len)41 void 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 
parseNDRouteMessage(void * msg)66 void 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 
parseNewneighMessage(void * msg)113 void 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_LOGI("getIpv6RouteAddr: %{public}s", gateway);
139                 break;
140             }
141             rta = RTA_NEXT(rta, rtl);
142         }
143     }
144 }
145 
fillRouteData(char * buff,int & len)146 void 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 
handleKernelEvent(const uint8_t * data,int len)160 void 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)) {
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