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.
406 * The caller is responsible for freeing the pointer returned via u_arraylist_destroy().
408 * @return Dynamically allocated IP address list, or NULL if no change.
410 u_arraylist_t *CAFindInterfaceChange()
412 u_arraylist_t *iflist = u_arraylist_create();
415 OIC_LOG_V(ERROR, TAG, "Failed to create iflist: %s", strerror(errno));
419 oc_mutex_lock(g_CAIPNetworkMonitorMutex);
421 bool someAddressWentAway = g_CAIPNetworkMonitorSomeAddressWentAway;
422 g_CAIPNetworkMonitorSomeAddressWentAway = false;
424 bool newAddress = false;
426 // Pop whole new address in list.
427 while (g_CAIPNetworkMonitorNewAddressQueue)
429 CANewAddress_t *change = g_CAIPNetworkMonitorNewAddressQueue;
431 bool result = u_arraylist_add(iflist, change->ipAddressInfo);
434 OIC_LOG(ERROR, TAG, "u_arraylist_add failed.");
439 DL_DELETE(g_CAIPNetworkMonitorNewAddressQueue, change);
445 oc_mutex_unlock(g_CAIPNetworkMonitorMutex);
447 if (someAddressWentAway)
449 CAIPPassNetworkChangesToTransportAdapter(CA_INTERFACE_DOWN);
453 CAIPPassNetworkChangesToTransportAdapter(CA_INTERFACE_UP);
459 static bool IsValidNetworkAdapter(PIP_ADAPTER_ADDRESSES pAdapterAddr, int desiredIndex)
463 // If desiredIndex is non-zero, then only retrieve adapter corresponding to desiredIndex.
464 // If desiredIndex is zero, then retrieve all adapters.
465 if (desiredIndex && (pAdapterAddr->IfIndex != desiredIndex))
467 OIC_LOG_V(DEBUG, TAG, "\t\tInterface %i not interesting.", pAdapterAddr->IfIndex);
471 if (pAdapterAddr->IfType & IF_TYPE_SOFTWARE_LOOPBACK)
473 OIC_LOG_V(DEBUG, TAG, "\t\tInterface %i is loopback.", pAdapterAddr->IfIndex);
477 if ((pAdapterAddr->OperStatus & IfOperStatusUp) == 0)
479 OIC_LOG_V(DEBUG, TAG, "\t\tInterface %i is not operational.", pAdapterAddr->IfIndex);
486 * Allocate a new CAInterface_t structure and add it to a given list. A new entry is added
489 * @param[in/out] iflist List to add to.
490 * @param[in] name Interface name.
491 * @param[in] index Interface index.
492 * @param[in] family Address family.
493 * @param[in] addr Address.
494 * @return Pointer to entry added, or NULL on failure.
496 CAInterface_t *AddCAInterface(u_arraylist_t *iflist, const char *name, uint32_t index,
497 uint16_t family, const char *addr)
499 CAInterface_t *ifitem = AllocateCAInterface(index, name, family, addr, IFF_UP);
505 if (!u_arraylist_add(iflist, ifitem))
507 OIC_LOG(ERROR, TAG, "u_arraylist_add failed");
515 bool IsValidAddress(PIP_ADAPTER_UNICAST_ADDRESS pAddress)
517 if (pAddress->Address.lpSockaddr->sa_family != AF_INET6)
519 // All IPv4 addresses are valid.
523 PSOCKADDR_IN6 sockAddr = (PSOCKADDR_IN6)pAddress->Address.lpSockaddr;
524 if (Ipv6UnicastAddressScope(sockAddr->sin6_addr.s6_addr) == ScopeLevelLink)
526 // IPv6 link local addresses are valid.
530 // Other IPv6 addresses are valid if they are DNS eligible.
531 // That is, ignore temporary addresses.
532 return ((pAddress->Flags & IP_ADAPTER_ADDRESS_DNS_ELIGIBLE) != 0);
535 bool AddAddresses(PIP_ADAPTER_ADDRESSES pAdapterAddr, u_arraylist_t *iflist, int desiredIndex)
537 bool bSucceeded = true;
538 for (PIP_ADAPTER_ADDRESSES pCurAdapterAddr = pAdapterAddr;
539 pCurAdapterAddr != NULL; pCurAdapterAddr = pCurAdapterAddr->Next)
541 OIC_LOG_V(DEBUG, TAG, "\tInterface Index: %u", pCurAdapterAddr->IfIndex);
542 OIC_LOG_V(DEBUG, TAG, "\tInterface name: %s", pCurAdapterAddr->AdapterName);
544 if (!IsValidNetworkAdapter(pCurAdapterAddr, desiredIndex))
549 for (PIP_ADAPTER_UNICAST_ADDRESS pAddress = pCurAdapterAddr->FirstUnicastAddress;
551 pAddress = pAddress->Next)
553 if (!IsValidAddress(pAddress))
558 char addr[INET6_ADDRSTRLEN];
559 if (!inet_ntop(pAddress->Address.lpSockaddr->sa_family,
560 INETADDR_ADDRESS(pAddress->Address.lpSockaddr),
567 CAInterface_t *ipAddressInfo = AddCAInterface(iflist, pCurAdapterAddr->AdapterName,
568 pCurAdapterAddr->IfIndex,
569 pAddress->Address.lpSockaddr->sa_family,
573 OIC_LOG_V(ERROR, TAG, "\tAdding address on interface %i failed",
574 pCurAdapterAddr->IfIndex);
579 OIC_LOG_V(DEBUG, TAG, "\t\tAdded address %s", ipAddressInfo->addr);
586 * Get the set of IP_ADAPTER_ADDRESSES structures. The caller is responsible for
587 * freeng the set using OICFree on the pointer returned.
589 * @return List of network adapters.
591 PIP_ADAPTER_ADDRESSES GetAdapters()
593 ULONG ulOutBufLen = 0;
594 PIP_ADAPTER_ADDRESSES pAdapterAddr = NULL;
596 // We don't need most of the default information, so optimize this call by not
598 ULONG flags = GAA_FLAG_SKIP_ANYCAST |
599 GAA_FLAG_SKIP_MULTICAST |
600 GAA_FLAG_SKIP_DNS_SERVER |
601 GAA_FLAG_SKIP_FRIENDLY_NAME;
603 // Call up to 3 times: once to get the size, once to get the data, and once more
604 // just in case there was an increase in length in between the first two. If the
605 // length is still increasing due to more addresses being added, even this may fail
606 // and we'll have to wait for the next IP address change notification.
607 for (int i = 0; i < 3; i++)
609 ULONG ret = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, pAdapterAddr, &ulOutBufLen);
610 if (ERROR_BUFFER_OVERFLOW == ret)
612 // Redo with updated length.
613 if (pAdapterAddr != NULL)
615 OICFree(pAdapterAddr);
617 pAdapterAddr = (PIP_ADAPTER_ADDRESSES) OICMalloc(ulOutBufLen);
618 if (pAdapterAddr == NULL) {
619 OIC_LOG(ERROR, TAG, "Allocating memory for GetAdaptersAddresses() failed");
626 OIC_LOG(ERROR, TAG, "GetAdaptersAddresses() failed");
630 // Succeeded getting adapters
634 if (pAdapterAddr != NULL)
636 OICFree(pAdapterAddr);
642 * Get the list of CAInterface_t items. Currently only 0 is passed as the desiredIndex by any
645 * @param[in] desiredIndex Network interface index, or 0 for all.
646 * @return List of CAInterface_t items.
648 static u_arraylist_t *GetInterfaceInformation(int desiredIndex)
650 if (desiredIndex < 0)
652 OIC_LOG_V(ERROR, TAG, "invalid index : %d", desiredIndex);
656 u_arraylist_t *iflist = u_arraylist_create();
659 OIC_LOG(ERROR, TAG, "Failed to create iflist");
663 PIP_ADAPTER_ADDRESSES pAdapterAddr = GetAdapters();
666 OIC_LOG(ERROR, TAG, "Enumerating Adapters failed");
667 u_arraylist_destroy(iflist);
671 // Cycle through network adapters.
672 // Add valid network addresses to the address list.
673 bool ret = AddAddresses(pAdapterAddr, iflist, desiredIndex);
676 OIC_LOG(ERROR, TAG, "AddAddresses() failed");
677 u_arraylist_destroy(iflist);
681 // Finished with network adapter list.
682 OICFree(pAdapterAddr);
687 u_arraylist_t *CAIPGetInterfaceInformation(int desiredIndex)
689 u_arraylist_t *iflist = u_arraylist_create();
692 OIC_LOG(ERROR, TAG, "Failed to create iflist");
696 // Avoid extra kernel calls by just duplicating what's in our cache.
697 oc_mutex_lock(g_CAIPNetworkMonitorMutex);
699 uint32_t list_length = u_arraylist_length(g_CAIPNetworkMonitorAddressList);
700 for (uint32_t list_index = 0; list_index < list_length; list_index++)
702 CAInterface_t *currItem = (CAInterface_t *)u_arraylist_get(g_CAIPNetworkMonitorAddressList,
704 if (!AddCAInterface(iflist, currItem->name, currItem->index, currItem->family,
707 OIC_LOG(ERROR, TAG, "AddCAInterface() failed");
708 u_arraylist_destroy(iflist);
714 oc_mutex_unlock(g_CAIPNetworkMonitorMutex);