Patches from Alec Peterson, plus some work of my own, to let a multihomed
authorKen Raeburn <raeburn@mit.edu>
Fri, 25 Feb 2000 20:46:35 +0000 (20:46 +0000)
committerKen Raeburn <raeburn@mit.edu>
Fri, 25 Feb 2000 20:46:35 +0000 (20:46 +0000)
KDC respond to requests from the same IP address that the requests were sent
to.

**N.B. This will perform worse in the case of addresses dynamically added
and removed after the KDC has started, since it will be incapable of using
any new addresses.

I'm unclear on why the loopback interface address needs to be included in
the list of addresses.  Apparently, on NetBSD-current, if it's not, packets
sent to other local addresses but over the loopback interface are queued but
not received??  Needs further investigation; could just be a NetBSD bug.

* configure.in: Invoke KRB5_SOCKADDR_SA_LEN.

* network.c: Include <sys/ioctl.h>, <syslog.h>, <net/if.h>.
(foreach_localaddr): New function, copied from
lib/krb5/os/localaddr.c.  Tweaked to not exclude loopback
interface.
(NEED_SOCKETS): Define before including k5-int.h.
(n_sockets): New variable.
(setup_port): New function; creates listening udp ports given an
address.
(setup_network): Call foreach_localaddr to set up listening
sockets on each local address, so we can always respond from the
receiving address.
(listen_and_process): Use n_sockets as upper bound of loop.

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@12070 dc483132-0cff-0310-8789-dd5450dbe970

src/kdc/ChangeLog
src/kdc/configure.in
src/kdc/network.c

index 11bee1b88abc25109b60e388a74db73770fb0c11..30aea06b797780558113df1c82da9ee0af16082e 100644 (file)
@@ -1,3 +1,21 @@
+2000-02-25  Ken Raeburn  <raeburn@mit.edu>
+           Alec H. Peterson  <ahp@hilander.com>
+
+       * configure.in: Invoke KRB5_SOCKADDR_SA_LEN.
+
+       * network.c: Include <sys/ioctl.h>, <syslog.h>, <net/if.h>.
+       (foreach_localaddr): New function, copied from
+       lib/krb5/os/localaddr.c.  Tweaked to not exclude loopback
+       interface.
+       (NEED_SOCKETS): Define before including k5-int.h.
+       (n_sockets): New variable.
+       (setup_port): New function; creates listening udp ports given an
+       address.
+       (setup_network): Call foreach_localaddr to set up listening
+       sockets on each local address, so we can always respond from the
+       receiving address.
+       (listen_and_process): Use n_sockets as upper bound of loop.
+
 2000-02-24  Ken Raeburn  <raeburn@mit.edu>
 
        * kerberos_v4.c (v4_klog): Don't treat the formatted text as a
index bf4d06edb7afd3b94e8e84f32f0e707c98d039a7..76b3ab83e6341381190d31d71deffd73952387d7 100644 (file)
@@ -5,6 +5,7 @@ AC_HEADER_CHECK(termios.h,AC_FUNC_CHECK([tcsetattr],AC_DEFINE(POSIX_TERMIOS)))
 AC_CHECK_HEADERS(syslog.h stdarg.h sys/select.h)
 AC_CHECK_FUNCS(openlog syslog closelog strftime vsprintf)
 AC_PROG_AWK
+KRB5_SOCKADDR_SA_LEN
 CHECK_SIGNALS
 HAS_ANSI_VOLATILE
 dnl
index 3ff47e0f0eede6a1ac4d6563fcada18b1cc7dd59..05ca79aa99846b6f76384d0d6772d855eb91e82f 100644 (file)
  * Network code for Kerberos v5 KDC.
  */
 
+#define NEED_SOCKETS
 #include "k5-int.h"
 #include "com_err.h"
 #include "kdc_util.h"
 #include "extern.h"
 #include "kdc5_err.h"
+#include <sys/ioctl.h>
+#include <syslog.h>
 
 #include <ctype.h>
 #ifdef HAVE_NETINET_IN_H
 #endif
 #include <arpa/inet.h>
 
+#include <net/if.h>
+
 extern int errno;
 
 static int *udp_port_fds = (int *) NULL;
 static u_short *udp_port_nums = (u_short *) NULL;
 static int n_udp_ports = 0;
