Update snapshot(2017-12-06)
[platform/upstream/iotivity.git] / resource / csdk / connectivity / src / ip_adapter / tizen / caipnwmonitor.c
index 88427f3..079ecd5 100644 (file)
@@ -18,8 +18,6 @@
 *
 ******************************************************************/
 
-#include "caipinterface.h"
-
 #include <sys/types.h>
 #include <ifaddrs.h>
 #include <net/if.h>
 #include <errno.h>
 #include <unistd.h>
 #include <sys/ioctl.h>
-#include <wifi.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <net_connection.h>
 
+#include "caipinterface.h"
+#include "caipnwmonitor.h"
 #include "caadapterutils.h"
 #include "logger.h"
 #include "oic_malloc.h"
 #include "oic_string.h"
+#include <coap/utlist.h>
 
-#define TAG "IP_MONITOR"
+#define TAG "OIC_CA_IP_MONITOR"
 #define MAX_INTERFACE_INFO_LENGTH (1024)
-
+#define NETLINK_MESSAGE_LENGTH  (4096)
+#define IFC_LABEL_LOOP          "lo"
+#define IFC_ADDR_LOOP_IPV4      "127.0.0.1"
+#define IFC_ADDR_LOOP_IPV6      "::1"
+
+/**
+ * Used to storing a connection handle for managing data connections.
+ */
+static connection_h connection = NULL;
+
+/**
+ * Used to storing adapter changes callback interface.
+ */
+static struct CAIPCBData_t *g_adapterCallbackList = NULL;
+
+/**
+ * Create new interface item.
+ */
 static CAInterface_t *CANewInterfaceItem(int index, char *name, int family,
-                                         uint32_t addr, int flags);
+                                         const char *addr, int flags);
 
+/**
+ * Add new network interface in list.
+ */
 static CAResult_t CAAddInterfaceItem(u_arraylist_t *iflist, int index,
-                                     char *name, int family, uint32_t addr, int flags);
-
-static void CAWIFIConnectionStateChangedCb(wifi_connection_state_e state, wifi_ap_h ap,
-                                           void *userData);
+                                     char *name, int family, const char *addr, int flags);
 
-static void CAWIFIDeviceStateChangedCb(wifi_device_state_e state, void *userData);
+/**
+ * Pass the changed network status through the stored callback.
+ */
+static void CAIPPassNetworkChangesToAdapter(CANetworkStatus_t status);
 
+/**
+ * Callback function to received connection state changes.
+ */
+static void CAIPConnectionStateChangedCb(connection_type_e type, void* userData);
 
 int CAGetPollingInterval(int interval)
 {
     return interval;
 }
 
-CAInterface_t *CAFindInterfaceChange()
+static void CAIPPassNetworkChangesToAdapter(CANetworkStatus_t status)
 {
-    char buf[MAX_INTERFACE_INFO_LENGTH] = { 0 };
-    struct ifconf ifc  = { .ifc_len = MAX_INTERFACE_INFO_LENGTH, .ifc_buf = buf };
-
-    int s = caglobals.ip.u6.fd != -1 ? caglobals.ip.u6.fd : caglobals.ip.u4.fd;
-    if (ioctl(s, SIOCGIFCONF, &ifc) < 0)
+    CAIPCBData_t *cbitem = NULL;
+    LL_FOREACH(g_adapterCallbackList, cbitem)
     {
-        OIC_LOG_V(ERROR, TAG, "SIOCGIFCONF failed: %s", strerror(errno));
-        return NULL;
+        if (cbitem && cbitem->adapter)
+        {
+            cbitem->callback(cbitem->adapter, status);
+        }
     }
+}
 
