| /* Determine protocol families for which interfaces exist. Linux version. |
| Copyright (C) 2003, 2006, 2007, 2008, 2010 Free Software Foundation, Inc. |
| This file is part of the GNU C Library. |
| |
| The GNU C Library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Lesser General Public |
| License as published by the Free Software Foundation; either |
| version 2.1 of the License, or (at your option) any later version. |
| |
| The GNU C Library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with the GNU C Library; if not, write to the Free |
| Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
| 02111-1307 USA. */ |
| |
| #include <assert.h> |
| #include <errno.h> |
| #include <ifaddrs.h> |
| #include <netdb.h> |
| #include <stddef.h> |
| #include <string.h> |
| #include <time.h> |
| #include <unistd.h> |
| #include <sys/socket.h> |
| |
| #include <asm/types.h> |
| #include <linux/netlink.h> |
| #include <linux/rtnetlink.h> |
| |
| #include <not-cancel.h> |
| #include <kernel-features.h> |
| |
| |
| #ifndef IFA_F_HOMEADDRESS |
| # define IFA_F_HOMEADDRESS 0 |
| #endif |
| #ifndef IFA_F_OPTIMISTIC |
| # define IFA_F_OPTIMISTIC 0 |
| #endif |
| |
| |
| static int |
| make_request (int fd, pid_t pid, bool *seen_ipv4, bool *seen_ipv6, |
| struct in6addrinfo **in6ai, size_t *in6ailen) |
| { |
| struct req |
| { |
| struct nlmsghdr nlh; |
| struct rtgenmsg g; |
| /* struct rtgenmsg consists of a single byte. This means there |
| are three bytes of padding included in the REQ definition. |
| We make them explicit here. */ |
| char pad[3]; |
| } req; |
| struct sockaddr_nl nladdr; |
| |
| req.nlh.nlmsg_len = sizeof (req); |
| req.nlh.nlmsg_type = RTM_GETADDR; |
| req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; |
| req.nlh.nlmsg_pid = 0; |
| req.nlh.nlmsg_seq = time (NULL); |
| req.g.rtgen_family = AF_UNSPEC; |
| |
| assert (sizeof (req) - offsetof (struct req, pad) == 3); |
| memset (req.pad, '\0', sizeof (req.pad)); |
| |
| memset (&nladdr, '\0', sizeof (nladdr)); |
| nladdr.nl_family = AF_NETLINK; |
| |
| #ifdef PAGE_SIZE |
| /* Help the compiler optimize out the malloc call if PAGE_SIZE |
| is constant and smaller or equal to PTHREAD_STACK_MIN/4. */ |
| const size_t buf_size = PAGE_SIZE; |
| #else |
| const size_t buf_size = __getpagesize (); |
| #endif |
| bool use_malloc = false; |
| char *buf; |
| |
| if (__libc_use_alloca (buf_size)) |
| buf = alloca (buf_size); |
| else |
| { |
| buf = malloc (buf_size); |
| if (buf != NULL) |
| use_malloc = true; |
| else |
| goto out_fail; |
| } |
| |
| struct iovec iov = { buf, buf_size }; |
| |
| if (TEMP_FAILURE_RETRY (__sendto (fd, (void *) &req, sizeof (req), 0, |
| (struct sockaddr *) &nladdr, |
| sizeof (nladdr))) < 0) |
| goto out_fail; |
| |
| *seen_ipv4 = false; |
| *seen_ipv6 = false; |
| |
| bool done = false; |
| struct in6ailist |
| { |
| struct in6addrinfo info; |
| struct in6ailist *next; |
| } *in6ailist = NULL; |
| size_t in6ailistlen = 0; |
| |
| do |
| { |
| struct msghdr msg = |
| { |
| (void *) &nladdr, sizeof (nladdr), |
| &iov, 1, |
| NULL, 0, |
| 0 |
| }; |
| |
| ssize_t read_len = TEMP_FAILURE_RETRY (__recvmsg (fd, &msg, 0)); |
| if (read_len < 0) |
| goto out_fail; |
| |
| if (msg.msg_flags & MSG_TRUNC) |
| goto out_fail; |
| |
| struct nlmsghdr *nlmh; |
| for (nlmh = (struct nlmsghdr *) buf; |
| NLMSG_OK (nlmh, (size_t) read_len); |
| nlmh = (struct nlmsghdr *) NLMSG_NEXT (nlmh, read_len)) |
| { |
| if (nladdr.nl_pid != 0 || (pid_t) nlmh->nlmsg_pid != pid |
| || nlmh->nlmsg_seq != req.nlh.nlmsg_seq) |
| continue; |
| |
| if (nlmh->nlmsg_type == RTM_NEWADDR) |
| { |
| struct ifaddrmsg *ifam = (struct ifaddrmsg *) NLMSG_DATA (nlmh); |
| struct rtattr *rta = IFA_RTA (ifam); |
| size_t len = nlmh->nlmsg_len - NLMSG_LENGTH (sizeof (*ifam)); |
| |
| if (ifam->ifa_family != AF_INET |
| && ifam->ifa_family != AF_INET6) |
| continue; |
| |
| const void *local = NULL; |
| const void *address = NULL; |
| while (RTA_OK (rta, len)) |
| { |
| switch (rta->rta_type) |
| { |
| case IFA_LOCAL: |
| local = RTA_DATA (rta); |
| break; |
| |
| case IFA_ADDRESS: |
| address = RTA_DATA (rta); |
| goto out; |
| } |
| |
| rta = RTA_NEXT (rta, len); |
| } |
| |
| if (local != NULL) |
| { |
| address = local; |
| out: |
| if (ifam->ifa_family == AF_INET) |
| { |
| if (*(const in_addr_t *) address |
| != htonl (INADDR_LOOPBACK)) |
| *seen_ipv4 = true; |
| } |
| else |
| { |
| if (!IN6_IS_ADDR_LOOPBACK (address)) |
| *seen_ipv6 = true; |
| } |
| } |
| |
| struct in6ailist *newp = alloca (sizeof (*newp)); |
| newp->info.flags = (((ifam->ifa_flags |
| & (IFA_F_DEPRECATED |
| | IFA_F_OPTIMISTIC)) |
| ? in6ai_deprecated : 0) |
| | ((ifam->ifa_flags |
| & IFA_F_HOMEADDRESS) |
| ? in6ai_homeaddress : 0)); |
| newp->info.prefixlen = ifam->ifa_prefixlen; |
| newp->info.index = ifam->ifa_index; |
| if (ifam->ifa_family == AF_INET) |
| { |
| newp->info.addr[0] = 0; |
| newp->info.addr[1] = 0; |
| newp->info.addr[2] = htonl (0xffff); |
| newp->info.addr[3] = *(const in_addr_t *) address; |
| } |
| else |
| memcpy (newp->info.addr, address, sizeof (newp->info.addr)); |
| newp->next = in6ailist; |
| in6ailist = newp; |
| ++in6ailistlen; |
| } |
| else if (nlmh->nlmsg_type == NLMSG_DONE) |
| /* We found the end, leave the loop. */ |
| done = true; |
| } |
| } |
| while (! done); |
| |
| close_not_cancel_no_status (fd); |
| |
| if (*seen_ipv6 && in6ailist != NULL) |
| { |
| *in6ai = malloc (in6ailistlen * sizeof (**in6ai)); |
| if (*in6ai == NULL) |
| goto out_fail; |
| |
| *in6ailen = in6ailistlen; |
| |
| do |
| { |
| (*in6ai)[--in6ailistlen] = in6ailist->info; |
| in6ailist = in6ailist->next; |
| } |
| while (in6ailist != NULL); |
| } |
| |
| if (use_malloc) |
| free (buf); |
| return 0; |
| |
| out_fail: |
| if (use_malloc) |
| free (buf); |
| return -1; |
| } |
| |
| |
| /* We don't know if we have NETLINK support compiled in in our |
| Kernel. */ |
| #if __ASSUME_NETLINK_SUPPORT == 0 |
| /* Define in ifaddrs.h. */ |
| extern int __no_netlink_support attribute_hidden; |
| #else |
| # define __no_netlink_support 0 |
| #endif |
| |
| |
| void |
| attribute_hidden |
| __check_pf (bool *seen_ipv4, bool *seen_ipv6, |
| struct in6addrinfo **in6ai, size_t *in6ailen) |
| { |
| *in6ai = NULL; |
| *in6ailen = 0; |
| |
| if (! __no_netlink_support) |
| { |
| int fd = __socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); |
| |
| struct sockaddr_nl nladdr; |
| memset (&nladdr, '\0', sizeof (nladdr)); |
| nladdr.nl_family = AF_NETLINK; |
| |
| socklen_t addr_len = sizeof (nladdr); |
| |
| if (fd >= 0 |
| && __bind (fd, (struct sockaddr *) &nladdr, sizeof (nladdr)) == 0 |
| && __getsockname (fd, (struct sockaddr *) &nladdr, &addr_len) == 0 |
| && make_request (fd, nladdr.nl_pid, seen_ipv4, seen_ipv6, |
| in6ai, in6ailen) == 0) |
| /* It worked. */ |
| return; |
| |
| if (fd >= 0) |
| __close (fd); |
| |
| #if __ASSUME_NETLINK_SUPPORT == 0 |
| /* Remember that there is no netlink support. */ |
| __no_netlink_support = 1; |
| #else |
| /* We cannot determine what interfaces are available. Be |
| pessimistic. */ |
| *seen_ipv4 = true; |
| *seen_ipv6 = true; |
| #endif |
| } |
| |
| #if __ASSUME_NETLINK_SUPPORT == 0 |
| /* No netlink. Get the interface list via getifaddrs. */ |
| struct ifaddrs *ifa = NULL; |
| if (getifaddrs (&ifa) != 0) |
| { |
| /* We cannot determine what interfaces are available. Be |
| pessimistic. */ |
| *seen_ipv4 = true; |
| *seen_ipv6 = true; |
| return; |
| } |
| |
| struct ifaddrs *runp; |
| for (runp = ifa; runp != NULL; runp = runp->ifa_next) |
| if (runp->ifa_addr != NULL) |
| { |
| if (runp->ifa_addr->sa_family == PF_INET) |
| *seen_ipv4 = true; |
| else if (runp->ifa_addr->sa_family == PF_INET6) |
| *seen_ipv6 = true; |
| } |
| |
| (void) freeifaddrs (ifa); |
| #endif |
| } |