ifaddrs.c revision 4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6b
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane GraberCopyright (c) 2013, Kenneth MacKay
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane GraberAll rights reserved.
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane GraberRedistribution and use in source and binary forms, with or without modification,
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graberare permitted provided that the following conditions are met:
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber * Redistributions of source code must retain the above copyright notice, this
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber list of conditions and the following disclaimer.
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber * Redistributions in binary form must reproduce the above copyright notice,
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber this list of conditions and the following disclaimer in the documentation
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber and/or other materials provided with the distribution.
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane GraberTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane GraberANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane GraberWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane GraberDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane GraberANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane GraberLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane GraberANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane GraberSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber int l_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber if(bind(l_socket, (struct sockaddr *)&l_addr, sizeof(l_addr)) < 0)
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graberstatic int netlink_send(int p_socket, int p_request)
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber char l_buffer[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + NLMSG_ALIGN(sizeof(struct rtgenmsg))];
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber struct nlmsghdr *l_hdr = (struct nlmsghdr *)l_buffer;
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber struct rtgenmsg *l_msg = (struct rtgenmsg *)NLMSG_DATA(l_hdr);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber l_hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*l_msg));
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber l_hdr->nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber return (sendto(p_socket, l_hdr, l_hdr->nlmsg_len, 0, (struct sockaddr *)&l_addr, sizeof(l_addr)));
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graberstatic int netlink_recv(int p_socket, void *p_buffer, size_t p_len)
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber int l_result = recvmsg(p_socket, &l_msg, 0);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber { // buffer was too small
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graberstatic struct nlmsghdr *getNetlinkResponse(int p_socket, int *p_size, int *p_done)
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber int l_read = netlink_recv(p_socket, l_buffer, l_size);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber for(l_hdr = (struct nlmsghdr *)l_buffer; NLMSG_OK(l_hdr, (unsigned int)l_read); l_hdr = (struct nlmsghdr *)NLMSG_NEXT(l_hdr, l_read))
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket)
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graberstatic NetlinkList *newListItem(struct nlmsghdr *p_data, unsigned int p_size)
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber NetlinkList *l_item = malloc(sizeof(NetlinkList));
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graberstatic void freeResultList(NetlinkList *p_list)
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graberstatic NetlinkList *getResultList(int p_socket, int p_request)
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber struct nlmsghdr *l_hdr = getNetlinkResponse(p_socket, &l_size, &l_done);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber NetlinkList *l_item = newListItem(l_hdr, l_size);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber return (a > b ? a : b);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graberstatic size_t calcAddrLen(sa_family_t p_family, int p_dataSize)
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber return sizeof(struct sockaddr_in);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber return sizeof(struct sockaddr_in6);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber return maxSize(sizeof(struct sockaddr_ll), offsetof(struct sockaddr_ll, sll_addr) + p_dataSize);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber return maxSize(sizeof(struct sockaddr), offsetof(struct sockaddr, sa_data) + p_dataSize);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graberstatic void makeSockaddr(sa_family_t p_family, struct sockaddr *p_dest, void *p_data, size_t p_size)
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber memcpy(&((struct sockaddr_in*)p_dest)->sin_addr, p_data, p_size);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber memcpy(&((struct sockaddr_in6*)p_dest)->sin6_addr, p_data, p_size);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber memcpy(((struct sockaddr_ll*)p_dest)->sll_addr, p_data, p_size);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber ((struct sockaddr_ll*)p_dest)->sll_halen = p_size;
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graberstatic void addToEnd(struct ifaddrs **p_resultList, struct ifaddrs *p_entry)
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graberstatic void interpretLink(struct nlmsghdr *p_hdr, struct ifaddrs **p_links, struct ifaddrs **p_resultList)
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber struct ifinfomsg *l_info = (struct ifinfomsg *)NLMSG_DATA(p_hdr);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg));
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber for(l_rta = (struct rtattr *)(((char *)l_info) + NLMSG_ALIGN(sizeof(struct ifinfomsg))); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber l_addrSize += NLMSG_ALIGN(calcAddrLen(AF_PACKET, l_rtaDataSize));
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber struct ifaddrs *l_entry = malloc(sizeof(struct ifaddrs) + l_nameSize + l_addrSize + l_dataSize);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber char *l_name = ((char *)l_entry) + sizeof(struct ifaddrs);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg));
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber for(l_rta = (struct rtattr *)(((char *)l_info) + NLMSG_ALIGN(sizeof(struct ifinfomsg))); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber size_t l_addrLen = calcAddrLen(AF_PACKET, l_rtaDataSize);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber makeSockaddr(AF_PACKET, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber ((struct sockaddr_ll *)l_addr)->sll_ifindex = l_info->ifi_index;
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber ((struct sockaddr_ll *)l_addr)->sll_hatype = l_info->ifi_type;
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber l_entry->ifa_addr = (struct sockaddr *)l_addr;
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber l_entry->ifa_broadaddr = (struct sockaddr *)l_addr;
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graberstatic void interpretAddr(struct nlmsghdr *p_hdr, struct ifaddrs **p_links, struct ifaddrs **p_resultList)
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber struct ifaddrmsg *l_info = (struct ifaddrmsg *)NLMSG_DATA(p_hdr);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg));
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber for(l_rta = (struct rtattr *)(((char *)l_info) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber if((l_info->ifa_family == AF_INET || l_info->ifa_family == AF_INET6) && !l_addedNetmask)
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber { // make room for netmask
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize));
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize));
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber struct ifaddrs *l_entry = malloc(sizeof(struct ifaddrs) + l_nameSize + l_addrSize);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber l_entry->ifa_name = p_links[l_info->ifa_index - 1]->ifa_name;
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber char *l_name = ((char *)l_entry) + sizeof(struct ifaddrs);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber l_entry->ifa_flags = l_info->ifa_flags | p_links[l_info->ifa_index - 1]->ifa_flags;
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg));
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber for(l_rta = (struct rtattr *)(((char *)l_info) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber size_t l_addrLen = calcAddrLen(l_info->ifa_family, l_rtaDataSize);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber makeSockaddr(l_info->ifa_family, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber if(IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)l_rtaData) || IN6_IS_ADDR_MC_LINKLOCAL((struct in6_addr *)l_rtaData))
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber ((struct sockaddr_in6 *)l_addr)->sin6_scope_id = l_info->ifa_index;
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber { // apparently in a point-to-point network IFA_ADDRESS contains the dest address and IFA_LOCAL contains the local address
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber l_entry->ifa_dstaddr = (struct sockaddr *)l_addr;
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber l_entry->ifa_addr = (struct sockaddr *)l_addr;
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber l_entry->ifa_addr = (struct sockaddr *)l_addr;
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber l_entry->ifa_broadaddr = (struct sockaddr *)l_addr;
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber if(l_entry->ifa_addr && (l_entry->ifa_addr->sa_family == AF_INET || l_entry->ifa_addr->sa_family == AF_INET6))
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber unsigned l_maxPrefix = (l_entry->ifa_addr->sa_family == AF_INET ? 32 : 128);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber unsigned l_prefix = (l_info->ifa_prefixlen > l_maxPrefix ? l_maxPrefix : l_info->ifa_prefixlen);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber makeSockaddr(l_entry->ifa_addr->sa_family, (struct sockaddr *)l_addr, l_mask, l_maxPrefix / 8);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber l_entry->ifa_netmask = (struct sockaddr *)l_addr;
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graberstatic void interpret(int p_socket, NetlinkList *p_netlinkList, struct ifaddrs **p_links, struct ifaddrs **p_resultList)
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next)
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber unsigned int l_nlsize = p_netlinkList->m_size;
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber for(l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize))
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket)
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber interpretLink(l_hdr, p_links, p_resultList);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber interpretAddr(l_hdr, p_links, p_resultList);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graberstatic unsigned countLinks(int p_socket, NetlinkList *p_netlinkList)
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next)
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber unsigned int l_nlsize = p_netlinkList->m_size;
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber for(l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize))
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket)
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber NetlinkList *l_linkResults = getResultList(l_socket, RTM_GETLINK);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber NetlinkList *l_addrResults = getResultList(l_socket, RTM_GETADDR);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber unsigned l_numLinks = countLinks(l_socket, l_linkResults) + countLinks(l_socket, l_addrResults);
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber memset(l_links, 0, l_numLinks * sizeof(struct ifaddrs *));
4ba0d9af63fbf7e9acfa068a1fe36b3d287b9c6bStéphane Graber interpret(l_socket, l_linkResults, l_links, ifap);