This is the mail archive of the libc-hacker@sources.redhat.com mailing list for the glibc project.

Note that libc-hacker is a closed list. You may look at the archives of this list, but subscription and posting are not open.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Handle MSG_TRUNC in getifaddrs/if_index


Hi!

If there are many networking interfaces in a machine, 4096 bytes buffer
namely for RTM_GETLINK query can be too small.  ATM the calls just
fail if MSG_TRUNC is seen, while the following patch handles
that case by resending the request (with increased seq number) and
doubling the buffer size for receive.

Although I don't have that many interfaces myself (IP aliases don't
count), I have tested it by decreasing the starting buf_size
from 4096 to 256, so that it needs enlargement 2 times even on
my box.

2005-06-13  Jakub Jelinek  <jakub@redhat.com>

	* sysdeps/unix/sysv/linux/netlinkaccess.h (__netlink_sendreq,
	__netlink_receive): Remove prototypes.
	(__netlink_request): New prototype.
	* sysdeps/unix/sysv/linux/ifaddrs.c: Include <alloca.h> and
	<stdint.h>.
	(__netlink_sendreq): Make static.
	(__netlink_receive): Rename to...
	(__netlink_request): ... this.  Add type argument, call
	__netlink_sendreq.  If MSG_TRUNC is set after recvmsg, retry
	with a bigger buffer.  Don't record buffers that contain no
	messages we are expecting.
	(getifaddrs): Use __netlink_request instead of __netlink_sendreq
	and __netlink_receive pairs.  Formatting.
	* sysdeps/unix/sysv/linux/if_index.c (if_nameindex_netlink): Use
	__netlink_request instead of __netlink_sendreq and __netlink_receive
	pair.

--- libc/sysdeps/unix/sysv/linux/netlinkaccess.h.jj	2004-06-30 09:40:24.000000000 +0200
+++ libc/sysdeps/unix/sysv/linux/netlinkaccess.h	2005-06-11 11:26:16.000000000 +0200
@@ -1,4 +1,4 @@
-/* Copyright (C) 2004 Free Software Foundation, Inc.
+/* 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
@@ -55,8 +55,7 @@ extern int __no_netlink_support attribut
 extern int __netlink_open (struct netlink_handle *h);
 extern void __netlink_close (struct netlink_handle *h);
 extern void __netlink_free_handle (struct netlink_handle *h);
-extern int __netlink_sendreq (struct netlink_handle *h, int type);
-extern int __netlink_receive (struct netlink_handle *h);
+extern int __netlink_request (struct netlink_handle *h, int type);
 
 
 #endif /* netlinkaccess.h */
--- libc/sysdeps/unix/sysv/linux/if_index.c.jj	2004-07-12 17:50:25.000000000 +0200
+++ libc/sysdeps/unix/sysv/linux/if_index.c	2005-06-11 12:36:48.000000000 +0200
@@ -1,4 +1,5 @@
-/* Copyright (C) 1997,98,99,2000,2002,2003,2004 Free Software Foundation, Inc.
+/* Copyright (C) 1997, 1998, 1999, 2000, 2002, 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
@@ -188,12 +189,8 @@ if_nameindex_netlink (void)
 
 
   /* Tell the kernel that we wish to get a list of all
-     active interfaces.  */
-  if (__netlink_sendreq (&nh, RTM_GETLINK) < 0)
-    goto exit_close;
-
-  /* Collect all data for every interface.  */
-  if (__netlink_receive (&nh) < 0)
+     active interfaces.  Collect all data for every interface.  */
+  if (__netlink_request (&nh, RTM_GETLINK) < 0)
     goto exit_free;
 
   /* Count the interfaces.  */
@@ -290,7 +287,6 @@ if_nameindex_netlink (void)
 
  exit_free:
   __netlink_free_handle (&nh);
- exit_close:
   __netlink_close (&nh);
 
   return idx;
--- libc/sysdeps/unix/sysv/linux/ifaddrs.c.jj	2005-06-10 00:53:27.000000000 +0200
+++ libc/sysdeps/unix/sysv/linux/ifaddrs.c	2005-06-11 14:00:51.000000000 +0200
@@ -1,5 +1,5 @@
 /* getifaddrs -- get names and addresses of all network interfaces
-   Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+   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
@@ -17,6 +17,7 @@
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA.  */
 
+#include <alloca.h>
 #include <assert.h>
 #include <errno.h>
 #include <ifaddrs.h>
@@ -24,6 +25,7 @@
 #include <netinet/in.h>
 #include <netpacket/packet.h>
 #include <stdbool.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/ioctl.h>
@@ -84,7 +86,7 @@ __netlink_free_handle (struct netlink_ha
 }
 
 
