diff options
Diffstat (limited to 'REORG.TODO/sysdeps/unix/sysv/linux/check_pf.c')
-rw-r--r-- | REORG.TODO/sysdeps/unix/sysv/linux/check_pf.c | 391 |
1 files changed, 391 insertions, 0 deletions
diff --git a/REORG.TODO/sysdeps/unix/sysv/linux/check_pf.c b/REORG.TODO/sysdeps/unix/sysv/linux/check_pf.c new file mode 100644 index 0000000000..3bf221a177 --- /dev/null +++ b/REORG.TODO/sysdeps/unix/sysv/linux/check_pf.c @@ -0,0 +1,391 @@ +/* Determine protocol families for which interfaces exist. Linux version. + Copyright (C) 2003-2017 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, see + <http://www.gnu.org/licenses/>. */ + +#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 <stdint.h> +#include <sys/socket.h> + +#include <asm/types.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> + +#include <not-cancel.h> +#include <libc-lock.h> +#include <atomic.h> +#include <nscd/nscd-client.h> + +#include "netlinkaccess.h" + +#ifndef IFA_F_HOMEADDRESS +# define IFA_F_HOMEADDRESS 0 +#endif +#ifndef IFA_F_OPTIMISTIC +# define IFA_F_OPTIMISTIC 0 +#endif + + +struct cached_data +{ + uint32_t timestamp; + uint32_t usecnt; + bool seen_ipv4; + bool seen_ipv6; + size_t in6ailen; + struct in6addrinfo in6ai[0]; +}; + +static struct cached_data noai6ai_cached = + { + .usecnt = 1, /* Make sure we never try to delete this entry. */ + .in6ailen = 0 + }; + +static struct cached_data *cache; +__libc_lock_define_initialized (static, lock); + + +#if IS_IN (nscd) +static uint32_t nl_timestamp; + +uint32_t +__bump_nl_timestamp (void) +{ + if (atomic_increment_val (&nl_timestamp) == 0) + atomic_increment (&nl_timestamp); + + return nl_timestamp; +} +#endif + +static inline uint32_t +get_nl_timestamp (void) +{ +#if IS_IN (nscd) + return nl_timestamp; +#elif defined USE_NSCD + return __nscd_get_nl_timestamp (); +#else + return 0; +#endif +} + +static inline bool +cache_valid_p (void) +{ + if (cache != NULL) + { + uint32_t timestamp = get_nl_timestamp (); + return timestamp != 0 && cache->timestamp == timestamp; + } + return false; +} + + +static struct cached_data * +make_request (int fd, pid_t pid) +{ + struct cached_data *result = NULL; + + size_t result_len = 0; + size_t result_cap = 32; + + 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 + const size_t buf_size = PAGE_SIZE; +#else + const size_t buf_size = 4096; +#endif + char buf[buf_size]; + + 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; + + bool done = false; + + bool seen_ipv4 = false; + bool seen_ipv6 = false; + + do + { + struct msghdr msg = + { + .msg_name = (void *) &nladdr, + .msg_namelen = sizeof (nladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = NULL, + .msg_controllen = 0, + .msg_flags = 0 + }; + + ssize_t read_len = TEMP_FAILURE_RETRY (__recvmsg (fd, &msg, 0)); + __netlink_assert_response (fd, read_len); + 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; + } + } + + if (result_len == 0 || result_len == result_cap) + { + result_cap = 2 * result_cap; + result = realloc (result, sizeof (*result) + + result_cap + * sizeof (struct in6addrinfo)); + } + + if (!result) + goto out_fail; + + struct in6addrinfo *info = &result->in6ai[result_len++]; + + info->flags = (((ifam->ifa_flags + & (IFA_F_DEPRECATED | IFA_F_OPTIMISTIC)) + ? in6ai_deprecated : 0) + | ((ifam->ifa_flags & IFA_F_HOMEADDRESS) + ? in6ai_homeaddress : 0)); + info->prefixlen = ifam->ifa_prefixlen; + info->index = ifam->ifa_index; + if (ifam->ifa_family == AF_INET) + { + info->addr[0] = 0; + info->addr[1] = 0; + info->addr[2] = htonl (0xffff); + info->addr[3] = *(const in_addr_t *) address; + } + else + memcpy (info->addr, address, sizeof (info->addr)); + } + else if (nlmh->nlmsg_type == NLMSG_DONE) + /* We found the end, leave the loop. */ + done = true; + } + } + while (! done); + + if (seen_ipv6 && result != NULL) + { + result->timestamp = get_nl_timestamp (); + result->usecnt = 2; + result->seen_ipv4 = seen_ipv4; + result->seen_ipv6 = true; + result->in6ailen = result_len; + } + else + { + free (result); + + atomic_add (&noai6ai_cached.usecnt, 2); + noai6ai_cached.seen_ipv4 = seen_ipv4; + noai6ai_cached.seen_ipv6 = seen_ipv6; + result = &noai6ai_cached; + } + + return result; + + out_fail: + + free (result); + return NULL; +} + + +void +attribute_hidden +__check_pf (bool *seen_ipv4, bool *seen_ipv6, + struct in6addrinfo **in6ai, size_t *in6ailen) +{ + *in6ai = NULL; + *in6ailen = 0; + + struct cached_data *olddata = NULL; + struct cached_data *data = NULL; + + __libc_lock_lock (lock); + + if (cache_valid_p ()) + { + data = cache; + atomic_increment (&cache->usecnt); + } + else + { + int fd = __socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + + if (__glibc_likely (fd >= 0)) + { + struct sockaddr_nl nladdr; + memset (&nladdr, '\0', sizeof (nladdr)); + nladdr.nl_family = AF_NETLINK; + + socklen_t addr_len = sizeof (nladdr); + + if (__bind (fd, (struct sockaddr *) &nladdr, sizeof (nladdr)) == 0 + && __getsockname (fd, (struct sockaddr *) &nladdr, + &addr_len) == 0) + data = make_request (fd, nladdr.nl_pid); + + close_not_cancel_no_status (fd); + } + + if (data != NULL) + { + olddata = cache; + cache = data; + } + } + + __libc_lock_unlock (lock); + + if (data != NULL) + { + /* It worked. */ + *seen_ipv4 = data->seen_ipv4; + *seen_ipv6 = data->seen_ipv6; + *in6ailen = data->in6ailen; + *in6ai = data->in6ai; + + if (olddata != NULL && olddata->usecnt > 0 + && atomic_add_zero (&olddata->usecnt, -1)) + free (olddata); + + return; + } + + /* We cannot determine what interfaces are available. Be + pessimistic. */ + *seen_ipv4 = true; + *seen_ipv6 = true; +} + +/* Free the cache if it has been allocated. */ +libc_freeres_fn (freecache) +{ + if (cache) + __free_in6ai (cache->in6ai); +} + +void +__free_in6ai (struct in6addrinfo *ai) +{ + if (ai != NULL) + { + struct cached_data *data = + (struct cached_data *) ((char *) ai + - offsetof (struct cached_data, in6ai)); + + if (atomic_add_zero (&data->usecnt, -1)) + { + __libc_lock_lock (lock); + + if (data->usecnt == 0) + /* Still unused. */ + free (data); + + __libc_lock_unlock (lock); + } + } +} |