Blame sysdeps/unix/sysv/linux/ifaddrs.c

Packit 6c4009
/* getifaddrs -- get names and addresses of all network interfaces
Packit 6c4009
   Copyright (C) 2003-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <alloca.h>
Packit 6c4009
#include <assert.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <ifaddrs.h>
Packit 6c4009
#include <net/if.h>
Packit 6c4009
#include <netinet/in.h>
Packit 6c4009
#include <netpacket/packet.h>
Packit 6c4009
#include <stdbool.h>
Packit 6c4009
#include <stdint.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <sys/ioctl.h>
Packit 6c4009
#include <sys/socket.h>
Packit 6c4009
#include <sysdep.h>
Packit 6c4009
#include <time.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
Packit 6c4009
#include "netlinkaccess.h"
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* There is a problem with this type.  The address length for
Packit 6c4009
   Infiniband sockets is much longer than the 8 bytes allocated in the
Packit 6c4009
   sockaddr_ll definition.  Hence we use here a special
Packit 6c4009
   definition.  */
Packit 6c4009
struct sockaddr_ll_max
Packit 6c4009
  {
Packit 6c4009
    unsigned short int sll_family;
Packit 6c4009
    unsigned short int sll_protocol;
Packit 6c4009
    int sll_ifindex;
Packit 6c4009
    unsigned short int sll_hatype;
Packit 6c4009
    unsigned char sll_pkttype;
Packit 6c4009
    unsigned char sll_halen;
Packit 6c4009
    unsigned char sll_addr[24];
Packit 6c4009
  };
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* struct to hold the data for one ifaddrs entry, so we can allocate
Packit 6c4009
   everything at once.  */
