#include "caipinterface.h"
+#include <stdio.h>
+#include <string.h>
#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/select.h>
#include <ifaddrs.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
#include <net/if.h>
-#include <sys/socket.h>
#include <netdb.h>
-#include <string.h>
#include <errno.h>
-#include <unistd.h>
+#ifdef __linux__
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#endif
+
+#include "octhread.h"
+#include "caipnwmonitor.h"
#include "caadapterutils.h"
#include "logger.h"
#include "oic_malloc.h"
#include "oic_string.h"
+#include <coap/utlist.h>
+
+#define TAG "OIC_CA_IP_MONITOR"
+
+/**
+ * Mutex for synchronizing access to cached interface and IP address information.
+ */
+static oc_mutex g_networkMonitorContextMutex = NULL;
+
+/**
+ * Used to storing network interface.
+ */
+static u_arraylist_t *g_netInterfaceList = NULL;
+
+/**
+ * Used to storing adapter changes callback interface.
+ */
+static struct CAIPCBData_t *g_adapterCallbackList = NULL;
+
+/**
+ * Initialize the network interface monitoring list.
+ */
+static CAResult_t CAIPInitializeNetworkMonitorList();
+
+/**
+ * Destroy the network interface monitoring list.
+ */
+static void CAIPDestroyNetworkMonitorList();
+
+/**
+ * Compare the interface with the already added interface in list.
+ */
+static bool CACmpNetworkList(uint32_t ifiindex);
+
+/**
+ * Add new network interface in list.
+ */
+static CAResult_t CAAddNetworkMonitorList(CAInterface_t *ifitem);
+
+/**
+ * Remove network interace from list.
+ */
+static void CARemoveNetworkMonitorList(int ifiindex);
+
+/**
+ * Pass the changed network status through the stored callback.
+ */
+static void CAIPPassNetworkChangesToAdapter(CANetworkStatus_t status);
+
+/**
+ * Create new interface item.
+ */
+static CAInterface_t *CANewInterfaceItem(int index, const char *name, int family,
+ const char *addr, int flags);
+
+static CAResult_t CAIPInitializeNetworkMonitorList()
+{
+ if (!g_networkMonitorContextMutex)
+ {
+ g_networkMonitorContextMutex = oc_mutex_new();
+ if (!g_networkMonitorContextMutex)
+ {
+ OIC_LOG(ERROR, TAG, "oc_mutex_new has failed");
+ return CA_STATUS_FAILED;
+ }
+ }
+
+ if (!g_netInterfaceList)
+ {
+ g_netInterfaceList = u_arraylist_create();
+ if (!g_netInterfaceList)
+ {
+ OIC_LOG(ERROR, TAG, "u_arraylist_create has failed");
+ CAIPDestroyNetworkMonitorList();
+ return CA_STATUS_FAILED;
+ }
+ }
+ return CA_STATUS_OK;
+}
+
+static void CAIPDestroyNetworkMonitorList()
+{
+ if (g_netInterfaceList)
+ {
+ u_arraylist_destroy(g_netInterfaceList);
+ g_netInterfaceList = NULL;
+ }
+
+ if (g_networkMonitorContextMutex)
+ {
+ oc_mutex_free(g_networkMonitorContextMutex);
+ g_networkMonitorContextMutex = NULL;
+ }
+}
+
+static bool CACmpNetworkList(uint32_t ifiindex)
+{
+ if (!g_netInterfaceList)
+ {
+ OIC_LOG(ERROR, TAG, "g_netInterfaceList is NULL");
+ return false;
+ }
+
+ oc_mutex_lock(g_networkMonitorContextMutex);
+
+ uint32_t list_length = u_arraylist_length(g_netInterfaceList);
+ for (uint32_t list_index = 0; list_index < list_length; list_index++)
+ {
+ CAInterface_t *currItem = (CAInterface_t *) u_arraylist_get(g_netInterfaceList,
+ list_index);
+ if (currItem->index == ifiindex)
+ {
+ oc_mutex_unlock(g_networkMonitorContextMutex);
+ return true;
+ }
+ }
+ oc_mutex_unlock(g_networkMonitorContextMutex);
+ return false;
+}
+
+static CAResult_t CAAddNetworkMonitorList(CAInterface_t *ifitem)
+{
+ VERIFY_NON_NULL(g_netInterfaceList, TAG, "g_netInterfaceList is NULL");
+ VERIFY_NON_NULL(ifitem, TAG, "ifitem is NULL");
+
+ oc_mutex_lock(g_networkMonitorContextMutex);
+ bool result = u_arraylist_add(g_netInterfaceList, (void *) ifitem);
+ if (!result)
+ {
+ OIC_LOG(ERROR, TAG, "u_arraylist_add failed.");
+ oc_mutex_unlock(g_networkMonitorContextMutex);
+ return CA_STATUS_FAILED;
+ }
+ oc_mutex_unlock(g_networkMonitorContextMutex);
+ return CA_STATUS_OK;
+}
+
+static void CARemoveNetworkMonitorList(int ifiindex)
+{
+ VERIFY_NON_NULL_VOID(g_netInterfaceList, TAG, "g_netInterfaceList is NULL");
+
+ oc_mutex_lock(g_networkMonitorContextMutex);
+
+ uint32_t list_length = u_arraylist_length(g_netInterfaceList);
+ for (uint32_t list_index = 0; list_index < list_length; list_index++)
+ {
+ CAInterface_t *removedifitem = (CAInterface_t *) u_arraylist_get(
+ g_netInterfaceList, list_index);
+ if (removedifitem && ((int)removedifitem->index) == ifiindex)
+ {
+ if (u_arraylist_remove(g_netInterfaceList, list_index))
+ {
+ OICFree(removedifitem);
+ oc_mutex_unlock(g_networkMonitorContextMutex);
+ return;
+ }
+ continue;
+ }
+ }
+ oc_mutex_unlock(g_networkMonitorContextMutex);
+ return;
+}
+
+CAResult_t CAIPStartNetworkMonitor(CAIPAdapterStateChangeCallback callback,
+ CATransportAdapter_t adapter)
+{
+ CAResult_t res = CAIPInitializeNetworkMonitorList();
+ if (CA_STATUS_OK == res)
+ {
+ return CAIPSetNetworkMonitorCallback(callback, adapter);
+ }
+ return res;
+}
+
+CAResult_t CAIPStopNetworkMonitor(CATransportAdapter_t adapter)
+{
+ CAIPDestroyNetworkMonitorList();
+ return CAIPUnSetNetworkMonitorCallback(adapter);
+}
+
+int CAGetPollingInterval(int interval)
+{
+ return interval;
+}
+
+static void CAIPPassNetworkChangesToAdapter(CANetworkStatus_t status)
+{
+ CAIPCBData_t *cbitem = NULL;
+ LL_FOREACH(g_adapterCallbackList, cbitem)
+ {
+ if (cbitem && cbitem->adapter)
+ {
+ cbitem->callback(cbitem->adapter, status);
+ CALogAdapterStateInfo(cbitem->adapter, status);
+ }
+ }
+}
+
+CAResult_t CAIPSetNetworkMonitorCallback(CAIPAdapterStateChangeCallback callback,
+ CATransportAdapter_t adapter)
+{
+ if (!callback)
+ {
+ OIC_LOG(ERROR, TAG, "callback is null");
+ return CA_STATUS_INVALID_PARAM;
+ }
+
+ CAIPCBData_t *cbitem = NULL;
+ LL_FOREACH(g_adapterCallbackList, cbitem)
+ {
+ if (cbitem && adapter == cbitem->adapter && callback == cbitem->callback)
+ {
+ OIC_LOG(DEBUG, TAG, "this callback is already added");
+ return CA_STATUS_OK;
+ }
+ }
+
+ cbitem = (CAIPCBData_t *)OICCalloc(1, sizeof(*cbitem));
+ if (!cbitem)
+ {
+ OIC_LOG(ERROR, TAG, "Malloc failed");
+ return CA_STATUS_FAILED;
+ }
+
+ cbitem->adapter = adapter;
+ cbitem->callback = callback;
+ LL_APPEND(g_adapterCallbackList, cbitem);
+
+ 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(DEBUG, TAG, "remove specific callback");
+ LL_DELETE(g_adapterCallbackList, cbitem);
+ OICFree(cbitem);
+ return CA_STATUS_OK;
+ }
+ }
+ return CA_STATUS_OK;
+}
+
+static CAInterface_t *CANewInterfaceItem(int index, const char *name, int family,
+ const char *addr, int flags)
+{
+ CAInterface_t *ifitem = (CAInterface_t *)OICCalloc(1, sizeof (CAInterface_t));
+ if (!ifitem)
+ {
+ OIC_LOG(ERROR, TAG, "Malloc failed");
+ return NULL;
+ }
+
+ OICStrcpy(ifitem->name, sizeof (ifitem->name), name);
+ ifitem->index = index;
+ ifitem->family = family;
+ OICStrcpy(ifitem->addr, sizeof (ifitem->addr), addr);
+ ifitem->flags = flags;
+
+ return ifitem;
+}
+
+u_arraylist_t *CAFindInterfaceChange()
+{
+ u_arraylist_t *iflist = NULL;
+#ifdef __linux__
+ char buf[4096] = { 0 };
+ struct nlmsghdr *nh = NULL;
+ struct sockaddr_nl sa = { .nl_family = 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 (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;
+ }
-#define TAG "IP_MONITOR"
+ if (RTM_DELADDR == nh->nlmsg_type)
+ {
+ struct ifaddrmsg *ifa = (struct ifaddrmsg *)NLMSG_DATA (nh);
+ int ifiIndex = ifa->ifa_index;
+ bool isFound = CACmpNetworkList(ifiIndex);
+ if (isFound)
+ {
+ CARemoveNetworkMonitorList(ifiIndex);
+ CAIPPassNetworkChangesToAdapter(CA_INTERFACE_DOWN);
+ }
+ continue;
+ }
+
+ // Netlink message type is RTM_NEWADDR.
+ struct ifaddrmsg *ifa = (struct ifaddrmsg *)NLMSG_DATA (nh);
+ int ifiIndex = ifa->ifa_index;
+
+ iflist = CAIPGetInterfaceInformation(ifiIndex);
+ if (!iflist)
+ {
+ OIC_LOG_V(ERROR, TAG, "get interface info failed: %s", strerror(errno));
+ return NULL;
+ }
+ }
+#endif
+ return iflist;
+}
u_arraylist_t *CAIPGetInterfaceInformation(int desiredIndex)
{
+ if (desiredIndex < 0)
+ {
+ OIC_LOG_V(ERROR, TAG, "invalid index : %d", desiredIndex);
+ return NULL;
+ }
+
u_arraylist_t *iflist = u_arraylist_create();
if (!iflist)
{
u_arraylist_destroy(iflist);
return NULL;
}
- OIC_LOG(DEBUG, TAG, "Got ifaddrs");
struct ifaddrs *ifa = NULL;
for (ifa = ifp; ifa; ifa = ifa->ifa_next)
{
+ if (!ifa->ifa_addr)
+ {
+ continue;
+ }
int family = ifa->ifa_addr->sa_family;
if ((ifa->ifa_flags & IFF_LOOPBACK) || (AF_INET != family && AF_INET6 != family))
{
OICStrcpy(ifitem->name, INTERFACE_NAME_MAX, ifa->ifa_name);
ifitem->index = ifindex;
ifitem->family = family;
- ifitem->ipv4addr = ((struct sockaddr_in *)(ifa->ifa_addr))->sin_addr.s_addr;
ifitem->flags = ifa->ifa_flags;
- CAResult_t result = u_arraylist_add(iflist, ifitem);
- if (CA_STATUS_OK != result)
+ if (ifitem->family == AF_INET6)
+ {
+ struct sockaddr_in6 *in6 = (struct sockaddr_in6*) ifa->ifa_addr;
+ inet_ntop(ifitem->family, (void *)&(in6->sin6_addr), ifitem->addr,
+ sizeof(ifitem->addr));
+ }
+ else if (ifitem->family == AF_INET)
+ {
+ struct sockaddr_in *in = (struct sockaddr_in*) ifa->ifa_addr;
+ inet_ntop(ifitem->family, (void *)&(in->sin_addr), ifitem->addr,
+ sizeof(ifitem->addr));
+ }
+
+ bool result = u_arraylist_add(iflist, ifitem);
+ if (!result)
{
OIC_LOG(ERROR, TAG, "u_arraylist_add failed.");
goto exit;
}
- OIC_LOG_V(ERROR, TAG, "Added interface: %s (%d)", ifitem->name, family);
+ bool isFound = CACmpNetworkList(ifitem->index);
+ if (!isFound)
+ {
+ CAInterface_t *newifitem = CANewInterfaceItem(ifitem->index, ifitem->name, ifitem->family,
+ ifitem->addr, ifitem->flags);
+ CAResult_t ret = CAAddNetworkMonitorList(newifitem);
+ if (CA_STATUS_OK != ret)
+ {
+ OICFree(newifitem);
+ goto exit;
+ }
+ CAIPPassNetworkChangesToAdapter(CA_INTERFACE_UP);
+ OIC_LOG_V(DEBUG, TAG, "Added interface: %s (%d)", ifitem->name, ifitem->family);
+ }
}
-
+#ifndef __TIZENRT__
freeifaddrs(ifp);
+#endif
return iflist;
exit:
+#ifndef __TIZENRT__
freeifaddrs(ifp);
+#endif
u_arraylist_destroy(iflist);
return NULL;
}