This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
[PATCH] getaddrinfo() dest. addr. selection Rule 7 bugfix
- From: Sridhar Samudrala <sri at us dot ibm dot com>
- To: libc-alpha at sourceware dot org
- Date: Fri, 02 Mar 2007 14:22:23 -0800
- Subject: [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. */