-int
+static int
 __netlink_sendreq (struct netlink_handle *h, int type)
 {
   struct
@@ -114,15 +116,37 @@ __netlink_sendreq (struct netlink_handle
 
 
 int
-__netlink_receive (struct netlink_handle *h)
+__netlink_request (struct netlink_handle *h, int type)
 {
-  struct netlink_res *nlm_next;
-  char buf[4096];
-  struct iovec iov = { buf, sizeof (buf) };
+  struct netlink_res *nlm_next, **new_nlm_list;
+  static size_t buf_size = 4096;
+  char *buf;
   struct sockaddr_nl nladdr;
   struct nlmsghdr *nlmh;
-  int read_len;
+  ssize_t read_len;
   bool done = false;
+  bool use_malloc = false;
+
+  if (__netlink_sendreq (h, type) < 0)
+    return -1;
+
+  if (! __libc_use_alloca (buf_size))
+    {
+      buf = malloc (buf_size);
+      if (buf != NULL)
+	use_malloc = true;
+      else
+	buf = alloca (buf_size);
+    }
+  else
+    buf = alloca (buf_size);
+
+  struct iovec iov = { buf, buf_size };
+
+  if (h->nlm_list != NULL)
+    new_nlm_list = &h->end_ptr->next;
+  else
+    new_nlm_list = &h->nlm_list;
 
   while (! done)
     {
@@ -136,33 +160,66 @@ __netlink_receive (struct netlink_handle
 
       read_len = TEMP_FAILURE_RETRY (__recvmsg (h->fd, &msg, 0));
       if (read_len < 0)
-	return -1;
+	goto out_fail;
 
-      if (msg.msg_flags & MSG_TRUNC)
-	return -1;
+      if (nladdr.nl_pid != 0)
+	continue;
 
-      nlm_next = (struct netlink_res *) malloc (sizeof (struct netlink_res)
-						+ read_len);
-      if (nlm_next == NULL)
-	return -1;
-      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 (__builtin_expect (msg.msg_flags & MSG_TRUNC, 0))
+	{
+	  if (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;
+
+	  buf_size *= 2;
+	  if (use_malloc)
+	    {
+	      char *new_buf = realloc (buf, buf_size);
+	      if (new_buf == NULL)
+		goto out_fail;
+	      new_buf = buf;
+	    }
+	  else
+	    {
+	      buf = malloc (buf_size);
+	      if (buf == NULL)
+		goto out_fail;
+	      use_malloc = true;
+	    }
+
+	  iov.iov_base = buf;
+	  iov.iov_len = 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, remaining_len = read_len;
       for (nlmh = (struct nlmsghdr *) buf;
-	   NLMSG_OK (nlmh, (size_t) read_len);
-	   nlmh = (struct nlmsghdr *) NLMSG_NEXT (nlmh, read_len))
+	   NLMSG_OK (nlmh, remaining_len);
+	   nlmh = (struct nlmsghdr *) NLMSG_NEXT (nlmh, remaining_len))
 	{
-	  if (nladdr.nl_pid != 0 || (pid_t) nlmh->nlmsg_pid != h->pid
+	  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.  */
@@ -176,11 +233,38 @@ __netlink_receive (struct netlink_handle
 		errno = EIO;
 	      else
 		errno = -nlerr->error;
-	      return -1;
+	      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;
 }
 
 
@@ -268,7 +352,7 @@ getifaddrs (struct ifaddrs **ifap)
   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
+  char *ifa_data_ptr;	/* Pointer to the unused part of memory for
 				ifa_data.  */
   int result = 0;
 
@@ -288,28 +372,20 @@ getifaddrs (struct ifaddrs **ifap)
 #endif
 
   /* Tell the kernel that we wish to get a list of all
-     active interfaces.  */
-  if (__netlink_sendreq (&nh, RTM_GETLINK) < 0)
-    {
-      result = -1;
-      goto exit_close;
-    }
-  /* Collect all data for every interface.  */
-  if (__netlink_receive (&nh) < 0)
+     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.  Since we store the addresses after the
-     interfaces in the list, we will later always find the
-     interface before the corresponding addresses.  */
+     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_sendreq (&nh, RTM_GETADDR) < 0
-      /* Collect all data for every interface.  */
-      || __netlink_receive (&nh) < 0)
+  if (__netlink_request (&nh, RTM_GETADDR) < 0)
     {
       result = -1;
       goto exit_free;
@@ -327,7 +403,7 @@ getifaddrs (struct ifaddrs **ifap)
 	continue;
 
       /* Walk through all entries we got from the kernel and look, which
-         message type they contain.  */
+	 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.  */
@@ -423,7 +499,7 @@ getifaddrs (struct ifaddrs **ifap)
 	      /* 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,
+	      ifa_index = map_newlink (ifim->ifi_index - 1, ifas,
 				       map_newlink_data, newlink);
 	      ifas[ifa_index].ifa.ifa_flags = ifim->ifi_flags;
 
@@ -767,8 +843,6 @@ getifaddrs (struct ifaddrs **ifap)
 
  exit_free:
   __netlink_free_handle (&nh);
-
- exit_close:
   __netlink_close (&nh);
 
   return result;

	Jakub


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]