-    CAInterface_t *foundNewInterface = NULL;
-
-    struct ifreq* ifr = ifc.ifc_req;
-    size_t interfaces = ifc.ifc_len / sizeof (ifc.ifc_req[0]);
-    size_t ifreqsize = ifc.ifc_len;
-
-    CAIfItem_t *previous = (CAIfItem_t *)OICMalloc(ifreqsize);
-    if (!previous)
+CAResult_t CAIPSetNetworkMonitorCallback(CAIPAdapterStateChangeCallback callback,
+                                         CATransportAdapter_t adapter)
+{
+    if (!callback)
     {
-        OIC_LOG(ERROR, TAG, "OICMalloc failed");
-        return NULL;
+        OIC_LOG(ERROR, TAG, "callback is null");
+        return CA_STATUS_INVALID_PARAM;
     }
 
-    memcpy(previous, caglobals.ip.nm.ifItems, ifreqsize);
-    size_t numprevious = caglobals.ip.nm.numIfItems;
-
-    if (ifreqsize > caglobals.ip.nm.sizeIfItems)
+    CAIPCBData_t *cbitem = NULL;
+    LL_FOREACH(g_adapterCallbackList, cbitem)
     {
-
-        CAIfItem_t *items = (CAIfItem_t *)OICRealloc(caglobals.ip.nm.ifItems, ifreqsize);
-        if (!items)
+        if (cbitem && adapter == cbitem->adapter && callback == cbitem->callback)
         {
-            OIC_LOG(ERROR, TAG, "OICRealloc failed");
-            OICFree(previous);
-            return NULL;
+            OIC_LOG(DEBUG, TAG, "this callback is already added");
+            return CA_STATUS_OK;
         }
-        caglobals.ip.nm.ifItems = items;
-        caglobals.ip.nm.sizeIfItems = ifreqsize;
     }
 
-    caglobals.ip.nm.numIfItems = 0;
-    for (size_t i = 0; i < interfaces; i++)
+    cbitem = (CAIPCBData_t *)OICCalloc(1, sizeof(*cbitem));
+    if (!cbitem)
     {
-        struct ifreq* item = &ifr[i];
-        char *name = item->ifr_name;
-        struct sockaddr_in *sa4 = (struct sockaddr_in *)&item->ifr_addr;
-        uint32_t ipv4addr = sa4->sin_addr.s_addr;
+        OIC_LOG(ERROR, TAG, "Malloc failed");
+        return CA_STATUS_FAILED;
+    }
+
+    cbitem->adapter = adapter;
+    cbitem->callback = callback;
+    LL_APPEND(g_adapterCallbackList, cbitem);
 
-        if (ioctl(s, SIOCGIFFLAGS, item) < 0)
+    return CA_STATUS_OK;
+}
+
+CAResult_t CAIPUnSetNetworkMonitorCallback(CATransportAdapter_t adapter)
+{
+    CAIPCBData_t *cbitem = NULL;
+    CAIPCBData_t *tmpCbitem = NULL;
+    LL_FOREACH_SAFE(g_adapterCallbackList, cbitem, tmpCbitem)
+    {
+        if (cbitem && adapter == cbitem->adapter)
         {
-            OIC_LOG_V(ERROR, TAG, "SIOCGIFFLAGS failed: %s", strerror(errno));
-            continue;
+            OIC_LOG(DEBUG, TAG, "remove specific callback");
+            LL_DELETE(g_adapterCallbackList, cbitem);
+            OICFree(cbitem);
+            return CA_STATUS_OK;
         }
-        int16_t flags = item->ifr_flags;
-        if ((flags & IFF_LOOPBACK) || !(flags & IFF_RUNNING))
+    }
+    return CA_STATUS_OK;
+}
+
+u_arraylist_t *CAFindInterfaceChange()
+{
+    u_arraylist_t *iflist = NULL;
+    char buf[NETLINK_MESSAGE_LENGTH] = { 0 };
+    struct sockaddr_nl sa = { 0 };
+    struct iovec iov = { .iov_base = buf,
+                         .iov_len = sizeof (buf) };
+    struct msghdr msg = { .msg_name = (void *)&sa,
+                          .msg_namelen = sizeof (sa),
+                          .msg_iov = &iov,
+                          .msg_iovlen = 1 };
+
+    ssize_t len = recvmsg(caglobals.ip.netlinkFd, &msg, 0);
+
+    for (struct nlmsghdr *nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len))
+    {
+        if (nh != NULL && (nh->nlmsg_type != RTM_DELADDR && nh->nlmsg_type != RTM_NEWADDR))
         {
             continue;
         }
-        if (ioctl(s, SIOCGIFINDEX, item) < 0)
+
+        struct ifinfomsg *ifi = (struct ifinfomsg *)NLMSG_DATA(nh);
+        if (!ifi)
         {
-            OIC_LOG_V(ERROR, TAG, "SIOCGIFINDEX failed: %s", strerror(errno));
             continue;
         }
 
-        int ifIndex = item->ifr_ifindex;
-        caglobals.ip.nm.ifItems[i].ifIndex = ifIndex;  // refill interface list
-        caglobals.ip.nm.numIfItems++;
-
-        if (foundNewInterface)
+        if (RTM_DELADDR == nh->nlmsg_type)
         {
-            continue;   // continue updating interface list
+            CloseMulticastSocket();
         }
 
-        // see if this interface didn't previously exist
-        bool found = false;
-        for (size_t j = 0; j < numprevious; j++)
-        {
-            if (ifIndex == previous[j].ifIndex)
-            {
-                found = true;
-                break;
-            }
-        }
-        if (found)
+        int ifiIndex = ifi->ifi_index;
+        iflist = CAIPGetInterfaceInformation(ifiIndex);
+        if (!iflist)
         {
-            OIC_LOG_V(INFO, TAG, "Interface found: %s", name);
-            continue;
+            OIC_LOG_V(ERROR, TAG, "get interface info failed: %s", strerror(errno));
+            return NULL;
         }
-
-        foundNewInterface = CANewInterfaceItem(ifIndex, name, AF_INET, ipv4addr, flags);
+        CreateMulticastSocket();
     }
