* 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.
This commit is contained in:
Ulrich Drepper 2007-11-12 23:55:58 +00:00
parent c1600ce36d
commit 6f3914d5a3
3 changed files with 154 additions and 148 deletions

View File

@ -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> 2007-11-11 Roland McGrath <roland@frob.com>
* include/kernel-features.h: New file. * include/kernel-features.h: New file.

View File

@ -1006,6 +1006,7 @@ struct sort_result
uint8_t source_addr_len; uint8_t source_addr_len;
bool got_source_addr; bool got_source_addr;
uint8_t source_addr_flags; uint8_t source_addr_flags;
uint8_t prefixlen;
}; };
@ -1223,7 +1224,7 @@ static int
fls (uint32_t a) fls (uint32_t a)
{ {
uint32_t mask; uint32_t mask;
int n = 0; int n;
for (n = 0, mask = 1 << 31; n < 32; mask >>= 1, ++n) for (n = 0, mask = 1 << 31; n < 32; mask >>= 1, ++n)
if ((a & mask) != 0) if ((a & mask) != 0)
break; break;
@ -1350,20 +1351,31 @@ rfc3484_sort (const void *p1, const void *p2)
assert (a1->source_addr.ss_family == PF_INET); assert (a1->source_addr.ss_family == PF_INET);
assert (a2->source_addr.ss_family == PF_INET); assert (a2->source_addr.ss_family == PF_INET);
struct sockaddr_in *in1_dst; /* Outside of subnets, as defined by the network masks,
struct sockaddr_in *in1_src; common address prefixes for IPv4 addresses make no sense.
struct sockaddr_in *in2_dst; So, define a non-zero value only if source and
struct sockaddr_in *in2_src; 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);
in1_dst = (struct sockaddr_in *) a1->dest_addr->ai_addr; if ((in1_src_addr & netmask1) == (in1_dst_addr & netmask1))
in1_src = (struct sockaddr_in *) &a1->source_addr; bit1 = fls (in1_dst_addr ^ in1_src_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 struct sockaddr_in *in2_dst
^ in1_src->sin_addr.s_addr)); = (struct sockaddr_in *) a2->dest_addr->ai_addr;
bit2 = fls (ntohl (in2_dst->sin_addr.s_addr in_addr_t in2_dst_addr = ntohl (in2_dst->sin_addr.s_addr);
^ in2_src->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) else if (a1->dest_addr->ai_family == PF_INET6)
{ {
@ -1799,14 +1811,8 @@ getaddrinfo (const char *name, const char *service,
int sockfd = -1; int sockfd = -1;
pid_t nl_pid; pid_t nl_pid;
#endif #endif
/* We might need information about what kind of interfaces are available. /* We might need information about what interfaces are available.
But even if AI_ADDRCONFIG is not used, if the user requested IPv6 Also determine whether we have IPv4 or IPv6 interfaces or both. We
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 cannot cache the results since new interfaces could be added at
any time. */ any time. */
__check_pf (&seen_ipv4, &seen_ipv6, &in6ai, &in6ailen); __check_pf (&seen_ipv4, &seen_ipv6, &in6ai, &in6ailen);
@ -1836,26 +1842,11 @@ getaddrinfo (const char *name, const char *service,
if (sockfd >= 0) if (sockfd >= 0)
close_not_cancel_no_status (sockfd); close_not_cancel_no_status (sockfd);
#if __ASSUME_NETLINK_SUPPORT == 0
/* Remember that there is no netlink support. */ /* Remember that there is no netlink support. */
if (errno != EMFILE && errno != ENFILE) if (errno != EMFILE && errno != ENFILE)
__no_netlink_support = 1; __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
}
#endif
}
#ifdef HAVE_NETLINK_ROUTE #ifdef HAVE_NETLINK_ROUTE
got_netlink_socket: 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) for (i = 0, q = p; q != NULL; ++i, last = q, q = q->ai_next)
{ {
results[i].dest_addr = q; results[i].dest_addr = q;
results[i].got_source_addr = false;
results[i].service_order = i; results[i].service_order = i;
/* If we just looked up the address for a different /* 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].source_addr_len = results[i - 1].source_addr_len;
results[i].got_source_addr = results[i - 1].got_source_addr; results[i].got_source_addr = results[i - 1].got_source_addr;
results[i].source_addr_flags = results[i - 1].source_addr_flags; results[i].source_addr_flags = results[i - 1].source_addr_flags;
results[i].prefixlen = results[i - 1].prefixlen;
} }
else else
{ {
results[i].got_source_addr = false;
results[i].source_addr_flags = 0; results[i].source_addr_flags = 0;
results[i].prefixlen = 0;
/* We overwrite the type with SOCK_DGRAM since we do not /* We overwrite the type with SOCK_DGRAM since we do not
want connect() to connect to the other side. If we 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].source_addr_len = sl;
results[i].got_source_addr = true; 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 /* See whether the source address is on the list of
deprecated or temporary addresses. */ deprecated or temporary addresses. */
struct in6addrinfo tmp; struct in6addrinfo tmp;
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 *sin6p
= (struct sockaddr_in6 *) &results[i].source_addr; = (struct sockaddr_in6 *) &results[i].source_addr;
memcpy (tmp.addr, &sin6p->sin6_addr, IN6ADDRSZ); memcpy (tmp.addr, &sin6p->sin6_addr, IN6ADDRSZ);
}
struct in6addrinfo *found struct in6addrinfo *found
= bsearch (&tmp, in6ai, in6ailen, sizeof (*in6ai), = bsearch (&tmp, in6ai, in6ailen, sizeof (*in6ai),
in6aicmp); in6aicmp);
if (found != NULL) 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 /* We have to convert the address. The socket is
IPv6 and the request is for IPv4. */ 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 *sin
= (struct sockaddr_in *) &results[i].source_addr; = (struct sockaddr_in *) &results[i].source_addr;
assert (IN6_IS_ADDR_V4MAPPED (sin6->sin6_addr.s6_addr32)); 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, memcpy (&sin->sin_addr,
&sin6->sin6_addr.s6_addr32[3], INADDRSZ); &sin6->sin6_addr.s6_addr32[3], INADDRSZ);
results[i].source_addr_len = INADDRSZ; results[i].source_addr_len = sizeof (struct sockaddr_in);
sin->sin_family = AF_INET;
} }
} }
else if (errno == EAFNOSUPPORT && af == AF_INET6 else if (errno == EAFNOSUPPORT && af == AF_INET6

View File

@ -145,14 +145,12 @@ make_request (int fd, pid_t pid, bool *seen_ipv4, bool *seen_ipv6,
struct rtattr *rta = IFA_RTA (ifam); struct rtattr *rta = IFA_RTA (ifam);
size_t len = nlmh->nlmsg_len - NLMSG_LENGTH (sizeof (*ifam)); size_t len = nlmh->nlmsg_len - NLMSG_LENGTH (sizeof (*ifam));
switch (ifam->ifa_family) if (ifam->ifa_family != AF_INET
{ && ifam->ifa_family != AF_INET6)
const void *local; continue;
const void *address;
case AF_INET: const void *local = NULL;
local = NULL; const void *address = NULL;
address = NULL;
while (RTA_OK (rta, len)) while (RTA_OK (rta, len))
{ {
switch (rta->rta_type) switch (rta->rta_type)
@ -163,7 +161,7 @@ make_request (int fd, pid_t pid, bool *seen_ipv4, bool *seen_ipv6,
case IFA_ADDRESS: case IFA_ADDRESS:
address = RTA_DATA (rta); address = RTA_DATA (rta);
goto out_v4; goto out;
} }
rta = RTA_NEXT (rta, len); rta = RTA_NEXT (rta, len);
@ -171,44 +169,21 @@ make_request (int fd, pid_t pid, bool *seen_ipv4, bool *seen_ipv6,
if (local != NULL) if (local != NULL)
{ {
out_v4: address = local;
if (*(const in_addr_t *) (address ?: local) out:
if (ifam->ifa_family != AF_INET)
{
if (*(const in_addr_t *) address
!= htonl (INADDR_LOOPBACK)) != htonl (INADDR_LOOPBACK))
*seen_ipv4 = true; *seen_ipv4 = true;
} }
break; else
case AF_INET6:
local = NULL;
address = NULL;
while (RTA_OK (rta, len))
{ {
switch (rta->rta_type) if (!IN6_IS_ADDR_LOOPBACK (address))
{
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)
{
out_v6:
if (!IN6_IS_ADDR_LOOPBACK (address ?: local))
*seen_ipv6 = true; *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)); struct in6ailist *newp = alloca (sizeof (*newp));
newp->info.flags = (((ifam->ifa_flags newp->info.flags = (((ifam->ifa_flags
& (IFA_F_DEPRECATED & (IFA_F_DEPRECATED
@ -220,18 +195,20 @@ make_request (int fd, pid_t pid, bool *seen_ipv4, bool *seen_ipv6,
| ((ifam->ifa_flags | ((ifam->ifa_flags
& IFA_F_HOMEADDRESS) & IFA_F_HOMEADDRESS)
? in6ai_homeaddress : 0)); ? in6ai_homeaddress : 0));
memcpy (newp->info.addr, address ?: local, newp->info.prefixlen = ifam->ifa_prefixlen;
sizeof (newp->info.addr)); 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; newp->next = in6ailist;
in6ailist = newp; in6ailist = newp;
++in6ailistlen; ++in6ailistlen;
} }
break;
default:
/* Ignore. */
break;
}
}
else if (nlmh->nlmsg_type == NLMSG_DONE) else if (nlmh->nlmsg_type == NLMSG_DONE)
/* We found the end, leave the loop. */ /* We found the end, leave the loop. */
done = true; done = true;