From eba0ec199690e944da56f1398a9ab407f95af0d5 Mon Sep 17 00:00:00 2001 From: Ken Raeburn Date: Thu, 3 Dec 2009 02:17:28 +0000 Subject: [PATCH] fix slow behavior on Mac OS X with link-local addresses When using my previous patch, if a local hostname like "foobar.local" is looked up, you may get back a link-local IPv6 address. However, the KDC seems to be unable to respond from that address, resulting in a ~1s delay for each KDC exchange while waiting for the client to fail over to another address (in my case, another IPv6 address). Create a new object for holding whatever auxiliary information might be needed to properly transmit the response to the client. Currently, that only means the interface index number under IPv6. Fill it in on receipt, always; copy it back to the pktinfo structure when transmitting, ONLY if the local source address is link-local. If an error occurs while transmitting the reply, print both the remote destination address and the local source address. Use getnameinfo instead of inet_ntop. Apply the same changes to kadmind, to keep the versions of network.c more or less in sync. ticket: 6591 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@23440 dc483132-0cff-0310-8789-dd5450dbe970 --- src/kadmin/server/network.c | 58 ++++++++++++++++++++++++++++++------- src/kdc/network.c | 58 ++++++++++++++++++++++++++++++------- 2 files changed, 96 insertions(+), 20 deletions(-) diff --git a/src/kadmin/server/network.c b/src/kadmin/server/network.c index 0b8880e49..3cc0d74fa 100644 --- a/src/kadmin/server/network.c +++ b/src/kadmin/server/network.c @@ -1146,10 +1146,22 @@ static void init_addr(krb5_fulladdr *faddr, struct sockaddr *sa) } } +/* This holds whatever additional information might be needed to + properly send back to the client from the correct local address. + + In this case, we only need one datum so far: On Mac OS X, the + kernel doesn't seem to like sending from link-local addresses + unless we specify the correct interface. */ + +union aux_addressing_info { + int ipv6_ifindex; +}; + static int recv_from_to(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen, - struct sockaddr *to, socklen_t *tolen) + struct sockaddr *to, socklen_t *tolen, + union aux_addressing_info *auxaddr) { #if (!defined(IP_PKTINFO) && !defined(IPV6_PKTINFO)) || !defined(CMSG_SPACE) if (to && tolen) { @@ -1219,6 +1231,7 @@ recv_from_to(int s, void *buf, size_t len, int flags, ((struct sockaddr_in6 *)to)->sin6_addr = pktinfo->ipi6_addr; ((struct sockaddr_in6 *)to)->sin6_family = AF_INET6; *tolen = sizeof(struct sockaddr_in6); + auxaddr->ipv6_ifindex = pktinfo->ipi6_ifindex; return r; } #endif @@ -1234,7 +1247,8 @@ recv_from_to(int s, void *buf, size_t len, int flags, static int send_to_from(int s, void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen, - const struct sockaddr *from, socklen_t fromlen) + const struct sockaddr *from, socklen_t fromlen, + union aux_addressing_info *auxaddr) { #if (!defined(IP_PKTINFO) && !defined(IPV6_PKTINFO)) || !defined(CMSG_SPACE) return sendto(s, buf, len, flags, to, tolen); @@ -1294,6 +1308,15 @@ send_to_from(int s, void *buf, size_t len, int flags, struct in6_pktinfo *p = (struct in6_pktinfo *)CMSG_DATA(cmsgptr); const struct sockaddr_in6 *from6 = (const struct sockaddr_in6 *)from; p->ipi6_addr = from6->sin6_addr; + /* Because of the possibility of asymmetric routing, we + normally don't want to specify an interface. However, + Mac OS X doesn't like sending from a link-local address + (which can come up in testing at least, if you wind up + with a "foo.local" name) unless we do specify the + interface. */ + if (IN6_IS_ADDR_LINKLOCAL(&from6->sin6_addr)) + p->ipi6_ifindex = auxaddr->ipv6_ifindex; + /* otherwise, already zero */ } msg.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); break; @@ -1376,14 +1399,17 @@ static void process_packet(void *handle, krb5_data *response; char pktbuf[MAX_DGRAM_SIZE]; int port_fd = conn->fd; + union aux_addressing_info auxaddr; kadm5_server_handle_t server_handle = (kadm5_server_handle_t)handle; response = NULL; saddr_len = sizeof(saddr); daddr_len = sizeof(daddr); + memset(&auxaddr, 0, sizeof(auxaddr)); cc = recv_from_to(port_fd, pktbuf, sizeof(pktbuf), 0, (struct sockaddr *)&saddr, &saddr_len, - (struct sockaddr *)&daddr, &daddr_len); + (struct sockaddr *)&daddr, &daddr_len, + &auxaddr); if (cc == -1) { if (errno != EINTR /* This is how Linux indicates that a previous @@ -1431,16 +1457,28 @@ static void process_packet(void *handle, return; cc = send_to_from(port_fd, response->data, (socklen_t) response->length, 0, (struct sockaddr *)&saddr, saddr_len, - (struct sockaddr *)&daddr, daddr_len); + (struct sockaddr *)&daddr, daddr_len, + &auxaddr); if (cc == -1) { - char addrbuf[46]; + /* Note that the local address (daddr*) has no port number + info associated with it. */ + char saddrbuf[NI_MAXHOST], sportbuf[NI_MAXSERV]; + char daddrbuf[NI_MAXHOST]; + int e = errno; krb5_free_data(server_handle->context, response); - if (inet_ntop(((struct sockaddr *)&saddr)->sa_family, - addr.contents, addrbuf, sizeof(addrbuf)) == 0) { - strlcpy(addrbuf, "?", sizeof(addrbuf)); + if (getnameinfo((struct sockaddr *)&daddr, daddr_len, + daddrbuf, sizeof(daddrbuf), 0, 0, + NI_NUMERICHOST) != 0) { + strlcpy(daddrbuf, "?", sizeof(daddrbuf)); + } + if (getnameinfo((struct sockaddr *)&saddr, saddr_len, + saddrbuf, sizeof(saddrbuf), sportbuf, sizeof(sportbuf), + NI_NUMERICHOST|NI_NUMERICSERV) != 0) { + strlcpy(saddrbuf, "?", sizeof(saddrbuf)); + strlcpy(sportbuf, "?", sizeof(sportbuf)); } - com_err(prog, errno, "while sending reply to %s/%d", - addrbuf, faddr.port); + com_err(prog, e, "while sending reply to %s/%s from %s", + saddrbuf, sportbuf, daddrbuf); return; } if (cc != response->length) { diff --git a/src/kdc/network.c b/src/kdc/network.c index a96bc3028..24a613720 100644 --- a/src/kdc/network.c +++ b/src/kdc/network.c @@ -1003,10 +1003,22 @@ static void init_addr(krb5_fulladdr *faddr, struct sockaddr *sa) } } +/* This holds whatever additional information might be needed to + properly send back to the client from the correct local address. + + In this case, we only need one datum so far: On Mac OS X, the + kernel doesn't seem to like sending from link-local addresses + unless we specify the correct interface. */ + +union aux_addressing_info { + int ipv6_ifindex; +}; + static int recv_from_to(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen, - struct sockaddr *to, socklen_t *tolen) + struct sockaddr *to, socklen_t *tolen, + union aux_addressing_info *auxaddr) { #if (!defined(IP_PKTINFO) && !defined(IPV6_PKTINFO)) || !defined(CMSG_SPACE) if (to && tolen) { @@ -1075,6 +1087,7 @@ recv_from_to(int s, void *buf, size_t len, int flags, ((struct sockaddr_in6 *)to)->sin6_addr = pktinfo->ipi6_addr; ((struct sockaddr_in6 *)to)->sin6_family = AF_INET6; *tolen = sizeof(struct sockaddr_in6); + auxaddr->ipv6_ifindex = pktinfo->ipi6_ifindex; return r; } #endif @@ -1090,7 +1103,8 @@ recv_from_to(int s, void *buf, size_t len, int flags, static int send_to_from(int s, void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen, - const struct sockaddr *from, socklen_t fromlen) + const struct sockaddr *from, socklen_t fromlen, + union aux_addressing_info *auxaddr) { #if (!defined(IP_PKTINFO) && !defined(IPV6_PKTINFO)) || !defined(CMSG_SPACE) return sendto(s, buf, len, flags, to, tolen); @@ -1150,6 +1164,15 @@ send_to_from(int s, void *buf, size_t len, int flags, struct in6_pktinfo *p = (struct in6_pktinfo *)CMSG_DATA(cmsgptr); const struct sockaddr_in6 *from6 = (const struct sockaddr_in6 *)from; p->ipi6_addr = from6->sin6_addr; + /* Because of the possibility of asymmetric routing, we + normally don't want to specify an interface. However, + Mac OS X doesn't like sending from a link-local address + (which can come up in testing at least, if you wind up + with a "foo.local" name) unless we do specify the + interface. */ + if (IN6_IS_ADDR_LINKLOCAL(&from6->sin6_addr)) + p->ipi6_ifindex = auxaddr->ipv6_ifindex; + /* otherwise, already zero */ } msg.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); break; @@ -1206,13 +1229,16 @@ static void process_packet(struct connection *conn, int selflags) krb5_data *response; char pktbuf[MAX_DGRAM_SIZE]; int port_fd = conn->fd; + union aux_addressing_info auxaddr; response = NULL; saddr_len = sizeof(saddr); daddr_len = sizeof(daddr); + memset(&auxaddr, 0, sizeof(auxaddr)); cc = recv_from_to(port_fd, pktbuf, sizeof(pktbuf), 0, (struct sockaddr *)&saddr, &saddr_len, - (struct sockaddr *)&daddr, &daddr_len); + (struct sockaddr *)&daddr, &daddr_len, + &auxaddr); if (cc == -1) { if (errno != EINTR /* This is how Linux indicates that a previous @@ -1259,16 +1285,28 @@ static void process_packet(struct connection *conn, int selflags) } cc = send_to_from(port_fd, response->data, (socklen_t) response->length, 0, (struct sockaddr *)&saddr, saddr_len, - (struct sockaddr *)&daddr, daddr_len); + (struct sockaddr *)&daddr, daddr_len, + &auxaddr); if (cc == -1) { - char addrbuf[46]; + /* Note that the local address (daddr*) has no port number + info associated with it. */ + char saddrbuf[NI_MAXHOST], sportbuf[NI_MAXSERV]; + char daddrbuf[NI_MAXHOST]; + int e = errno; krb5_free_data(kdc_context, response); - if (inet_ntop(((struct sockaddr *)&saddr)->sa_family, - addr.contents, addrbuf, sizeof(addrbuf)) == 0) { - strlcpy(addrbuf, "?", sizeof(addrbuf)); + if (getnameinfo((struct sockaddr *)&daddr, daddr_len, + daddrbuf, sizeof(daddrbuf), 0, 0, + NI_NUMERICHOST) != 0) { + strlcpy(daddrbuf, "?", sizeof(daddrbuf)); + } + if (getnameinfo((struct sockaddr *)&saddr, saddr_len, + saddrbuf, sizeof(saddrbuf), sportbuf, sizeof(sportbuf), + NI_NUMERICHOST|NI_NUMERICSERV) != 0) { + strlcpy(saddrbuf, "?", sizeof(saddrbuf)); + strlcpy(sportbuf, "?", sizeof(sportbuf)); } - kdc_err(NULL, errno, "while sending reply to %s/%d", - addrbuf, faddr.port); + kdc_err(NULL, e, "while sending reply to %s/%s from %s", + saddrbuf, sportbuf, daddrbuf); return; } if (cc != response->length) { -- 2.26.2