+static int n_sockets = 0;
 static int max_udp_ports = 0;
 static fd_set select_fds;
 static int select_nfds;
@@ -87,19 +93,196 @@ static krb5_error_code add_port(port)
 }
 #undef safe_realloc
 
+/* Keep in sync with lib/krb5/os/localaddr.c version.  */
+
+/*
+ * BSD 4.4 defines the size of an ifreq to be
+ * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len
+ * However, under earlier systems, sa_len isn't present, so the size is 
+ * just sizeof(struct ifreq)
+ */
+#define USE_AF AF_INET
+#define USE_TYPE SOCK_DGRAM
+#define USE_PROTO 0
+#define SOCKET_ERRNO errno
+
+#ifdef HAVE_SA_LEN
+#ifndef max
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#endif
+#define ifreq_size(i) max(sizeof(struct ifreq),\
+     sizeof((i).ifr_name)+(i).ifr_addr.sa_len)
+#else
+#define ifreq_size(i) sizeof(struct ifreq)
+#endif /* HAVE_SA_LEN*/
+
+static int
+foreach_localaddr (data, pass1fn, betweenfn, pass2fn)
+    void *data;
+    int (*pass1fn) (void *, struct sockaddr *);
+    int (*betweenfn) (void *);
+    int (*pass2fn) (void *, struct sockaddr *);
+{
+    struct ifreq *ifr, ifreq;
+    struct ifconf ifc;
+    int s, code, n, i;
+    int est_if_count = 8, est_ifreq_size;
+    char *buf = 0;
+    size_t current_buf_size = 0;
+    
+    s = socket (USE_AF, USE_TYPE, USE_PROTO);
+    if (s < 0)
+       return SOCKET_ERRNO;
+
+    /* At least on NetBSD, an ifreq can hold an IPv4 address, but
+       isn't big enough for an IPv6 or ethernet address.  So add a
+       little more space.  */
+    est_ifreq_size = sizeof (struct ifreq) + 8;
+    current_buf_size = est_ifreq_size * est_if_count;
+    buf = malloc (current_buf_size);
+
+ ask_again:
+    memset(buf, 0, current_buf_size);
+    ifc.ifc_len = current_buf_size;
+    ifc.ifc_buf = buf;
+
+    code = ioctl (s, SIOCGIFCONF, (char *)&ifc);
+    if (code < 0) {
+       int retval = errno;
+       closesocket (s);
+       return retval;
+    }
+    /* Test that the buffer was big enough that another ifreq could've
+       fit easily, if the OS wanted to provide one.  That seems to be
+       the only indication we get, complicated by the fact that the
+       associated address may make the required storage a little
+       bigger than the size of an ifreq.  */
+    if (current_buf_size - ifc.ifc_len < sizeof (struct ifreq) + 40) {
+       int new_size;
+       char *newbuf;
+
+       est_if_count *= 2;
+       new_size = est_ifreq_size * est_if_count;
+       newbuf = realloc (buf, new_size);
+       if (newbuf == 0) {
+           krb5_error_code e = errno;
+           free (buf);
+           return e;
+       }
+       current_buf_size = new_size;
+       buf = newbuf;
+       goto ask_again;
+    }
+
+    n = ifc.ifc_len;
+
+    for (i = 0; i < n; i+= ifreq_size(*ifr) ) {
+       ifr = (struct ifreq *)((caddr_t) ifc.ifc_buf+i);
+
+       strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof (ifreq.ifr_name));
+       if (ioctl (s, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
+       skip:
+           /* mark for next pass */
+           ifr->ifr_name[0] = 0;
+
+           continue;
+       }
+#if 0 /* Access from same host doesn't work if loopback is omitted?  */
+#ifdef IFF_LOOPBACK
+           /* None of the current callers want loopback addresses.  */
+       if (ifreq.ifr_flags & IFF_LOOPBACK)
+           goto skip;
+#endif
+#endif
+       /* Ignore interfaces that are down.  */
+       if (!(ifreq.ifr_flags & IFF_UP))
+           goto skip;
+
+       if ((*pass1fn) (data, &ifr->ifr_addr)) {
+           abort ();
+       }
+    }
+
+    if (betweenfn && (*betweenfn)(data)) {
+       abort ();
+    }
+
+    if (pass2fn)
+       for (i = 0; i < n; i+= ifreq_size(*ifr) ) {
+           ifr = (struct ifreq *)((caddr_t) ifc.ifc_buf+i);
+
+           if (ifr->ifr_name[0] == 0)
+               /* Marked in first pass to be ignored.  */
+               continue;
+
+           if ((*pass2fn) (data, &ifr->ifr_addr)) {
+               abort ();
+           }
+       }
+    closesocket(s);
+    free (buf);
+
+    return 0;
+}
+
+struct socksetup {
+    const char *prog;
+    krb5_error_code retval;
+};
+
+static int
+setup_port(void *P_data, struct sockaddr *addr)
+{
+    struct socksetup *data = P_data;
+    int sock, i;
+
+    switch (addr->sa_family) {
+    case AF_INET:
+    {
+       struct sockaddr_in *sin = (struct sockaddr_in *) addr, psin;
+       for (i = 0; i < n_udp_ports; i++) {
+           sock = socket (PF_INET, SOCK_DGRAM, 0);
+           if (sock == -1) {
+               data->retval = errno;
+               com_err(data->prog, data->retval,
+                       "Cannot create server socket for port %d address %s",
+                       udp_port_nums[i], inet_ntoa (sin->sin_addr));
+               return 1;
+           }
+           psin = *sin;
+           psin.sin_port = htons (udp_port_nums[i]);
+           if (bind (sock, (struct sockaddr *)&psin, sizeof (psin)) == -1) {
+               data->retval = errno;
+               com_err(data->prog, data->retval,
+                       "Cannot bind server socket to port %d address %s",
+                       udp_port_nums[i], inet_ntoa (sin->sin_addr));
+               return 1;
+           }
+           FD_SET (sock, &select_fds);
+           if (sock > select_nfds)
+               select_nfds = sock;
+           udp_port_fds[n_sockets++] = sock;
+           krb5_klog_syslog (LOG_INFO, "listening on fd %d: %s port %d", sock,
+                            inet_ntoa (sin->sin_addr), udp_port_nums[i]);
+       }
+    }
+    default:
+       break;
+    }
+    return 0;
+}
+
 krb5_error_code
 setup_network(prog)
