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