2 Copyright (c) 2013, Kenneth MacKay
5 Redistribution and use in source and binary forms, with or without modification,
6 are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright notice, this
8 list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright notice,
10 this list of conditions and the following disclaimer in the documentation
11 and/or other materials provided with the distribution.
13 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
17 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 #include <compat/ifaddrs.h>
32 #include <sys/socket.h>
33 #include <netpacket/packet.h>
34 #include <net/if_arp.h>
35 #include <netinet/in.h>
36 #include <linux/netlink.h>
37 #include <linux/rtnetlink.h>
39 typedef struct NetlinkList
41 struct NetlinkList *m_next;
42 struct nlmsghdr *m_data;
46 static int netlink_socket(void)
48 struct sockaddr_nl l_addr;
49 int l_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
54 memset(&l_addr, 0, sizeof(l_addr));
55 l_addr.nl_family = AF_NETLINK;
57 if (bind(l_socket, (struct sockaddr *)&l_addr, sizeof(l_addr)) < 0)
66 static int netlink_send(int p_socket, int p_request)
70 struct nlmsghdr m_hdr;
71 struct rtgenmsg m_msg;
73 struct sockaddr_nl l_addr;
75 memset(&l_data, 0, sizeof(l_data));
77 l_data.m_hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
78 l_data.m_hdr.nlmsg_type = p_request;
79 l_data.m_hdr.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
80 l_data.m_hdr.nlmsg_pid = 0;
81 l_data.m_hdr.nlmsg_seq = p_socket;
82 l_data.m_msg.rtgen_family = AF_UNSPEC;
84 memset(&l_addr, 0, sizeof(l_addr));
85 l_addr.nl_family = AF_NETLINK;
86 return (sendto(p_socket, &l_data.m_hdr, l_data.m_hdr.nlmsg_len, 0, (struct sockaddr *)&l_addr, sizeof(l_addr)));
89 static int netlink_recv(int p_socket, void *p_buffer, size_t p_len)
92 struct iovec l_iov = { p_buffer, p_len };
93 struct sockaddr_nl l_addr;
99 l_msg.msg_name = (void *)&l_addr;
100 l_msg.msg_namelen = sizeof(l_addr);
101 l_msg.msg_iov = &l_iov;
102 l_msg.msg_iovlen = 1;
103 l_msg.msg_control = NULL;
104 l_msg.msg_controllen = 0;
107 l_result = recvmsg(p_socket, &l_msg, 0);
116 if (l_msg.msg_flags & MSG_TRUNC) /* buffer too small */
122 static struct nlmsghdr *getNetlinkResponse(int p_socket,
123 int *p_size, int *p_done)
125 size_t l_size = 4096;
126 void *l_buffer = NULL;
133 l_buffer = malloc(l_size);
137 l_read = netlink_recv(p_socket, l_buffer, l_size);
148 pid_t l_pid = getpid();
149 struct nlmsghdr *l_hdr;
151 for (l_hdr = (struct nlmsghdr *)l_buffer;
152 NLMSG_OK(l_hdr, (unsigned int)l_read);
153 l_hdr = (struct nlmsghdr *)NLMSG_NEXT(l_hdr, l_read))
155 if ( (pid_t)l_hdr->nlmsg_pid != l_pid ||
156 (int)l_hdr->nlmsg_seq != p_socket)
159 if (l_hdr->nlmsg_type == NLMSG_DONE)
165 if (l_hdr->nlmsg_type == NLMSG_ERROR)
178 static NetlinkList *newListItem(struct nlmsghdr *p_data, unsigned int p_size)
180 NetlinkList *l_item = (NetlinkList*)malloc(sizeof(NetlinkList));
184 l_item->m_next = NULL;
185 l_item->m_data = p_data;
186 l_item->m_size = p_size;
190 static void freeResultList(NetlinkList *p_list)
197 p_list = p_list->m_next;
203 static NetlinkList *getResultList(int p_socket, int p_request)
206 NetlinkList *l_list = NULL;
207 NetlinkList *l_end = NULL;
210 if (netlink_send(p_socket, p_request) < 0)
215 NetlinkList *l_item = NULL;
216 struct nlmsghdr *l_hdr = getNetlinkResponse(p_socket, &l_size, &l_done);
220 l_item = newListItem(l_hdr, l_size);
227 l_end->m_next = l_item;
234 freeResultList(l_list);
238 static size_t maxSize(size_t a, size_t b)
240 return (a > b ? a : b);
243 static size_t calcAddrLen(sa_family_t p_family, int p_dataSize)
248 return sizeof(struct sockaddr_in);
250 return sizeof(struct sockaddr_in6);
252 return maxSize(sizeof(struct sockaddr_ll), offsetof(struct sockaddr_ll, sll_addr) + p_dataSize);
257 return maxSize(sizeof(struct sockaddr), offsetof(struct sockaddr, sa_data) + p_dataSize);
260 static void makeSockaddr(sa_family_t p_family, struct sockaddr *p_dest, void *p_data, size_t p_size)
265 memcpy(&((struct sockaddr_in*)p_dest)->sin_addr, p_data, p_size);
268 memcpy(&((struct sockaddr_in6*)p_dest)->sin6_addr, p_data, p_size);
271 memcpy(((struct sockaddr_ll*)p_dest)->sll_addr, p_data, p_size);
272 ((struct sockaddr_ll*)p_dest)->sll_halen = p_size;
275 memcpy(p_dest->sa_data, p_data, p_size);
278 p_dest->sa_family = p_family;
281 static void addToEnd(struct ifaddrs **p_resultList, struct ifaddrs *p_entry)
284 *p_resultList = p_entry;
287 struct ifaddrs *l_cur = *p_resultList;
288 while (l_cur->ifa_next)
289 l_cur = l_cur->ifa_next;
290 l_cur->ifa_next = p_entry;
294 static int interpretLink(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList)
296 struct ifaddrs *l_entry = NULL;
297 struct rtattr *l_rta = NULL;
298 struct ifinfomsg *l_info = (struct ifinfomsg *)NLMSG_DATA(p_hdr);
299 size_t l_nameSize = 0;
300 size_t l_addrSize = 0;
301 size_t l_dataSize = 0;
302 size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg));
304 for (l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize);
305 l_rta = RTA_NEXT(l_rta, l_rtaSize))
307 size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
308 switch(l_rta->rta_type)
312 l_addrSize += NLMSG_ALIGN(calcAddrLen(AF_PACKET, l_rtaDataSize));
315 l_nameSize += NLMSG_ALIGN(l_rtaSize + 1);
318 l_dataSize += NLMSG_ALIGN(l_rtaSize);
325 l_entry = (struct ifaddrs*)malloc(sizeof(struct ifaddrs) + sizeof(int) + l_nameSize + l_addrSize + l_dataSize);
329 memset(l_entry, 0, sizeof(struct ifaddrs));
330 l_entry->ifa_name = "";
332 char *l_index = ((char *)l_entry) + sizeof(struct ifaddrs);
333 char *l_name = l_index + sizeof(int);
334 char *l_addr = l_name + l_nameSize;
335 char *l_data = l_addr + l_addrSize;
337 /* save the interface index so we can look
338 * it up when handling the addresses. */
339 memcpy(l_index, &l_info->ifi_index, sizeof(int));
341 l_entry->ifa_flags = l_info->ifi_flags;
343 l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg));
345 for (l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize);
346 l_rta = RTA_NEXT(l_rta, l_rtaSize))
348 void *l_rtaData = RTA_DATA(l_rta);
349 size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
351 switch(l_rta->rta_type)
356 size_t l_addrLen = calcAddrLen(AF_PACKET, l_rtaDataSize);
357 makeSockaddr(AF_PACKET, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize);
358 ((struct sockaddr_ll *)l_addr)->sll_ifindex = l_info->ifi_index;
359 ((struct sockaddr_ll *)l_addr)->sll_hatype = l_info->ifi_type;
360 if (l_rta->rta_type == IFLA_ADDRESS)
361 l_entry->ifa_addr = (struct sockaddr *)l_addr;
363 l_entry->ifa_broadaddr = (struct sockaddr *)l_addr;
364 l_addr += NLMSG_ALIGN(l_addrLen);
368 strncpy(l_name, l_rtaData, l_rtaDataSize);
369 l_name[l_rtaDataSize] = '\0';
370 l_entry->ifa_name = l_name;
373 memcpy(l_data, l_rtaData, l_rtaDataSize);
374 l_entry->ifa_data = l_data;
381 addToEnd(p_resultList, l_entry);
385 static struct ifaddrs *findInterface(int p_index,
386 struct ifaddrs **p_links, int p_numLinks)
389 struct ifaddrs *l_cur = *p_links;
391 while (l_cur && l_num < p_numLinks)
394 char *l_indexPtr = ((char *)l_cur) + sizeof(struct ifaddrs);
396 memcpy(&l_index, l_indexPtr, sizeof(int));
397 if (l_index == p_index)
400 l_cur = l_cur->ifa_next;
406 static int interpretAddr(struct nlmsghdr *p_hdr,
407 struct ifaddrs **p_resultList, int p_numLinks)
409 struct rtattr *l_rta;
411 size_t l_nameSize = 0;
412 size_t l_addrSize = 0;
413 int l_addedNetmask = 0;
414 struct ifaddrmsg *l_info = (struct ifaddrmsg *)NLMSG_DATA(p_hdr);
415 struct ifaddrs *l_interface = findInterface(l_info->ifa_index, p_resultList, p_numLinks);
417 if (l_info->ifa_family == AF_PACKET)
420 l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg));
422 for (l_rta = IFA_RTA(l_info); RTA_OK(l_rta, l_rtaSize);
423 l_rta = RTA_NEXT(l_rta, l_rtaSize))
425 size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
427 switch(l_rta->rta_type)
431 if ((l_info->ifa_family == AF_INET || l_info->ifa_family == AF_INET6) && !l_addedNetmask)
433 /* make room for netmask */
434 l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize));
438 l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize));
441 l_nameSize += NLMSG_ALIGN(l_rtaSize + 1);
448 struct ifaddrs *l_entry = (struct ifaddrs*)malloc(sizeof(struct ifaddrs) + l_nameSize + l_addrSize);
452 memset(l_entry, 0, sizeof(struct ifaddrs));
453 l_entry->ifa_name = (l_interface ? l_interface->ifa_name : "");
455 char *l_name = ((char *)l_entry) + sizeof(struct ifaddrs);
456 char *l_addr = l_name + l_nameSize;
458 l_entry->ifa_flags = l_info->ifa_flags;
460 l_entry->ifa_flags |= l_interface->ifa_flags;
462 l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg));
463 for (l_rta = IFA_RTA(l_info); RTA_OK(l_rta, l_rtaSize);
464 l_rta = RTA_NEXT(l_rta, l_rtaSize))
466 void *l_rtaData = RTA_DATA(l_rta);
467 size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
468 switch(l_rta->rta_type)
474 size_t l_addrLen = calcAddrLen(l_info->ifa_family, l_rtaDataSize);
475 makeSockaddr(l_info->ifa_family, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize);
476 if (l_info->ifa_family == AF_INET6)
478 if (IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)l_rtaData) || IN6_IS_ADDR_MC_LINKLOCAL((struct in6_addr *)l_rtaData))
479 ((struct sockaddr_in6 *)l_addr)->sin6_scope_id = l_info->ifa_index;
482 if (l_rta->rta_type == IFA_ADDRESS)
484 /* apparently in a point-to-point network IFA_ADDRESS
485 * contains the dest address and IFA_LOCAL contains the local address */
486 if (l_entry->ifa_addr)
487 l_entry->ifa_dstaddr = (struct sockaddr *)l_addr;
489 l_entry->ifa_addr = (struct sockaddr *)l_addr;
491 else if (l_rta->rta_type == IFA_LOCAL)
493 if (l_entry->ifa_addr)
494 l_entry->ifa_dstaddr = l_entry->ifa_addr;
495 l_entry->ifa_addr = (struct sockaddr *)l_addr;
498 l_entry->ifa_broadaddr = (struct sockaddr *)l_addr;
499 l_addr += NLMSG_ALIGN(l_addrLen);
503 strncpy(l_name, l_rtaData, l_rtaDataSize);
504 l_name[l_rtaDataSize] = '\0';
505 l_entry->ifa_name = l_name;
512 if (l_entry->ifa_addr &&
513 ( l_entry->ifa_addr->sa_family == AF_INET
514 || l_entry->ifa_addr->sa_family == AF_INET6))
518 unsigned l_maxPrefix = (l_entry->ifa_addr->sa_family == AF_INET
520 unsigned l_prefix = (l_info->ifa_prefixlen > l_maxPrefix
521 ? l_maxPrefix : l_info->ifa_prefixlen);
525 for (i=0; i<(l_prefix/8); ++i)
528 l_mask[i] = 0xff << (8 - (l_prefix % 8));
530 makeSockaddr(l_entry->ifa_addr->sa_family,
531 (struct sockaddr *)l_addr, l_mask, l_maxPrefix / 8);
532 l_entry->ifa_netmask = (struct sockaddr *)l_addr;
535 addToEnd(p_resultList, l_entry);
539 static int interpretLinks(int p_socket, NetlinkList *p_netlinkList,
540 struct ifaddrs **p_resultList)
543 pid_t l_pid = getpid();
545 for (; p_netlinkList; p_netlinkList = p_netlinkList->m_next)
547 struct nlmsghdr *l_hdr = NULL;
548 unsigned int l_nlsize = p_netlinkList->m_size;
550 for (l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize);
551 l_hdr = NLMSG_NEXT(l_hdr, l_nlsize))
553 if ( (pid_t)l_hdr->nlmsg_pid != l_pid ||
554 (int)l_hdr->nlmsg_seq != p_socket)
557 if (l_hdr->nlmsg_type == NLMSG_DONE)
560 if (l_hdr->nlmsg_type == RTM_NEWLINK)
562 if (interpretLink(l_hdr, p_resultList) == -1)
571 static int interpretAddrs(int p_socket, NetlinkList *p_netlinkList,
572 struct ifaddrs **p_resultList, int p_numLinks)
574 pid_t l_pid = getpid();
575 for (; p_netlinkList; p_netlinkList = p_netlinkList->m_next)
577 struct nlmsghdr *l_hdr = NULL;
578 unsigned int l_nlsize = p_netlinkList->m_size;
580 for (l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize);
581 l_hdr = NLMSG_NEXT(l_hdr, l_nlsize))
583 if ( (pid_t)l_hdr->nlmsg_pid != l_pid
584 || (int)l_hdr->nlmsg_seq != p_socket)
587 if (l_hdr->nlmsg_type == NLMSG_DONE)
590 if (l_hdr->nlmsg_type == RTM_NEWADDR)
592 if (interpretAddr(l_hdr, p_resultList, p_numLinks) == -1)
600 int getifaddrs(struct ifaddrs **ifap)
602 NetlinkList *l_linkResults;
603 NetlinkList *l_addrResults;
612 l_socket = netlink_socket();
617 l_linkResults = getResultList(l_socket, RTM_GETLINK);
624 l_addrResults = getResultList(l_socket, RTM_GETADDR);
628 freeResultList(l_linkResults);
632 l_numLinks = interpretLinks(l_socket, l_linkResults, ifap);
634 if ( l_numLinks == -1 ||
635 interpretAddrs(l_socket, l_addrResults, ifap, l_numLinks) == -1)
638 freeResultList(l_linkResults);
639 freeResultList(l_addrResults);
644 void freeifaddrs(struct ifaddrs *ifa)
646 struct ifaddrs *l_cur = NULL;