-const char *prog;
+    const char *prog;
 {
-    struct sockaddr_in sin;
+    struct socksetup setup_data;
     krb5_error_code retval;
-    u_short port;
     char *cp;
-    int i;
+    int i, port;
 
     FD_ZERO(&select_fds);
     select_nfds = 0;
-    memset((char *)&sin, 0, sizeof(sin));
 
     /* Handle each realm's ports */
     for (i=0; i<kdc_numrealms; i++) {
@@ -118,24 +301,16 @@ const char *prog;
        }
     }
 
-    for (i=0; i<n_udp_ports; i++) {
-       if ((udp_port_fds[i] = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
-           retval = errno;
-           com_err(prog, 0, "Cannot create server socket on port %d",
-                   udp_port_nums[i]);
-           return(retval);
-       }
-       sin.sin_port = htons(udp_port_nums[i]);
-       if (bind(udp_port_fds[i], (struct sockaddr *) &sin,
-                sizeof(sin)) == -1) {
-           retval = errno;
-           com_err(prog, 0, "Cannot bind server socket on port %d",
-                   udp_port_nums[i]);
-           return(retval);
-       }
-       FD_SET(udp_port_fds[i], &select_fds);
-       if (udp_port_fds[i]+1 > select_nfds)
-           select_nfds = udp_port_fds[i]+1;
+    setup_data.prog = prog;
+    setup_data.retval = 0;
+    krb5_klog_syslog (LOG_INFO, "setting up network...");
+    if (foreach_localaddr (&setup_data, setup_port, 0, 0)) {
+       return setup_data.retval;
+    }
+    krb5_klog_syslog (LOG_INFO, "set up %d sockets", n_sockets);
+    if (n_sockets == 0) {
+       com_err(prog, 0, "no sockets set up?");
+       return -1;
     }
 
     return 0;
@@ -223,7 +398,7 @@ const char *prog;
            com_err(prog, errno, "while selecting for network input");
            continue;
        }
-       for (i=0; i<n_udp_ports; i++) {
+       for (i=0; i<n_sockets; i++) {
            if (FD_ISSET(udp_port_fds[i], &readfds)) {
                process_packet(udp_port_fds[i], prog, udp_port_nums[i]);
                nfound--;