[IOT-1346] Enable network status monitoring for tcp network
[platform/upstream/iotivity.git] / resource / csdk / connectivity / src / ip_adapter / tizen / caipnwmonitor.c
1 /******************************************************************
2 *
3 * Copyright 2014 Samsung Electronics All Rights Reserved.
4 *
5 *
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *      http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 ******************************************************************/
20
21 #include "caipinterface.h"
22
23 #include <sys/types.h>
24 #include <ifaddrs.h>
25 #include <net/if.h>
26 #include <sys/socket.h>
27 #include <netdb.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <unistd.h>
31 #include <sys/ioctl.h>
32 #include <wifi.h>
33 #include <linux/netlink.h>
34 #include <linux/rtnetlink.h>
35 #include <arpa/inet.h>
36 #include <netinet/in.h>
37
38 #include "caipnwmonitor.h"
39 #include "caadapterutils.h"
40 #include "logger.h"
41 #include "oic_malloc.h"
42 #include "oic_string.h"
43 #include <coap/utlist.h>
44
45 #define TAG "IP_MONITOR"
46
47 #define MAX_INTERFACE_INFO_LENGTH (1024)
48
49 #define NETLINK_MESSAGE_LENGTH  (4096)
50
51 /**
52  * Used to storing adapter changes callback interface.
53  */
54 static struct CAIPCBData_t *g_adapterCallbackList = NULL;
55
56 /**
57  * Create new interface item.
58  */
59 static CAInterface_t *CANewInterfaceItem(int index, char *name, int family,
60                                          const char *addr, int flags);
61
62 /**
63  * Add new network interface in list.
64  */
65 static CAResult_t CAAddInterfaceItem(u_arraylist_t *iflist, int index,
66                                      char *name, int family, const char *addr, int flags);
67
68 /**
69  * Pass the changed network status through the stored callback.
70  */
71 static void CAIPPassNetworkChangesToAdapter(CANetworkStatus_t status);
72
73 /**
74  * Callback function to received connection state changes.
75  */
76 static void CAWIFIConnectionStateChangedCb(wifi_connection_state_e state, wifi_ap_h ap,
77                                            void *userData);
78
79 /**
80  * Callback function to received device state changes.
81  */
82 static void CAWIFIDeviceStateChangedCb(wifi_device_state_e state, void *userData);
83
84
85 int CAGetPollingInterval(int interval)
86 {
87     return interval;
88 }
89
90 static void CAIPPassNetworkChangesToAdapter(CANetworkStatus_t status)
91 {
92     CAIPCBData_t *cbitem = NULL;
93     LL_FOREACH(g_adapterCallbackList, cbitem)
94     {
95         if (cbitem && cbitem->adapter)
96         {
97             cbitem->callback(cbitem->adapter, status);
98         }
99     }
100 }
101
102 CAResult_t CAIPSetNetworkMonitorCallback(CAIPAdapterStateChangeCallback callback,
103                                          CATransportAdapter_t adapter)
104 {
105     if (!callback)
106     {
107         OIC_LOG(ERROR, TAG, "callback is null");
108         return CA_STATUS_INVALID_PARAM;
109     }
110
111     CAIPCBData_t *cbitem = NULL;
112     LL_FOREACH(g_adapterCallbackList, cbitem)
113     {
114         if (cbitem && adapter == cbitem->adapter && callback == cbitem->callback)
115         {
116             OIC_LOG(DEBUG, TAG, "this callback is already added");
117             return CA_STATUS_OK;
118         }
119     }
120
121     cbitem = (CAIPCBData_t *)OICCalloc(1, sizeof(*cbitem));
122     if (!cbitem)
123     {
124         OIC_LOG(ERROR, TAG, "Malloc failed");
125         return CA_STATUS_FAILED;
126     }
127
128     cbitem->adapter = adapter;
129     cbitem->callback = callback;
130     LL_APPEND(g_adapterCallbackList, cbitem);
131
132     return CA_STATUS_OK;
133 }
134
135 CAResult_t CAIPUnSetNetworkMonitorCallback(CATransportAdapter_t adapter)
136 {
137     CAIPCBData_t *cbitem = NULL;
138     CAIPCBData_t *tmpCbitem = NULL;
139     LL_FOREACH_SAFE(g_adapterCallbackList, cbitem, tmpCbitem)
140     {
141         if (cbitem && adapter == cbitem->adapter)
142         {
143             OIC_LOG(DEBUG, TAG, "remove specific callback");
144             LL_DELETE(g_adapterCallbackList, cbitem);
145             OICFree(cbitem);
146             return CA_STATUS_OK;
147         }
148     }
149     return CA_STATUS_OK;
150 }
151
152 CAInterface_t *CAFindInterfaceChange()
153 {
154     CAInterface_t *foundNewInterface = NULL;
155     char buf[NETLINK_MESSAGE_LENGTH] = { 0 };
156     struct sockaddr_nl sa = { 0 };
157     struct iovec iov = { .iov_base = buf,
158                          .iov_len = sizeof (buf) };
159     struct msghdr msg = { .msg_name = (void *)&sa,
160                           .msg_namelen = sizeof (sa),
161                           .msg_iov = &iov,
162                           .msg_iovlen = 1 };
163
164     size_t len = recvmsg(caglobals.ip.netlinkFd, &msg, 0);
165
166     for (struct nlmsghdr *nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len))
167     {
168         if (nh != NULL && nh->nlmsg_type != RTM_NEWLINK)
169         {
170             continue;
171         }
172
173         struct ifinfomsg *ifi = (struct ifinfomsg *)NLMSG_DATA(nh);
174
175         int ifiIndex = ifi->ifi_index;
176         u_arraylist_t *iflist = CAIPGetInterfaceInformation(ifiIndex);
177
178         if ((!ifi || (ifi->ifi_flags & IFF_LOOPBACK) || !(ifi->ifi_flags & IFF_RUNNING)))
179         {
180             continue;
181         }
182
183         if (!iflist)
184         {
185             OIC_LOG_V(ERROR, TAG, "get interface info failed: %s", strerror(errno));
186             return NULL;
187         }
188
189         uint32_t listLength = u_arraylist_length(iflist);
190         for (uint32_t i = 0; i < listLength; i++)
191         {
192             CAInterface_t *ifitem = (CAInterface_t *)u_arraylist_get(iflist, i);
193             if (!ifitem)
194             {
195                 continue;
196             }
197
198             if ((int)ifitem->index != ifiIndex)
199             {
200                 continue;
201             }
202
203             foundNewInterface = CANewInterfaceItem(ifitem->index, ifitem->name, ifitem->family,
204                                                    ifitem->addr, ifitem->flags);
205             break;    // we found the one we were looking for
206         }
207         u_arraylist_destroy(iflist);
208     }
209     return foundNewInterface;
210 }
211
212 CAResult_t CAIPStartNetworkMonitor(CAIPAdapterStateChangeCallback callback,
213                                    CATransportAdapter_t adapter)
214 {
215     OIC_LOG(DEBUG, TAG, "IN");
216
217     if (!g_adapterCallbackList)
218     {
219         // Initialize Wifi service
220        wifi_error_e ret = wifi_initialize();
221        if (WIFI_ERROR_NONE != ret)
222        {
223            OIC_LOG(ERROR, TAG, "wifi_initialize failed");
224            return CA_STATUS_FAILED;
225        }
226
227        // Set callback for receiving state changes
228        ret = wifi_set_device_state_changed_cb(CAWIFIDeviceStateChangedCb, NULL);
229        if (WIFI_ERROR_NONE != ret)
230        {
231            OIC_LOG(ERROR, TAG, "wifi_set_device_state_changed_cb failed");
232            return CA_STATUS_FAILED;
233        }
234
235        // Set callback for receiving connection state changes
236        ret = wifi_set_connection_state_changed_cb(CAWIFIConnectionStateChangedCb, NULL);
237        if (WIFI_ERROR_NONE != ret)
238        {
239            OIC_LOG(ERROR, TAG, "wifi_set_connection_state_changed_cb failed");
240            return CA_STATUS_FAILED;
241        }
242     }
243
244     return CAIPSetNetworkMonitorCallback(callback, adapter);
245 }
246
247 CAResult_t CAIPStopNetworkMonitor(CATransportAdapter_t adapter)
248 {
249     OIC_LOG(DEBUG, TAG, "IN");
250
251     CAIPUnSetNetworkMonitorCallback(adapter);
252     if (!g_adapterCallbackList)
253     {
254         // Reset callback for receiving state changes
255        wifi_error_e ret = wifi_unset_device_state_changed_cb();
256        if (WIFI_ERROR_NONE != ret)
257        {
258            OIC_LOG(ERROR, TAG, "wifi_unset_device_state_changed_cb failed");
259        }
260
261        // Reset callback for receiving connection state changes
262        ret = wifi_unset_connection_state_changed_cb();
263        if (WIFI_ERROR_NONE != ret)
264        {
265            OIC_LOG(ERROR, TAG, "wifi_unset_connection_state_changed_cb failed");
266        }
267
268        // Deinitialize Wifi service
269        ret = wifi_deinitialize();
270        if (WIFI_ERROR_NONE != ret)
271        {
272            OIC_LOG(ERROR, TAG, "wifi_deinitialize failed");
273        }
274     }
275
276     return CA_STATUS_OK;
277 }
278
279 u_arraylist_t *CAIPGetInterfaceInformation(int desiredIndex)
280 {
281     u_arraylist_t *iflist = u_arraylist_create();
282     if (!iflist)
283     {
284         OIC_LOG_V(ERROR, TAG, "Failed to create iflist: %s", strerror(errno));
285         return NULL;
286     }
287
288     char buf[MAX_INTERFACE_INFO_LENGTH] = { 0 };
289     struct ifconf ifc = { .ifc_len = MAX_INTERFACE_INFO_LENGTH, .ifc_buf = buf };
290
291     int s = caglobals.ip.u6.fd != -1 ? caglobals.ip.u6.fd : caglobals.ip.u4.fd;
292     if (ioctl(s, SIOCGIFCONF, &ifc) < 0)
293     {
294         OIC_LOG_V(ERROR, TAG, "SIOCGIFCONF failed: %s", strerror(errno));
295         u_arraylist_destroy(iflist);
296         return NULL;
297     }
298
299     struct ifreq* ifr = ifc.ifc_req;
300     size_t interfaces = ifc.ifc_len / sizeof (ifc.ifc_req[0]);
301     size_t ifreqsize = ifc.ifc_len;
302
303     if (ifreqsize > caglobals.ip.nm.sizeIfItems)
304     {
305         CAIfItem_t *items = (CAIfItem_t *)OICRealloc(caglobals.ip.nm.ifItems, ifreqsize);
306         if (!items)
307         {
308             OIC_LOG(ERROR, TAG, "OICRealloc failed");
309             goto exit;
310         }
311         caglobals.ip.nm.ifItems = items;
312         caglobals.ip.nm.sizeIfItems = ifreqsize;
313     }
314
315     caglobals.ip.nm.numIfItems = 0;
316     for (size_t i = 0; i < interfaces; i++)
317     {
318         struct ifreq* item = &ifr[i];
319         char *name = item->ifr_name;
320
321         if (ioctl(s, SIOCGIFFLAGS, item) < 0)
322         {
323             OIC_LOG_V(ERROR, TAG, "SIOCGIFFLAGS failed: %s", strerror(errno));
324             continue;
325         }
326         int16_t flags = item->ifr_flags;
327         if ((flags & IFF_LOOPBACK) || !(flags & IFF_RUNNING))
328         {
329             continue;
330         }
331         if (ioctl(s, SIOCGIFINDEX, item) < 0)
332         {
333             OIC_LOG_V(ERROR, TAG, "SIOCGIFINDEX failed: %s", strerror(errno));
334             continue;
335         }
336
337         int ifindex = item->ifr_ifindex;
338         caglobals.ip.nm.ifItems[i].ifIndex = ifindex;
339         caglobals.ip.nm.numIfItems++;
340
341         if (desiredIndex && (ifindex != desiredIndex))
342         {
343             continue;
344         }
345
346         // Get address of network interface.
347         char addr[MAX_ADDR_STR_SIZE_CA] = { 0 };
348         struct sockaddr_in *sa = (struct sockaddr_in *)&item->ifr_addr;
349         inet_ntop(AF_INET, (void *)&(sa->sin_addr), addr, sizeof(addr));
350
351         // Add IPv4 interface
352         CAResult_t result = CAAddInterfaceItem(iflist, ifindex, name, AF_INET, addr, flags);
353         if (CA_STATUS_OK != result)
354         {
355             goto exit;
356         }
357
358         // Add IPv6 interface
359         result = CAAddInterfaceItem(iflist, ifindex, name, AF_INET6, addr, flags);
360         if (CA_STATUS_OK != result)
361         {
362             goto exit;
363         }
364     }
365     return iflist;
366
367 exit:
368     u_arraylist_destroy(iflist);
369     return NULL;
370 }
371
372 static CAResult_t CAAddInterfaceItem(u_arraylist_t *iflist, int index,
373                                      char *name, int family, const char *addr, int flags)
374 {
375     CAInterface_t *ifitem = CANewInterfaceItem(index, name, family, addr, flags);
376     if (!ifitem)
377     {
378         return CA_STATUS_FAILED;
379     }
380     bool result = u_arraylist_add(iflist, ifitem);
381     if (!result)
382     {
383         OIC_LOG(ERROR, TAG, "u_arraylist_add failed.");
384         OICFree(ifitem);
385         return CA_STATUS_FAILED;
386     }
387
388     return CA_STATUS_OK;
389 }
390
391 static CAInterface_t *CANewInterfaceItem(int index, char *name, int family,
392                                          const char *addr, int flags)
393 {
394     CAInterface_t *ifitem = (CAInterface_t *)OICCalloc(1, sizeof (CAInterface_t));
395     if (!ifitem)
396     {
397         OIC_LOG(ERROR, TAG, "Malloc failed");
398         return NULL;
399     }
400
401     OICStrcpy(ifitem->name, INTERFACE_NAME_MAX, name);
402     ifitem->index = index;
403     ifitem->family = family;
404     OICStrcpy(ifitem->addr, sizeof(ifitem->addr), addr);
405     ifitem->flags = flags;
406
407     return ifitem;
408 }
409
410 void CAWIFIConnectionStateChangedCb(wifi_connection_state_e state, wifi_ap_h ap,
411                                     void *userData)
412 {
413     OIC_LOG(DEBUG, TAG, "IN");
414
415     if (WIFI_CONNECTION_STATE_ASSOCIATION == state
416         || WIFI_CONNECTION_STATE_CONFIGURATION == state)
417     {
418         OIC_LOG(DEBUG, TAG, "Connection is in Association State");
419         return;
420     }
421
422     if (WIFI_CONNECTION_STATE_CONNECTED == state)
423     {
424         CAIPPassNetworkChangesToAdapter(CA_INTERFACE_UP);
425     }
426     else
427     {
428         CAIPPassNetworkChangesToAdapter(CA_INTERFACE_DOWN);
429     }
430
431     OIC_LOG(DEBUG, TAG, "OUT");
432 }
433
434 void CAWIFIDeviceStateChangedCb(wifi_device_state_e state, void *userData)
435 {
436     OIC_LOG(DEBUG, TAG, "IN");
437
438     if (WIFI_DEVICE_STATE_ACTIVATED == state)
439     {
440         OIC_LOG(DEBUG, TAG, "Wifi is in Activated State");
441     }
442     else
443     {
444         CAWIFIConnectionStateChangedCb(WIFI_CONNECTION_STATE_DISCONNECTED, NULL, NULL);
445         OIC_LOG(DEBUG, TAG, "Wifi is in Deactivated State");
446     }
447
448     OIC_LOG(DEBUG, TAG, "OUT");
449 }