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