Packit 6c4009
struct ifaddrs_storage
Packit 6c4009
{
Packit 6c4009
  struct ifaddrs ifa;
Packit 6c4009
  union
Packit 6c4009
  {
Packit 6c4009
    /* Save space for the biggest of the four used sockaddr types and
Packit 6c4009
       avoid a lot of casts.  */
Packit 6c4009
    struct sockaddr sa;
Packit 6c4009
    struct sockaddr_ll_max sl;
Packit 6c4009
    struct sockaddr_in s4;
Packit 6c4009
    struct sockaddr_in6 s6;
Packit 6c4009
  } addr, netmask, broadaddr;
Packit 6c4009
  char name[IF_NAMESIZE + 1];
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
__netlink_free_handle (struct netlink_handle *h)
Packit 6c4009
{
Packit 6c4009
  struct netlink_res *ptr;
Packit 6c4009
  int saved_errno = errno;
Packit 6c4009
Packit 6c4009
  ptr = h->nlm_list;
Packit 6c4009
  while (ptr != NULL)
Packit 6c4009
    {
Packit 6c4009
      struct netlink_res *tmpptr;
Packit 6c4009
Packit 6c4009
      tmpptr = ptr->next;
Packit 6c4009
      free (ptr);
Packit 6c4009
      ptr = tmpptr;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  __set_errno (saved_errno);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
__netlink_sendreq (struct netlink_handle *h, int type)
Packit 6c4009
{
Packit 6c4009
  struct req
Packit 6c4009
  {
Packit 6c4009
    struct nlmsghdr nlh;
Packit 6c4009
    struct rtgenmsg g;
Packit 6c4009
    char pad[0];
Packit 6c4009
  } req;
Packit 6c4009
  struct sockaddr_nl nladdr;
Packit 6c4009
Packit 6c4009
  if (h->seq == 0)
Packit 6c4009
    h->seq = time (NULL);
Packit 6c4009
Packit 6c4009
  req.nlh.nlmsg_len = sizeof (req);
Packit 6c4009
  req.nlh.nlmsg_type = type;
Packit 6c4009
  req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
Packit 6c4009
  req.nlh.nlmsg_pid = 0;
Packit 6c4009
  req.nlh.nlmsg_seq = h->seq;
Packit 6c4009
  req.g.rtgen_family = AF_UNSPEC;
Packit 6c4009
  if (sizeof (req) != offsetof (struct req, pad))
Packit 6c4009
    memset (req.pad, '\0', sizeof (req) - offsetof (struct req, pad));
Packit 6c4009
Packit 6c4009
  memset (&nladdr, '\0', sizeof (nladdr));
Packit 6c4009
  nladdr.nl_family = AF_NETLINK;
Packit 6c4009
Packit 6c4009
  return TEMP_FAILURE_RETRY (__sendto (h->fd, (void *) &req, sizeof (req), 0,
Packit 6c4009
				       (struct sockaddr *) &nladdr,
Packit 6c4009
				       sizeof (nladdr)));
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
__netlink_request (struct netlink_handle *h, int type)
Packit 6c4009
{
Packit 6c4009
  struct netlink_res *nlm_next;
Packit 6c4009
  struct sockaddr_nl nladdr;
Packit 6c4009
  struct nlmsghdr *nlmh;
Packit 6c4009
  ssize_t read_len;
Packit 6c4009
  bool done = false;
Packit 6c4009
Packit 6c4009
#ifdef PAGE_SIZE
Packit 6c4009
  /* Help the compiler optimize out the malloc call if PAGE_SIZE
Packit 6c4009
     is constant and smaller or equal to PTHREAD_STACK_MIN/4.  */
Packit 6c4009
  const size_t buf_size = PAGE_SIZE;
Packit 6c4009
#else
Packit 6c4009
  const size_t buf_size = __getpagesize ();
Packit 6c4009
#endif
Packit 6c4009
  bool use_malloc = false;
Packit 6c4009
  char *buf;
Packit 6c4009
Packit 6c4009
  if (__libc_use_alloca (buf_size))
Packit 6c4009
    buf = alloca (buf_size);
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      buf = malloc (buf_size);
Packit 6c4009
      if (buf != NULL)
Packit 6c4009
	use_malloc = true;
Packit 6c4009
      else
Packit 6c4009
	goto out_fail;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  struct iovec iov = { buf, buf_size };
Packit 6c4009
Packit 6c4009
  if (__netlink_sendreq (h, type) < 0)
Packit 6c4009
    goto out_fail;
Packit 6c4009
Packit 6c4009
  while (! done)
Packit 6c4009
    {
Packit 6c4009
      struct msghdr msg =
Packit 6c4009
	{
Packit 6c4009
	  .msg_name = (void *) &nladdr,
Packit 6c4009
	  .msg_namelen =  sizeof (nladdr),
Packit 6c4009
	  .msg_iov = &iov,
Packit 6c4009
	  .msg_iovlen = 1,
Packit 6c4009
	  .msg_control = NULL,
Packit 6c4009
	  .msg_controllen = 0,
Packit 6c4009
	  .msg_flags = 0
Packit 6c4009
	};
Packit 6c4009
Packit 6c4009
      read_len = TEMP_FAILURE_RETRY (__recvmsg (h->fd, &msg, 0));
Packit 6c4009
      __netlink_assert_response (h->fd, read_len);
Packit 6c4009
      if (read_len < 0)
Packit 6c4009
	goto out_fail;
Packit 6c4009
Packit 6c4009
      if (nladdr.nl_pid != 0)
Packit 6c4009
	continue;
Packit 6c4009
Packit 6c4009
      if (__glibc_unlikely (msg.msg_flags & MSG_TRUNC))
Packit 6c4009
	goto out_fail;
Packit 6c4009
Packit 6c4009
      size_t count = 0;
Packit 6c4009
      size_t remaining_len = read_len;
Packit 6c4009
      for (nlmh = (struct nlmsghdr *) buf;
Packit 6c4009
	   NLMSG_OK (nlmh, remaining_len);
Packit 6c4009
	   nlmh = (struct nlmsghdr *) NLMSG_NEXT (nlmh, remaining_len))
Packit 6c4009
	{
Packit 6c4009
	  if ((pid_t) nlmh->nlmsg_pid != h->pid
Packit 6c4009
	      || nlmh->nlmsg_seq != h->seq)
Packit 6c4009
	    continue;
Packit 6c4009
Packit 6c4009
	  ++count;
Packit 6c4009
	  if (nlmh->nlmsg_type == NLMSG_DONE)
Packit 6c4009
	    {
Packit 6c4009
	      /* We found the end, leave the loop.  */
Packit 6c4009
	      done = true;
Packit 6c4009
	      break;
Packit 6c4009
	    }
Packit 6c4009
	  if (nlmh->nlmsg_type == NLMSG_ERROR)
Packit 6c4009
	    {
Packit 6c4009
	      struct nlmsgerr *nlerr = (struct nlmsgerr *) NLMSG_DATA (nlmh);
Packit 6c4009
	      if (nlmh->nlmsg_len < NLMSG_LENGTH (sizeof (struct nlmsgerr)))
Packit 6c4009
		errno = EIO;
Packit 6c4009
	      else
Packit 6c4009
		errno = -nlerr->error;
Packit 6c4009
	      goto out_fail;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* If there was nothing with the expected nlmsg_pid and nlmsg_seq,
Packit 6c4009
	 there is no point to record it.  */
Packit 6c4009
      if (count == 0)
Packit 6c4009
	continue;
Packit 6c4009
Packit 6c4009
      nlm_next = (struct netlink_res *) malloc (sizeof (struct netlink_res)
Packit 6c4009
						+ read_len);
Packit 6c4009
      if (nlm_next == NULL)
Packit 6c4009
	goto out_fail;
Packit 6c4009
      nlm_next->next = NULL;
Packit 6c4009
      nlm_next->nlh = memcpy (nlm_next + 1, buf, read_len);
Packit 6c4009
      nlm_next->size = read_len;
Packit 6c4009
      nlm_next->seq = h->seq;
Packit 6c4009
      if (h->nlm_list == NULL)
Packit 6c4009
	h->nlm_list = nlm_next;
Packit 6c4009
      else
Packit 6c4009
	h->end_ptr->next = nlm_next;
Packit 6c4009
      h->end_ptr = nlm_next;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (use_malloc)
Packit 6c4009
    free (buf);
Packit 6c4009
  return 0;
Packit 6c4009
Packit 6c4009
out_fail:
Packit 6c4009
  if (use_malloc)
Packit 6c4009
    free (buf);
Packit 6c4009
  return -1;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
__netlink_close (struct netlink_handle *h)
Packit 6c4009
{
Packit 6c4009
  /* Don't modify errno.  */
Packit 6c4009
  INTERNAL_SYSCALL_DECL (err);
Packit 6c4009
  (void) INTERNAL_SYSCALL (close, err, 1, h->fd);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Open a NETLINK socket.  */
Packit 6c4009
int
Packit 6c4009
__netlink_open (struct netlink_handle *h)
Packit 6c4009
{
Packit 6c4009
  struct sockaddr_nl nladdr;
Packit 6c4009
Packit 6c4009
  h->fd = __socket (PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
Packit 6c4009
  if (h->fd < 0)
Packit 6c4009
    goto out;
Packit 6c4009
Packit 6c4009
  memset (&nladdr, '\0', sizeof (nladdr));
Packit 6c4009
  nladdr.nl_family = AF_NETLINK;
Packit 6c4009
  if (__bind (h->fd, (struct sockaddr *) &nladdr, sizeof (nladdr)) < 0)
Packit 6c4009
    {
Packit 6c4009
    close_and_out:
Packit 6c4009
      __netlink_close (h);
Packit 6c4009
    out:
Packit 6c4009
      return -1;
Packit 6c4009
    }
Packit 6c4009
  /* Determine the ID the kernel assigned for this netlink connection.
Packit 6c4009
     It is not necessarily the PID if there is more than one socket
Packit 6c4009
     open.  */
Packit 6c4009
  socklen_t addr_len = sizeof (nladdr);
Packit 6c4009
  if (__getsockname (h->fd, (struct sockaddr *) &nladdr, &addr_len) < 0)
Packit 6c4009
    goto close_and_out;
Packit 6c4009
  h->pid = nladdr.nl_pid;
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* We know the number of RTM_NEWLINK entries, so we reserve the first
Packit 6c4009
   # of entries for this type. All RTM_NEWADDR entries have an index
Packit 6c4009
   pointer to the RTM_NEWLINK entry.  To find the entry, create
Packit 6c4009
   a table to map kernel index entries to our index numbers.
Packit 6c4009
   Since we get at first all RTM_NEWLINK entries, it can never happen
Packit 6c4009
   that a RTM_NEWADDR index is not known to this map.  */
Packit 6c4009
static int
Packit 6c4009
map_newlink (int index, struct ifaddrs_storage *ifas, int *map, int max)
Packit 6c4009
{
Packit 6c4009
  int i;
Packit 6c4009
Packit 6c4009
  for (i = 0; i < max; i++)
Packit 6c4009
    {
Packit 6c4009
      if (map[i] == -1)
Packit 6c4009
	{
Packit 6c4009
	  map[i] = index;
Packit 6c4009
	  if (i > 0)
Packit 6c4009
	    ifas[i - 1].ifa.ifa_next = &ifas[i].ifa;
Packit 6c4009
	  return i;
Packit 6c4009
	}
Packit 6c4009
      else if (map[i] == index)
Packit 6c4009
	return i;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* This means interfaces changed between the reading of the
Packit 6c4009
     RTM_GETLINK and RTM_GETADDR information.  We have to repeat
Packit 6c4009
     everything.  */
Packit 6c4009
  return -1;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Create a linked list of `struct ifaddrs' structures, one for each
Packit 6c4009
   network interface on the host machine.  If successful, store the
Packit 6c4009
   list in *IFAP and return 0.  On errors, return -1 and set `errno'.  */
Packit 6c4009
static int
Packit 6c4009
getifaddrs_internal (struct ifaddrs **ifap)
Packit 6c4009
{
Packit 6c4009
  struct netlink_handle nh = { 0, 0, 0, NULL, NULL };
Packit 6c4009
  struct netlink_res *nlp;
Packit 6c4009
  struct ifaddrs_storage *ifas;
Packit 6c4009
  unsigned int i, newlink, newaddr, newaddr_idx;
Packit 6c4009
  int *map_newlink_data;
Packit 6c4009
  size_t ifa_data_size = 0;  /* Size to allocate for all ifa_data.  */
Packit 6c4009
  char *ifa_data_ptr;	/* Pointer to the unused part of memory for
Packit 6c4009
				ifa_data.  */
Packit 6c4009
  int result = 0;
Packit 6c4009
Packit 6c4009
  *ifap = NULL;
Packit 6c4009
Packit 6c4009
  if (__netlink_open (&nh) < 0)
Packit 6c4009
    return -1;
Packit 6c4009
Packit 6c4009
  /* Tell the kernel that we wish to get a list of all
Packit 6c4009
     active interfaces, collect all data for every interface.  */
Packit 6c4009
  if (__netlink_request (&nh, RTM_GETLINK) < 0)
Packit 6c4009
    {
Packit 6c4009
      result = -1;
Packit 6c4009
      goto exit_free;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Now ask the kernel for all addresses which are assigned
Packit 6c4009
     to an interface and collect all data for every interface.
Packit 6c4009
     Since we store the addresses after the interfaces in the
Packit 6c4009
     list, we will later always find the interface before the
Packit 6c4009
     corresponding addresses.  */
Packit 6c4009
  ++nh.seq;
Packit 6c4009
  if (__netlink_request (&nh, RTM_GETADDR) < 0)
Packit 6c4009
    {
Packit 6c4009
      result = -1;
Packit 6c4009
      goto exit_free;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Count all RTM_NEWLINK and RTM_NEWADDR entries to allocate
Packit 6c4009
     enough memory.  */
Packit 6c4009
  newlink = newaddr = 0;
Packit 6c4009
  for (nlp = nh.nlm_list; nlp; nlp = nlp->next)
Packit 6c4009
    {
Packit 6c4009
      struct nlmsghdr *nlh;
Packit 6c4009
      size_t size = nlp->size;
Packit 6c4009
Packit 6c4009
      if (nlp->nlh == NULL)
Packit 6c4009
	continue;
Packit 6c4009
Packit 6c4009
      /* Walk through all entries we got from the kernel and look, which
Packit 6c4009
	 message type they contain.  */
Packit 6c4009
      for (nlh = nlp->nlh; NLMSG_OK (nlh, size); nlh = NLMSG_NEXT (nlh, size))
Packit 6c4009
	{
Packit 6c4009
	  /* Check if the message is what we want.  */
Packit 6c4009
	  if ((pid_t) nlh->nlmsg_pid != nh.pid || nlh->nlmsg_seq != nlp->seq)
Packit 6c4009
	    continue;
Packit 6c4009
Packit 6c4009
	  /* If the dump got interrupted, we can't rely on the results
Packit 6c4009
	     so try again. */
Packit 6c4009
	  if (nlh->nlmsg_flags & NLM_F_DUMP_INTR)
Packit 6c4009
	    {
Packit 6c4009
	      result = -EAGAIN;
Packit 6c4009
	      goto exit_free;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  if (nlh->nlmsg_type == NLMSG_DONE)
Packit 6c4009
	    break;		/* ok */
Packit 6c4009
Packit 6c4009
	  if (nlh->nlmsg_type == RTM_NEWLINK)
Packit 6c4009
	    {
Packit 6c4009
	      /* A RTM_NEWLINK message can have IFLA_STATS data. We need to
Packit 6c4009
		 know the size before creating the list to allocate enough
Packit 6c4009
		 memory.  */
Packit 6c4009
	      struct ifinfomsg *ifim = (struct ifinfomsg *) NLMSG_DATA (nlh);
Packit 6c4009
	      struct rtattr *rta = IFLA_RTA (ifim);
Packit 6c4009
	      size_t rtasize = IFLA_PAYLOAD (nlh);
Packit 6c4009
Packit 6c4009
	      while (RTA_OK (rta, rtasize))
Packit 6c4009
		{
Packit 6c4009
		  size_t rta_payload = RTA_PAYLOAD (rta);
Packit 6c4009
Packit 6c4009
		  if (rta->rta_type == IFLA_STATS)
Packit 6c4009
		    {
Packit 6c4009
		      ifa_data_size += rta_payload;
Packit 6c4009
		      break;
Packit 6c4009
		    }
Packit 6c4009
		  else
Packit 6c4009
		    rta = RTA_NEXT (rta, rtasize);
Packit 6c4009
		}
Packit 6c4009
	      ++newlink;
Packit 6c4009
	    }
Packit 6c4009
	  else if (nlh->nlmsg_type == RTM_NEWADDR)
Packit 6c4009
	    ++newaddr;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Return if no interface is up.  */
Packit 6c4009
  if ((newlink + newaddr) == 0)
Packit 6c4009
    goto exit_free;
Packit 6c4009
Packit 6c4009
  /* Allocate memory for all entries we have and initialize next
Packit 6c4009
     pointer.  */
Packit 6c4009
  ifas = (struct ifaddrs_storage *) calloc (1,
Packit 6c4009
					    (newlink + newaddr)
Packit 6c4009
					    * sizeof (struct ifaddrs_storage)
Packit 6c4009
					    + ifa_data_size);
Packit 6c4009
  if (ifas == NULL)
Packit 6c4009
    {
Packit 6c4009
      result = -1;
Packit 6c4009
      goto exit_free;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Table for mapping kernel index to entry in our list.  */
Packit 6c4009
  map_newlink_data = alloca (newlink * sizeof (int));
Packit 6c4009
  memset (map_newlink_data, '\xff', newlink * sizeof (int));
Packit 6c4009
Packit 6c4009
  ifa_data_ptr = (char *) &ifas[newlink + newaddr];
Packit 6c4009
  newaddr_idx = 0;		/* Counter for newaddr index.  */
Packit 6c4009
Packit 6c4009
  /* Walk through the list of data we got from the kernel.  */
Packit 6c4009
  for (nlp = nh.nlm_list; nlp; nlp = nlp->next)
Packit 6c4009
    {
Packit 6c4009
      struct nlmsghdr *nlh;
Packit 6c4009
      size_t size = nlp->size;
Packit 6c4009
Packit 6c4009
      if (nlp->nlh == NULL)
Packit 6c4009
	continue;
Packit 6c4009
Packit 6c4009
      /* Walk through one message and look at the type: If it is our
Packit 6c4009
	 message, we need RTM_NEWLINK/RTM_NEWADDR and stop if we reach
Packit 6c4009
	 the end or we find the end marker (in this case we ignore the
Packit 6c4009
	 following data.  */
Packit 6c4009
      for (nlh = nlp->nlh; NLMSG_OK (nlh, size); nlh = NLMSG_NEXT (nlh, size))
Packit 6c4009
	{
Packit 6c4009
	  int ifa_index = 0;
Packit 6c4009
Packit 6c4009
	  /* Check if the message is the one we want */
Packit 6c4009
	  if ((pid_t) nlh->nlmsg_pid != nh.pid || nlh->nlmsg_seq != nlp->seq)
Packit 6c4009
	    continue;
Packit 6c4009
Packit 6c4009
	  if (nlh->nlmsg_type == NLMSG_DONE)
Packit 6c4009
	    break;		/* ok */
Packit 6c4009
Packit 6c4009
	  if (nlh->nlmsg_type == RTM_NEWLINK)
Packit 6c4009
	    {
Packit 6c4009
	      /* We found a new interface. Now extract everything from the
Packit 6c4009
		 interface data we got and need.  */
Packit 6c4009
	      struct ifinfomsg *ifim = (struct ifinfomsg *) NLMSG_DATA (nlh);
Packit 6c4009
	      struct rtattr *rta = IFLA_RTA (ifim);
Packit 6c4009
	      size_t rtasize = IFLA_PAYLOAD (nlh);
Packit 6c4009
Packit 6c4009
	      /* Interfaces are stored in the first "newlink" entries
Packit 6c4009
		 of our list, starting in the order as we got from the
Packit 6c4009
		 kernel.  */
Packit 6c4009
	      ifa_index = map_newlink (ifim->ifi_index - 1, ifas,
Packit 6c4009
				       map_newlink_data, newlink);
Packit 6c4009
	      if (__glibc_unlikely (ifa_index == -1))
Packit 6c4009
		{
Packit 6c4009
		try_again:
Packit 6c4009
		  result = -EAGAIN;
Packit 6c4009
		  free (ifas);
Packit 6c4009
		  goto exit_free;
Packit 6c4009
		}
Packit 6c4009
	      ifas[ifa_index].ifa.ifa_flags = ifim->ifi_flags;
Packit 6c4009
Packit 6c4009
	      while (RTA_OK (rta, rtasize))
Packit 6c4009
		{
Packit 6c4009
		  char *rta_data = RTA_DATA (rta);
Packit 6c4009
		  size_t rta_payload = RTA_PAYLOAD (rta);
Packit 6c4009
Packit 6c4009
		  switch (rta->rta_type)
Packit 6c4009
		    {
Packit 6c4009
		    case IFLA_ADDRESS:
Packit 6c4009
		      if (rta_payload <= sizeof (ifas[ifa_index].addr))
Packit 6c4009
			{
Packit 6c4009
			  ifas[ifa_index].addr.sl.sll_family = AF_PACKET;
Packit 6c4009
			  memcpy (ifas[ifa_index].addr.sl.sll_addr,
Packit 6c4009
				  (char *) rta_data, rta_payload);
Packit 6c4009
			  ifas[ifa_index].addr.sl.sll_halen = rta_payload;
Packit 6c4009
			  ifas[ifa_index].addr.sl.sll_ifindex
Packit 6c4009
			    = ifim->ifi_index;
Packit 6c4009
			  ifas[ifa_index].addr.sl.sll_hatype = ifim->ifi_type;
Packit 6c4009
Packit 6c4009
			  ifas[ifa_index].ifa.ifa_addr
Packit 6c4009
			    = &ifas[ifa_index].addr.sa;
Packit 6c4009
			}
Packit 6c4009
		      break;
Packit 6c4009
Packit 6c4009
		    case IFLA_BROADCAST:
Packit 6c4009
		      if (rta_payload <= sizeof (ifas[ifa_index].broadaddr))
Packit 6c4009
			{
Packit 6c4009
			  ifas[ifa_index].broadaddr.sl.sll_family = AF_PACKET;
Packit 6c4009
			  memcpy (ifas[ifa_index].broadaddr.sl.sll_addr,
Packit 6c4009
				  (char *) rta_data, rta_payload);
Packit 6c4009
			  ifas[ifa_index].broadaddr.sl.sll_halen = rta_payload;
Packit 6c4009
			  ifas[ifa_index].broadaddr.sl.sll_ifindex
Packit 6c4009
			    = ifim->ifi_index;
Packit 6c4009
			  ifas[ifa_index].broadaddr.sl.sll_hatype
Packit 6c4009
			    = ifim->ifi_type;
Packit 6c4009
Packit 6c4009
			  ifas[ifa_index].ifa.ifa_broadaddr
Packit 6c4009
			    = &ifas[ifa_index].broadaddr.sa;
Packit 6c4009
			}
Packit 6c4009
		      break;
Packit 6c4009
Packit 6c4009
		    case IFLA_IFNAME:	/* Name of Interface */
Packit 6c4009
		      if ((rta_payload + 1) <= sizeof (ifas[ifa_index].name))
Packit 6c4009
			{
Packit 6c4009
			  ifas[ifa_index].ifa.ifa_name = ifas[ifa_index].name;
Packit 6c4009
			  *(char *) __mempcpy (ifas[ifa_index].name, rta_data,
Packit 6c4009
					       rta_payload) = '\0';
Packit 6c4009
			}
Packit 6c4009
		      break;
Packit 6c4009
Packit 6c4009
		    case IFLA_STATS:	/* Statistics of Interface */
Packit 6c4009
		      ifas[ifa_index].ifa.ifa_data = ifa_data_ptr;
Packit 6c4009
		      ifa_data_ptr += rta_payload;
Packit 6c4009
		      memcpy (ifas[ifa_index].ifa.ifa_data, rta_data,
Packit 6c4009
			      rta_payload);
Packit 6c4009
		      break;
Packit 6c4009
Packit 6c4009
		    case IFLA_UNSPEC:
Packit 6c4009
		      break;
Packit 6c4009
		    case IFLA_MTU:
Packit 6c4009
		      break;
Packit 6c4009
		    case IFLA_LINK:
Packit 6c4009
		      break;
Packit 6c4009
		    case IFLA_QDISC:
Packit 6c4009
		      break;
Packit 6c4009
		    default:
Packit 6c4009
		      break;
Packit 6c4009
		    }
Packit 6c4009
Packit 6c4009
		  rta = RTA_NEXT (rta, rtasize);
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
	  else if (nlh->nlmsg_type == RTM_NEWADDR)
Packit 6c4009
	    {
Packit 6c4009
	      struct ifaddrmsg *ifam = (struct ifaddrmsg *) NLMSG_DATA (nlh);
Packit 6c4009
	      struct rtattr *rta = IFA_RTA (ifam);
Packit 6c4009
	      size_t rtasize = IFA_PAYLOAD (nlh);
Packit 6c4009
Packit 6c4009
	      /* New Addresses are stored in the order we got them from
Packit 6c4009
		 the kernel after the interfaces. Theoretically it is possible
Packit 6c4009
		 that we have holes in the interface part of the list,
Packit 6c4009
		 but we always have already the interface for this address.  */
Packit 6c4009
	      ifa_index = newlink + newaddr_idx;
Packit 6c4009
	      int idx = map_newlink (ifam->ifa_index - 1, ifas,
Packit 6c4009
				     map_newlink_data, newlink);
Packit 6c4009
	      if (__glibc_unlikely (idx == -1))
Packit 6c4009
		goto try_again;
Packit 6c4009
	      ifas[ifa_index].ifa.ifa_flags = ifas[idx].ifa.ifa_flags;
Packit 6c4009
	      if (ifa_index > 0)
Packit 6c4009
		ifas[ifa_index - 1].ifa.ifa_next = &ifas[ifa_index].ifa;
Packit 6c4009
	      ++newaddr_idx;
Packit 6c4009
Packit 6c4009
	      while (RTA_OK (rta, rtasize))
Packit 6c4009
		{
Packit 6c4009
		  char *rta_data = RTA_DATA (rta);
Packit 6c4009
		  size_t rta_payload = RTA_PAYLOAD (rta);
Packit 6c4009
Packit 6c4009
		  switch (rta->rta_type)
Packit 6c4009
		    {
Packit 6c4009
		    case IFA_ADDRESS:
Packit 6c4009
		      {
Packit 6c4009
			struct sockaddr *sa;
Packit 6c4009
Packit 6c4009
			if (ifas[ifa_index].ifa.ifa_addr != NULL)
Packit 6c4009
			  {
Packit 6c4009
			    /* In a point-to-poing network IFA_ADDRESS
Packit 6c4009
			       contains the destination address, local
Packit 6c4009
			       address is supplied in IFA_LOCAL attribute.
Packit 6c4009
			       destination address and broadcast address
Packit 6c4009
			       are stored in an union, so it doesn't matter
Packit 6c4009
			       which name we use.  */
Packit 6c4009
			    ifas[ifa_index].ifa.ifa_broadaddr
Packit 6c4009
			      = &ifas[ifa_index].broadaddr.sa;
Packit 6c4009
			    sa = &ifas[ifa_index].broadaddr.sa;
Packit 6c4009
			  }
Packit 6c4009
			else
Packit 6c4009
			  {
Packit 6c4009
			    ifas[ifa_index].ifa.ifa_addr
Packit 6c4009
			      = &ifas[ifa_index].addr.sa;
Packit 6c4009
			    sa = &ifas[ifa_index].addr.sa;
Packit 6c4009
			  }
Packit 6c4009
Packit 6c4009
			sa->sa_family = ifam->ifa_family;
Packit 6c4009
Packit 6c4009
			switch (ifam->ifa_family)
Packit 6c4009
			  {
Packit 6c4009
			  case AF_INET:
Packit 6c4009
			    /* Size must match that of an address for IPv4.  */
Packit 6c4009
			    if (rta_payload == 4)
Packit 6c4009
			      memcpy (&((struct sockaddr_in *) sa)->sin_addr,
Packit 6c4009
				      rta_data, rta_payload);
Packit 6c4009
			    break;
Packit 6c4009
Packit 6c4009
			  case AF_INET6:
Packit 6c4009
			    /* Size must match that of an address for IPv6.  */
Packit 6c4009
			    if (rta_payload == 16)
Packit 6c4009
			      {
Packit 6c4009
				memcpy (&((struct sockaddr_in6 *) sa)->sin6_addr,
Packit 6c4009
					rta_data, rta_payload);
Packit 6c4009
				if (IN6_IS_ADDR_LINKLOCAL (rta_data)
Packit 6c4009
				    || IN6_IS_ADDR_MC_LINKLOCAL (rta_data))
Packit 6c4009
				  ((struct sockaddr_in6 *) sa)->sin6_scope_id
Packit 6c4009
				    = ifam->ifa_index;
Packit 6c4009
			      }
Packit 6c4009
			    break;
Packit 6c4009
Packit 6c4009
			  default:
Packit 6c4009
			    if (rta_payload <= sizeof (ifas[ifa_index].addr))
Packit 6c4009
			      memcpy (sa->sa_data, rta_data, rta_payload);
Packit 6c4009
			    break;
Packit 6c4009
			  }
Packit 6c4009
		      }
Packit 6c4009
		      break;
Packit 6c4009
Packit 6c4009
		    case IFA_LOCAL:
Packit 6c4009
		      if (ifas[ifa_index].ifa.ifa_addr != NULL)
Packit 6c4009
			{
Packit 6c4009
			  /* If ifa_addr is set and we get IFA_LOCAL,
Packit 6c4009
			     assume we have a point-to-point network.
Packit 6c4009
			     Move address to correct field.  */
Packit 6c4009
			  ifas[ifa_index].broadaddr = ifas[ifa_index].addr;
Packit 6c4009
			  ifas[ifa_index].ifa.ifa_broadaddr
Packit 6c4009
			    = &ifas[ifa_index].broadaddr.sa;
Packit 6c4009
			  memset (&ifas[ifa_index].addr, '\0',
Packit 6c4009
				  sizeof (ifas[ifa_index].addr));
Packit 6c4009
			}
Packit 6c4009
Packit 6c4009
		      ifas[ifa_index].ifa.ifa_addr = &ifas[ifa_index].addr.sa;
Packit 6c4009
		      ifas[ifa_index].ifa.ifa_addr->sa_family
Packit 6c4009
			= ifam->ifa_family;
Packit 6c4009
Packit 6c4009
		      switch (ifam->ifa_family)
Packit 6c4009
			{
Packit 6c4009
			case AF_INET:
Packit 6c4009
			  /* Size must match that of an address for IPv4.  */
Packit 6c4009
			  if (rta_payload == 4)
Packit 6c4009
			    memcpy (&ifas[ifa_index].addr.s4.sin_addr,
Packit 6c4009
				  rta_data, rta_payload);
Packit 6c4009
			  break;
Packit 6c4009
Packit 6c4009
			case AF_INET6:
Packit 6c4009
			  /* Size must match that of an address for IPv6.  */
Packit 6c4009
			  if (rta_payload == 16)
Packit 6c4009
			    {
Packit 6c4009
			      memcpy (&ifas[ifa_index].addr.s6.sin6_addr,
Packit 6c4009
				      rta_data, rta_payload);
Packit 6c4009
			      if (IN6_IS_ADDR_LINKLOCAL (rta_data)
Packit 6c4009
				  || IN6_IS_ADDR_MC_LINKLOCAL (rta_data))
Packit 6c4009
				ifas[ifa_index].addr.s6.sin6_scope_id =
Packit 6c4009
				  ifam->ifa_index;
Packit 6c4009
			    }
Packit 6c4009
			  break;
Packit 6c4009
Packit 6c4009
			default:
Packit 6c4009
			  if (rta_payload <= sizeof (ifas[ifa_index].addr))
Packit 6c4009
			    memcpy (ifas[ifa_index].addr.sa.sa_data,
Packit 6c4009
				    rta_data, rta_payload);
Packit 6c4009
			  break;
Packit 6c4009
			}
Packit 6c4009
		      break;
Packit 6c4009
Packit 6c4009
		    case IFA_BROADCAST:
Packit 6c4009
		      /* We get IFA_BROADCAST, so IFA_LOCAL was too much.  */
Packit 6c4009
		      if (ifas[ifa_index].ifa.ifa_broadaddr != NULL)
Packit 6c4009
			memset (&ifas[ifa_index].broadaddr, '\0',
Packit 6c4009
				sizeof (ifas[ifa_index].broadaddr));
Packit 6c4009
Packit 6c4009
		      ifas[ifa_index].ifa.ifa_broadaddr
Packit 6c4009
			= &ifas[ifa_index].broadaddr.sa;
Packit 6c4009
		      ifas[ifa_index].ifa.ifa_broadaddr->sa_family
Packit 6c4009
			= ifam->ifa_family;
Packit 6c4009
Packit 6c4009
		      switch (ifam->ifa_family)
Packit 6c4009
			{
Packit 6c4009
			case AF_INET:
Packit 6c4009
			  /* Size must match that of an address for IPv4.  */
Packit 6c4009
			  if (rta_payload == 4)
Packit 6c4009
			    memcpy (&ifas[ifa_index].broadaddr.s4.sin_addr,
Packit 6c4009
				    rta_data, rta_payload);
Packit 6c4009
			  break;
Packit 6c4009
Packit 6c4009
			case AF_INET6:
Packit 6c4009
			  /* Size must match that of an address for IPv6.  */
Packit 6c4009
			  if (rta_payload == 16)
Packit 6c4009
			    {
Packit 6c4009
			      memcpy (&ifas[ifa_index].broadaddr.s6.sin6_addr,
Packit 6c4009
				      rta_data, rta_payload);
Packit 6c4009
			      if (IN6_IS_ADDR_LINKLOCAL (rta_data)
Packit 6c4009
				  || IN6_IS_ADDR_MC_LINKLOCAL (rta_data))
Packit 6c4009
				ifas[ifa_index].broadaddr.s6.sin6_scope_id
Packit 6c4009
				  = ifam->ifa_index;
Packit 6c4009
			    }
Packit 6c4009
			  break;
Packit 6c4009
Packit 6c4009
			default:
Packit 6c4009
			  if (rta_payload <= sizeof (ifas[ifa_index].addr))
Packit 6c4009
			    memcpy (&ifas[ifa_index].broadaddr.sa.sa_data,
Packit 6c4009
				    rta_data, rta_payload);
Packit 6c4009
			  break;
Packit 6c4009
			}
Packit 6c4009
		      break;
Packit 6c4009
Packit 6c4009
		    case IFA_LABEL:
Packit 6c4009
		      if (rta_payload + 1 <= sizeof (ifas[ifa_index].name))
Packit 6c4009
			{
Packit 6c4009
			  ifas[ifa_index].ifa.ifa_name = ifas[ifa_index].name;
Packit 6c4009
			  *(char *) __mempcpy (ifas[ifa_index].name, rta_data,
Packit 6c4009
					       rta_payload) = '\0';
Packit 6c4009
			}
Packit 6c4009
		      else
Packit 6c4009
			abort ();
Packit 6c4009
		      break;
Packit 6c4009
Packit 6c4009
		    case IFA_UNSPEC:
Packit 6c4009
		      break;
Packit 6c4009
		    case IFA_CACHEINFO:
Packit 6c4009
		      break;
Packit 6c4009
		    default:
Packit 6c4009
		      break;
Packit 6c4009
		    }
Packit 6c4009
Packit 6c4009
		  rta = RTA_NEXT (rta, rtasize);
Packit 6c4009
		}
Packit 6c4009
Packit 6c4009
	      /* If we didn't get the interface name with the
Packit 6c4009
		 address, use the name from the interface entry.  */
Packit 6c4009
	      if (ifas[ifa_index].ifa.ifa_name == NULL)
Packit 6c4009
		{
Packit 6c4009
		  int idx = map_newlink (ifam->ifa_index - 1, ifas,
Packit 6c4009
					 map_newlink_data, newlink);
Packit 6c4009
		  if (__glibc_unlikely (idx == -1))
Packit 6c4009
		    goto try_again;
Packit 6c4009
		  ifas[ifa_index].ifa.ifa_name = ifas[idx].ifa.ifa_name;
Packit 6c4009
		}
Packit 6c4009
Packit 6c4009
	      /* Calculate the netmask.  */
Packit 6c4009
	      if (ifas[ifa_index].ifa.ifa_addr
Packit 6c4009
		  && ifas[ifa_index].ifa.ifa_addr->sa_family != AF_UNSPEC
Packit 6c4009
		  && ifas[ifa_index].ifa.ifa_addr->sa_family != AF_PACKET)
Packit 6c4009
		{
Packit 6c4009
		  uint32_t max_prefixlen = 0;
Packit 6c4009
		  char *cp = NULL;
Packit 6c4009
Packit 6c4009
		  ifas[ifa_index].ifa.ifa_netmask
Packit 6c4009
		    = &ifas[ifa_index].netmask.sa;
Packit 6c4009
Packit 6c4009
		  switch (ifas[ifa_index].ifa.ifa_addr->sa_family)
Packit 6c4009
		    {
Packit 6c4009
		    case AF_INET:
Packit 6c4009
		      cp = (char *) &ifas[ifa_index].netmask.s4.sin_addr;
Packit 6c4009
		      max_prefixlen = 32;
Packit 6c4009
		      break;
Packit 6c4009
Packit 6c4009
		    case AF_INET6:
Packit 6c4009
		      cp = (char *) &ifas[ifa_index].netmask.s6.sin6_addr;
Packit 6c4009
		      max_prefixlen = 128;
Packit 6c4009
		      break;
Packit 6c4009
		    }
Packit 6c4009
Packit 6c4009
		  ifas[ifa_index].ifa.ifa_netmask->sa_family
Packit 6c4009
		    = ifas[ifa_index].ifa.ifa_addr->sa_family;
Packit 6c4009
Packit 6c4009
		  if (cp != NULL)
Packit 6c4009
		    {
Packit 6c4009
		      unsigned int preflen;
Packit 6c4009
Packit 6c4009
		      if (ifam->ifa_prefixlen > max_prefixlen)
Packit 6c4009
			preflen = max_prefixlen;
Packit 6c4009
		      else
Packit 6c4009
			preflen = ifam->ifa_prefixlen;
Packit 6c4009
Packit 6c4009
		      for (i = 0; i < preflen / 8; i++)
Packit 6c4009
			*cp++ = 0xff;
Packit 6c4009
		      if (preflen % 8)
Packit 6c4009
			*cp = 0xff << (8 - preflen % 8);
Packit 6c4009
		    }
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  assert (ifa_data_ptr <= (char *) &ifas[newlink + newaddr] + ifa_data_size);
Packit 6c4009
Packit 6c4009
  if (newaddr_idx > 0)
Packit 6c4009
    {
Packit 6c4009
      for (i = 0; i < newlink; ++i)
Packit 6c4009
	if (map_newlink_data[i] == -1)
Packit 6c4009
	  {
Packit 6c4009
	    /* We have fewer links then we anticipated.  Adjust the
Packit 6c4009
	       forward pointer to the first address entry.  */
Packit 6c4009
	    ifas[i - 1].ifa.ifa_next = &ifas[newlink].ifa;
Packit 6c4009
	  }
Packit 6c4009
Packit 6c4009
      if (i == 0 && newlink > 0)
Packit 6c4009
	/* No valid link, but we allocated memory.  We have to
Packit 6c4009
	   populate the first entry.  */
Packit 6c4009
	memmove (ifas, &ifas[newlink], sizeof (struct ifaddrs_storage));
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  *ifap = &ifas[0].ifa;
Packit 6c4009
Packit 6c4009
 exit_free:
Packit 6c4009
  __netlink_free_handle (&nh);
Packit 6c4009
  __netlink_close (&nh);
Packit 6c4009
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Create a linked list of `struct ifaddrs' structures, one for each
Packit 6c4009
   network interface on the host machine.  If successful, store the
Packit 6c4009
   list in *IFAP and return 0.  On errors, return -1 and set `errno'.  */
Packit 6c4009
int
Packit 6c4009
__getifaddrs (struct ifaddrs **ifap)
Packit 6c4009
{
Packit 6c4009
  int res;
Packit 6c4009
Packit 6c4009
  do
Packit 6c4009
    res = getifaddrs_internal (ifap);
Packit 6c4009
  while (res == -EAGAIN);
Packit 6c4009
Packit 6c4009
  return res;
Packit 6c4009
}
Packit 6c4009
weak_alias (__getifaddrs, getifaddrs)
Packit 6c4009
libc_hidden_def (__getifaddrs)
Packit 6c4009
libc_hidden_weak (getifaddrs)
Packit 6c4009
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
__freeifaddrs (struct ifaddrs *ifa)
Packit 6c4009
{
Packit 6c4009
  free (ifa);
Packit 6c4009
}
Packit 6c4009
weak_alias (__freeifaddrs, freeifaddrs)
Packit 6c4009
libc_hidden_def (__freeifaddrs)
Packit 6c4009
libc_hidden_weak (freeifaddrs)