svn rev #23440: trunk/src/ kadmin/server/ kdc/

raeburn@MIT.EDU raeburn at MIT.EDU
Wed Dec 2 21:17:28 EST 2009


http://src.mit.edu/fisheye/changelog/krb5/?cs=23440
Commit By: raeburn
Log Message:
ticket: 6591
subject: 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.


Changed Files:
U   trunk/src/kadmin/server/network.c
U   trunk/src/kdc/network.c
Modified: trunk/src/kadmin/server/network.c
===================================================================
--- trunk/src/kadmin/server/network.c	2009-12-03 02:17:24 UTC (rev 23439)
+++ trunk/src/kadmin/server/network.c	2009-12-03 02:17:28 UTC (rev 23440)
@@ -1146,10 +1146,22 @@
     }
 }
 
+/* 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 @@
                 ((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 @@
 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 @@
             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 @@
     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 @@
         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));
         }
-        com_err(prog, errno, "while sending reply to %s/%d",
-                addrbuf, faddr.port);
+        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, e, "while sending reply to %s/%s from %s",
+                saddrbuf, sportbuf, daddrbuf);
         return;
     }
     if (cc != response->length) {

Modified: trunk/src/kdc/network.c
===================================================================
--- trunk/src/kdc/network.c	2009-12-03 02:17:24 UTC (rev 23439)
+++ trunk/src/kdc/network.c	2009-12-03 02:17:28 UTC (rev 23440)
@@ -1003,10 +1003,22 @@
     }
 }
 
+/* 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 @@
                 ((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 @@
 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 @@
             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 @@
     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 @@
     }
     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));
         }
-        kdc_err(NULL, errno, "while sending reply to %s/%d",
-                addrbuf, faddr.port);
+        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, e, "while sending reply to %s/%s from %s",
+                saddrbuf, sportbuf, daddrbuf);
         return;
     }
     if (cc != response->length) {




More information about the cvs-krb5 mailing list