This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


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] getaddrinfo() dest. addr. selection Rule 7 bugfix


The following patch fixes Rule 7(Prefer native transport) in the destination 
address selection process as implemented by getaddrinfo().

It uses netlink RTM_GETROUTE and SIOCGIFHWADDR interfaces to get the outgoing
interface type in order to determine if a particular destination address can be 
reached via native transport or not. Using RTM_GETROUTE, we can also get the
source address and hence we can avoid the sequence of socket, connect and 
getsockname to get this info.

I tried putting these netlink specific helper functions in linux specific
directory(sysdeps/unix/sysv/linux), but i ran into some problems while building
glibc. So i added them to getaddinfo.c. As such, the patch needs some code 
re-arrangement, but i think the idea behind the implementation is fine.

Please review and apply with appropriate changes if it looks OK.

Thanks
Sridhar

-------------------------------------------------------------------------------


diff --git a/nscd/gai.c b/nscd/gai.c
index 2e706bd..da2a5c9 100644
--- a/nscd/gai.c
+++ b/nscd/gai.c
@@ -26,6 +26,8 @@ #define __bind bind
 #define __sendto sendto
 #define __strchrnul strchrnul
 #define __getline getline
+#define __recv recv
+#define __ioctl ioctl
 
 #include <getaddrinfo.c>
 
diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 9cebf77..c3aafae 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -60,6 +60,10 @@ #include <bits/libc-lock.h>
 #include <not-cancel.h>
 #include <nscd/nscd-client.h>
 #include <nscd/nscd_proto.h>
+#include <sys/ioctl.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <net/if_arp.h>
 
 #ifdef HAVE_LIBIDN
 extern int __idna_to_ascii_lz (const char *input, char **output, int flags);
@@ -1124,9 +1128,9 @@ struct sort_result
   uint8_t source_addr_len;
   bool got_source_addr;
   uint8_t source_addr_flags;
+  uint8_t native_transport;
 };
 
