Add network monitor support for Windows
authorDave Thaler <dthaler@microsoft.com>
Thu, 6 Oct 2016 19:20:44 +0000 (12:20 -0700)
committerAshok Babu Channa <ashok.channa@samsung.com>
Wed, 12 Oct 2016 04:55:37 +0000 (04:55 +0000)
Also fix a memory leak in the Linux code

Change-Id: I8d243604a49cf4f2947849ac13624b2c044259d5
Signed-off-by: Dave Thaler <dthaler@microsoft.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/12953
Tested-by: jenkins-iotivity <jenkins-iotivity@opendaylight.org>
Reviewed-by: Soemin Tjong <stjong@microsoft.com>
Reviewed-by: Dan Mihai <Daniel.Mihai@microsoft.com>
Reviewed-by: David Antler <david.a.antler@intel.com>
Reviewed-by: Ashok Babu Channa <ashok.channa@samsung.com>
resource/csdk/connectivity/api/cacommon.h
resource/csdk/connectivity/inc/caipinterface.h
resource/csdk/connectivity/inc/caipnwmonitor.h
resource/csdk/connectivity/src/ip_adapter/caipserver.c
resource/csdk/connectivity/src/ip_adapter/linux/caipnwmonitor.c
resource/csdk/connectivity/src/ip_adapter/windows/caipnwmonitor.c

index 9fd62d1..8f455e6 100644 (file)
@@ -553,10 +553,11 @@ typedef struct
         CASocket_t m6s;             /**< multicast IPv6 secure */
         CASocket_t m4;              /**< multicast IPv4 */
         CASocket_t m4s;             /**< multicast IPv4 secure */
-        CASocketFd_t netlinkFd;     /**< netlink */
 #if defined(_WIN32)
+        WSAEVENT addressChangeEvent;/**< Event used to signal address changes */
         WSAEVENT shutdownEvent;     /**< Event used to signal threads to stop */
 #else
+        int netlinkFd;              /**< netlink */
         int shutdownFds[2];         /**< fds used to signal threads to stop */
 #endif
         int selectTimeout;          /**< in seconds */
index 5cf683a..e08d7f0 100644 (file)
@@ -120,6 +120,7 @@ CAResult_t CAIPStopListenServer();
  */
 void CAIPSetPacketReceiveCallback(CAIPPacketReceivedCallback callback);
 
+#ifdef WITH_ARDUINO
 /**
  * Set socket description for sending unicast UDP data.
  * Once the Unicast server is started,
@@ -128,6 +129,7 @@ void CAIPSetPacketReceiveCallback(CAIPPacketReceivedCallback callback);
  * @param[in]  socketFD   Socket descriptor used for sending UDP data.
  */
 void CAIPSetUnicastSocket(int socketFD);
+#endif
 
 /**
  * Set the port number for sending unicast UDP data.
@@ -164,8 +166,8 @@ void CAIPPullData();
 #define CA_SECURE_COAP 5684
 
 /**
- * Let the network monitor update the polling interval.
- * @param   [in] current polling interval
+ * Let the network monitor update the polling interval, in seconds.
+ * @param[in]  interval  current polling interval
  *
  * @return  desired polling interval
  */
index 8946135..d76c6fe 100644 (file)
@@ -47,6 +47,11 @@ extern "C"
 typedef void (*CAIPAdapterStateChangeCallback)(CATransportAdapter_t adapter,
                                                CANetworkStatus_t status);
 
+/*
+ * Structure for IP address information, to be used to construct a CAEndpoint_t.  The
+ * structure name is a misnomer, as there is one entry per address not one per interface.
+ * An interface with 4 addresses should result in 4 instances of CAInterface_t.
+ */
 typedef struct
 {
     char name[INTERFACE_NAME_MAX];
index 1e2a1f9..10f988b 100644 (file)
@@ -368,7 +368,11 @@ static void CAFindReadyMessage()
         PUSH_HANDLE(caglobals.ip.shutdownEvent, eventArray, arraySize);
     }
 
-    /** @todo Support netlink events */
+    if (WSA_INVALID_EVENT != caglobals.ip.addressChangeEvent)
+    {
+        INSERT_SOCKET(OC_INVALID_SOCKET, socketArray, arraySize);
+        PUSH_HANDLE(caglobals.ip.addressChangeEvent, eventArray, arraySize);
+    }
 
     // Should not have overflowed buffer
     assert(arraySize <= (_countof(socketArray)));
@@ -400,7 +404,20 @@ static void CAFindReadyMessage()
                         OIC_LOG_V(ERROR, TAG, "WSAResetEvent failed 0x%08x", WSAGetLastError());
                     }
 
