1 /*
2 * Copyright (c) Huawei Device Co., Ltd. 2021-2023 All right reserved.
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
16 #include <string.h>
17 #include <stdio.h>
18 #include <arpa/inet.h>
19 #include <net/if.h>
20 #include <pthread.h>
21 #include <sys/socket.h>
22 #include <sys/types.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <linux/netlink.h>
26 #include <linux/rtnetlink.h>
27
28 #include "netlink_monitor.h"
29
30 #define LOG(fmt, args...) printf("[LAN_UPGRADE] "fmt"\n", ##args)
31
32 #undef NLMSG_OK
33 #define NLMSG_OK(nlh, len) (((len) >= (int32_t)(sizeof(struct nlmsghdr))) && (((nlh)->nlmsg_len) >= \
34 sizeof(struct nlmsghdr)) && ((int32_t)((nlh)->nlmsg_len) <= (len)))
35
36 #define DEFAULT_NETLINK_RECVBUF (4 * 1024)
37
38 static PFUN_NNL_EVENT_HANDLER g_eventHandler = NULL;
39
CreateNetlinkSocket(void)40 static int32_t CreateNetlinkSocket(void)
41 {
42 int32_t sockFd;
43 struct sockaddr_nl nladdr;
44 int32_t sz = DEFAULT_NETLINK_RECVBUF;
45
46 sockFd = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_ROUTE);
47 if (sockFd < 0) {
48 LOG("open netlink socket failed");
49 return -1;
50 }
51 if (setsockopt(sockFd, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0 &&
52 setsockopt(sockFd, SOL_SOCKET, SO_RCVBUF, &sz, sizeof(sz)) < 0) {
53 LOG("set uevent socket SO_RCVBUF option failed");
54 close(sockFd);
55 return -1;
56 }
57 (void)memset_s(&nladdr, sizeof(nladdr), 0, sizeof(nladdr));
58 nladdr.nl_family = AF_NETLINK;
59 // Kernel will assign a unique nl_pid if set to zero.
60 nladdr.nl_pid = 0;
61 nladdr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR;
62 if (bind(sockFd, (struct sockaddr *)&nladdr, sizeof(nladdr)) < 0) {
63 LOG("bind netlink socket failed");
64 close(sockFd);
65 return -1;
66 }
67 return sockFd;
68 }
69
ParseRtAttr(struct rtattr ** tb,int max,struct rtattr * attr,int len)70 static void ParseRtAttr(struct rtattr **tb, int max, struct rtattr *attr, int len)
71 {
72 struct rtattr *attr1 = attr;
73 for (; RTA_OK(attr1, len); attr1 = RTA_NEXT(attr1, len)) {
74 if (attr1->rta_type <= max) {
75 tb[attr1->rta_type] = attr1;
76 }
77 }
78 }
79
GetAddrTypeByIfname(const char * ifname,CONNECTION_ADDR_TYPE_E * type)80 static int32_t GetAddrTypeByIfname(const char *ifname, CONNECTION_ADDR_TYPE_E *type)
81 {
82 if (ifname == NULL || type == NULL) {
83 LOG("parameters are NULL!");
84 return -1;
85 }
86
87 if (!strncasecmp(ifname, "eth", 3L)) {
88 *type = CONNECTION_ADDR_ETH;
89 } else if (!strncasecmp(ifname, "wlan", 4L)) {
90 *type = CONNECTION_ADDR_WLAN;
91 } else {
92 *type = CONNECTION_ADDR_BUTT;
93 }
94
95 return 0;
96 }
97
ProcessAddrEvent(struct nlmsghdr * nlh)98 static void ProcessAddrEvent(struct nlmsghdr *nlh)
99 {
100 struct ifaddrmsg *ifa = (struct ifaddrmsg *)NLMSG_DATA(nlh);
101 char name[IFNAMSIZ];
102 CONNECTION_ADDR_TYPE_E type = CONNECTION_ADDR_BUTT;
103
104 if (if_indextoname(ifa->ifa_index, name) == 0) {
105 LOG("invalid iface index");
106 return;
107 }
108 if (GetAddrTypeByIfname(name, &type) != 0) {
109 LOG("ProcessAddrEvent GetAddrTypeByIfname error");
110 return;
111 }
112 if (type == CONNECTION_ADDR_ETH || type == CONNECTION_ADDR_WLAN) {
113 LOG("network addr changed, type:%d", type);
114 if (g_eventHandler) {
115 g_eventHandler(NNL_EVENT_IP_ADDR_CHANGED, NULL);
116 }
117 }
118 }
119
ProcessLinkEvent(struct nlmsghdr * nlh)120 static void ProcessLinkEvent(struct nlmsghdr *nlh)
121 {
122 int len;
123 struct rtattr *tb[IFLA_MAX + 1] = {NULL};
124 struct ifinfomsg *ifinfo = NLMSG_DATA(nlh);
125 CONNECTION_ADDR_TYPE_E type = CONNECTION_ADDR_BUTT;
126
127 len = (int32_t)nlh->nlmsg_len - NLMSG_SPACE(sizeof(*ifinfo));
128 ParseRtAttr(tb, IFLA_MAX, IFLA_RTA(ifinfo), len);
129
130 if (tb[IFLA_IFNAME] == NULL) {
131 LOG("netlink msg is invalid");
132 return;
133 }
134 if (GetAddrTypeByIfname(RTA_DATA(tb[IFLA_IFNAME]), &type) != 0) {
135 LOG("ProcessAddrEvent GetAddrTypeByIfname error");
136 return;
137 }
138 if (type == CONNECTION_ADDR_ETH || type == CONNECTION_ADDR_WLAN) {
139 LOG("link status changed, type:%d", type);
140 if (g_eventHandler) {
141 g_eventHandler(NNL_EVENT_IP_ADDR_CHANGED, NULL);
142 }
143 }
144 }
145
NetlinkMonitorThread(void * para)146 static void *NetlinkMonitorThread(void *para)
147 {
148 int32_t sockFd;
149 int32_t len;
150 uint8_t buffer[DEFAULT_NETLINK_RECVBUF];
151 struct nlmsghdr *nlh = NULL;
152
153 (void)para;
154 LOG("netlink monitor thread start");
155 sockFd = CreateNetlinkSocket();
156 if (sockFd < 0) {
157 LOG("create netlink socket failed");
158 return NULL;
159 }
160 while (1) {
161 len = recv(sockFd, buffer, DEFAULT_NETLINK_RECVBUF, 0);
162 if (len < 0 && errno == EINTR) {
163 continue;
164 }
165 if (len < 0) {
166 LOG("recv netlink socket error");
167 break;
168 }
169 if (len < (int32_t)sizeof(struct nlmsghdr)) {
170 LOG("recv buffer not enough");
171 continue;
172 }
173 nlh = (struct nlmsghdr *)buffer;
174 while (NLMSG_OK(nlh, len) && nlh->nlmsg_type != NLMSG_DONE) {
175 switch (nlh->nlmsg_type) {
176 case RTM_NEWADDR:
177 case RTM_DELADDR:
178 ProcessAddrEvent(nlh);
179 break;
180 case RTM_NEWLINK:
181 case RTM_DELLINK:
182 ProcessLinkEvent(nlh);
183 break;
184 default:
185 break;
186 }
187 nlh = NLMSG_NEXT(nlh, len);
188 }
189 }
190 close(sockFd);
191 LOG("netlink monitor thread exit");
192 return NULL;
193 }
194
NetlinkMonitorStart(PFUN_NNL_EVENT_HANDLER handler)195 int32_t NetlinkMonitorStart(PFUN_NNL_EVENT_HANDLER handler)
196 {
197 pthread_t tid;
198
199 if (handler == NULL) {
200 LOG("netlink event handler is null");
201 return -1;
202 }
203 if (pthread_create(&tid, NULL, NetlinkMonitorThread, NULL) != 0) {
204 LOG("create ip change monitor thread failed");
205 return -1;
206 }
207 g_eventHandler = handler;
208 return 0;
209 }
210
211