#include <arpa/inet.h>
#include <linux/if.h>
#include <coap/utlist.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
#include "caadapterutils.h"
#include "caipnwmonitor.h"
#include "oic_malloc.h"
#include "oic_string.h"
#include "org_iotivity_ca_CaIpInterface.h"
+#include "ifaddrs.h"
#define TAG "OIC_CA_IP_MONITOR"
#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 adapter changes callback interface.
*/
u_arraylist_t *CAFindInterfaceChange()
{
// release netlink event
- char *bufPtr = (char *)OICCalloc(NETLINK_MESSAGE_LENGTH, sizeof (char));
- if (!bufPtr)
- {
- OIC_LOG(ERROR, TAG, "Netlink malloc failed in CAFindInterfaceChange");
- return NULL;
- }
- recv(caglobals.ip.netlinkFd, bufPtr, NETLINK_MESSAGE_LENGTH, 0);
- OICFree(bufPtr);
- bufPtr = NULL;
-
- 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)
- {
- OIC_LOG_V(ERROR, TAG, "SIOCGIFCONF failed: %s", strerror(errno));
- return NULL;
- }
-
- u_arraylist_t *iflist = NULL;
- 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;
+ 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 };
+
+ size_t len = recvmsg(caglobals.ip.netlinkFd, &msg, 0);
+ return NULL;
+}
- CAIfItem_t *previous = (CAIfItem_t *)OICMalloc(ifreqsize);
- if (!previous)
+/**
+ * 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 CAParsingNetorkInfo(int idx, u_arraylist_t *iflist)
+{
+ if ((idx < 0) || (iflist == NULL))
{
- OIC_LOG(ERROR, TAG, "OICMalloc failed");
- return NULL;
+ return false;
}
- memcpy(previous, caglobals.ip.nm.ifItems, ifreqsize);
- size_t numprevious = caglobals.ip.nm.numIfItems;
-
- if (ifreqsize > caglobals.ip.nm.sizeIfItems)
+ struct ifaddrs *ifp = NULL;
+ CAResult_t ret = CAGetIfaddrsUsingNetlink(&ifp);
+ if (CA_STATUS_OK != ret)
{
-
- CAIfItem_t *items = (CAIfItem_t *)OICRealloc(caglobals.ip.nm.ifItems, ifreqsize);
- if (!items)
- {
- OIC_LOG(ERROR, TAG, "OICRealloc failed");
- OICFree(previous);
- return NULL;
- }
- caglobals.ip.nm.ifItems = items;
- caglobals.ip.nm.sizeIfItems = ifreqsize;
+ OIC_LOG_V(ERROR, TAG, "Failed to get ifaddrs err code is: %d", ret);
+ return false;
}
- caglobals.ip.nm.numIfItems = 0;
- for (size_t i = 0; i < interfaces; i++)
+ struct ifaddrs *ifa = NULL;
+ for (ifa = ifp; ifa; ifa = ifa->ifa_next)
{
- struct ifreq* item = &ifr[i];
- char *name = item->ifr_name;
-
- if (ioctl(s, SIOCGIFFLAGS, item) < 0)
- {
- OIC_LOG_V(ERROR, TAG, "SIOCGIFFLAGS failed: %s", strerror(errno));
- continue;
- }
- int16_t flags = item->ifr_flags;
- if ((flags & IFF_LOOPBACK) || !(flags & IFF_RUNNING))
+ if (!ifa->ifa_addr)
{
continue;
}
- if (ioctl(s, SIOCGIFINDEX, 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, "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)
+ int ifindex = if_nametoindex(ifa->ifa_name);
+ if (idx && (ifindex != idx))
{
- continue; // continue updating interface list
+ continue;
}
- // see if this interface didn't previously exist
- bool found = false;
- for (size_t j = 0; j < numprevious; j++)
+ char ipaddr[MAX_ADDR_STR_SIZE_CA] = {0};
+ if (family == AF_INET6)
{
- if (ifIndex == previous[j].ifIndex)
- {
- found = true;
- break;
- }
+ struct sockaddr_in6 *in6 = (struct sockaddr_in6*) ifa->ifa_addr;
+ inet_ntop(family, (void *)&(in6->sin6_addr), ipaddr, sizeof(ipaddr));
}
- if (found)
+ else if (family == AF_INET)
{
- OIC_LOG_V(INFO, TAG, "Interface found: %s", name);
- continue;
+ struct sockaddr_in *in = (struct sockaddr_in*) ifa->ifa_addr;
+ inet_ntop(family, (void *)&(in->sin_addr), ipaddr, sizeof(ipaddr));
}
- // Get address of network interface.
- char addr[MAX_ADDR_STR_SIZE_CA] = { 0 };
- struct sockaddr_in *sa = (struct sockaddr_in *)&item->ifr_addr;
- inet_ntop(AF_INET, (void *)&(sa->sin_addr), addr, sizeof(addr));
-
- foundNewInterface = CANewInterfaceItem(ifIndex, name, AF_INET, addr, flags);
- }
-
- OICFree(previous);
- // below code is temporary impl for consistency with caipserver.
- // TODO: whole code which using ioctl will be removed and changed with internal getifaddrs impl.
- if (foundNewInterface)
- {
- iflist = u_arraylist_create();
-
- if (!iflist)
+ if ((strcmp(ipaddr, IFC_ADDR_LOOP_IPV4) == 0) ||
+ (strcmp(ipaddr, IFC_ADDR_LOOP_IPV6) == 0) ||
+ (strcmp(ifa->ifa_name, IFC_LABEL_LOOP) == 0))
{
- OIC_LOG_V(ERROR, TAG, "Failed to create iflist: %s", strerror(errno));
- goto exit;
+ OIC_LOG(DEBUG, TAG, "LOOPBACK continue!!!");
+ continue;
}
- CAResult_t result = CAAddInterfaceItem(iflist,
- foundNewInterface->index,
- foundNewInterface->name,
- foundNewInterface->family,
- foundNewInterface->addr,
- foundNewInterface->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;
}
-
- // release foundNewInterface
- OICFree(foundNewInterface);
- foundNewInterface = NULL;
}
- return iflist;
+ CAFreeIfAddrs(ifp);
+ return true;
+
exit:
- OICFree(foundNewInterface);
- foundNewInterface = NULL;
- u_arraylist_destroy(iflist);
- return NULL;
+ CAFreeIfAddrs(ifp);
+ return false;
}
u_arraylist_t *CAIPGetInterfaceInformation(int desiredIndex)
return NULL;
}
- 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)
+ if (!CAParsingNetorkInfo(desiredIndex, iflist))
{
- OIC_LOG_V(ERROR, TAG, "SIOCGIFCONF failed: %s", strerror(errno));
- u_arraylist_destroy(iflist);
- return NULL;
+ goto exit;
}
- 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)
- {
- CAIfItem_t *items = (CAIfItem_t *)OICRealloc(caglobals.ip.nm.ifItems, ifreqsize);
- if (!items)
- {
- OIC_LOG(ERROR, TAG, "OICRealloc failed");
- goto exit;
- }
- caglobals.ip.nm.ifItems = items;
- caglobals.ip.nm.sizeIfItems = ifreqsize;
- }
-
- caglobals.ip.nm.numIfItems = 0;
- for (size_t i = 0; i < interfaces; i++)
- {
- struct ifreq* item = &ifr[i];
- char *name = item->ifr_name;
-
- if (ioctl(s, SIOCGIFFLAGS, item) < 0)
- {
- OIC_LOG_V(ERROR, TAG, "SIOCGIFFLAGS failed: %s", strerror(errno));
- continue;
- }
- int16_t flags = item->ifr_flags;
- if ((flags & IFF_LOOPBACK) || !(flags & IFF_RUNNING))
- {
- continue;
- }
- if (ioctl(s, SIOCGIFINDEX, item) < 0)
- {
- OIC_LOG_V(ERROR, TAG, "SIOCGIFINDEX failed: %s", strerror(errno));
- continue;
- }
-
- int ifindex = item->ifr_ifindex;
- caglobals.ip.nm.ifItems[i].ifIndex = ifindex;
- caglobals.ip.nm.numIfItems++;
-
- if (desiredIndex && (ifindex != desiredIndex))
- {
- continue;
- }
-
- // Get address of network interface.
- char addr[MAX_ADDR_STR_SIZE_CA] = { 0 };
- struct sockaddr_in *sa = (struct sockaddr_in *)&item->ifr_addr;
- inet_ntop(AF_INET, (void *)&(sa->sin_addr), addr, sizeof(addr));
-
- // Add IPv4 interface
- CAResult_t result = CAAddInterfaceItem(iflist, ifindex, name, AF_INET, addr, flags);
- if (CA_STATUS_OK != result)
- {
- goto exit;
- }
-
- // Add IPv6 interface
- result = CAAddInterfaceItem(iflist, ifindex, name, AF_INET6, addr, flags);
- if (CA_STATUS_OK != result)
- {
- goto exit;
- }
- }
return iflist;
exit:
OIC_LOG(DEBUG, TAG, "Wifi is in Activated State");
CAIPPassNetworkChangesToAdapter(CA_INTERFACE_UP);
+
+ // Apply network interface changes.
+ u_arraylist_t *iflist = CAIPGetInterfaceInformation(0);
+ if (!iflist)
+ {
+ OIC_LOG_V(ERROR, TAG, "get interface info failed: %s", strerror(errno));
+ return;
+ }
+
+ uint32_t listLength = u_arraylist_length(iflist);
+ for (uint32_t i = 0; i < listLength; i++)
+ {
+ CAInterface_t *ifitem = (CAInterface_t *)u_arraylist_get(iflist, i);
+ if (!ifitem)
+ {
+ continue;
+ }
+
+ CAProcessNewInterface(ifitem);
+
+ }
+ u_arraylist_destroy(iflist);
}
JNIEXPORT void JNICALL
--- /dev/null
+/******************************************************************
+*
+* Copyright 2014 Samsung Electronics All Rights Reserved.
+*
+*
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+******************************************************************/
+
+#include "ifaddrs.h"
+
+#include <stdbool.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <net/if_arp.h>
+#include <netinet/in.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include "logger.h"
+#define TAG "OIC_CA_ifaddrs"
+
+#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"
+
+typedef struct {
+ struct nlmsghdr msgInfo;
+ struct ifaddrmsg ifaddrInfo;
+} CANetlintReq_t;
+
+
+static bool CASendNetlinkMessage(int netlinkFd, const void* data, size_t len)
+{
+ ssize_t sentByteCount = TEMP_FAILURE_RETRY(send(netlinkFd, data, len, 0));
+ return (sentByteCount == (ssize_t)(len));
+}
+
+void CAFreeIfAddrs(struct ifaddrs *ifa)
+{
+ struct ifaddrs *cur;
+ while (ifa)
+ {
+ cur = ifa;
+ ifa = ifa->ifa_next;
+ free(cur);
+ }
+}
+
+static struct ifaddrs *CAParsingAddr(struct nlmsghdr *recvMsg)
+{
+ struct ifaddrmsg *ifaddrmsgData = (struct ifaddrmsg*)NLMSG_DATA(recvMsg);
+ if (ifaddrmsgData-> ifa_family != AF_INET && ifaddrmsgData-> ifa_family != AF_INET6)
+ {
+ return NULL;
+ }
+
+ struct rtattr *rtattrData = (struct rtattr*)IFA_RTA(ifaddrmsgData);
+ int ifaddrmsgLen = IFA_PAYLOAD(recvMsg);
+
+ struct ifaddrs *node = (struct ifaddrs *)OICCalloc(1, sizeof(struct ifaddrs));
+ char nameBuf[IFNAMSIZ] = { 0 };
+ node->ifa_next = NULL;
+ if_indextoname(ifaddrmsgData->ifa_index, nameBuf);
+ node->ifa_name = (char *)OICCalloc(strlen(nameBuf)+1, sizeof(char));
+ OICStrcpy(node->ifa_name, strlen(nameBuf)+1, nameBuf);
+ node->ifa_flags = ifaddrmsgData->ifa_flags;
+ node->ifa_flags |= (IFF_UP|IFF_RUNNING);
+ void *dest = NULL;
+ struct sockaddr_storage* ss = NULL;
+
+ for (; RTA_OK(rtattrData, ifaddrmsgLen); rtattrData = RTA_NEXT(rtattrData, ifaddrmsgLen))
+ {
+ switch (rtattrData->rta_type)
+ {
+ case IFA_ADDRESS:
+ ss = (struct sockaddr_storage*)OICCalloc(1, sizeof(struct sockaddr_storage));
+ ss->ss_family = ifaddrmsgData-> ifa_family;
+
+ if (ifaddrmsgData-> ifa_family == AF_INET)
+ {
+ dest = &((struct sockaddr_in*)ss)->sin_addr;
+ memcpy(dest, RTA_DATA(rtattrData), RTA_PAYLOAD(rtattrData));
+ }
+ else if (ifaddrmsgData-> ifa_family == AF_INET6)
+ {
+ dest = &((struct sockaddr_in6*)ss)->sin6_addr;
+ memcpy(dest, RTA_DATA(rtattrData), RTA_PAYLOAD(rtattrData));
+ }
+
+ node->ifa_addr = (struct sockaddr*)ss;
+ break;
+
+ default :
+ // do nothing
+ break;
+ }
+ }
+
+ return node;
+}
+
+CAResult_t CAGetIfaddrsUsingNetlink(struct ifaddrs **ifap)
+{
+ if (!ifap)
+ {
+ OIC_LOG(ERROR, TAG, "netlink argument error");
+ return CA_STATUS_INVALID_PARAM;
+ }
+ *ifap = NULL;
+
+ int netlinkFd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_ROUTE);
+ int state = -1;
+ if (-1 == netlinkFd)
+ {
+ OIC_LOG_V(ERROR, TAG, "netlink socket failed: %s", strerror(errno));
+ return CA_SOCKET_OPERATION_FAILED;
+ }
+
+ // send request to kernel
+ CANetlintReq_t req;
+ memset(&req, 0, sizeof(req));
+ req.msgInfo.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(req)));
+ req.msgInfo.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ req.msgInfo.nlmsg_type = RTM_GETADDR;
+ req.ifaddrInfo.ifa_family = AF_UNSPEC;
+ req.ifaddrInfo.ifa_index = 0;
+
+ if (!CASendNetlinkMessage(netlinkFd, &req, req.msgInfo.nlmsg_len))
+ {
+ OIC_LOG(ERROR, TAG, "netlink send failed");
+ goto exit;
+ }
+
+ while (1)
+ {
+ char recvBuf[NETLINK_MESSAGE_LENGTH] = {0};
+ int len = recv(netlinkFd, recvBuf, sizeof(recvBuf), 0);
+ struct nlmsghdr *recvMsg = (struct nlmsghdr*)recvBuf;
+ struct ifaddrs *node = NULL;
+ for (; NLMSG_OK(recvMsg, len); recvMsg = NLMSG_NEXT(recvMsg, len))
+ {
+ switch (recvMsg->nlmsg_type)
+ {
+ case NLMSG_DONE:
+ OIC_LOG(DEBUG, TAG, "NLMSG_DONE");
+ state = 0;
+ goto exit;
+
+ case NLMSG_ERROR:
+ OIC_LOG(ERROR, TAG, "NLMSG is invalid");
+ state = -1;
+ goto exit;
+
+ case RTM_NEWADDR:
+ OIC_LOG(DEBUG, TAG, "RTM_NEWADDR");
+ node = CAParsingAddr(recvMsg);
+
+ if (*ifap == NULL)
+ {
+ *ifap = node;
+ }
+ else
+ {
+ node->ifa_next = *ifap;
+ *ifap = node;
+ }
+
+ break;
+
+ case RTM_NEWLINK:
+ default:
+ OIC_LOG(DEBUG, TAG, "ignore unknown NLMSG");
+ break;
+ }
+ }
+ }
+
+
+exit:
+ // release all resources
+ close(netlinkFd);
+ if (state == -1)
+ {
+ CAFreeIfAddrs(*ifap);
+ return CA_SOCKET_OPERATION_FAILED;
+ }
+ return CA_STATUS_OK;
+}
\ No newline at end of file