-
 static int
 get_scope (const struct sockaddr_storage *ss)
 {
@@ -1434,14 +1438,10 @@ rfc3484_sort (const void *p1, const void
   /* Rule 7: Prefer native transport.  */
   if (a1->got_source_addr)
     {
-      if (!(a1->source_addr_flags & in6ai_temporary)
-	  && (a2->source_addr_flags & in6ai_temporary))
-	return -1;
-      if ((a1->source_addr_flags & in6ai_temporary)
-	  && !(a2->source_addr_flags & in6ai_temporary))
-	return 1;
-
-      /* XXX Do we need to check anything beside temporary addresses?  */
+	if (a1->native_transport && !a2->native_transport)
+		return -1;
+	if (!a1->native_transport && a2->native_transport)
+		return 1;
     }
 
 
@@ -1850,6 +1850,156 @@ gaiconf_reload (void)
     gaiconf_init ();
 }
 
+int
+if_name2type(char *ifname)
+{
+  struct ifreq ifr;
+  int fd;
+
+  strncpy (ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+
+  fd = __socket (AF_INET, SOCK_DGRAM, 0);
+  if (fd < 0)
+    return 0;
+
+  if (__ioctl (fd, SIOCGIFHWADDR, &ifr) < 0)
+    {
+      __close (fd);
+      return 0;
+    }
+
+  __close (fd);
+  return ifr.ifr_hwaddr.sa_family;
+}
+
+/* Get the source address and outgoing interface type for a given destination 
+ * address.
+ * If netlink support is present, RTM_GET_ROUTE interface is used to get the
+ * source address and outgoing interface type of the matching route.
+ * If not, UDP connect followed by getsockname is used to get the source 
+ * address. We cannot get oif_type when using this mechanism. 
+ */
+int
+get_src_and_oif_type(int family, struct sockaddr *dst, socklen_t dst_len,
+                     struct sockaddr_storage *src, int *oif_type)
+{
+  struct {
+    struct nlmsghdr  n;
+    struct rtmsg     r;
+    char             buf[1024];
+  } req;
+  struct nlmsghdr *nlmp;
+  struct rtmsg *rtmp;
+  struct rtattr *rtatp;
+  int rtattrlen;
+  struct sockaddr_in6 *sin6p;
+  struct sockaddr_in *sinp;
+  struct in6_addr *in6p;
+  struct in_addr *inp;
+  int rval = -1;
+  char buf[4096];
+  char oif_name[IFNAMSIZ];
+
+  int fd = __socket (PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+  if (fd == -1)
+    {
+      fd = __socket (family, SOCK_DGRAM, IPPROTO_IP);
+      socklen_t sl = sizeof (struct sockaddr_storage);
+      if (fd != -1
+          && __connect (fd, dst, dst_len) == 0
+          && __getsockname (fd, (struct sockaddr *)src, &sl) == 0)
+        {
+          __close(fd);
+          *oif_type = ARPHRD_VOID;
+          return 0;
+        }
+      else
+        return -1;
+    }
+
+  memset(&req, 0, sizeof (req));
+  req.n.nlmsg_len = NLMSG_LENGTH (sizeof (struct rtmsg));
+  req.n.nlmsg_flags = NLM_F_REQUEST;
+  req.n.nlmsg_type = RTM_GETROUTE;
+
+  req.r.rtm_family = family;
+  rtatp = (struct rtattr *)(((char *)&req) + NLMSG_ALIGN (req.n.nlmsg_len));
+  rtatp->rta_type = RTA_DST;
+  switch (family)
+    {
+    case AF_INET6:
+      sin6p = (struct sockaddr_in6 *)dst;
+      rtatp->rta_len = RTA_LENGTH (16);
+      memcpy (RTA_DATA (rtatp), &sin6p->sin6_addr, 16);
+      break;
+    case AF_INET:
+      sinp = (struct sockaddr_in *)dst;
+      rtatp->rta_len = RTA_LENGTH (4);
+      memcpy (RTA_DATA (rtatp), &sinp->sin_addr, 4);
+      break;
+    default:
+      goto fail; 
+    }
+  req.n.nlmsg_len = NLMSG_ALIGN (req.n.nlmsg_len) + RTA_ALIGN (rtatp->rta_len);
+
+  rval = TEMP_FAILURE_RETRY (__send (fd, &req, req.n.nlmsg_len, 0));
+  if (rval < 0)
+    goto fail; 
+
+  rval = TEMP_FAILURE_RETRY (__recv (fd, buf, sizeof(buf), 0)); 
+  if (rval < 0)
+    goto fail;
+
+  nlmp = (struct nlmsghdr *)buf;
+  if (!NLMSG_OK (nlmp, rval))
+    {
+      rval = -1;
+      goto fail;
+    }
+
+  rtmp = (struct rtmsg *)NLMSG_DATA (nlmp);
+  rtatp = (struct rtattr *)RTM_RTA (rtmp);
+  rtattrlen = RTM_PAYLOAD (nlmp);
+
+  for (; RTA_OK (rtatp, rtattrlen); rtatp = RTA_NEXT (rtatp, rtattrlen))
+    {
+      switch (rtatp->rta_type)
+        {
+        case RTA_OIF:
+          {
+            int oif_index = *(int *)RTA_DATA (rtatp);
+
+            if_indextoname (oif_index, oif_name);
+            *oif_type = if_name2type (oif_name);
+            break;
+          }
+        case RTA_PREFSRC:
+          switch (rtmp->rtm_family)
+            {
+              case AF_INET6:
+                sin6p = (struct sockaddr_in6 *)src;		
+                in6p = (struct in6_addr *)RTA_DATA (rtatp);
+                sin6p->sin6_family = AF_INET6;
+                memcpy (&sin6p->sin6_addr, in6p, 16);
+                break;
+              case AF_INET:
+                sinp = (struct sockaddr_in *)src;
+                inp = (struct in_addr *)RTA_DATA (rtatp);
+                sinp->sin_family = AF_INET;
+                sinp->sin_addr.s_addr = inp->s_addr;
+                break;
+            }
+            break;
+        default:
+	  break;
+        }
+    } 
+  rval = 0;
+
+fail:
+  __close(fd);
+  return rval;
+}
 
 int
 getaddrinfo (const char *name, const char *service,
@@ -2067,19 +2217,13 @@ #endif
 	  else
 	    {
 	      results[i].source_addr_flags = 0;
+	      results[i].native_transport = 1;
 
-	      /* We overwrite the type with SOCK_DGRAM since we do not
-		 want connect() to connect to the other side.  If we
-		 cannot determine the source address remember this
-		 fact.  */
-	      int fd = __socket (q->ai_family, SOCK_DGRAM, IPPROTO_IP);
 	      socklen_t sl = sizeof (results[i].source_addr);
-	      if (fd != -1
-		  && __connect (fd, q->ai_addr, q->ai_addrlen) == 0
-		  && __getsockname (fd,
-				    (struct sockaddr *) &results[i].source_addr,
-				    &sl) == 0)
-		{
+	      int oif_type;
+	      if (get_src_and_oif_type(q->ai_family, q->ai_addr, q->ai_addrlen,
+				       &results[i].source_addr, &oif_type) == 0)
+	        {
 		  results[i].source_addr_len = sl;
 		  results[i].got_source_addr = true;
 
@@ -2098,14 +2242,16 @@ #endif
 		      if (found != NULL)
 			results[i].source_addr_flags = found->flags;
 		    }
+
+		   if  (oif_type == ARPHRD_SIT ||
+		        oif_type == ARPHRD_TUNNEL ||
+		        oif_type == ARPHRD_TUNNEL6)
+			   results[i].native_transport = 0;
 		}
 	      else
 		/* Just make sure that if we have to process the same
 		   address again we do not copy any memory.  */
 		results[i].source_addr_len = 0;
-
-	      if (fd != -1)
-		close_not_cancel_no_status (fd);
 	    }
 
 	  /* Remember the canonical name.  */



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