-
-    OICFree(previous);
-    return foundNewInterface;
+    return iflist;
 }
 
-CAResult_t CAIPStartNetworkMonitor()
+CAResult_t CAIPStartNetworkMonitor(CAIPAdapterStateChangeCallback callback,
+                                   CATransportAdapter_t adapter)
 {
-    OIC_LOG(DEBUG, TAG, "IN");
-
-     // Initialize Wifi service
-    wifi_error_e ret = wifi_initialize();
-    if (WIFI_ERROR_NONE != ret)
-    {
-        OIC_LOG(ERROR, TAG, "wifi_initialize failed");
-        return CA_STATUS_FAILED;
-    }
-
-    // Set callback for receiving state changes
-    ret = wifi_set_device_state_changed_cb(CAWIFIDeviceStateChangedCb, NULL);
-    if (WIFI_ERROR_NONE != ret)
+    if (!g_adapterCallbackList)
     {
-        OIC_LOG(ERROR, TAG, "wifi_set_device_state_changed_cb failed");
-        return CA_STATUS_FAILED;
-    }
+        // Initialize Connections.
+        connection_error_e ret = connection_create(&connection);
+        if (CONNECTION_ERROR_NONE != ret)
+        {
+            OIC_LOG(ERROR, TAG, "connection_create failed");
+            return CA_STATUS_FAILED;
+        }
 
-    // Set callback for receiving connection state changes
-    ret = wifi_set_connection_state_changed_cb(CAWIFIConnectionStateChangedCb, NULL);
-    if (WIFI_ERROR_NONE != ret)
-    {
-        OIC_LOG(ERROR, TAG, "wifi_set_connection_state_changed_cb failed");
-        return CA_STATUS_FAILED;
+        // Set callback for receiving state changes.
+        ret = connection_set_type_changed_cb(connection, CAIPConnectionStateChangedCb, NULL);
+        if (CONNECTION_ERROR_NONE != ret)
+        {
+            OIC_LOG(ERROR, TAG, "connection_set_type_changed_cb failed");
+            return CA_STATUS_FAILED;
+        }
     }
 
-    OIC_LOG(DEBUG, TAG, "OUT");
-    return CA_STATUS_OK;
+    OIC_LOG(DEBUG, TAG, "Initialize network monitoring successfully");
+    return CAIPSetNetworkMonitorCallback(callback, adapter);
 }
 
