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 */  | 
