1 /* *****************************************************************
3 * Copyright 2016 Microsoft
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 ******************************************************************/
20 #include "iotivity_config.h"
21 #include "caipinterface.h"
24 #include <sys/types.h>
33 #include "caadapterutils.h"
35 #include "oic_malloc.h"
36 #include "oic_string.h"
37 #include "caipnwmonitor.h"
38 #include <coap/utlist.h>
40 #define TAG "IP_MONITOR"
43 * Mutex for synchronizing access to cached address information.
45 static oc_mutex g_CAIPNetworkMonitorMutex = NULL;
47 static bool g_CAIPNetworkMonitorSomeAddressWentAway = false;
49 typedef struct CANewAddress_t {
50 struct CANewAddress_t *next;
51 struct CANewAddress_t *prev;
52 CAInterface_t *ipAddressInfo;
56 * List of network addresses that we've already reported.
58 static u_arraylist_t *g_CAIPNetworkMonitorAddressList = NULL;
61 * Queue of new addresses that haven't yet been returned in CAFindInterfaceChange().
63 static CANewAddress_t *g_CAIPNetworkMonitorNewAddressQueue = NULL;
66 * Transport adapter change callback list.
68 static struct CAIPCBData_t *g_CAIPNetworkMonitorAdapterCallbackList = NULL;
70 static CAInterface_t *AllocateCAInterface(int index, const char *name, int family,
71 const char *addr, int flags);
73 static u_arraylist_t *GetInterfaceInformation(int desiredIndex);
75 static void CAIPDestroyNetworkMonitorList();
77 static CAResult_t CAIPInitializeNetworkMonitorList()
79 assert(!g_CAIPNetworkMonitorMutex);
80 assert(!g_CAIPNetworkMonitorAddressList);
82 g_CAIPNetworkMonitorMutex = oc_mutex_new();
83 if (!g_CAIPNetworkMonitorMutex)
85 OIC_LOG(ERROR, TAG, "oc_mutex_new has failed");
86 return CA_STATUS_FAILED;
89 g_CAIPNetworkMonitorAddressList = u_arraylist_create();
90 if (!g_CAIPNetworkMonitorAddressList)
92 OIC_LOG(ERROR, TAG, "u_arraylist_create has failed");
93 CAIPDestroyNetworkMonitorList();
94 return CA_STATUS_FAILED;
101 * Destroy the network monitoring list.
103 static void CAIPDestroyNetworkMonitorList()
105 // Free any new addresses waiting to be indicated up.
106 while (g_CAIPNetworkMonitorNewAddressQueue)
108 CANewAddress_t *change = g_CAIPNetworkMonitorNewAddressQueue;
109 DL_DELETE(g_CAIPNetworkMonitorNewAddressQueue, change);
110 OICFree(change->ipAddressInfo);
114 // Free our cache of operational addresses.
115 if (g_CAIPNetworkMonitorAddressList)
117 u_arraylist_destroy(g_CAIPNetworkMonitorAddressList);
118 g_CAIPNetworkMonitorAddressList = NULL;
121 if (g_CAIPNetworkMonitorMutex)
123 oc_mutex_free(g_CAIPNetworkMonitorMutex);
124 g_CAIPNetworkMonitorMutex = NULL;
129 * See if a CAInterface_t with a given index and address already exists in a list.
131 * @param[in] ifIndex Interface index to look for.
132 * @param[in] family Family of address to look for.
133 * @param[in] addr Address to look for.
134 * @return true if already in the list, false if not.
136 static bool CACmpNetworkList(uint32_t ifIndex, int family, const char *addr, u_arraylist_t *iflist)
138 uint32_t list_length = u_arraylist_length(iflist);
139 for (uint32_t list_index = 0; list_index < list_length; list_index++)
141 CAInterface_t *currItem = (CAInterface_t *) u_arraylist_get(iflist, list_index);
142 if ((currItem->index == ifIndex) && (currItem->family == family) &&
143 (strcmp(currItem->addr, addr) == 0))
151 static HANDLE g_CAIPNetworkMonitorChangeNotificationHandle = NULL;
154 * Handle a notification that the IP address info changed.
156 * @param[in] context Context passed to NotifyUnicastIpChange.
157 * @param[in] row Interface that changed, or NULL on the initial callback.
158 * @param[in] notificationType Type of change that occurred.
160 static void CALLBACK IpAddressChangeCallback(void *context,
161 MIB_UNICASTIPADDRESS_ROW *row,
162 MIB_NOTIFICATION_TYPE notificationType)
164 oc_mutex_lock(g_CAIPNetworkMonitorMutex);
166 // Fetch new network address info.
167 u_arraylist_t *newList = GetInterfaceInformation(0);
168 uint32_t newLen = u_arraylist_length(newList);
170 u_arraylist_t *oldList = g_CAIPNetworkMonitorAddressList;
171 uint32_t oldLen = u_arraylist_length(oldList);
173 if (caglobals.ip.addressChangeEvent)
175 // Check whether any addresses went away.
176 for (uint32_t i = 0; i < oldLen; i++)
178 CAInterface_t *ifitem = (CAInterface_t *)u_arraylist_get(oldList, i);
179 if (!CACmpNetworkList(ifitem->index, ifitem->family, ifitem->addr, newList))
181 g_CAIPNetworkMonitorSomeAddressWentAway = true;
186 // Check whether any new addresses are available.
187 for (uint32_t i = 0; i < newLen; i++)
189 CAInterface_t *ifitem = (CAInterface_t *)u_arraylist_get(newList, i);
190 if (!CACmpNetworkList(ifitem->index, ifitem->family, ifitem->addr, oldList))
192 // Create a new CAInterface_t to add to the queue to indicate to the higher
193 // layer. We cannot simply invoke the callback here currently, since the
194 // higher layer is not thread-safe.
195 CAInterface_t *dup = AllocateCAInterface(ifitem->index, ifitem->name,
196 ifitem->family, ifitem->addr,
198 CANewAddress_t *change = (CANewAddress_t *)OICCalloc(1, sizeof(*change));
201 change->ipAddressInfo = dup;
202 DL_APPEND(g_CAIPNetworkMonitorNewAddressQueue, change);
206 OIC_LOG(WARNING, TAG, "Couldn't allocate memory for CANewAddress_t");
211 // If the new address queue is not empty, signal the transport server that it needs
212 // to call CAFindInterfaceChange(). We don't need to set the event if an address
213 // went away, since the higher layer just uses the event to ask for new addresses
214 // in order to join the multicast group on the associated interface and address family.
215 if (g_CAIPNetworkMonitorNewAddressQueue)
217 int ret = WSASetEvent(caglobals.ip.addressChangeEvent);
219 // Setting the event should always succeed, since the handle should always be
220 // valid when this code is reached.
225 // Replace old cached info.
226 g_CAIPNetworkMonitorAddressList = newList;
227 u_arraylist_destroy(oldList);
229 oc_mutex_unlock(g_CAIPNetworkMonitorMutex);
233 * Start network monitor.
235 * @param[in] callback Callback to be notified when IP/TCP adapter connection state changes.
236 * @param[in] adapter Transport adapter.
237 * @return ::CA_STATUS_OK or an appropriate error code.
239 CAResult_t CAIPStartNetworkMonitor(CAIPAdapterStateChangeCallback callback,
240 CATransportAdapter_t adapter)
242 CAResult_t res = CAIPInitializeNetworkMonitorList();
243 if (res != CA_STATUS_OK)
248 res = CAIPSetNetworkMonitorCallback(callback, adapter);
249 if (res != CA_STATUS_OK)
254 if (g_CAIPNetworkMonitorChangeNotificationHandle == NULL)
256 int err = NotifyUnicastIpAddressChange(AF_UNSPEC, IpAddressChangeCallback, NULL,
258 &g_CAIPNetworkMonitorChangeNotificationHandle);
261 return CA_STATUS_FAILED;
268 * Stops network monitor.
270 * @param[in] adapter Transport adapter.
271 * @return ::CA_STATUS_OK or an appropriate error code.
273 CAResult_t CAIPStopNetworkMonitor(CATransportAdapter_t adapter)
275 if (g_CAIPNetworkMonitorChangeNotificationHandle != NULL)
277 int err = CancelMibChangeNotify2(g_CAIPNetworkMonitorChangeNotificationHandle);
278 assert(err == NO_ERROR);
279 g_CAIPNetworkMonitorChangeNotificationHandle = NULL;
282 CAIPDestroyNetworkMonitorList();
283 return CAIPUnSetNetworkMonitorCallback(adapter);
287 * Let the network monitor update the polling interval.
288 * @param[in] interval Current polling interval, in seconds
290 * @return desired polling interval
292 int CAGetPollingInterval(int interval)
294 // Don't change the polling interval.
299 * Pass the changed network status through the stored callback.
300 * Note that the current API doesn't allow us to specify which address changed,
301 * the caller has to look at the return from CAFindInterfaceChange() to learn about
302 * each new address, and look through CAIPGetInterfaceInformation() to see what's
303 * missing to detect any removed addresses.
305 * @param[in] status Network status to pass to the callback.
307 static void CAIPPassNetworkChangesToTransportAdapter(CANetworkStatus_t status)
309 CAIPCBData_t *cbitem = NULL;
310 LL_FOREACH(g_CAIPNetworkMonitorAdapterCallbackList, cbitem)
312 if (cbitem && cbitem->adapter)
314 cbitem->callback(cbitem->adapter, status);
320 * Set callback for receiving local IP/TCP adapter connection status.
322 * @param[in] callback Callback to be notified when IP/TCP adapter connection state changes.
323 * @param[in] adapter Transport adapter.
324 * @return ::CA_STATUS_OK or an appropriate error code.
326 CAResult_t CAIPSetNetworkMonitorCallback(CAIPAdapterStateChangeCallback callback,
327 CATransportAdapter_t adapter)
331 OIC_LOG(ERROR, TAG, "callback is null");
332 return CA_STATUS_INVALID_PARAM;
335 CAIPCBData_t *cbitem = NULL;
336 LL_FOREACH(g_CAIPNetworkMonitorAdapterCallbackList, cbitem)
338 if ((adapter == cbitem->adapter) && (callback == cbitem->callback))
340 OIC_LOG(DEBUG, TAG, "this callback is already added");
345 cbitem = (CAIPCBData_t *)OICCalloc(1, sizeof(*cbitem));
348 OIC_LOG(ERROR, TAG, "Malloc failed");
349 return CA_STATUS_FAILED;
352 cbitem->adapter = adapter;
353 cbitem->callback = callback;
354 LL_APPEND(g_CAIPNetworkMonitorAdapterCallbackList, cbitem);
360 * Unset callback for receiving local IP/TCP adapter connection status.
362 * @param[in] adapter Transport adapter.
363 * @return CA_STATUS_OK.
365 CAResult_t CAIPUnSetNetworkMonitorCallback(CATransportAdapter_t adapter)
367 CAIPCBData_t *cbitem = NULL;
368 CAIPCBData_t *tmpCbitem = NULL;
369 LL_FOREACH_SAFE(g_CAIPNetworkMonitorAdapterCallbackList, cbitem, tmpCbitem)
371 if (cbitem && adapter == cbitem->adapter)
373 OIC_LOG(DEBUG, TAG, "remove specific callback");
374 LL_DELETE(g_CAIPNetworkMonitorAdapterCallbackList, cbitem);
383 * Allocate a new CAInterface_t entry for a given IP address.
385 static CAInterface_t *AllocateCAInterface(int index, const char *name, int family,
386 const char *addr, int flags)
388 CAInterface_t *ifitem = (CAInterface_t *)OICCalloc(1, sizeof(*ifitem));
391 OIC_LOG(ERROR, TAG, "Allocating memory for a CAInterface_t failed");
395 OICStrcpy(ifitem->name, sizeof(ifitem->name), name);
396 ifitem->index = index;
397 ifitem->family = family;
398 OICStrcpy(ifitem->addr, sizeof(ifitem->addr), addr);
399 ifitem->flags = flags;
405 * Find a new IP address. Note that this can only return one, so the caller must
406 * call multiple times to get the list, which is pretty inefficient. The caller is
407 * responsible for freeing the pointer returned via OICFree().
408 * @todo Change the API to allow returning a list or, even better, allow calling
409 * CAIPPassNetworkChangesToTransportAdapter() at any time from IpAddressChangeCallback.
411 * @return Dynamically allocated IP address entry, or NULL if no change.
413 CAInterface_t *CAFindInterfaceChange()
415 oc_mutex_lock(g_CAIPNetworkMonitorMutex);
417 bool someAddressWentAway = g_CAIPNetworkMonitorSomeAddressWentAway;
418 g_CAIPNetworkMonitorSomeAddressWentAway = false;
420 CAInterface_t *newAddress = NULL;
421 if (g_CAIPNetworkMonitorNewAddressQueue)
423 // Pop the first new address to return.
424 CANewAddress_t *change = g_CAIPNetworkMonitorNewAddressQueue;
425 DL_DELETE(g_CAIPNetworkMonitorNewAddressQueue, change);
426 newAddress = change->ipAddressInfo;
430 oc_mutex_unlock(g_CAIPNetworkMonitorMutex);
432 if (someAddressWentAway)
434 CAIPPassNetworkChangesToTransportAdapter(CA_INTERFACE_DOWN);
438 CAIPPassNetworkChangesToTransportAdapter(CA_INTERFACE_UP);
444 static bool IsValidNetworkAdapter(PIP_ADAPTER_ADDRESSES pAdapterAddr, int desiredIndex)
448 // If desiredIndex is non-zero, then only retrieve adapter corresponding to desiredIndex.
449 // If desiredIndex is zero, then retrieve all adapters.
450 if (desiredIndex && (pAdapterAddr->IfIndex != desiredIndex))
452 OIC_LOG_V(DEBUG, TAG, "\t\tInterface %i not interesting.", pAdapterAddr->IfIndex);
456 if (pAdapterAddr->IfType & IF_TYPE_SOFTWARE_LOOPBACK)
458 OIC_LOG_V(DEBUG, TAG, "\t\tInterface %i is loopback.", pAdapterAddr->IfIndex);
462 if ((pAdapterAddr->OperStatus & IfOperStatusUp) == 0)
464 OIC_LOG_V(DEBUG, TAG, "\t\tInterface %i is not operational.", pAdapterAddr->IfIndex);
471 * Allocate a new CAInterface_t structure and add it to a given list. A new entry is added
474 * @param[in/out] iflist List to add to.
475 * @param[in] name Interface name.
476 * @param[in] index Interface index.
477 * @param[in] family Address family.
478 * @param[in] addr Address.
479 * @return Pointer to entry added, or NULL on failure.
481 CAInterface_t *AddCAInterface(u_arraylist_t *iflist, const char *name, uint32_t index,
482 uint16_t family, const char *addr)
484 CAInterface_t *ifitem = AllocateCAInterface(index, name, family, addr, IFF_UP);
490 if (!u_arraylist_add(iflist, ifitem))
492 OIC_LOG(ERROR, TAG, "u_arraylist_add failed");
500 bool IsValidAddress(PIP_ADAPTER_UNICAST_ADDRESS pAddress)
502 if (pAddress->Address.lpSockaddr->sa_family != AF_INET6)
504 // All IPv4 addresses are valid.
508 PSOCKADDR_IN6 sockAddr = (PSOCKADDR_IN6)pAddress->Address.lpSockaddr;
509 if (Ipv6UnicastAddressScope(sockAddr->sin6_addr.s6_addr) == ScopeLevelLink)
511 // IPv6 link local addresses are valid.
515 // Other IPv6 addresses are valid if they are DNS eligible.
516 // That is, ignore temporary addresses.
517 return ((pAddress->Flags & IP_ADAPTER_ADDRESS_DNS_ELIGIBLE) != 0);
520 bool AddAddresses(PIP_ADAPTER_ADDRESSES pAdapterAddr, u_arraylist_t *iflist, int desiredIndex)
522 bool bSucceeded = true;
523 for (PIP_ADAPTER_ADDRESSES pCurAdapterAddr = pAdapterAddr;
524 pCurAdapterAddr != NULL; pCurAdapterAddr = pCurAdapterAddr->Next)
526 OIC_LOG_V(DEBUG, TAG, "\tInterface Index: %u", pCurAdapterAddr->IfIndex);
527 OIC_LOG_V(DEBUG, TAG, "\tInterface name: %s", pCurAdapterAddr->AdapterName);
529 if (!IsValidNetworkAdapter(pCurAdapterAddr, desiredIndex))
534 for (PIP_ADAPTER_UNICAST_ADDRESS pAddress = pCurAdapterAddr->FirstUnicastAddress;
536 pAddress = pAddress->Next)
538 if (!IsValidAddress(pAddress))
543 char addr[INET6_ADDRSTRLEN];
544 if (!inet_ntop(pAddress->Address.lpSockaddr->sa_family,
545 INETADDR_ADDRESS(pAddress->Address.lpSockaddr),
552 CAInterface_t *ipAddressInfo = AddCAInterface(iflist, pCurAdapterAddr->AdapterName,
553 pCurAdapterAddr->IfIndex,
554 pAddress->Address.lpSockaddr->sa_family,
558 OIC_LOG_V(ERROR, TAG, "\tAdding address on interface %i failed",
559 pCurAdapterAddr->IfIndex);
564 OIC_LOG_V(DEBUG, TAG, "\t\tAdded address %s", ipAddressInfo->addr);
571 * Get the set of IP_ADAPTER_ADDRESSES structures. The caller is responsible for
572 * freeng the set using OICFree on the pointer returned.
574 * @return List of network adapters.
576 PIP_ADAPTER_ADDRESSES GetAdapters()
578 ULONG ulOutBufLen = 0;
579 PIP_ADAPTER_ADDRESSES pAdapterAddr = NULL;
581 // We don't need most of the default information, so optimize this call by not
583 ULONG flags = GAA_FLAG_SKIP_ANYCAST |
584 GAA_FLAG_SKIP_MULTICAST |
585 GAA_FLAG_SKIP_DNS_SERVER |
586 GAA_FLAG_SKIP_FRIENDLY_NAME;
588 // Call up to 3 times: once to get the size, once to get the data, and once more
589 // just in case there was an increase in length in between the first two. If the
590 // length is still increasing due to more addresses being added, even this may fail
591 // and we'll have to wait for the next IP address change notification.
592 for (int i = 0; i < 3; i++)
594 ULONG ret = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, pAdapterAddr, &ulOutBufLen);
595 if (ERROR_BUFFER_OVERFLOW == ret)
597 // Redo with updated length.
598 if (pAdapterAddr != NULL)
600 OICFree(pAdapterAddr);
602 pAdapterAddr = (PIP_ADAPTER_ADDRESSES) OICMalloc(ulOutBufLen);
603 if (pAdapterAddr == NULL) {
604 OIC_LOG(ERROR, TAG, "Allocating memory for GetAdaptersAddresses() failed");
611 OIC_LOG(ERROR, TAG, "GetAdaptersAddresses() failed");
615 // Succeeded getting adapters
619 if (pAdapterAddr != NULL)
621 OICFree(pAdapterAddr);
627 * Get the list of CAInterface_t items. Currently only 0 is passed as the desiredIndex by any
630 * @param[in] desiredIndex Network interface index, or 0 for all.
631 * @return List of CAInterface_t items.
633 static u_arraylist_t *GetInterfaceInformation(int desiredIndex)
635 if (desiredIndex < 0)
637 OIC_LOG_V(ERROR, TAG, "invalid index : %d", desiredIndex);
641 u_arraylist_t *iflist = u_arraylist_create();
644 OIC_LOG(ERROR, TAG, "Failed to create iflist");
648 PIP_ADAPTER_ADDRESSES pAdapterAddr = GetAdapters();
651 OIC_LOG(ERROR, TAG, "Enumerating Adapters failed");
652 u_arraylist_destroy(iflist);
656 // Cycle through network adapters.
657 // Add valid network addresses to the address list.
658 bool ret = AddAddresses(pAdapterAddr, iflist, desiredIndex);
661 OIC_LOG(ERROR, TAG, "AddAddresses() failed");
662 u_arraylist_destroy(iflist);
666 // Finished with network adapter list.
667 OICFree(pAdapterAddr);
672 u_arraylist_t *CAIPGetInterfaceInformation(int desiredIndex)
674 u_arraylist_t *iflist = u_arraylist_create();
677 OIC_LOG(ERROR, TAG, "Failed to create iflist");
681 // Avoid extra kernel calls by just duplicating what's in our cache.
682 oc_mutex_lock(g_CAIPNetworkMonitorMutex);
684 uint32_t list_length = u_arraylist_length(g_CAIPNetworkMonitorAddressList);
685 for (uint32_t list_index = 0; list_index < list_length; list_index++)
687 CAInterface_t *currItem = (CAInterface_t *)u_arraylist_get(g_CAIPNetworkMonitorAddressList,
689 if (!AddCAInterface(iflist, currItem->name, currItem->index, currItem->family,
692 OIC_LOG(ERROR, TAG, "AddCAInterface() failed");
693 u_arraylist_destroy(iflist);
699 oc_mutex_unlock(g_CAIPNetworkMonitorMutex);