-CAResult_t CAIPStopNetworkMonitor()
+CAResult_t CAIPStopNetworkMonitor(CATransportAdapter_t adapter)
 {
     OIC_LOG(DEBUG, TAG, "IN");
 
-     // Reset callback for receiving state changes
-    wifi_error_e ret = wifi_unset_device_state_changed_cb();
-    if (WIFI_ERROR_NONE != ret)
-    {
-        OIC_LOG(ERROR, TAG, "wifi_unset_device_state_changed_cb failed");
-    }
-
-    // Reset callback for receiving connection state changes
-    ret = wifi_unset_connection_state_changed_cb();
-    if (WIFI_ERROR_NONE != ret)
+    CAIPUnSetNetworkMonitorCallback(adapter);
+    if (!g_adapterCallbackList)
     {
-        OIC_LOG(ERROR, TAG, "wifi_unset_connection_state_changed_cb failed");
-    }
+        // Reset callback for receiving state changes.
+        if (connection)
+        {
+            connection_error_e ret = connection_unset_type_changed_cb(connection);
+            if (CONNECTION_ERROR_NONE != ret)
+            {
+                OIC_LOG(ERROR, TAG, "connection_unset_type_changed_cb failed");
+            }
 
-    // Deinitialize Wifi service
-    ret = wifi_deinitialize();
-    if (WIFI_ERROR_NONE != ret)
-    {
-        OIC_LOG(ERROR, TAG, "wifi_deinitialize failed");
+            // Deinitialize Wifi service.
+            ret = connection_destroy(connection);
+            if (CONNECTION_ERROR_NONE != ret)
+            {
+                OIC_LOG(ERROR, TAG, "connection_destroy failed");
+            }
+            connection = NULL;
+        }
     }
 
-    OIC_LOG(DEBUG, TAG, "OUT");
+    OIC_LOG(DEBUG, TAG, "Network monitoring terminated successfully");
     return CA_STATUS_OK;
 }
 
