aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog12
-rw-r--r--sysdeps/posix/getaddrinfo.c165
-rw-r--r--sysdeps/unix/sysv/linux/check_pf.c123
3 files changed, 153 insertions, 147 deletions
diff --git a/ChangeLog b/ChangeLog
index 37cda4cb9f..b28845df41 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2007-11-12 Ulrich Drepper <drepper@redhat.com>
+
+ * include/ifaddrs.c (struct in6addrinfo): Add prefixlen field.
+ * sysdeps/unix/sysv/linux/check_pf.c (make_request): Always return
+ list of interfaces. Also store prefix length.
+ * sysdeps/posix/getaddrinfo.c (sort_result): Add prefixlen element.
+ (rfc3484_sort): In rule 9, for IPv4 addresses count only matching
+ prefix if source and destination address are in the same subnet.
+ (getaddrinfo): Always call __check_pf. Fill in prefixlen field.
+ Always look for matching record in in6ai list.
+ Correct source_addr_len value for IPv6->IPv4 converted records.
+
2007-11-11 Roland McGrath <roland@frob.com>
* include/kernel-features.h: New file.
diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 8cf9c6bdfe..57970b4363 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -1006,6 +1006,7 @@ struct sort_result
uint8_t source_addr_len;
bool got_source_addr;
uint8_t source_addr_flags;
+ uint8_t prefixlen;
};
@@ -1223,7 +1224,7 @@ static int
fls (uint32_t a)
{
uint32_t mask;
- int n = 0;
+ int n;
for (n = 0, mask = 1 << 31; n < 32; mask >>= 1, ++n)
if ((a & mask) != 0)
break;
@@ -1350,20 +1351,31 @@ rfc3484_sort (const void *p1, const void *p2)
assert (a1->source_addr.ss_family == PF_INET);
assert (a2->source_addr.ss_family == PF_INET);
- struct sockaddr_in *in1_dst;
- struct sockaddr_in *in1_src;
- struct sockaddr_in *in2_dst;
- struct sockaddr_in *in2_src;
-
- in1_dst = (struct sockaddr_in *) a1->dest_addr->ai_addr;
- in1_src = (struct sockaddr_in *) &a1->source_addr;
- in2_dst = (struct sockaddr_in *) a2->dest_addr->ai_addr;
- in2_src = (struct sockaddr_in *) &a2->source_addr;
-
- bit1 = fls (ntohl (in1_dst->sin_addr.s_addr
- ^ in1_src->sin_addr.s_addr));
- bit2 = fls (ntohl (in2_dst->sin_addr.s_addr
- ^ in2_src->sin_addr.s_addr));
+ /* Outside of subnets, as defined by the network masks,
+ common address prefixes for IPv4 addresses make no sense.
+ So, define a non-zero value only if source and
+ destination address are on the same subnet. */
+ struct sockaddr_in *in1_dst
+ = (struct sockaddr_in *) a1->dest_addr->ai_addr;
+ in_addr_t in1_dst_addr = ntohl (in1_dst->sin_addr.s_addr);
+ struct sockaddr_in *in1_src
+ = (struct sockaddr_in *) &a1->source_addr;
+ in_addr_t in1_src_addr = ntohl (in1_src->sin_addr.s_addr);
+ in_addr_t netmask1 = 0xffffffffu << (32 - a1->prefixlen);
+
+ if ((in1_src_addr & netmask1) == (in1_dst_addr & netmask1))
+ bit1 = fls (in1_dst_addr ^ in1_src_addr);
+
+ struct sockaddr_in *in2_dst
+ = (struct sockaddr_in *) a2->dest_addr->ai_addr;
+ in_addr_t in2_dst_addr = ntohl (in2_dst->sin_addr.s_addr);
+ struct sockaddr_in *in2_src
+ = (struct sockaddr_in *) &a2->source_addr;
+ in_addr_t in2_src_addr = ntohl (in2_src->sin_addr.s_addr);
+ in_addr_t netmask2 = 0xffffffffu << (32 - a2->prefixlen);
+
+ if ((in2_src_addr & netmask2) == (in2_dst_addr & netmask2))
+ bit2 = fls (in2_dst_addr ^ in2_src_addr);
}
else if (a1->dest_addr->ai_family == PF_INET6)
{
@@ -1799,63 +1811,42 @@ getaddrinfo (const char *name, const char *service,
int sockfd = -1;
pid_t nl_pid;
#endif
- /* We might need information about what kind of interfaces are available.
- But even if AI_ADDRCONFIG is not used, if the user requested IPv6
- addresses we have to know whether an address is deprecated or
- temporary. */
- if ((hints->ai_flags & AI_ADDRCONFIG) || hints->ai_family == PF_UNSPEC
- || hints->ai_family == PF_INET6)
- {
- /* Determine whether we have IPv4 or IPv6 interfaces or both. We
- cannot cache the results since new interfaces could be added at
- any time. */
- __check_pf (&seen_ipv4, &seen_ipv6, &in6ai, &in6ailen);
+ /* We might need information about what interfaces are available.
+ Also determine whether we have IPv4 or IPv6 interfaces or both. We
+ cannot cache the results since new interfaces could be added at
+ any time. */
+ __check_pf (&seen_ipv4, &seen_ipv6, &in6ai, &in6ailen);
#ifdef HAVE_NETLINK_ROUTE
- if (! __no_netlink_support)
- {
- sockfd = __socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (! __no_netlink_support)
+ {
+ sockfd = __socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
- struct sockaddr_nl nladdr;
- memset (&nladdr, '\0', sizeof (nladdr));
- nladdr.nl_family = AF_NETLINK;
+ struct sockaddr_nl nladdr;
+ memset (&nladdr, '\0', sizeof (nladdr));
+ nladdr.nl_family = AF_NETLINK;
- socklen_t addr_len = sizeof (nladdr);
+ socklen_t addr_len = sizeof (nladdr);
- if (sockfd >= 0
- && __bind (fd, (struct sockaddr *) &nladdr, sizeof (nladdr)) == 0
- && __getsockname (sockfd, (struct sockaddr *) &nladdr,
- &addr_len) == 0
- && make_request (sockfd, nladdr.nl_pid, &seen_ipv4, &seen_ipv6,
- in6ai, in6ailen) == 0)
- {
- /* It worked. */
- nl_pid = nladdr.nl_pid;
- goto got_netlink_socket;
- }
+ if (sockfd >= 0
+ && __bind (fd, (struct sockaddr *) &nladdr, sizeof (nladdr)) == 0
+ && __getsockname (sockfd, (struct sockaddr *) &nladdr,
+ &addr_len) == 0
+ && make_request (sockfd, nladdr.nl_pid, &seen_ipv4, &seen_ipv6,
+ in6ai, in6ailen) == 0)
+ {
+ /* It worked. */
+ nl_pid = nladdr.nl_pid;
+ goto got_netlink_socket;
+ }
- if (sockfd >= 0)
- close_not_cancel_no_status (sockfd);
+ if (sockfd >= 0)
+ close_not_cancel_no_status (sockfd);
-#if __ASSUME_NETLINK_SUPPORT == 0
- /* Remember that there is no netlink support. */
- if (errno != EMFILE && errno != ENFILE)
- __no_netlink_support = 1;
-#else
- else
- {
- if (errno != EMFILE && errno != ENFILE)
- sockfd = -2;
-
- /* We cannot determine what interfaces are available. Be
- pessimistic. */
- seen_ipv4 = true;
- seen_ipv6 = true;
- return;
- }
-#endif
- }
-#endif
+ /* Remember that there is no netlink support. */
+ if (errno != EMFILE && errno != ENFILE)
+ __no_netlink_support = 1;
}
+#endif
#ifdef HAVE_NETLINK_ROUTE
got_netlink_socket:
@@ -1958,7 +1949,6 @@ getaddrinfo (const char *name, const char *service,
for (i = 0, q = p; q != NULL; ++i, last = q, q = q->ai_next)
{
results[i].dest_addr = q;
- results[i].got_source_addr = false;
results[i].service_order = i;
/* If we just looked up the address for a different
@@ -1971,10 +1961,13 @@ getaddrinfo (const char *name, const char *service,
results[i].source_addr_len = results[i - 1].source_addr_len;
results[i].got_source_addr = results[i - 1].got_source_addr;
results[i].source_addr_flags = results[i - 1].source_addr_flags;
+ results[i].prefixlen = results[i - 1].prefixlen;
}
else
{
+ results[i].got_source_addr = false;
results[i].source_addr_flags = 0;
+ results[i].prefixlen = 0;
/* We overwrite the type with SOCK_DGRAM since we do not
want connect() to connect to the other side. If we
@@ -2005,22 +1998,39 @@ getaddrinfo (const char *name, const char *service,
results[i].source_addr_len = sl;
results[i].got_source_addr = true;
- if (q->ai_family == AF_INET6 && in6ai != NULL)
+ if (in6ai != NULL)
{
/* See whether the source address is on the list of
deprecated or temporary addresses. */
struct in6addrinfo tmp;
- struct sockaddr_in6 *sin6p
- = (struct sockaddr_in6 *) &results[i].source_addr;
- memcpy (tmp.addr, &sin6p->sin6_addr, IN6ADDRSZ);
+
+ if (q->ai_family == AF_INET && af == AF_INET)
+ {
+ struct sockaddr_in *sinp
+ = (struct sockaddr_in *) &results[i].source_addr;
+ tmp.addr[0] = 0;
+ tmp.addr[1] = 0;
+ tmp.addr[2] = htonl (0xffff);
+ tmp.addr[3] = sinp->sin_addr.s_addr;
+ }
+ else
+ {
+ struct sockaddr_in6 *sin6p
+ = (struct sockaddr_in6 *) &results[i].source_addr;
+ memcpy (tmp.addr, &sin6p->sin6_addr, IN6ADDRSZ);
+ }
struct in6addrinfo *found
= bsearch (&tmp, in6ai, in6ailen, sizeof (*in6ai),
in6aicmp);
if (found != NULL)
- results[i].source_addr_flags = found->flags;
+ {
+ results[i].source_addr_flags = found->flags;
+ results[i].prefixlen = found->prefixlen;
+ }
}
- else if (q->ai_family == AF_INET && af == AF_INET6)
+
+ if (q->ai_family == AF_INET && af == AF_INET6)
{
/* We have to convert the address. The socket is
IPv6 and the request is for IPv4. */
@@ -2029,10 +2039,17 @@ getaddrinfo (const char *name, const char *service,
struct sockaddr_in *sin
= (struct sockaddr_in *) &results[i].source_addr;
assert (IN6_IS_ADDR_V4MAPPED (sin6->sin6_addr.s6_addr32));
+ sin->sin_family = AF_INET;
+ /* We do not have to initialize sin_port since this
+ fields has the same position and size in the IPv6
+ structure. */
+ assert (offsetof (struct sockaddr_in, sin_port)
+ == offsetof (struct sockaddr_in6, sin6_port));
+ assert (sizeof (sin->sin_port)
+ == sizeof (sin6->sin6_port));
memcpy (&sin->sin_addr,
&sin6->sin6_addr.s6_addr32[3], INADDRSZ);
- results[i].source_addr_len = INADDRSZ;
- sin->sin_family = AF_INET;
+ results[i].source_addr_len = sizeof (struct sockaddr_in);
}
}
else if (errno == EAFNOSUPPORT && af == AF_INET6
diff --git a/sysdeps/unix/sysv/linux/check_pf.c b/sysdeps/unix/sysv/linux/check_pf.c
index df7cbb1897..532e1d923d 100644
--- a/sysdeps/unix/sysv/linux/check_pf.c
+++ b/sysdeps/unix/sysv/linux/check_pf.c
@@ -145,92 +145,69 @@ make_request (int fd, pid_t pid, bool *seen_ipv4, bool *seen_ipv6,
struct rtattr *rta = IFA_RTA (ifam);
size_t len = nlmh->nlmsg_len - NLMSG_LENGTH (sizeof (*ifam));
- switch (ifam->ifa_family)
- {
- const void *local;
- const void *address;
+ if (ifam->ifa_family != AF_INET
+ && ifam->ifa_family != AF_INET6)
+ continue;
- case AF_INET:
- local = NULL;
- address = NULL;
- while (RTA_OK (rta, len))
+ const void *local = NULL;
+ const void *address = NULL;
+ while (RTA_OK (rta, len))
+ {
+ switch (rta->rta_type)
{
- switch (rta->rta_type)
- {
- case IFA_LOCAL:
- local = RTA_DATA (rta);
- break;
-
- case IFA_ADDRESS:
- address = RTA_DATA (rta);
- goto out_v4;
- }
-
- rta = RTA_NEXT (rta, len);
+ case IFA_LOCAL:
+ local = RTA_DATA (rta);
+ break;
+
+ case IFA_ADDRESS:
+ address = RTA_DATA (rta);
+ goto out;
}
- if (local != NULL)
+ rta = RTA_NEXT (rta, len);
+ }
+
+ if (local != NULL)
+ {
+ address = local;
+ out:
+ if (ifam->ifa_family != AF_INET)
{
- out_v4:
- if (*(const in_addr_t *) (address ?: local)
+ if (*(const in_addr_t *) address
!= htonl (INADDR_LOOPBACK))
*seen_ipv4 = true;
}
- break;
-
- case AF_INET6:
- local = NULL;
- address = NULL;
- while (RTA_OK (rta, len))
- {
- switch (rta->rta_type)
- {
- case IFA_LOCAL:
- local = RTA_DATA (rta);
- break;
-
- case IFA_ADDRESS:
- address = RTA_DATA (rta);
- goto out_v6;
- }
-
- rta = RTA_NEXT (rta, len);
- }
-
- if (local != NULL)
+ else
{
- out_v6:
- if (!IN6_IS_ADDR_LOOPBACK (address ?: local))
+ if (!IN6_IS_ADDR_LOOPBACK (address))
*seen_ipv6 = true;
}
+ }
- if (ifam->ifa_flags & (IFA_F_DEPRECATED
- | IFA_F_TEMPORARY
- | IFA_F_HOMEADDRESS
- | IFA_F_OPTIMISTIC))
- {
- struct in6ailist *newp = alloca (sizeof (*newp));
- newp->info.flags = (((ifam->ifa_flags
- & (IFA_F_DEPRECATED
- | IFA_F_OPTIMISTIC))
- ? in6ai_deprecated : 0)
- | ((ifam->ifa_flags
- & IFA_F_TEMPORARY)
- ? in6ai_temporary : 0)
- | ((ifam->ifa_flags
- & IFA_F_HOMEADDRESS)
- ? in6ai_homeaddress : 0));
- memcpy (newp->info.addr, address ?: local,
- sizeof (newp->info.addr));
- newp->next = in6ailist;
- in6ailist = newp;
- ++in6ailistlen;
- }
- break;
- default:
- /* Ignore. */
- break;
+ struct in6ailist *newp = alloca (sizeof (*newp));
+ newp->info.flags = (((ifam->ifa_flags
+ & (IFA_F_DEPRECATED
+ | IFA_F_OPTIMISTIC))
+ ? in6ai_deprecated : 0)
+ | ((ifam->ifa_flags
+ & IFA_F_TEMPORARY)
+ ? in6ai_temporary : 0)
+ | ((ifam->ifa_flags
+ & IFA_F_HOMEADDRESS)
+ ? in6ai_homeaddress : 0));
+ newp->info.prefixlen = ifam->ifa_prefixlen;
+ if (ifam->ifa_family == AF_INET)
+ {
+ newp->info.addr[0] = 0;
+ newp->info.addr[1] = 0;
+ newp->info.addr[2] = htonl (0xffff);
+ newp->info.addr[3] = *(const in_addr_t *) address;
}
+ else
+ memcpy (newp->info.addr, address, sizeof (newp->info.addr));
+ newp->next = in6ailist;
+ in6ailist = newp;
+ ++in6ailistlen;
}
else if (nlmh->nlmsg_type == NLMSG_DONE)
/* We found the end, leave the loop. */