-                    // Break out if shutdownEvent is triggered
+                    // Handle address changes if addressChangeEvent is triggered.
+                    if ((caglobals.ip.addressChangeEvent != WSA_INVALID_EVENT) &&
+                        (caglobals.ip.addressChangeEvent == eventArray[eventIndex]))
+                    {
+                        CAInterface_t *newAddress;
+                        while ((newAddress = CAFindInterfaceChange()) != NULL)
+                        {
+                            CAProcessNewInterface(newAddress);
+                            OICFree(newAddress);
+                        }
+                        break;
+                    }
+
+                    // Break out if shutdownEvent is triggered.
                     if ((caglobals.ip.shutdownEvent != WSA_INVALID_EVENT) &&
                         (caglobals.ip.shutdownEvent == eventArray[eventIndex]))
                     {
@@ -460,6 +477,23 @@ static void CAEventReturned(CASocketFd_t socket)
 
 #endif
 
+void CAUnregisterForAddressChanges()
+{
+#ifdef _WIN32
+    if (caglobals.ip.addressChangeEvent != WSA_INVALID_EVENT)
+    {
+        WSACloseEvent(caglobals.ip.addressChangeEvent);
+        caglobals.ip.addressChangeEvent = WSA_INVALID_EVENT;
+    }
+#else
+    if (caglobals.ip.netlinkFd != OC_INVALID_SOCKET)
+    {
+        close(caglobals.ip.netlinkFd);
+        caglobals.ip.netlinkFd = OC_INVALID_SOCKET;
+    }
+#endif
+}
+
 void CADeInitializeIPGlobals()
 {
     CLOSE_SOCKET(u6);
@@ -471,15 +505,7 @@ void CADeInitializeIPGlobals()
     CLOSE_SOCKET(m4);
     CLOSE_SOCKET(m4s);
 
-    if (caglobals.ip.netlinkFd != OC_INVALID_SOCKET)
-    {
-#ifdef _WIN32
-        closesocket(caglobals.ip.netlinkFd);
-#else
-        close(caglobals.ip.netlinkFd);
-#endif
-        caglobals.ip.netlinkFd = OC_INVALID_SOCKET;
-    }
+    CAUnregisterForAddressChanges();
 }
 
 static CAResult_t CAReceiveMessage(CASocketFd_t fd, CATransportFlags_t flags)
@@ -769,8 +795,15 @@ static CASocketFd_t CACreateSocket(int family, uint16_t *port, bool isMulticast)
     }   \
     CHECKFD(caglobals.ip.NAME.fd)
 
-static void CAInitializeNetlink()
+static void CARegisterForAddressChanges()
 {
+#ifdef _WIN32
+    caglobals.ip.addressChangeEvent = WSACreateEvent();
+    if (WSA_INVALID_EVENT != caglobals.ip.addressChangeEvent)
+    {
+        OIC_LOG(ERROR, TAG, "WSACreateEvent failed");
+    }
+#else
     caglobals.ip.netlinkFd = OC_INVALID_SOCKET;
 #ifdef __linux__
     // create NETLINK fd for interface change notifications
@@ -796,6 +829,7 @@ static void CAInitializeNetlink()
         }
     }
 #endif
+#endif
 }
 
 static void CAInitializeFastShutdownMechanism()
@@ -925,8 +959,8 @@ CAResult_t CAIPStartServer(const ca_thread_pool_t threadPool)
     // set up appropriate FD mechanism for fast shutdown
     CAInitializeFastShutdownMechanism();
 
-    // create source of network interface change notifications
-    CAInitializeNetlink();
+    // create source of network address change notifications
+    CARegisterForAddressChanges();
 
     caglobals.ip.selectTimeout = CAGetPollingInterval(caglobals.ip.selectTimeout);
 
@@ -1031,7 +1065,18 @@ static void applyMulticastToInterface4(uint32_t ifindex)
 #if !defined(WSAEINVAL)
         if (EADDRINUSE != errno)
 #else
