1 /*
2  * Copyright (C) 2021 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 
16 #include "nstackx_dev.h"
17 #include "nstackx_util.h"
18 #include "nstackx_error.h"
19 #include "nstackx_log.h"
20 #include "securec.h"
21 
22 #define TAG "nStackXDev"
23 
GetConnectionTypeByDevName(const char * devName,uint32_t devNameLen,uint16_t * connectType)24 static int32_t GetConnectionTypeByDevName(const char *devName, uint32_t devNameLen, uint16_t *connectType)
25 {
26     int32_t ret = NSTACKX_EFAILED;
27     uint32_t p2pNameLen = (uint32_t)strlen(P2P_DEV_NAME_PRE);
28     uint32_t wlanNameLen = (uint32_t)strlen(WLAN_DEV_NAME_PRE);
29 
30     if (devNameLen >= p2pNameLen && memcmp(devName, P2P_DEV_NAME_PRE, p2pNameLen) == 0) {
31         *connectType = CONNECT_TYPE_P2P;
32         ret = NSTACKX_EOK;
33         LOGI(TAG, "connType is P2P(%hu)", *connectType);
34     } else if (devNameLen >= wlanNameLen && memcmp(devName, WLAN_DEV_NAME_PRE, wlanNameLen) == 0) {
35         *connectType = CONNECT_TYPE_WLAN;
36         LOGI(TAG, "connType is WLAN(%hu)", *connectType);
37         ret = NSTACKX_EOK;
38     }
39     return ret;
40 }
41 
GetInterfaceInfo(int32_t fd,int32_t option,struct ifreq * interface)42 static int32_t GetInterfaceInfo(int32_t fd, int32_t option, struct ifreq *interface)
43 {
44     if (interface == NULL) {
45         return NSTACKX_EINVAL;
46     }
47     if (ioctl(fd, SIOCGIFFLAGS, (char*)interface) < 0) {
48         LOGE(TAG, "ioctl fail, errno = %d", errno);
49         return NSTACKX_EFAILED;
50     }
51     if (!((uint16_t)interface->ifr_flags & IFF_UP)) {
52         LOGE(TAG, "interface is not up");
53         return NSTACKX_EINVAL;
54     }
55 
56     /* get IP of this interface */
57     if (ioctl(fd, option, (char*)interface) < 0) {
58         LOGE(TAG, "ioctl fail, errno = %d", errno);
59         return NSTACKX_EFAILED;
60     }
61     return NSTACKX_EOK;
62 }
63 
GetInterfaceIP(int32_t fd,struct ifreq * interface)64 int32_t GetInterfaceIP(int32_t fd, struct ifreq *interface)
65 {
66     return GetInterfaceInfo(fd, SIOCGIFADDR, interface);
67 }
68 
GetInterfaceNetMask(int32_t fd,struct ifreq * interface)69 static int32_t GetInterfaceNetMask(int32_t fd, struct ifreq *interface)
70 {
71     return GetInterfaceInfo(fd, SIOCGIFNETMASK, interface);
72 }
73 
GetInterfaceList(struct ifconf * ifc,struct ifreq * buf,uint32_t size)74 int32_t GetInterfaceList(struct ifconf *ifc, struct ifreq *buf, uint32_t size)
75 {
76     int32_t fd = socket(AF_INET, SOCK_DGRAM, 0);
77     if (fd < 0) {
78         return NSTACKX_EFAILED;
79     }
80     ifc->ifc_len = (int32_t)size;
81     ifc->ifc_buf = (char*)buf;
82     if (ioctl(fd, SIOCGIFCONF, (char*)ifc) < 0) {
83         LOGE(TAG, "ioctl fail, errno = %d", errno);
84         CloseSocketInner(fd);
85         return NSTACKX_EFAILED;
86     }
87     return fd;
88 }
89 
GetConnectionTypeByDev(const uint32_t sourceIp,uint16_t * connectType)90 int32_t GetConnectionTypeByDev(const uint32_t sourceIp, uint16_t *connectType)
91 {
92     struct ifreq buf[INTERFACE_MAX];
93     struct ifconf ifc;
94 
95     uint32_t ethNameLen = (uint32_t)strlen(ETH_DEV_NAME_PRE);
96     uint32_t wlanNameLen = (uint32_t)strlen(WLAN_DEV_NAME_PRE);
97     int32_t fd = GetInterfaceList(&ifc, buf, sizeof(buf));
98     if (fd < 0) {
99         LOGE(TAG, "get interfacelist failed");
100         return NSTACKX_EFAILED;
101     }
102 
103     int32_t ifreqLen = (int32_t)sizeof(struct ifreq);
104     int32_t interfaceNum = (int32_t)(ifc.ifc_len / ifreqLen);
105     for (int32_t i = 0; i < interfaceNum && i < INTERFACE_MAX; i++) {
106         LOGI(TAG, "ndevice name: %s", buf[i].ifr_name);
107         uint32_t ifrNameLen = (uint32_t)strlen(buf[i].ifr_name);
108         if (ifrNameLen < ethNameLen && ifrNameLen < wlanNameLen) {
109             continue;
110         }
111 
112         /* get IP of this interface */
113         int32_t state = GetInterfaceIP(fd, &buf[i]);
114         if (state == NSTACKX_EFAILED) {
115             goto L_ERROR;
116         } else if (state == NSTACKX_EINVAL) {
117             continue;
118         }
119         if (sourceIp == ((struct sockaddr_in *)&(buf[i].ifr_addr))->sin_addr.s_addr) {
120             if (GetConnectionTypeByDevName(buf[i].ifr_name, ifrNameLen, connectType) == NSTACKX_EOK) {
121                 break;
122             }
123         }
124     }
125     CloseSocketInner(fd);
126     return NSTACKX_EOK;
127 L_ERROR:
128     CloseSocketInner(fd);
129     LOGE(TAG, "get connect type failed");
130     return NSTACKX_EFAILED;
131 }
132 
FindDevByInterfaceIP(int32_t fd,struct ifconf ifc,struct ifreq buf[],uint32_t sourceIP)133 static int32_t FindDevByInterfaceIP(int32_t fd, struct ifconf ifc, struct ifreq buf[], uint32_t sourceIP)
134 {
135     int32_t i;
136     int32_t ifreqLen = (int32_t)sizeof(struct ifreq);
137     int32_t interfaceNum = (int32_t)(ifc.ifc_len / ifreqLen);
138     for (i = 0; i < interfaceNum && i < INTERFACE_MAX; i++) {
139         /* get IP of this interface */
140         int32_t state = GetInterfaceIP(fd, &buf[i]);
141         if (state == NSTACKX_EFAILED) {
142             return NSTACKX_EFAILED;
143         } else if (state == NSTACKX_EINVAL) {
144             continue;
145         }
146         if (sourceIP == ((struct sockaddr_in *)&(buf[i].ifr_addr))->sin_addr.s_addr) {
147             return i;
148         }
149     }
150     return NSTACKX_EFAILED;
151 }
152 
GetInterfaceNameByIP(uint32_t sourceIP,char * interfaceName,size_t nameLen)153 int32_t GetInterfaceNameByIP(uint32_t sourceIP, char *interfaceName, size_t nameLen)
154 {
155     struct ifreq buf[INTERFACE_MAX];
156     struct ifconf ifc;
157     int32_t devIndex;
158     int32_t ret = NSTACKX_EOK;
159     int32_t fd = GetInterfaceList(&ifc, buf, sizeof(buf));
160     if (fd < 0) {
161         LOGE(TAG, "can't GetInterfaceList");
162         return NSTACKX_EFAILED;
163     }
164     devIndex = FindDevByInterfaceIP(fd, ifc, buf, sourceIP);
165     CloseSocketInner(fd);
166     if (devIndex >= 0) {
167         if (strcpy_s(interfaceName, nameLen, buf[devIndex].ifr_name) != EOK) {
168             LOGE(TAG, "strcpy failed");
169             ret = NSTACKX_EFAILED;
170         }
171     }
172     return ret;
173 }
174 
BindToDeviceInner(int32_t sockfd,const struct ifreq * ifBinding)175 static int32_t BindToDeviceInner(int32_t sockfd, const struct ifreq *ifBinding)
176 {
177     struct ifreq ifr;
178 
179     if (ifBinding == NULL) {
180         LOGE(TAG, "no right interface for binding");
181         return NSTACKX_EFAILED;
182     }
183     if (strncpy_s(ifr.ifr_ifrn.ifrn_name, IFNAMSIZ, ifBinding->ifr_name, strlen(ifBinding->ifr_name)) != EOK) {
184         LOGE(TAG, "strncpy fail");
185         return NSTACKX_EFAILED;
186     }
187     if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, (char *)&ifr, sizeof(ifr)) < 0) {
188         LOGE(TAG, "setsockopt fail, errno = %d", errno);
189         return NSTACKX_EFAILED;
190     }
191     LOGI(TAG, "binding interface %s success", ifBinding->ifr_name);
192     return NSTACKX_EOK;
193 }
194 
195 /*
196  * If localAddr isn't NULL, bind to interface correspond to ip,
197  * otherwise, bind to interface which is choosed by strategy.
198  */
BindToDevice(SocketDesc sockfd,const struct sockaddr_in * localAddr)199 int32_t BindToDevice(SocketDesc sockfd, const struct sockaddr_in *localAddr)
200 {
201     struct ifreq buf[INTERFACE_MAX];
202     struct ifconf ifc;
203     struct ifreq *ifBinding = NULL;
204     uint32_t ethNameLen = (uint32_t)strlen(ETH_DEV_NAME_PRE);
205     uint32_t wlanNameLen = (uint32_t)strlen(WLAN_DEV_NAME_PRE);
206     int32_t fd = GetInterfaceList(&ifc, buf, sizeof(buf));
207     if (fd < 0) {
208         return NSTACKX_EFAILED;
209     }
210     int32_t ifreqLen = (int32_t)sizeof(struct ifreq);
211     int32_t interfaceNum = (int32_t)(ifc.ifc_len / ifreqLen);
212     for (int32_t i = 0; i < interfaceNum && i < INTERFACE_MAX; i++) {
213         LOGI(TAG, "device name: %s", buf[i].ifr_name);
214         if (strlen(buf[i].ifr_name) < ethNameLen && strlen(buf[i].ifr_name) < wlanNameLen) {
215             continue;
216         }
217         /* get IP of this interface */
218         int32_t state = GetInterfaceIP(fd, &buf[i]);
219         if (state == NSTACKX_EFAILED) {
220             goto L_ERROR;
221         } else if (state == NSTACKX_EINVAL) {
222             continue;
223         }
224         if (localAddr != NULL) {
225             /* find corresponding interface by ip */
226             if (localAddr->sin_addr.s_addr == ((struct sockaddr_in *)&(buf[i].ifr_addr))->sin_addr.s_addr) {
227                 ifBinding = &buf[i];
228                 break;
229             }
230         } else {
231             /* strategy: ethernet have higher priority */
232             if (memcmp(buf[i].ifr_name, ETH_DEV_NAME_PRE, ethNameLen) == 0) {
233                 ifBinding = &buf[i];
234                 break;
235             } else if (memcmp(buf[i].ifr_name, WLAN_DEV_NAME_PRE, wlanNameLen) == 0) {
236                 ifBinding = &buf[i];
237             }
238         }
239     }
240     CloseSocketInner(fd);
241     return BindToDeviceInner(sockfd, ifBinding);
242 L_ERROR:
243     LOGE(TAG, "ioctl fail, errno = %d", errno);
244     CloseSocketInner(fd);
245     return NSTACKX_EFAILED;
246 }
247 
GetIfBroadcastIp(const char * ifName,char * ipString,size_t ipStringLen)248 int32_t GetIfBroadcastIp(const char *ifName, char *ipString, size_t ipStringLen)
249 {
250     struct ifreq buf[INTERFACE_MAX];
251     struct ifconf ifc;
252     uint8_t foundIp = NSTACKX_FALSE;
253 
254     if (ifName == NULL) {
255         return NSTACKX_EFAILED;
256     }
257 
258     int32_t fd = GetInterfaceList(&ifc, buf, sizeof(buf));
259     if (fd < 0) {
260         return NSTACKX_EFAILED;
261     }
262 
263     int32_t ifreqLen = (int32_t)sizeof(struct ifreq);
264     int32_t interfaceNum = (int32_t)(ifc.ifc_len / ifreqLen);
265     for (int32_t i = 0; i < interfaceNum && i < INTERFACE_MAX; i++) {
266         if (strlen(buf[i].ifr_name) < strlen(ifName)) {
267             continue;
268         }
269         if (memcmp(buf[i].ifr_name, ifName, strlen(ifName)) != 0) {
270             continue;
271         }
272         if (GetInterfaceInfo(fd, SIOCGIFBRDADDR, &buf[i]) != NSTACKX_EOK) {
273             continue;
274         }
275         if (buf[i].ifr_addr.sa_family != AF_INET) {
276             continue;
277         }
278 
279         if (inet_ntop(AF_INET, &(((struct sockaddr_in *)&(buf[i].ifr_addr))->sin_addr), ipString,
280             (socklen_t)ipStringLen) == NULL) {
281             continue;
282         }
283         foundIp = NSTACKX_TRUE;
284         break;
285     }
286     CloseSocketInner(fd);
287 
288     if (!foundIp) {
289         return NSTACKX_EFAILED;
290     }
291 
292     return NSTACKX_EOK;
293 }
294 
IsValidInterface(const char * interfaceName)295 static uint8_t IsValidInterface(const char *interfaceName)
296 {
297     if (interfaceName == NULL) {
298         return NSTACKX_FALSE;
299     }
300     uint32_t targetDevLen = (uint32_t)strlen(P2P_DEV_NAME_PRE);
301     if (strlen(interfaceName) >= targetDevLen && memcmp(interfaceName, P2P_DEV_NAME_PRE, targetDevLen) == 0) {
302         return NSTACKX_TRUE;
303     }
304     targetDevLen = (uint32_t)strlen(ETH_DEV_NAME_PRE);
305     if (strlen(interfaceName) >= targetDevLen && memcmp(interfaceName, ETH_DEV_NAME_PRE, targetDevLen) == 0) {
306         return NSTACKX_TRUE;
307     }
308     targetDevLen = (uint32_t)strlen(WLAN_DEV_NAME_PRE);
309     if (strlen(interfaceName) >= targetDevLen && memcmp(interfaceName, WLAN_DEV_NAME_PRE, targetDevLen) == 0) {
310         return NSTACKX_TRUE;
311     }
312     return NSTACKX_FALSE;
313 }
314 
GetTargetInterface(const struct sockaddr_in * dstAddr,struct ifreq * localDev)315 int32_t GetTargetInterface(const struct sockaddr_in *dstAddr, struct ifreq *localDev)
316 {
317     struct ifreq buf[INTERFACE_MAX];
318     struct ifconf ifc;
319     uint32_t localIp;
320     uint32_t netMask;
321     int32_t fd = GetInterfaceList(&ifc, buf, sizeof(buf));
322     if (fd < 0) {
323         return NSTACKX_EFAILED;
324     }
325     int32_t ifreqLen = (int32_t)sizeof(struct ifreq);
326     int32_t interfaceNum = (int32_t)(ifc.ifc_len / ifreqLen);
327     for (int32_t i = 0; i < interfaceNum && i < INTERFACE_MAX; i++) {
328         if (!IsValidInterface(buf[i].ifr_name)) {
329             continue;
330         }
331         /* get IP of this interface */
332         int32_t state = GetInterfaceIP(fd, &buf[i]);
333         if (state == NSTACKX_EFAILED) {
334             goto L_ERROR;
335         } else if (state == NSTACKX_EINVAL) {
336             continue;
337         }
338         localIp = ((struct sockaddr_in *)(&buf[i].ifr_addr))->sin_addr.s_addr;
339         state = GetInterfaceNetMask(fd, &buf[i]);
340         if (state == NSTACKX_EFAILED) {
341             goto L_ERROR;
342         } else if (state == NSTACKX_EINVAL) {
343             continue;
344         }
345         netMask = ((struct sockaddr_in *)&(buf[i].ifr_netmask))->sin_addr.s_addr;
346         /* if localIp and dstIp are in the same LAN, fetch the interface name of thie localIp and return */
347         if ((dstAddr->sin_addr.s_addr & netMask) == (localIp & netMask)) {
348             if (strncpy_s(localDev->ifr_ifrn.ifrn_name, IFNAMSIZ, buf[i].ifr_name, strlen(buf[i].ifr_name)) != EOK) {
349                 LOGE(TAG, "ifreq name copy failed");
350                 goto L_ERROR;
351             }
352             CloseSocketInner(fd);
353             return NSTACKX_EOK;
354         }
355     }
356 L_ERROR:
357     CloseSocketInner(fd);
358     return NSTACKX_EFAILED;
359 }
360 
BindToDevInTheSameLan(SocketDesc sockfd,const struct sockaddr_in * sockAddr)361 void BindToDevInTheSameLan(SocketDesc sockfd, const struct sockaddr_in *sockAddr)
362 {
363     struct ifreq localInterface;
364     if (sockfd < 0) {
365         return;
366     }
367     (void)memset_s(&localInterface, sizeof(localInterface), 0, sizeof(localInterface));
368     if (GetTargetInterface(sockAddr, &localInterface) != NSTACKX_EOK) {
369         LOGE(TAG, "get target interface fail");
370         return;
371     }
372     if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, (char *)&localInterface, sizeof(localInterface)) < 0) {
373         LOGE(TAG, "bind to device fail, errno = %d", errno);
374         return;
375     }
376     LOGI(TAG, "bind to %s successfully", localInterface.ifr_name);
377 }
378 
BindToTargetDev(SocketDesc sockfd,const char * targetInterfaceName)379 int32_t BindToTargetDev(SocketDesc sockfd, const char *targetInterfaceName)
380 {
381     struct ifreq buf[INTERFACE_MAX];
382     struct ifconf ifc;
383     int32_t ret = NSTACKX_EFAILED;
384     int32_t fd = GetInterfaceList(&ifc, buf, sizeof(buf));
385     if (fd < 0) {
386         return NSTACKX_EFAILED;
387     }
388     int32_t ifreqLen = (int32_t)sizeof(struct ifreq);
389     int32_t interfaceNum = (int32_t)(ifc.ifc_len / ifreqLen);
390     for (int32_t i = 0; i < interfaceNum && i < INTERFACE_MAX; i++) {
391         /* get IP of this interface */
392         int32_t state = GetInterfaceIP(fd, &buf[i]);
393         if (state == NSTACKX_EFAILED) {
394             break;
395         } else if (state == NSTACKX_EINVAL) {
396             continue;
397         }
398         if (strlen(buf[i].ifr_name) == strlen(targetInterfaceName) &&
399             strcmp(buf[i].ifr_name, targetInterfaceName) == 0) {
400             ret = BindToDeviceInner(sockfd, &buf[i]);
401             break;
402         }
403     }
404     CloseSocketInner(fd);
405     return ret;
406 }
407