diff options
-rw-r--r-- | extra/Configs/Config.in | 14 | ||||
-rw-r--r-- | libc/inet/Makefile.in | 2 | ||||
-rw-r--r-- | libc/inet/if_index.c | 133 | ||||
-rw-r--r-- | libc/inet/ifaddrs.c | 873 | ||||
-rw-r--r-- | libc/inet/netlinkaccess.h | 69 |
5 files changed, 1088 insertions, 3 deletions
diff --git a/extra/Configs/Config.in b/extra/Configs/Config.in index d110a19c7..61402c0f8 100644 --- a/extra/Configs/Config.in +++ b/extra/Configs/Config.in @@ -597,6 +597,20 @@ config UCLIBC_HAS_REENTRANT_RPC help Most packages utilize the normal (non-reentrant) RPC functions, but some (like exportfs from nfs-utils) need these reentrant versions. + + Most people can safely answer N. + +config UCLIBC_USE_NETLINK + bool "Use netlink to query interfaces" + default n + help + In newer versions of Linux (2.4.17+), support was added for querying + network device information via netlink rather than the old style + ioctl's. Most of the time, the older ioctl style is sufficient (and + it is smaller than netlink), but if you find that not all of your + devices are being returned by the if_nameindex() function, you will + have to use the netlink implementation. + Most people can safely answer N. endmenu diff --git a/libc/inet/Makefile.in b/libc/inet/Makefile.in index f4b6d26c5..49786ad5a 100644 --- a/libc/inet/Makefile.in +++ b/libc/inet/Makefile.in @@ -10,7 +10,7 @@ include $(top_srcdir)libc/inet/rpc/Makefile.in CSRC:= getservice.c getproto.c hostid.c getnetent.c getnetbynm.c getnetbyad.c \ inet_net.c ntop.c herror.c if_index.c gai_strerror.c getaddrinfo.c \ - in6_addr.c ether_addr.c ntohl.c opensock.c + in6_addr.c ether_addr.c ntohl.c opensock.c ifaddrs.c MSRC1:= addr.c MOBJ1:= inet_aton.o inet_addr.o inet_ntoa.o inet_makeaddr.o inet_lnaof.o \ diff --git a/libc/inet/if_index.c b/libc/inet/if_index.c index dfddbc0d4..40e46d538 100644 --- a/libc/inet/if_index.c +++ b/libc/inet/if_index.c @@ -36,6 +36,8 @@ #include <sys/ioctl.h> #include <libc-internal.h> +#include "netlinkaccess.h" + extern int __opensock(void) attribute_hidden; unsigned int @@ -78,7 +80,9 @@ if_freenameindex (struct if_nameindex *ifn) } free (ifn); } +hidden_strong_alias(if_freenameindex,__if_freenameindex) +#if !__ASSUME_NETLINK_SUPPORT struct if_nameindex * if_nameindex (void) { @@ -157,6 +161,131 @@ if_nameindex (void) return idx; #endif } +#else +struct if_nameindex * +if_nameindex (void) +{ + unsigned int nifs = 0; + struct netlink_handle nh = { 0, 0, 0, NULL, NULL }; + struct if_nameindex *idx = NULL; + struct netlink_res *nlp; + + if (__netlink_open (&nh) < 0) + return NULL; + + + /* Tell the kernel that we wish to get a list of all + active interfaces. Collect all data for every interface. */ + if (__netlink_request (&nh, RTM_GETLINK) < 0) + goto exit_free; + + /* Count the interfaces. */ + for (nlp = nh.nlm_list; nlp; nlp = nlp->next) + { + struct nlmsghdr *nlh; + size_t size = nlp->size; + + if (nlp->nlh == NULL) + continue; + + /* Walk through all entries we got from the kernel and look, which + message type they contain. */ + for (nlh = nlp->nlh; NLMSG_OK (nlh, size); nlh = NLMSG_NEXT (nlh, size)) + { + /* Check if the message is what we want. */ + if ((pid_t) nlh->nlmsg_pid != nh.pid || nlh->nlmsg_seq != nlp->seq) + continue; + + if (nlh->nlmsg_type == NLMSG_DONE) + break; /* ok */ + + if (nlh->nlmsg_type == RTM_NEWLINK) + ++nifs; + } + } + + idx = malloc ((nifs + 1) * sizeof (struct if_nameindex)); + if (idx == NULL) + { + nomem: + __set_errno (ENOBUFS); + goto exit_free; + } + + /* Add the interfaces. */ + nifs = 0; + for (nlp = nh.nlm_list; nlp; nlp = nlp->next) + { + struct nlmsghdr *nlh; + size_t size = nlp->size; + + if (nlp->nlh == NULL) + continue; + + /* Walk through all entries we got from the kernel and look, which + message type they contain. */ + for (nlh = nlp->nlh; NLMSG_OK (nlh, size); nlh = NLMSG_NEXT (nlh, size)) + { + /* Check if the message is what we want. */ + if ((pid_t) nlh->nlmsg_pid != nh.pid || nlh->nlmsg_seq != nlp->seq) + continue; + + if (nlh->nlmsg_type == NLMSG_DONE) + break; /* ok */ + + if (nlh->nlmsg_type == RTM_NEWLINK) + { + struct ifinfomsg *ifim = (struct ifinfomsg *) NLMSG_DATA (nlh); + struct rtattr *rta = IFLA_RTA (ifim); + size_t rtasize = IFLA_PAYLOAD (nlh); + + idx[nifs].if_index = ifim->ifi_index; + + while (RTA_OK (rta, rtasize)) + { + char *rta_data = RTA_DATA (rta); + size_t rta_payload = RTA_PAYLOAD (rta); + + if (rta->rta_type == IFLA_IFNAME) + { + idx[nifs].if_name = __strndup (rta_data, rta_payload); + if (idx[nifs].if_name == NULL) + { + idx[nifs].if_index = 0; + __if_freenameindex (idx); + idx = NULL; + goto nomem; + } + break; + } + + rta = RTA_NEXT (rta, rtasize); + } + + ++nifs; + } + } + } + + idx[nifs].if_index = 0; + idx[nifs].if_name = NULL; + + exit_free: + __netlink_free_handle (&nh); + __netlink_close (&nh); + + return idx; +} +#endif +hidden_strong_alias(if_nameindex,__if_nameindex) + +#if 0 +struct if_nameindex * +if_nameindex (void) +{ + return (if_nameindex_netlink () != NULL ? : if_nameindex_ioctl ()); +} +#endif char * if_indextoname (unsigned int ifindex, char *ifname) @@ -194,7 +323,7 @@ if_indextoname (unsigned int ifindex, char *ifname) struct if_nameindex *p; char *result = NULL; - idx = if_nameindex(); + idx = __if_nameindex(); if (idx != NULL) { @@ -205,7 +334,7 @@ if_indextoname (unsigned int ifindex, char *ifname) break; } - if_freenameindex (idx); + __if_freenameindex (idx); if (result == NULL) __set_errno (ENXIO); diff --git a/libc/inet/ifaddrs.c b/libc/inet/ifaddrs.c new file mode 100644 index 000000000..74f3622f8 --- /dev/null +++ b/libc/inet/ifaddrs.c @@ -0,0 +1,873 @@ +/* getifaddrs -- get names and addresses of all network interfaces + Copyright (C) 2003, 2004, 2005 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. */ + +#define time __time +#define sendto __sendto +#define recvmsg __recvmsg +#define bind __bind +#define mempcpy __mempcpy +#define getsockname __getsockname + +#define __FORCE_GLIBC +#include <features.h> +#define __USE_GNU +#include <alloca.h> +#include <assert.h> +#include <errno.h> +/*#include <ifaddrs.h>*/ +#include <net/if.h> +#include <netinet/in.h> +#include <netpacket/packet.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <libc-internal.h> +#include <time.h> +#include <unistd.h> + +#include "netlinkaccess.h" + + +#ifndef __libc_use_alloca +# define __libc_use_alloca(x) (x < __MAX_ALLOCA_CUTOFF) +#endif + + +#if __ASSUME_NETLINK_SUPPORT +#if 0 /* unused code */ +/* struct to hold the data for one ifaddrs entry, so we can allocate + everything at once. */ +struct ifaddrs_storage +{ + struct ifaddrs ifa; + union + { + /* Save space for the biggest of the four used sockaddr types and + avoid a lot of casts. */ + struct sockaddr sa; + struct sockaddr_ll sl; + struct sockaddr_in s4; + struct sockaddr_in6 s6; + } addr, netmask, broadaddr; + char name[IF_NAMESIZE + 1]; +}; +#endif /* unused code */ + + +void +__netlink_free_handle (struct netlink_handle *h) +{ + struct netlink_res *ptr; + int saved_errno = errno; + + ptr = h->nlm_list; + while (ptr != NULL) + { + struct netlink_res *tmpptr; + + tmpptr = ptr->next; + free (ptr); + ptr = tmpptr; + } + + __set_errno (saved_errno); +} + + +static int +__netlink_sendreq (struct netlink_handle *h, int type) +{ + struct + { + struct nlmsghdr nlh; + struct rtgenmsg g; + } req; + struct sockaddr_nl nladdr; + + if (h->seq == 0) + h->seq = time (NULL); + + req.nlh.nlmsg_len = sizeof (req); + req.nlh.nlmsg_type = type; + req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; + req.nlh.nlmsg_pid = 0; + req.nlh.nlmsg_seq = h->seq; + req.g.rtgen_family = AF_UNSPEC; + + memset (&nladdr, '\0', sizeof (nladdr)); + nladdr.nl_family = AF_NETLINK; + + return TEMP_FAILURE_RETRY (__sendto (h->fd, (void *) &req, sizeof (req), 0, + (struct sockaddr *) &nladdr, + sizeof (nladdr))); +} + + +int +__netlink_request (struct netlink_handle *h, int type) +{ + struct netlink_res *nlm_next; + struct netlink_res **new_nlm_list; + static volatile size_t buf_size = 4096; + char *buf; + struct sockaddr_nl nladdr; + struct nlmsghdr *nlmh; + ssize_t read_len; + bool done = false; + bool use_malloc = false; + + if (__netlink_sendreq (h, type) < 0) + return -1; + + size_t this_buf_size = buf_size; + if (__libc_use_alloca (this_buf_size)) + buf = alloca (this_buf_size); + else + { + buf = malloc (this_buf_size); + if (buf != NULL) + use_malloc = true; + else + goto out_fail; + } + + struct iovec iov = { buf, this_buf_size }; + + if (h->nlm_list != NULL) + new_nlm_list = &h->end_ptr->next; + else + new_nlm_list = &h->nlm_list; + + while (! done) + { + struct msghdr msg = + { + (void *) &nladdr, sizeof (nladdr), + &iov, 1, + NULL, 0, + 0 + }; + + read_len = TEMP_FAILURE_RETRY (__recvmsg (h->fd, &msg, 0)); + if (read_len < 0) + goto out_fail; + + if (nladdr.nl_pid != 0) + continue; + + if (__builtin_expect (msg.msg_flags & MSG_TRUNC, 0)) + { + if (this_buf_size >= SIZE_MAX / 2) + goto out_fail; + + nlm_next = *new_nlm_list; + while (nlm_next != NULL) + { + struct netlink_res *tmpptr; + + tmpptr = nlm_next->next; + free (nlm_next); + nlm_next = tmpptr; + } + *new_nlm_list = NULL; + + if (__libc_use_alloca (2 * this_buf_size)) + buf = extend_alloca (buf, this_buf_size, 2 * this_buf_size); + else + { + this_buf_size *= 2; + + char *new_buf = realloc (use_malloc ? buf : NULL, this_buf_size); + if (new_buf == NULL) + goto out_fail; + new_buf = buf; + + use_malloc = true; + } + buf_size = this_buf_size; + + iov.iov_base = buf; + iov.iov_len = this_buf_size; + + /* Increase sequence number, so that we can distinguish + between old and new request messages. */ + h->seq++; + + if (__netlink_sendreq (h, type) < 0) + goto out_fail; + + continue; + } + + size_t count = 0; + size_t remaining_len = read_len; + for (nlmh = (struct nlmsghdr *) buf; + NLMSG_OK (nlmh, remaining_len); + nlmh = (struct nlmsghdr *) NLMSG_NEXT (nlmh, remaining_len)) + { + if ((pid_t) nlmh->nlmsg_pid != h->pid + || nlmh->nlmsg_seq != h->seq) + continue; + + ++count; + if (nlmh->nlmsg_type == NLMSG_DONE) + { + /* We found the end, leave the loop. */ + done = true; + break; + } + if (nlmh->nlmsg_type == NLMSG_ERROR) + { + struct nlmsgerr *nlerr = (struct nlmsgerr *) NLMSG_DATA (nlmh); + if (nlmh->nlmsg_len < NLMSG_LENGTH (sizeof (struct nlmsgerr))) + errno = EIO; + else + errno = -nlerr->error; + goto out_fail; + } + } + + /* If there was nothing with the expected nlmsg_pid and nlmsg_seq, + there is no point to record it. */ + if (count == 0) + continue; + + nlm_next = (struct netlink_res *) malloc (sizeof (struct netlink_res) + + read_len); + if (nlm_next == NULL) + goto out_fail; + nlm_next->next = NULL; + nlm_next->nlh = memcpy (nlm_next + 1, buf, read_len); + nlm_next->size = read_len; + nlm_next->seq = h->seq; + if (h->nlm_list == NULL) + h->nlm_list = nlm_next; + else + h->end_ptr->next = nlm_next; + h->end_ptr = nlm_next; + } + + if (use_malloc) + free (buf); + return 0; + +out_fail: + if (use_malloc) + free (buf); + return -1; +} + + +void +__netlink_close (struct netlink_handle *h) +{ + /* Don't modify errno. */ + int serrno = errno; + __close(h->fd); + __set_errno(serrno); +} + + +/* Open a NETLINK socket. */ +int +__netlink_open (struct netlink_handle *h) +{ + struct sockaddr_nl nladdr; + + h->fd = __socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (h->fd < 0) + goto out; + + memset (&nladdr, '\0', sizeof (nladdr)); + nladdr.nl_family = AF_NETLINK; + if (__bind (h->fd, (struct sockaddr *) &nladdr, sizeof (nladdr)) < 0) + { + close_and_out: + __netlink_close (h); + out: +#if __ASSUME_NETLINK_SUPPORT == 0 + __no_netlink_support = 1; +#endif + return -1; + } + /* Determine the ID the kernel assigned for this netlink connection. + It is not necessarily the PID if there is more than one socket + open. */ + socklen_t addr_len = sizeof (nladdr); + if (__getsockname (h->fd, (struct sockaddr *) &nladdr, &addr_len) < 0) + goto close_and_out; + h->pid = nladdr.nl_pid; + return 0; +} + + +#if 0 /* unused code */ +/* We know the number of RTM_NEWLINK entries, so we reserve the first + # of entries for this type. All RTM_NEWADDR entries have an index + pointer to the RTM_NEWLINK entry. To find the entry, create + a table to map kernel index entries to our index numbers. + Since we get at first all RTM_NEWLINK entries, it can never happen + that a RTM_NEWADDR index is not known to this map. */ +static int +internal_function +map_newlink (int index, struct ifaddrs_storage *ifas, int *map, int max) +{ + int i; + + for (i = 0; i < max; i++) + { + if (map[i] == -1) + { + map[i] = index; + if (i > 0) + ifas[i - 1].ifa.ifa_next = &ifas[i].ifa; + return i; + } + else if (map[i] == index) + return i; + } + /* This should never be reached. If this will be reached, we have + a very big problem. */ + abort (); +} + + +/* Create a linked list of `struct ifaddrs' structures, one for each + network interface on the host machine. If successful, store the + list in *IFAP and return 0. On errors, return -1 and set `errno'. */ +int +getifaddrs (struct ifaddrs **ifap) +{ + struct netlink_handle nh = { 0, 0, 0, NULL, NULL }; + struct netlink_res *nlp; + struct ifaddrs_storage *ifas; + unsigned int i, newlink, newaddr, newaddr_idx; + int *map_newlink_data; + size_t ifa_data_size = 0; /* Size to allocate for all ifa_data. */ + char *ifa_data_ptr; /* Pointer to the unused part of memory for + ifa_data. */ + int result = 0; + + if (ifap) + *ifap = NULL; + + if (! __no_netlink_support && __netlink_open (&nh) < 0) + { +#if __ASSUME_NETLINK_SUPPORT != 0 + return -1; +#endif + } + +#if __ASSUME_NETLINK_SUPPORT == 0 + if (__no_netlink_support) + return fallback_getifaddrs (ifap); +#endif + + /* Tell the kernel that we wish to get a list of all + active interfaces, collect all data for every interface. */ + if (__netlink_request (&nh, RTM_GETLINK) < 0) + { + result = -1; + goto exit_free; + } + + /* Now ask the kernel for all addresses which are assigned + to an interface and collect all data for every interface. + Since we store the addresses after the interfaces in the + list, we will later always find the interface before the + corresponding addresses. */ + ++nh.seq; + if (__netlink_request (&nh, RTM_GETADDR) < 0) + { + result = -1; + goto exit_free; + } + + /* Count all RTM_NEWLINK and RTM_NEWADDR entries to allocate + enough memory. */ + newlink = newaddr = 0; + for (nlp = nh.nlm_list; nlp; nlp = nlp->next) + { + struct nlmsghdr *nlh; + size_t size = nlp->size; + + if (nlp->nlh == NULL) + continue; + + /* Walk through all entries we got from the kernel and look, which + message type they contain. */ + for (nlh = nlp->nlh; NLMSG_OK (nlh, size); nlh = NLMSG_NEXT (nlh, size)) + { + /* Check if the message is what we want. */ + if ((pid_t) nlh->nlmsg_pid != nh.pid || nlh->nlmsg_seq != nlp->seq) + continue; + + if (nlh->nlmsg_type == NLMSG_DONE) + break; /* ok */ + + if (nlh->nlmsg_type == RTM_NEWLINK) + { + /* A RTM_NEWLINK message can have IFLA_STATS data. We need to + know the size before creating the list to allocate enough + memory. */ + struct ifinfomsg *ifim = (struct ifinfomsg *) NLMSG_DATA (nlh); + struct rtattr *rta = IFLA_RTA (ifim); + size_t rtasize = IFLA_PAYLOAD (nlh); + + while (RTA_OK (rta, rtasize)) + { + size_t rta_payload = RTA_PAYLOAD (rta); + + if (rta->rta_type == IFLA_STATS) + { + ifa_data_size += rta_payload; + break; + } + else + rta = RTA_NEXT (rta, rtasize); + } + ++newlink; + } + else if (nlh->nlmsg_type == RTM_NEWADDR) + ++newaddr; + } + } + + /* Return if no interface is up. */ + if ((newlink + newaddr) == 0) + goto exit_free; + + /* Allocate memory for all entries we have and initialize next + pointer. */ + ifas = (struct ifaddrs_storage *) calloc (1, + (newlink + newaddr) + * sizeof (struct ifaddrs_storage) + + ifa_data_size); + if (ifas == NULL) + { + result = -1; + goto exit_free; + } + + /* Table for mapping kernel index to entry in our list. */ + map_newlink_data = alloca (newlink * sizeof (int)); + memset (map_newlink_data, '\xff', newlink * sizeof (int)); + + ifa_data_ptr = (char *) &ifas[newlink + newaddr]; + newaddr_idx = 0; /* Counter for newaddr index. */ + + /* Walk through the list of data we got from the kernel. */ + for (nlp = nh.nlm_list; nlp; nlp = nlp->next) + { + struct nlmsghdr *nlh; + size_t size = nlp->size; + + if (nlp->nlh == NULL) + continue; + + /* Walk through one message and look at the type: If it is our + message, we need RTM_NEWLINK/RTM_NEWADDR and stop if we reach + the end or we find the end marker (in this case we ignore the + following data. */ + for (nlh = nlp->nlh; NLMSG_OK (nlh, size); nlh = NLMSG_NEXT (nlh, size)) + { + int ifa_index = 0; + + /* Check if the message is the one we want */ + if ((pid_t) nlh->nlmsg_pid != nh.pid || nlh->nlmsg_seq != nlp->seq) + continue; + + if (nlh->nlmsg_type == NLMSG_DONE) + break; /* ok */ + + if (nlh->nlmsg_type == RTM_NEWLINK) + { + /* We found a new interface. Now extract everything from the + interface data we got and need. */ + struct ifinfomsg *ifim = (struct ifinfomsg *) NLMSG_DATA (nlh); + struct rtattr *rta = IFLA_RTA (ifim); + size_t rtasize = IFLA_PAYLOAD (nlh); + + /* Interfaces are stored in the first "newlink" entries + of our list, starting in the order as we got from the + kernel. */ + ifa_index = map_newlink (ifim->ifi_index - 1, ifas, + map_newlink_data, newlink); + ifas[ifa_index].ifa.ifa_flags = ifim->ifi_flags; + + while (RTA_OK (rta, rtasize)) + { + char *rta_data = RTA_DATA (rta); + size_t rta_payload = RTA_PAYLOAD (rta); + + switch (rta->rta_type) + { + case IFLA_ADDRESS: + if (rta_payload <= sizeof (ifas[ifa_index].addr)) + { + ifas[ifa_index].addr.sl.sll_family = AF_PACKET; + memcpy (ifas[ifa_index].addr.sl.sll_addr, + (char *) rta_data, rta_payload); + ifas[ifa_index].addr.sl.sll_halen = rta_payload; + ifas[ifa_index].addr.sl.sll_ifindex + = ifim->ifi_index; + ifas[ifa_index].addr.sl.sll_hatype = ifim->ifi_type; + + ifas[ifa_index].ifa.ifa_addr + = &ifas[ifa_index].addr.sa; + } + break; + + case IFLA_BROADCAST: + if (rta_payload <= sizeof (ifas[ifa_index].broadaddr)) + { + ifas[ifa_index].broadaddr.sl.sll_family = AF_PACKET; + memcpy (ifas[ifa_index].broadaddr.sl.sll_addr, + (char *) rta_data, rta_payload); + ifas[ifa_index].broadaddr.sl.sll_halen = rta_payload; + ifas[ifa_index].broadaddr.sl.sll_ifindex + = ifim->ifi_index; + ifas[ifa_index].broadaddr.sl.sll_hatype + = ifim->ifi_type; + + ifas[ifa_index].ifa.ifa_broadaddr + = &ifas[ifa_index].broadaddr.sa; + } + break; + + case IFLA_IFNAME: /* Name of Interface */ + if ((rta_payload + 1) <= sizeof (ifas[ifa_index].name)) + { + ifas[ifa_index].ifa.ifa_name = ifas[ifa_index].name; + *(char *) __mempcpy (ifas[ifa_index].name, rta_data, + rta_payload) = '\0'; + } + break; + + case IFLA_STATS: /* Statistics of Interface */ + ifas[ifa_index].ifa.ifa_data = ifa_data_ptr; + ifa_data_ptr += rta_payload; + memcpy (ifas[ifa_index].ifa.ifa_data, rta_data, + rta_payload); + break; + + case IFLA_UNSPEC: + break; + case IFLA_MTU: + break; + case IFLA_LINK: + break; + case IFLA_QDISC: + break; + default: + break; + } + + rta = RTA_NEXT (rta, rtasize); + } + } + else if (nlh->nlmsg_type == RTM_NEWADDR) + { + struct ifaddrmsg *ifam = (struct ifaddrmsg *) NLMSG_DATA (nlh); + struct rtattr *rta = IFA_RTA (ifam); + size_t rtasize = IFA_PAYLOAD (nlh); + + /* New Addresses are stored in the order we got them from + the kernel after the interfaces. Theoretically it is possible + that we have holes in the interface part of the list, + but we always have already the interface for this address. */ + ifa_index = newlink + newaddr_idx; + ifas[ifa_index].ifa.ifa_flags + = ifas[map_newlink (ifam->ifa_index - 1, ifas, + map_newlink_data, newlink)].ifa.ifa_flags; + if (ifa_index > 0) + ifas[ifa_index - 1].ifa.ifa_next = &ifas[ifa_index].ifa; + ++newaddr_idx; + + while (RTA_OK (rta, rtasize)) + { + char *rta_data = RTA_DATA (rta); + size_t rta_payload = RTA_PAYLOAD (rta); + + switch (rta->rta_type) + { + case IFA_ADDRESS: + { + struct sockaddr *sa; + + if (ifas[ifa_index].ifa.ifa_addr != NULL) + { + /* In a point-to-poing network IFA_ADDRESS + contains the destination address, local + address is supplied in IFA_LOCAL attribute. + destination address and broadcast address + are stored in an union, so it doesn't matter + which name we use. */ + ifas[ifa_index].ifa.ifa_broadaddr + = &ifas[ifa_index].broadaddr.sa; + sa = &ifas[ifa_index].broadaddr.sa; + } + else + { + ifas[ifa_index].ifa.ifa_addr + = &ifas[ifa_index].addr.sa; + sa = &ifas[ifa_index].addr.sa; + } + + sa->sa_family = ifam->ifa_family; + + switch (ifam->ifa_family) + { + case AF_INET: + /* Size must match that of an address for IPv4. */ + if (rta_payload == 4) + memcpy (&((struct sockaddr_in *) sa)->sin_addr, + rta_data, rta_payload); + break; + + case AF_INET6: + /* Size must match that of an address for IPv6. */ + if (rta_payload == 16) + { + memcpy (&((struct sockaddr_in6 *) sa)->sin6_addr, + rta_data, rta_payload); + if (IN6_IS_ADDR_LINKLOCAL (rta_data) + || IN6_IS_ADDR_MC_LINKLOCAL (rta_data)) + ((struct sockaddr_in6 *) sa)->sin6_scope_id + = ifam->ifa_index; + } + break; + + default: + if (rta_payload <= sizeof (ifas[ifa_index].addr)) + memcpy (sa->sa_data, rta_data, rta_payload); + break; + } + } + break; + + case IFA_LOCAL: + if (ifas[ifa_index].ifa.ifa_addr != NULL) + { + /* If ifa_addr is set and we get IFA_LOCAL, + assume we have a point-to-point network. + Move address to correct field. */ + ifas[ifa_index].broadaddr = ifas[ifa_index].addr; + ifas[ifa_index].ifa.ifa_broadaddr + = &ifas[ifa_index].broadaddr.sa; + memset (&ifas[ifa_index].addr, '\0', + sizeof (ifas[ifa_index].addr)); + } + + ifas[ifa_index].ifa.ifa_addr = &ifas[ifa_index].addr.sa; + ifas[ifa_index].ifa.ifa_addr->sa_family + = ifam->ifa_family; + + switch (ifam->ifa_family) + { + case AF_INET: + /* Size must match that of an address for IPv4. */ + if (rta_payload == 4) + memcpy (&ifas[ifa_index].addr.s4.sin_addr, + rta_data, rta_payload); + break; + + case AF_INET6: + /* Size must match that of an address for IPv6. */ + if (rta_payload == 16) + { + memcpy (&ifas[ifa_index].addr.s6.sin6_addr, + rta_data, rta_payload); + if (IN6_IS_ADDR_LINKLOCAL (rta_data) + || IN6_IS_ADDR_MC_LINKLOCAL (rta_data)) + ifas[ifa_index].addr.s6.sin6_scope_id = + ifam->ifa_index; + } + break; + + default: + if (rta_payload <= sizeof (ifas[ifa_index].addr)) + memcpy (ifas[ifa_index].addr.sa.sa_data, + rta_data, rta_payload); + break; + } + break; + + case IFA_BROADCAST: + /* We get IFA_BROADCAST, so IFA_LOCAL was too much. */ + if (ifas[ifa_index].ifa.ifa_broadaddr != NULL) + memset (&ifas[ifa_index].broadaddr, '\0', + sizeof (ifas[ifa_index].broadaddr)); + + ifas[ifa_index].ifa.ifa_broadaddr + = &ifas[ifa_index].broadaddr.sa; + ifas[ifa_index].ifa.ifa_broadaddr->sa_family + = ifam->ifa_family; + + switch (ifam->ifa_family) + { + case AF_INET: + /* Size must match that of an address for IPv4. */ + if (rta_payload == 4) + memcpy (&ifas[ifa_index].broadaddr.s4.sin_addr, + rta_data, rta_payload); + break; + + case AF_INET6: + /* Size must match that of an address for IPv6. */ + if (rta_payload == 16) + { + memcpy (&ifas[ifa_index].broadaddr.s6.sin6_addr, + rta_data, rta_payload); + if (IN6_IS_ADDR_LINKLOCAL (rta_data) + || IN6_IS_ADDR_MC_LINKLOCAL (rta_data)) + ifas[ifa_index].broadaddr.s6.sin6_scope_id + = ifam->ifa_index; + } + break; + + default: + if (rta_payload <= sizeof (ifas[ifa_index].addr)) + memcpy (&ifas[ifa_index].broadaddr.sa.sa_data, + rta_data, rta_payload); + break; + } + break; + + case IFA_LABEL: + if (rta_payload + 1 <= sizeof (ifas[ifa_index].name)) + { + ifas[ifa_index].ifa.ifa_name = ifas[ifa_index].name; + *(char *) __mempcpy (ifas[ifa_index].name, rta_data, + rta_payload) = '\0'; + } + else + abort (); + break; + + case IFA_UNSPEC: + break; + case IFA_CACHEINFO: + break; + default: + break; + } + + rta = RTA_NEXT (rta, rtasize); + } + + /* If we didn't get the interface name with the + address, use the name from the interface entry. */ + if (ifas[ifa_index].ifa.ifa_name == NULL) + ifas[ifa_index].ifa.ifa_name + = ifas[map_newlink (ifam->ifa_index - 1, ifas, + map_newlink_data, newlink)].ifa.ifa_name; + + /* Calculate the netmask. */ + if (ifas[ifa_index].ifa.ifa_addr + && ifas[ifa_index].ifa.ifa_addr->sa_family != AF_UNSPEC + && ifas[ifa_index].ifa.ifa_addr->sa_family != AF_PACKET) + { + uint32_t max_prefixlen = 0; + char *cp = NULL; + + ifas[ifa_index].ifa.ifa_netmask + = &ifas[ifa_index].netmask.sa; + + switch (ifas[ifa_index].ifa.ifa_addr->sa_family) + { + case AF_INET: + cp = (char *) &ifas[ifa_index].netmask.s4.sin_addr; + max_prefixlen = 32; + break; + + case AF_INET6: + cp = (char *) &ifas[ifa_index].netmask.s6.sin6_addr; + max_prefixlen = 128; + break; + } + + ifas[ifa_index].ifa.ifa_netmask->sa_family + = ifas[ifa_index].ifa.ifa_addr->sa_family; + + if (cp != NULL) + { + char c; + unsigned int preflen; + + if ((max_prefixlen > 0) && + (ifam->ifa_prefixlen > max_prefixlen)) + preflen = max_prefixlen; + else + preflen = ifam->ifa_prefixlen; + + for (i = 0; i < (preflen / 8); i++) + *cp++ = 0xff; + c = 0xff; + c <<= (8 - (preflen % 8)); + *cp = c; + } + } + } + } + } + + assert (ifa_data_ptr <= (char *) &ifas[newlink + newaddr] + ifa_data_size); + + if (newaddr_idx > 0) + { + for (i = 0; i < newlink; ++i) + if (map_newlink_data[i] == -1) + { + /* We have fewer links then we anticipated. Adjust the + forward pointer to the first address entry. */ + ifas[i - 1].ifa.ifa_next = &ifas[newlink].ifa; + } + + if (i == 0 && newlink > 0) + /* No valid link, but we allocated memory. We have to + populate the first entry. */ + memmove (ifas, &ifas[newlink], sizeof (struct ifaddrs_storage)); + } + + if (ifap != NULL) + *ifap = &ifas[0].ifa; + + exit_free: + __netlink_free_handle (&nh); + __netlink_close (&nh); + + return result; +} + + +#if __ASSUME_NETLINK_SUPPORT != 0 +void +freeifaddrs (struct ifaddrs *ifa) +{ + free (ifa); +} +#endif + +#endif /* unused code */ + +#endif /* __ASSUME_NETLINK_SUPPORT */ diff --git a/libc/inet/netlinkaccess.h b/libc/inet/netlinkaccess.h new file mode 100644 index 000000000..12269bfcf --- /dev/null +++ b/libc/inet/netlinkaccess.h @@ -0,0 +1,69 @@ +/* Copyright (C) 2004, 2005 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. */ + +#ifndef _NETLINKACCESS_H +#define _NETLINKACCESS_H 1 + +#include <features.h> +#include <stdint.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> + +/* Should prob be a configure option or something */ +#ifdef __UCLIBC_USE_NETLINK__ +# define __ASSUME_NETLINK_SUPPORT 1 +#else +# define __ASSUME_NETLINK_SUPPORT 0 +#endif + + +struct netlink_res +{ + struct netlink_res *next; + struct nlmsghdr *nlh; + size_t size; /* Size of response. */ + uint32_t seq; /* sequential number we used. */ +}; + + +struct netlink_handle +{ + int fd; /* Netlink file descriptor. */ + pid_t pid; /* Process ID. */ + uint32_t seq; /* The sequence number we use currently. */ + struct netlink_res *nlm_list; /* Pointer to list of responses. */ + struct netlink_res *end_ptr; /* For faster append of new entries. */ +}; + + +#if 0 /* unused code */ +#if __ASSUME_NETLINK_SUPPORT == 0 +extern int __no_netlink_support attribute_hidden; +#else +# define __no_netlink_support 0 +#endif +#endif /* unused code */ + + +extern int __netlink_open (struct netlink_handle *h) attribute_hidden; +extern void __netlink_close (struct netlink_handle *h) attribute_hidden; +extern void __netlink_free_handle (struct netlink_handle *h) attribute_hidden; +extern int __netlink_request (struct netlink_handle *h, int type) attribute_hidden; + + +#endif /* netlinkaccess.h */ |