-        if (WSAEINVAL != WSAGetLastError()) // Joining multicast group more than once (IPv4 Flavor)
+        if (WSAEINVAL == WSAGetLastError())
+        {
+            // We're trying to join the multicast group again.
+            // If the interface has gone down and come back up, the socket might be in
+            // an inconsistent state where it still thinks we're joined when the interface
+            // doesn't think we're joined.  So try to leave and rejoin the group just in case.
+            setsockopt(caglobals.ip.m4s.fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, OPTVAL_T(&mreq),
+                             sizeof(mreq));
+            ret = setsockopt(caglobals.ip.m4s.fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, OPTVAL_T(&mreq),
+                             sizeof(mreq));
+        }
+        if (OC_SOCKET_ERROR == ret)
 #endif
         {
             OIC_LOG_V(ERROR, TAG, "SECURE IPv4 IP_ADD_MEMBERSHIP failed: %s", CAIPS_GET_ERROR);
@@ -1051,9 +1096,18 @@ static void applyMulticast6(int fd, struct in6_addr *addr, uint32_t ifindex)
     if (OC_SOCKET_ERROR == ret)
     {
 #if !defined(_WIN32)
-                if (EADDRINUSE != errno)
+        if (EADDRINUSE != errno)
 #else
-                if (WSAEINVAL != WSAGetLastError()) // Joining multicast group more than once (IPv6 Flavor)
+        if (WSAEINVAL == WSAGetLastError())
+        {
+            // We're trying to join the multicast group again.
+            // If the interface has gone down and come back up, the socket might be in
+            // an inconsistent state where it still thinks we're joined when the interface
+            // doesn't think we're joined.  So try to leave and rejoin the group just in case.
+            setsockopt(fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, OPTVAL_T(&mreq), sizeof(mreq));
+            ret = setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, OPTVAL_T(&mreq), sizeof(mreq));
+        }
+        if (OC_SOCKET_ERROR == ret)
 #endif
         {
             OIC_LOG_V(ERROR, TAG, "IPv6 IPV6_JOIN_GROUP failed: %s", CAIPS_GET_ERROR);
index d7cb718..c1471b2 100644 (file)
@@ -85,7 +85,7 @@ static bool CACmpNetworkList(uint32_t ifiindex);
 static CAResult_t CAAddNetworkMonitorList(CAInterface_t *ifitem);
 
 /**
- * Remove network interace from list.
+ * Remove network interface from list.
  */
 static void CARemoveNetworkMonitorList(int ifiindex);
 
@@ -337,7 +337,6 @@ CAInterface_t *CAFindInterfaceChange()
         struct ifinfomsg *ifi = (struct ifinfomsg *)NLMSG_DATA(nh);
 
         int ifiIndex = ifi->ifi_index;
-        u_arraylist_t *iflist = CAIPGetInterfaceInformation(ifiIndex);
 
         if ((!ifi || (ifi->ifi_flags & IFF_LOOPBACK) || !(ifi->ifi_flags & IFF_RUNNING)))
         {
@@ -350,6 +349,7 @@ CAInterface_t *CAFindInterfaceChange()
             continue;
         }
 
+        u_arraylist_t *iflist = CAIPGetInterfaceInformation(ifiIndex);
         if (!iflist)
         {
             OIC_LOG_V(ERROR, TAG, "get interface info failed: %s", strerror(errno));
index 53b058c..5d3f37b 100644 (file)
@@ -1,7 +1,6 @@
 /* *****************************************************************
 *
-* Copyright 2016 Intel Corporation
-*
+* Copyright 2016 Microsoft
 *
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 *
 ******************************************************************/
 
+#include "iotivity_config.h"
 #include "caipinterface.h"
 
+#include <assert.h>
 #include <sys/types.h>
 #include <string.h>
-#include <errno.h>
 #include <winsock2.h>
+#include <ws2tcpip.h>
 #include <iphlpapi.h>
-#include "platform_features.h"
+#include <mstcpip.h>
 #include <iptypes.h>
 #include <stdbool.h>
+#include "octhread.h"
 #include "caadapterutils.h"
 #include "logger.h"
 #include "oic_malloc.h"
 #include "oic_string.h"
 #include "caipnwmonitor.h"
+#include <coap/utlist.h>
 
 #define TAG "IP_MONITOR"
 
 /**
- * @todo Implement network interface monitoring in case the IP changes.
- * Not critical for win32 bring-up.
+ * Mutex for synchronizing access to cached address information.
+ */
+static oc_mutex g_CAIPNetworkMonitorMutex = NULL;
+
+static bool g_CAIPNetworkMonitorSomeAddressWentAway = false;
+
+typedef struct CANewAddress_t {
+    struct CANewAddress_t *next;
+    struct CANewAddress_t *prev;
+    CAInterface_t *ipAddressInfo; 
+} CANewAddress_t;
+
+/**
+ * List of network addresses that we've already reported.
+ */
+static u_arraylist_t *g_CAIPNetworkMonitorAddressList = NULL;
+
+/**
+ * Queue of new addresses that haven't yet been returned in CAFindInterfaceChange().
+ */
+static CANewAddress_t *g_CAIPNetworkMonitorNewAddressQueue = NULL;
+
+/**
+ * Transport adapter change callback list.
+ */
+static struct CAIPCBData_t *g_CAIPNetworkMonitorAdapterCallbackList = NULL;
+
+static CAInterface_t *AllocateCAInterface(int index, const char *name, int family,
+                                          const char *addr, int flags);
+
+static u_arraylist_t *GetInterfaceInformation(int desiredIndex);
+
+static void CAIPDestroyNetworkMonitorList();
+
+static CAResult_t CAIPInitializeNetworkMonitorList()
+{
+    assert(!g_CAIPNetworkMonitorMutex);
+    assert(!g_CAIPNetworkMonitorAddressList);
+
+    g_CAIPNetworkMonitorMutex = oc_mutex_new();
+    if (!g_CAIPNetworkMonitorMutex)
+    {
+        OIC_LOG(ERROR, TAG, "oc_mutex_new has failed");
+        return CA_STATUS_FAILED;
+    }
+
+    g_CAIPNetworkMonitorAddressList = u_arraylist_create();
+    if (!g_CAIPNetworkMonitorAddressList)
+    {
+        OIC_LOG(ERROR, TAG, "u_arraylist_create has failed");
+        CAIPDestroyNetworkMonitorList();
+        return CA_STATUS_FAILED;
+    }
+
+    return CA_STATUS_OK;
+}
+
+/**
+ * Destroy the network monitoring list.
+ */
+static void CAIPDestroyNetworkMonitorList()
+{
+    // Free any new addresses waiting to be indicated up.
+    while (g_CAIPNetworkMonitorNewAddressQueue)
+    {
+        CANewAddress_t *change = g_CAIPNetworkMonitorNewAddressQueue;
+        DL_DELETE(g_CAIPNetworkMonitorNewAddressQueue, change);
+        OICFree(change->ipAddressInfo);
+        OICFree(change);
+    }
+
+    // Free our cache of operational addresses.
+    if (g_CAIPNetworkMonitorAddressList)
+    {
+        u_arraylist_destroy(g_CAIPNetworkMonitorAddressList);
+        g_CAIPNetworkMonitorAddressList = NULL;
+    }
+
+    if (g_CAIPNetworkMonitorMutex)
+    {
+        oc_mutex_free(g_CAIPNetworkMonitorMutex);
+        g_CAIPNetworkMonitorMutex = NULL;
+    }
+}
+
+/**
+ * See if a CAInterface_t with a given index and address already exists in a list.
+ *
+ * @param[in] ifIndex  Interface index to look for.
+ * @param[in] family   Family of address to look for.
+ * @param[in] addr     Address to look for.
+ * @return true if already in the list, false if not.
+ */
+static bool CACmpNetworkList(uint32_t ifIndex, int family, const char *addr, u_arraylist_t *iflist)
+{
+    uint32_t list_length = u_arraylist_length(iflist);
+    for (uint32_t list_index = 0; list_index < list_length; list_index++)
+    {
+        CAInterface_t *currItem = (CAInterface_t *) u_arraylist_get(iflist, list_index);
+        if ((currItem->index == ifIndex) && (currItem->family == family) &&
+            (strcmp(currItem->addr, addr) == 0))
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
+static HANDLE g_CAIPNetworkMonitorChangeNotificationHandle = NULL;
+
+/**
+ * Handle a notification that the IP address info changed.
+ *
+ * @param[in]  context           Context passed to NotifyUnicastIpChange.
+ * @param[in]  row               Interface that changed, or NULL on the initial callback.
+ * @param[in]  notificationType  Type of change that occurred.
+ */
+static void IpAddressChangeCallback(void *context,
+                                    MIB_UNICASTIPADDRESS_ROW *row,
+                                    MIB_NOTIFICATION_TYPE notificationType)
+{
+    oc_mutex_lock(g_CAIPNetworkMonitorMutex);
+
+    // Fetch new network address info.
+    u_arraylist_t *newList = GetInterfaceInformation(0);
+    uint32_t newLen = u_arraylist_length(newList);
+
+    u_arraylist_t *oldList = g_CAIPNetworkMonitorAddressList;
+    uint32_t oldLen = u_arraylist_length(oldList);
+
+    if (caglobals.ip.addressChangeEvent)
+    {
+        // Check whether any addresses went away.
+        for (uint32_t i = 0; i < oldLen; i++)
+        {
+            CAInterface_t *ifitem = (CAInterface_t *)u_arraylist_get(oldList, i);
+            if (!CACmpNetworkList(ifitem->index, ifitem->family, ifitem->addr, newList))
+            {
+                g_CAIPNetworkMonitorSomeAddressWentAway = true;
+                break;
+            }
+        }
+
+        // Check whether any new addresses are available.
+        for (uint32_t i = 0; i < newLen; i++)
+        {
+            CAInterface_t *ifitem = (CAInterface_t *)u_arraylist_get(newList, i);
+            if (!CACmpNetworkList(ifitem->index, ifitem->family, ifitem->addr, oldList))
+            {
+                // Create a new CAInterface_t to add to the queue to indicate to the higher
+                // layer.  We cannot simply invoke the callback here currently, since the
+                // higher layer is not thread-safe.
+                CAInterface_t *dup = AllocateCAInterface(ifitem->index, ifitem->name,
+                                                         ifitem->family, ifitem->addr,
+                                                         ifitem->flags);
+                CANewAddress_t *change = (CANewAddress_t *)OICCalloc(1, sizeof(*change));
+                if (change)
+                {
+                    change->ipAddressInfo = dup;
+                    DL_APPEND(g_CAIPNetworkMonitorNewAddressQueue, change);
+                }
+                else
+                {
+                    OIC_LOG(WARNING, TAG, "Couldn't allocate memory for CANewAddress_t");
+                }
+            }
+        }
+
+        // If the new address queue is not empty, signal the transport server that it needs
+        // to call CAFindInterfaceChange().  We don't need to set the event if an address
+        // went away, since the higher layer just uses the event to ask for new addresses
+        // in order to join the multicast group on the associated interface and address family.
+        if (g_CAIPNetworkMonitorNewAddressQueue)
+        {
+            int ret = WSASetEvent(caglobals.ip.addressChangeEvent);
+
+            // Setting the event should always succeed, since the handle should always be
+            // valid when this code is reached.
+            assert(ret);
+        }
+    }
+
+    // Replace old cached info.
+    g_CAIPNetworkMonitorAddressList = newList;
+    u_arraylist_destroy(oldList);
+
+    oc_mutex_unlock(g_CAIPNetworkMonitorMutex);
+}
+
+/**
+ * Start network monitor.
+ *
+ * @param[in]  callback     Callback to be notified when IP/TCP adapter connection state changes.
+ * @param[in]  adapter      Transport adapter.
+ * @return ::CA_STATUS_OK or an appropriate error code.
  */
 CAResult_t CAIPStartNetworkMonitor(CAIPAdapterStateChangeCallback callback,
                                    CATransportAdapter_t adapter)
 {
+    CAResult_t res = CAIPInitializeNetworkMonitorList();
+    if (res != CA_STATUS_OK)
+    {
+        return res;
+    }
+
+    res = CAIPSetNetworkMonitorCallback(callback, adapter);
+    if (res != CA_STATUS_OK)
+    {
+        return res;
+    }
+
+    if (g_CAIPNetworkMonitorChangeNotificationHandle == NULL)
+    {
+        int err = NotifyUnicastIpAddressChange(AF_UNSPEC, IpAddressChangeCallback, NULL,
+                                               true,
+                                               &g_CAIPNetworkMonitorChangeNotificationHandle);
+        if (err != NO_ERROR)
+        {
+            return CA_STATUS_FAILED;
+        }
+    }
     return CA_STATUS_OK;
 }
 
 /**
- * @todo Implement network interface monitoring in case the IP changes.
- * Not critical for win32 bring-up.
+ * Stops network monitor.
+ *
+ * @param[in]  adapter      Transport adapter.
+ * @return ::CA_STATUS_OK or an appropriate error code.
  */
 CAResult_t CAIPStopNetworkMonitor(CATransportAdapter_t adapter)
-
 {
-    return CA_STATUS_OK;
+    if (g_CAIPNetworkMonitorChangeNotificationHandle != NULL)
+    {
+        int err = CancelMibChangeNotify2(g_CAIPNetworkMonitorChangeNotificationHandle);
+        assert(err == NO_ERROR);
+        g_CAIPNetworkMonitorChangeNotificationHandle = NULL;
+    }
+
+    CAIPDestroyNetworkMonitorList();
+    return CAIPUnSetNetworkMonitorCallback(adapter);
 }
 
 /**
- * @todo Implement network interface monitoring.
- * Not used in win32, but caipserver currently requires this function
- * be defined. not critical.
+ * Let the network monitor update the polling interval.
+ * @param[in] interval Current polling interval, in seconds
+ *
+ * @return  desired polling interval
  */
 int CAGetPollingInterval(int interval)
 {
+    // Don't change the polling interval.
     return interval;
 }
 
 /**
- * @todo Implement network interface monitoring.
- * Not used in win32, but caipserver currently requires this function
- * be defined. not critical.
+ * Pass the changed network status through the stored callback.
+ * Note that the current API doesn't allow us to specify which address changed,
+ * the caller has to look at the return from CAFindInterfaceChange() to learn about
+ * each new address, and look through CAIPGetInterfaceInformation() to see what's
+ * missing to detect any removed addresses.
+ *
+ * @param[in] status Network status to pass to the callback.
  */
-CAInterface_t *CAFindInterfaceChange()
+static void CAIPPassNetworkChangesToTransportAdapter(CANetworkStatus_t status)
 {
-    CAInterface_t *foundNewInterface = NULL;
-    return foundNewInterface;
+    CAIPCBData_t *cbitem = NULL;
+    LL_FOREACH(g_CAIPNetworkMonitorAdapterCallbackList, cbitem)
+    {
+        if (cbitem && cbitem->adapter)
+        {
+            cbitem->callback(cbitem->adapter, status);
+        }
+    }
 }
 
 /**
- * @todo Implement network interface monitoring.
- * Not critical for win32 bring-up.
+ * Set callback for receiving local IP/TCP adapter connection status.
+ *
+ * @param[in]  callback     Callback to be notified when IP/TCP adapter connection state changes.
+ * @param[in]  adapter      Transport adapter.
+ * @return ::CA_STATUS_OK or an appropriate error code.
  */
 CAResult_t CAIPSetNetworkMonitorCallback(CAIPAdapterStateChangeCallback callback,
                                          CATransportAdapter_t adapter)
 {
-    return CA_NOT_SUPPORTED;
+    if (!callback)
+    {
+        OIC_LOG(ERROR, TAG, "callback is null");
+        return CA_STATUS_INVALID_PARAM;
+    }
+
+    CAIPCBData_t *cbitem = NULL;
+    LL_FOREACH(g_CAIPNetworkMonitorAdapterCallbackList, cbitem)
+    {
+        if ((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_CAIPNetworkMonitorAdapterCallbackList, cbitem);
+
+    return CA_STATUS_OK;
+}
+
+/**
+ * Unset callback for receiving local IP/TCP adapter connection status.
+ *
+ * @param[in]  adapter      Transport adapter.
+ * @return CA_STATUS_OK.
+ */
+CAResult_t CAIPUnSetNetworkMonitorCallback(CATransportAdapter_t adapter)
+{
+    CAIPCBData_t *cbitem = NULL;
+    CAIPCBData_t *tmpCbitem = NULL;
+    LL_FOREACH_SAFE(g_CAIPNetworkMonitorAdapterCallbackList, cbitem, tmpCbitem)
+    {
+        if (cbitem && adapter == cbitem->adapter)
+        {
+            OIC_LOG(DEBUG, TAG, "remove specific callback");
+            LL_DELETE(g_CAIPNetworkMonitorAdapterCallbackList, cbitem);
+            OICFree(cbitem);
+            return CA_STATUS_OK;
+        }
+    }
+    return CA_STATUS_OK;
+}
+
+/**
+ * Allocate a new CAInterface_t entry for a given IP address.
+ */
+static CAInterface_t *AllocateCAInterface(int index, const char *name, int family,
+                                          const char *addr, int flags)
+{
+    CAInterface_t *ifitem = (CAInterface_t *)OICCalloc(1, sizeof(*ifitem));
+    if (!ifitem)
+    {
+        OIC_LOG(ERROR, TAG, "Allocating memory for a CAInterface_t 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;
+}
+
+/**
+ * Find a new IP address. Note that this can only return one, so the caller must
+ * call multiple times to get the list, which is pretty inefficient. The caller is
+ * responsible for freeing the pointer returned via OICFree().
+ * @todo Change the API to allow returning a list or, even better, allow calling
+ *       CAIPPassNetworkChangesToTransportAdapter() at any time from IpAddressChangeCallback.
+ *
+ * @return  Dynamically allocated IP address entry, or NULL if no change.
+ */
+CAInterface_t *CAFindInterfaceChange()
+{
+    oc_mutex_lock(g_CAIPNetworkMonitorMutex);
+
+    bool someAddressWentAway = g_CAIPNetworkMonitorSomeAddressWentAway;
+    g_CAIPNetworkMonitorSomeAddressWentAway = false;
+
+    CAInterface_t *newAddress = NULL;
+    if (g_CAIPNetworkMonitorNewAddressQueue)
+    {
+        // Pop the first new address to return.
+        CANewAddress_t *change = g_CAIPNetworkMonitorNewAddressQueue;
+        DL_DELETE(g_CAIPNetworkMonitorNewAddressQueue, change);
+        newAddress = change->ipAddressInfo;
+        OICFree(change);
+    }
+
+    oc_mutex_unlock(g_CAIPNetworkMonitorMutex);
+
+    if (someAddressWentAway)
+    {
+        CAIPPassNetworkChangesToTransportAdapter(CA_INTERFACE_DOWN);
+    }
+    if (newAddress)
+    {
+        CAIPPassNetworkChangesToTransportAdapter(CA_INTERFACE_UP);
+    }
+
+    return newAddress;
 }
 
-bool IsValidAdapter(PIP_ADAPTER_ADDRESSES pAdapterAddr, int desiredIndex, uint16_t family)
+static bool IsValidNetworkAdapter(PIP_ADAPTER_ADDRESSES pAdapterAddr, int desiredIndex)
 {
     bool valid = true;
 
@@ -105,189 +459,244 @@ bool IsValidAdapter(PIP_ADAPTER_ADDRESSES pAdapterAddr, int desiredIndex, uint16
         valid = false;
     }
 
-    // If the adapter must support the requested family
-    if ((family == AF_INET6) && ((pAdapterAddr->Flags & IP_ADAPTER_IPV6_ENABLED) == 0))
+    if ((pAdapterAddr->OperStatus & IfOperStatusUp) == 0)
     {
-        OIC_LOG_V(DEBUG, TAG, "\t\tInterface %i does not support IPv6", pAdapterAddr->IfIndex);
+        OIC_LOG_V(DEBUG, TAG, "\t\tInterface %i is not operational.", pAdapterAddr->IfIndex);
         valid = false;
     }
-    else if ((family == AF_INET) && ((pAdapterAddr->Flags & IP_ADAPTER_IPV4_ENABLED) == 0))
+    return valid;
+}
+
+/*
+ * Allocate a new CAInterface_t structure and add it to a given list.  A new entry is added
+ * for each address.
+ *
+ * @param[in/out] iflist  List to add to.
+ * @param[in]     name    Interface name.
+ * @param[in]     index   Interface index.
+ * @param[in]     family  Address family.
+ * @param[in]     addr    Address.
+ * @return Pointer to entry added, or NULL on failure.
+ */
+CAInterface_t *AddCAInterface(u_arraylist_t *iflist, const char *name, uint32_t index,
+                              uint16_t family, const char *addr)
+{
+    CAInterface_t *ifitem = AllocateCAInterface(index, name, family, addr, IFF_UP);
+    if (ifitem == NULL)
     {
-        OIC_LOG_V(DEBUG, TAG, "\t\tInterface %i does not support IPv4", pAdapterAddr->IfIndex);
-        valid = false;
+        return NULL;
     }
 
-    if ((pAdapterAddr->OperStatus & IfOperStatusUp) == 0)
+    if (!u_arraylist_add(iflist, ifitem))
     {
-        OIC_LOG_V(DEBUG, TAG, "\t\tInterface %i is not operational.", pAdapterAddr->IfIndex);
-        valid = false;
+        OIC_LOG(ERROR, TAG, "u_arraylist_add failed");
+        OICFree(ifitem);
+        return NULL;
     }
-    return valid;
 
+    return ifitem;
 }
 
-
-bool AddCAInterface(u_arraylist_t *iflist, const char * name, uint32_t index, uint16_t family)
+bool IsValidAddress(PIP_ADAPTER_UNICAST_ADDRESS pAddress)
 {
-    bool bSucceeded = false;
-    CAInterface_t *ifitem = (CAInterface_t *)OICCalloc(1, sizeof(*ifitem));
-    if (ifitem)
+    if (pAddress->Address.lpSockaddr->sa_family != AF_INET6)
     {
-        OICStrcpy(ifitem->name, INTERFACE_NAME_MAX, name);
-        ifitem->index = index;
-        ifitem->family = family;
-        ifitem->flags |= IFF_UP;// IsValidAddress() will have filtered out non-operational addresses already.
-
-        if (u_arraylist_add(iflist, ifitem))
-        {
-            bSucceeded = true;
-        }
-        else
-        {
-            OIC_LOG(ERROR, TAG, "u_arraylist_add failed");
-            OICFree(ifitem);
-        }
+        // All IPv4 addresses are valid.
+        return true;
     }
-    else
+
+    PSOCKADDR_IN6 sockAddr = (PSOCKADDR_IN6)pAddress->Address.lpSockaddr;
+    if (Ipv6UnicastAddressScope(sockAddr->sin6_addr.s6_addr) == ScopeLevelLink)
     {
-        OIC_LOG(ERROR, TAG, "Allocating memory for a CAInterface_t failed");
+        // IPv6 link local addresses are valid.
+        return true;
     }
-    return bSucceeded;
+
+    // Other IPv6 addresses are valid if they are DNS eligible.
+    // That is, ignore temporary addresses.
+    return ((pAddress->Flags & IP_ADAPTER_ADDRESS_DNS_ELIGIBLE) != 0);
 }
 
-bool AddInterfaces(PIP_ADAPTER_ADDRESSES pAdapterAddr, u_arraylist_t *iflist, int desiredIndex)
+bool AddAddresses(PIP_ADAPTER_ADDRESSES pAdapterAddr, u_arraylist_t *iflist, int desiredIndex)
 {
-    bool bSucceeded = false;
+    bool bSucceeded = true;
     for (PIP_ADAPTER_ADDRESSES pCurAdapterAddr = pAdapterAddr;
          pCurAdapterAddr != NULL; pCurAdapterAddr = pCurAdapterAddr->Next)
     {
         OIC_LOG_V(DEBUG, TAG, "\tInterface Index: %u", pCurAdapterAddr->IfIndex);
         OIC_LOG_V(DEBUG, TAG, "\tInterface  name: %s", pCurAdapterAddr->AdapterName);
 
-        // Prefer IPv6 over IPv4.
-        if (pCurAdapterAddr->Flags & IP_ADAPTER_IPV6_ENABLED)
+        if (!IsValidNetworkAdapter(pCurAdapterAddr, desiredIndex))
         {
-            // Do not add loopback, duplicate, or non-operational adapters
-            if (IsValidAdapter(pCurAdapterAddr, desiredIndex, AF_INET6))
-            {
-                if (AddCAInterface(iflist, pCurAdapterAddr->AdapterName, pCurAdapterAddr->IfIndex, AF_INET6))
-                {
-                    OIC_LOG_V(DEBUG, TAG, "\t\tAdded IPv6 interface %i", pCurAdapterAddr->IfIndex);
-                    bSucceeded = true;
-                }
-                else
-                {
-                    OIC_LOG_V(ERROR, TAG, "\tAdding IPv6 interface %i failed", pCurAdapterAddr->IfIndex);
-                    break;
-                }
-            }
-            else
-            {
-                OIC_LOG_V(DEBUG, TAG, "\t\tIPv6 interface %i not valid, skipping...", pCurAdapterAddr->IfIndex);
-            }
+            continue;
         }
 
-        if (pCurAdapterAddr->Flags & IP_ADAPTER_IPV4_ENABLED)
+        for (PIP_ADAPTER_UNICAST_ADDRESS pAddress = pCurAdapterAddr->FirstUnicastAddress;
+             pAddress != NULL;
+             pAddress = pAddress->Next)
         {
-            // Do not add loopback, duplicate, or non-operational adapters
-            if (IsValidAdapter(pCurAdapterAddr, desiredIndex, AF_INET))
+            if (!IsValidAddress(pAddress))
             {
-                if (AddCAInterface(iflist, pCurAdapterAddr->AdapterName, pCurAdapterAddr->IfIndex, AF_INET))
-                {
-                    OIC_LOG_V(DEBUG, TAG, "\t\tAdded IPv4 interface %i", pCurAdapterAddr->IfIndex);
-                    bSucceeded = true;
-                }
-                else
-                {
-                    OIC_LOG_V(ERROR, TAG, "\tAdding IPv4 interface %i failed", pCurAdapterAddr->IfIndex);
-                    break;
-                }
+                continue;
+            }
+
+            char addr[INET6_ADDRSTRLEN];
+            if (!inet_ntop(pAddress->Address.lpSockaddr->sa_family,
+                           INETADDR_ADDRESS(pAddress->Address.lpSockaddr),
+                           addr,
+                           sizeof(addr)))
+            {
+                continue;
             }
-            else
+
+            CAInterface_t *ipAddressInfo = AddCAInterface(iflist, pCurAdapterAddr->AdapterName,
+                                                          pCurAdapterAddr->IfIndex,
+                                                          pAddress->Address.lpSockaddr->sa_family,
+                                                          addr);
+            if (!ipAddressInfo)
             {
-                OIC_LOG_V(DEBUG, TAG, "\t\tIPv6 interface %i not valid, skipping...", pCurAdapterAddr->IfIndex);
+                OIC_LOG_V(ERROR, TAG, "\tAdding address on interface %i failed",
+                          pCurAdapterAddr->IfIndex);
+                bSucceeded = false;
+                break;
             }
 
+            OIC_LOG_V(DEBUG, TAG, "\t\tAdded address %s", ipAddressInfo->addr);
         }
     }
     return bSucceeded;
 }
 
+/*
+ * Get the set of IP_ADAPTER_ADDRESSES structures.  The caller is responsible for
+ * freeng the set using OICFree on the pointer returned.
+ *
+ * @return List of network adapters.
+ */
 PIP_ADAPTER_ADDRESSES GetAdapters()
 {
-    ULONG ulOutBufLen = sizeof(IP_ADAPTER_ADDRESSES);
-    PIP_ADAPTER_ADDRESSES pAdapterAddr = (IP_ADAPTER_ADDRESSES *) OICMalloc(ulOutBufLen);
-    if (pAdapterAddr != NULL)
+    ULONG ulOutBufLen = 0;
+    PIP_ADAPTER_ADDRESSES pAdapterAddr = NULL;
+
+    // We don't need most of the default information, so optimize this call by not
+    // asking for them.
+    ULONG flags = GAA_FLAG_SKIP_ANYCAST |
+                  GAA_FLAG_SKIP_MULTICAST |
+                  GAA_FLAG_SKIP_DNS_SERVER |
+                  GAA_FLAG_SKIP_FRIENDLY_NAME;
+
+    // Call up to 3 times: once to get the size, once to get the data, and once more
+    // just in case there was an increase in length in between the first two. If the
+    // length is still increasing due to more addresses being added, even this may fail
+    // and we'll have to wait for the next IP address change notification.
+    for (int i = 0; i < 3; i++)
     {
-        ULONG flags = 0;
         ULONG ret = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, pAdapterAddr, &ulOutBufLen);
         if (ERROR_BUFFER_OVERFLOW == ret)
         {
-            // Redo with updated length
-            OICFree(pAdapterAddr);
-            pAdapterAddr = (PIP_ADAPTER_ADDRESSES) OICMalloc(ulOutBufLen);
-            if (pAdapterAddr != NULL) {
-                ret = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, pAdapterAddr, &ulOutBufLen);
-                if (NO_ERROR != ret)
-                {
-                    OIC_LOG(ERROR, TAG, "GetAdaptersAddresses() failed");
-                    OICFree(pAdapterAddr);
-                    pAdapterAddr = NULL;
-                }
-                else
-                {
-                    // Succeeded getting adapters
-                }
-            }
-            else
+            // Redo with updated length.
+            if (pAdapterAddr != NULL)
             {
-                OIC_LOG(ERROR, TAG, "Second time allocating memory for GetAdaptersAddresses() failed");
+                OICFree(pAdapterAddr);
+            }
+            pAdapterAddr = (PIP_ADAPTER_ADDRESSES) OICMalloc(ulOutBufLen);
+            if (pAdapterAddr == NULL) {
+                OIC_LOG(ERROR, TAG, "Allocating memory for GetAdaptersAddresses() failed");
+                break;
             }
+            continue;
         }
-        else
+        if (NO_ERROR != ret)
         {
-            OIC_LOG(ERROR, TAG, "Expected GetAdaptersAddresses() to fail on first try, but it didn't.");
+            OIC_LOG(ERROR, TAG, "GetAdaptersAddresses() failed");
+            break;
         }
+
+        // Succeeded getting adapters
+        return pAdapterAddr;
     }
-    else
+
+    if (pAdapterAddr != NULL)
     {
-        OIC_LOG(ERROR, TAG, "First time allocating memory for GetAdaptersAddresses() failed");
+        OICFree(pAdapterAddr);
     }
-    return pAdapterAddr;
+    return NULL;
+}
+
+/**
+ * Get the list of CAInterface_t items.  Currently only 0 is passed as the desiredIndex by any
+ * caller.
+ *
+ * @param[in]  desiredIndex      Network interface index, or 0 for all.
+ * @return  List of CAInterface_t items.
+ */
+static u_arraylist_t *GetInterfaceInformation(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)
+    {
+        OIC_LOG(ERROR, TAG, "Failed to create iflist");
+        return NULL;
+    }
+
+    PIP_ADAPTER_ADDRESSES pAdapterAddr = GetAdapters();
+    if (!pAdapterAddr)
+    {
+        OIC_LOG(ERROR, TAG, "Enumerating Adapters failed");
+        u_arraylist_destroy(iflist);
+        return NULL;
+    }
+
+    // Cycle through network adapters.
+    // Add valid network addresses to the address list.
+    bool ret = AddAddresses(pAdapterAddr, iflist, desiredIndex);
+    if (false == ret)
+    {
+        OIC_LOG(ERROR, TAG, "AddAddresses() failed");
+        u_arraylist_destroy(iflist);
+        iflist = NULL;
+    }
+
+    // Finished with network adapter list.
+    OICFree(pAdapterAddr);
+
+    return iflist;
 }
 
 u_arraylist_t *CAIPGetInterfaceInformation(int desiredIndex)
 {
     u_arraylist_t *iflist = u_arraylist_create();
-    if (iflist)
+    if (!iflist)
     {
-        PIP_ADAPTER_ADDRESSES pAdapterAddr = NULL;
-        pAdapterAddr = GetAdapters();
-        if (pAdapterAddr)
-        {
-            // Cycle through adapters
-            // Add valid adapters to the interface list.
-            bool ret = AddInterfaces(pAdapterAddr, iflist, desiredIndex);
-            if (false == ret)
-            {
-                OIC_LOG(ERROR, TAG, "AddInterfaces() failed");
-                u_arraylist_destroy(iflist);
-                iflist = NULL;
-            }
+        OIC_LOG(ERROR, TAG, "Failed to create iflist");
+        return NULL;
+    }
 
-            // Finished with Adapter List
-            OICFree(pAdapterAddr);
-        }
-        else
+    // Avoid extra kernel calls by just duplicating what's in our cache.
+    oc_mutex_lock(g_CAIPNetworkMonitorMutex);
+
+    uint32_t list_length = u_arraylist_length(g_CAIPNetworkMonitorAddressList);
+    for (uint32_t list_index = 0; list_index < list_length; list_index++)
+    {
+        CAInterface_t *currItem = (CAInterface_t *)u_arraylist_get(g_CAIPNetworkMonitorAddressList,
+                                                                   list_index);
+        if (!AddCAInterface(iflist, currItem->name, currItem->index, currItem->family,
+                            currItem->addr))
         {
-            OIC_LOG(ERROR, TAG, "Enumerating Adapters failed");
+            OIC_LOG(ERROR, TAG, "AddCAInterface() failed");
             u_arraylist_destroy(iflist);
             iflist = NULL;
+            break;
         }
     }
-    else
-    {
-        OIC_LOG(ERROR, TAG, "Failed to create iflist");
-    }
+
+    oc_mutex_unlock(g_CAIPNetworkMonitorMutex);
+
     return iflist;
 }