-u_arraylist_t *CAIPGetInterfaceInformation(int desiredIndex)
+/**
+ * Used to send netlink query to kernel and recv response from kernel.
+ *
+ * @param[in]   idx       desired network interface index, 0 means all interfaces.
+ * @param[out]  iflist    linked list.
+ *
+ */
+static bool CAIPGetAddrInfo(int idx, u_arraylist_t *iflist)
 {
-    u_arraylist_t *iflist = u_arraylist_create();
-    if (!iflist)
+    if ((idx < 0) || (iflist == NULL))
     {
-        OIC_LOG_V(ERROR, TAG, "Failed to create iflist: %s", strerror(errno));
-        return NULL;
+        return false;
     }
 
-    char buf[MAX_INTERFACE_INFO_LENGTH] = { 0 };
-    struct ifconf ifc = { .ifc_len = MAX_INTERFACE_INFO_LENGTH, .ifc_buf = buf };
-
-    int s = caglobals.ip.u6.fd != -1 ? caglobals.ip.u6.fd : caglobals.ip.u4.fd;
-    if (ioctl(s, SIOCGIFCONF, &ifc) < 0)
+    struct ifaddrs *ifp = NULL;
+    if (-1 == getifaddrs(&ifp))
     {
-        OIC_LOG_V(ERROR, TAG, "SIOCGIFCONF failed: %s", strerror(errno));
-        u_arraylist_destroy(iflist);
-        return NULL;
+        OIC_LOG_V(ERROR, TAG, "Failed to get ifaddrs: %s", strerror(errno));
+        return false;
     }
 
-    struct ifreq* ifr = ifc.ifc_req;
-    size_t interfaces = ifc.ifc_len / sizeof (ifc.ifc_req[0]);
-    size_t ifreqsize = ifc.ifc_len;
-
-    if (ifreqsize > caglobals.ip.nm.sizeIfItems)
+    struct ifaddrs *ifa = NULL;
+    for (ifa = ifp; ifa; ifa = ifa->ifa_next)
     {
-        CAIfItem_t *items = (CAIfItem_t *)OICRealloc(caglobals.ip.nm.ifItems, ifreqsize);
-        if (!items)
+        if (!ifa->ifa_addr)
         {
-            OIC_LOG(ERROR, TAG, "OICRealloc failed");
-            goto exit;
+            continue;
         }
-        caglobals.ip.nm.ifItems = items;
-        caglobals.ip.nm.sizeIfItems = ifreqsize;
-    }
-
-    caglobals.ip.nm.numIfItems = 0;
-    for (size_t i = 0; i < interfaces; i++)
-    {
-        CAResult_t result = CA_STATUS_OK;
-        struct ifreq* item = &ifr[i];
-        char *name = item->ifr_name;
-        struct sockaddr_in *sa4 = (struct sockaddr_in *)&item->ifr_addr;
-        uint32_t ipv4addr = sa4->sin_addr.s_addr;
 
-        if (ioctl(s, SIOCGIFFLAGS, item) < 0)
+        int family = ifa->ifa_addr->sa_family;
+        if ((ifa->ifa_flags & IFF_LOOPBACK) || (AF_INET != family && AF_INET6 != family))
         {
-            OIC_LOG_V(ERROR, TAG, "SIOCGIFFLAGS failed: %s", strerror(errno));
             continue;
         }
-        int16_t flags = item->ifr_flags;
-        if ((flags & IFF_LOOPBACK) || !(flags & IFF_RUNNING))
+
+        int ifindex = if_nametoindex(ifa->ifa_name);
+        if (idx && (ifindex != idx))
         {
             continue;
         }
-        if (ioctl(s, SIOCGIFINDEX, item) < 0)
+
+        char ipaddr[MAX_ADDR_STR_SIZE_CA] = {0};
+        if (family == AF_INET6)
         {
-            OIC_LOG_V(ERROR, TAG, "SIOCGIFINDEX failed: %s", strerror(errno));
-            continue;
+            struct sockaddr_in6 *in6 = (struct sockaddr_in6*) ifa->ifa_addr;
+            inet_ntop(family, (void *)&(in6->sin6_addr), ipaddr, sizeof(ipaddr));
         }
-
-        int ifindex = item->ifr_ifindex;
-        caglobals.ip.nm.ifItems[i].ifIndex = ifindex;
-        caglobals.ip.nm.numIfItems++;
-
-        if (desiredIndex && (ifindex != desiredIndex))
+        else if (family == AF_INET)
         {
-            continue;
+            struct sockaddr_in *in = (struct sockaddr_in*) ifa->ifa_addr;
+            inet_ntop(family, (void *)&(in->sin_addr), ipaddr, sizeof(ipaddr));
         }
 
-        // Add IPv4 interface
-        result = CAAddInterfaceItem(iflist, ifindex, name, AF_INET, ipv4addr, flags);
-        if (CA_STATUS_OK != result)
+        if ((strcmp(ipaddr, IFC_ADDR_LOOP_IPV4) == 0) ||
+            (strcmp(ipaddr, IFC_ADDR_LOOP_IPV6) == 0) ||
+            (strcmp(ifa->ifa_name, IFC_LABEL_LOOP) == 0))
         {
-            goto exit;
+            OIC_LOG(DEBUG, TAG, "LOOPBACK continue!!!");
+            continue;
         }
 
-        // Add IPv6 interface
-        result = CAAddInterfaceItem(iflist, ifindex, name, AF_INET6, ipv4addr, flags);
+        CAResult_t result = CAAddInterfaceItem(iflist, ifindex,
+                                               ifa->ifa_name, family,
+                                               ipaddr, ifa->ifa_flags);
         if (CA_STATUS_OK != result)
         {
+            OIC_LOG(ERROR, TAG, "CAAddInterfaceItem fail");
             goto exit;
         }
     }
+    freeifaddrs(ifp);
+    return true;
+
+exit:
+    freeifaddrs(ifp);
+    return false;
+}
+
+u_arraylist_t *CAIPGetInterfaceInformation(int desiredIndex)
+{
+    u_arraylist_t *iflist = u_arraylist_create();
+    if (!iflist)
+    {
+        OIC_LOG_V(ERROR, TAG, "Failed to create iflist: %s", strerror(errno));
+        return NULL;
+    }
+
+    if (!CAIPGetAddrInfo(desiredIndex, iflist))
+    {
+        goto exit;
+    }
+
     return iflist;
 
 exit:
@@ -307,7 +347,7 @@ exit:
 }
 
 static CAResult_t CAAddInterfaceItem(u_arraylist_t *iflist, int index,
-                                     char *name, int family, uint32_t addr, int flags)
+                                     char *name, int family, const char *addr, int flags)
 {
     CAInterface_t *ifitem = CANewInterfaceItem(index, name, family, addr, flags);
     if (!ifitem)
@@ -326,7 +366,7 @@ static CAResult_t CAAddInterfaceItem(u_arraylist_t *iflist, int index,
 }
 
 static CAInterface_t *CANewInterfaceItem(int index, char *name, int family,
-                                         uint32_t addr, int flags)
+                                         const char *addr, int flags)
 {
     CAInterface_t *ifitem = (CAInterface_t *)OICCalloc(1, sizeof (CAInterface_t));
     if (!ifitem)
@@ -338,55 +378,35 @@ static CAInterface_t *CANewInterfaceItem(int index, char *name, int family,
     OICStrcpy(ifitem->name, INTERFACE_NAME_MAX, name);
     ifitem->index = index;
     ifitem->family = family;
-    ifitem->ipv4addr = addr;
+    OICStrcpy(ifitem->addr, sizeof(ifitem->addr), addr);
     ifitem->flags = flags;
 
     return ifitem;
 }
 
-void CAWIFIConnectionStateChangedCb(wifi_connection_state_e state, wifi_ap_h ap,
-                                    void *userData)
-{
-    OIC_LOG(DEBUG, TAG, "IN");
-
-    if (WIFI_CONNECTION_STATE_ASSOCIATION == state
-        || WIFI_CONNECTION_STATE_CONFIGURATION == state)
-    {
-        OIC_LOG(DEBUG, TAG, "Connection is in Association State");
-        return;
-    }
-
-    if (WIFI_CONNECTION_STATE_CONNECTED == state)
-    {
-        CAWakeUpForChange();
-    }
-    else
-    {
-        u_arraylist_t *iflist = CAIPGetInterfaceInformation(0);
-        if (!iflist)
-        {
-            OIC_LOG_V(ERROR, TAG, "get interface info failed");
-            return;
-        }
-        u_arraylist_destroy(iflist);
-    }
-
-    OIC_LOG(DEBUG, TAG, "OUT");
-}
-
-void CAWIFIDeviceStateChangedCb(wifi_device_state_e state, void *userData)
+void CAIPConnectionStateChangedCb(connection_type_e type, void* userData)
 {
-    OIC_LOG(DEBUG, TAG, "IN");
+    (void)userData;
 
-    if (WIFI_DEVICE_STATE_ACTIVATED == state)
-    {
-        OIC_LOG(DEBUG, TAG, "Wifi is in Activated State");
-    }
-    else
+    switch (type)
     {
-        CAWIFIConnectionStateChangedCb(WIFI_CONNECTION_STATE_DISCONNECTED, NULL, NULL);
-        OIC_LOG(DEBUG, TAG, "Wifi is in Deactivated State");
+        case CONNECTION_TYPE_DISCONNECTED:
+            OIC_LOG(DEBUG, TAG, "Connection is in CONNECTION_TYPE_DISCONNECTED");
+            CAIPPassNetworkChangesToAdapter(CA_INTERFACE_DOWN);
+            break;
+        case CONNECTION_TYPE_ETHERNET:
+            OIC_LOG(DEBUG, TAG, "Connection is in CONNECTION_TYPE_ETHERNET");
+            CAIPPassNetworkChangesToAdapter(CA_INTERFACE_UP);
+            break;
+        case CONNECTION_TYPE_WIFI:
+            OIC_LOG(DEBUG, TAG, "Connection is in CONNECTION_TYPE_WIFI");
+            CAIPPassNetworkChangesToAdapter(CA_INTERFACE_UP);
+            break;
+        case CONNECTION_TYPE_CELLULAR:
+            OIC_LOG(DEBUG, TAG, "Connection is in CONNECTION_TYPE_CELLULAR");
+            CAIPPassNetworkChangesToAdapter(CA_INTERFACE_UP);
+            break;
+        default:
+            break;
     }
-
-    OIC_LOG(DEBUG, TAG, "OUT");
 }