From 15dbd377dc6bd8cd5c678635c3c043446f38baee Mon Sep 17 00:00:00 2001 From: raeburn Date: Fri, 18 Jul 2008 06:45:34 +0000 Subject: [PATCH] ticket: 6019 status: open On systems with struct rt_msghdr, open a routing socket and wait for messages; when they come in, if the types suggest a possibility of network interface reconfiguration, shut down the KDC's networking and bring it back up again, rescanning the interfaces in the process. Leaving the ticket open because it should be improved: * It should only close down sockets on addresses we no longer have, and bring up sockets only on new addresses. * If we have IPV6_PKTINFO support, it should only listen for IPv4 routing changes. * If we also have IP_PKTINFO support, it shouldn't be used at all. * If we build a KDC on a system with neither struct rt_msghdr nor IP_PKTINFO (do we have any such?), we'll need another solution. Thanks to Nico Williams for the routing socket suggestion, and Apple for the initial (signal-driven) reconfiguration code. git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@20540 dc483132-0cff-0310-8789-dd5450dbe970 --- src/configure.in | 5 ++ src/kdc/network.c | 173 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 174 insertions(+), 4 deletions(-) diff --git a/src/configure.in b/src/configure.in index 362df04597..402e2516bd 100644 --- a/src/configure.in +++ b/src/configure.in @@ -611,6 +611,11 @@ AC_CHECK_TYPES([struct cmsghdr, struct in_pktinfo, struct in6_pktinfo], , , [ #include #include ]) +AC_CHECK_TYPES([struct rt_msghdr], , , [ +#include +#include +#include +]) dnl dnl stuff for util/profile dnl diff --git a/src/kdc/network.c b/src/kdc/network.c index 4b01ba4a9a..f471a3a709 100644 --- a/src/kdc/network.c +++ b/src/kdc/network.c @@ -175,7 +175,7 @@ static const char *paddr (struct sockaddr *sa) /* KDC data. */ -enum kdc_conn_type { CONN_UDP, CONN_UDP_PKTINFO, CONN_TCP_LISTENER, CONN_TCP }; +enum kdc_conn_type { CONN_UDP, CONN_UDP_PKTINFO, CONN_TCP_LISTENER, CONN_TCP, CONN_ROUTING }; /* Per-connection info. */ struct connection { @@ -770,6 +770,127 @@ static void klog_handler(const void *data, size_t len) } #endif +static int network_reconfiguration_needed = 0; + +#ifdef HAVE_STRUCT_RT_MSGHDR +#include + +static char *rtm_type_name(int type) +{ + switch (type) { + case RTM_ADD: return "RTM_ADD"; + case RTM_DELETE: return "RTM_DELETE"; + case RTM_NEWADDR: return "RTM_NEWADDR"; + case RTM_DELADDR: return "RTM_DELADDR"; + case RTM_IFINFO: return "RTM_IFINFO"; + case RTM_OLDADD: return "RTM_OLDADD"; + case RTM_OLDDEL: return "RTM_OLDDEL"; + case RTM_RESOLVE: return "RTM_RESOLVE"; +#ifdef RTM_NEWMADDR + case RTM_NEWMADDR: return "RTM_NEWMADDR"; + case RTM_DELMADDR: return "RTM_DELMADDR"; +#endif + case RTM_MISS: return "RTM_MISS"; + case RTM_REDIRECT: return "RTM_REDIRECT"; + case RTM_LOSING: return "RTM_LOSING"; + case RTM_GET: return "RTM_GET"; + default: return "?"; + } +} + +static void process_routing_update(struct connection *conn, const char *prog, + int selflags) +{ + static struct { + struct rt_msghdr rtm; + char scratchbuf[1024]; + } s; + int n_read; + + krb5_klog_syslog(LOG_INFO, "routing socket readable"); + while ((n_read = read(conn->fd, &s, sizeof(s))) > 0) { + if (n_read < sizeof(s.rtm)) { + krb5_klog_syslog(LOG_ERR, "short read (%d/%d) from routing socket", + n_read, (int) sizeof(s.rtm)); + /* Quick hack to figure out if the interesting + fields are present in a short read. */ +#define RS(FIELD) (offsetof(struct rt_msghdr, FIELD) + sizeof(s.rtm.FIELD)) + if (n_read < RS(rtm_type) || + n_read < RS(rtm_version) || + n_read < RS(rtm_msglen)) + return; + } + krb5_klog_syslog(LOG_INFO, + "got routing msg type %d(%s) v%d", + s.rtm.rtm_type, rtm_type_name(s.rtm.rtm_type), + s.rtm.rtm_version); + if (s.rtm.rtm_msglen > sizeof(s)) { + krb5_klog_syslog(LOG_ERR, + "routing message bigger than buffer (%d/%d)", + s.rtm.rtm_msglen, (int) sizeof(s)); + /* This will probably start spewing messages. Slow it + down. */ + sleep(1); + return; + } + if (s.rtm.rtm_msglen != n_read) { + krb5_klog_syslog(LOG_ERR, + "read %d from routing socket but msglen is %d", + n_read, s.rtm.rtm_msglen); + if (s.rtm.rtm_msglen < sizeof(s.rtm)) { + krb5_klog_syslog(LOG_ERR, + "routing msglen shorter than header??"); + return; + } + } + switch (s.rtm.rtm_type) { + case RTM_ADD: + case RTM_DELETE: + case RTM_NEWADDR: + case RTM_DELADDR: + case RTM_IFINFO: + case RTM_OLDADD: + case RTM_OLDDEL: + krb5_klog_syslog(LOG_INFO, "reconfiguration needed"); + network_reconfiguration_needed = 1; + break; + case RTM_RESOLVE: +#ifdef RTM_NEWMADDR + case RTM_NEWMADDR: + case RTM_DELMADDR: +#endif + case RTM_MISS: + case RTM_REDIRECT: + case RTM_LOSING: + case RTM_GET: + /* Not interesting. */ + krb5_klog_syslog(LOG_DEBUG, "routing msg not interesting"); + break; + default: + krb5_klog_syslog(LOG_INFO, "unhandled routing message type, will reconfigure just for the fun of it"); + network_reconfiguration_needed = 1; + break; + } + } +} + +static void +setup_routing_socket(struct socksetup *data) +{ + int sock = socket(PF_ROUTE, SOCK_RAW, 0); + if (sock < 0) { + int e = errno; + krb5_klog_syslog(LOG_INFO, "couldn't set up routing socket: %s", + strerror(e)); + } else { + krb5_klog_syslog(LOG_INFO, "routing socket is fd %d", sock); + add_fd(data, sock, CONN_ROUTING, process_routing_update); + setnbio(sock); + FD_SET(sock, &sstate.rfds); + } +} +#endif + /* XXX */ extern int krb5int_debug_sendto_kdc; extern void (*krb5int_sendtokdc_debug_handler)(const void*, size_t); @@ -824,6 +945,9 @@ setup_network(const char *prog) setup_data.prog = prog; setup_data.retval = 0; krb5_klog_syslog (LOG_INFO, "setting up network..."); +#ifdef HAVE_STRUCT_RT_MSGHDR + setup_routing_socket(&setup_data); +#endif /* To do: Use RFC 2292 interface (or follow-on) and IPV6_PKTINFO, so we might need only one UDP socket; fall back to binding sockets on each address only if IPV6_PKTINFO isn't @@ -1395,6 +1519,20 @@ static void service_conn(struct connection *conn, const char *prog, conn->service(conn, prog, selflags); } +/* from sendto_kdc.c */ +static int getcurtime(struct timeval *tvp) +{ +#ifdef _WIN32 + struct _timeb tb; + _ftime(&tb); + tvp->tv_sec = tb.time; + tvp->tv_usec = tb.millitm * 1000; + return 0; +#else + return gettimeofday(tvp, 0) ? errno : 0; +#endif +} + krb5_error_code listen_and_process(const char *prog) { @@ -1403,7 +1541,7 @@ listen_and_process(const char *prog) can be rather large. Making this static avoids putting all that junk on the stack. */ static struct select_state sout; - int i, sret; + int i, sret, netchanged = 0; krb5_error_code err; if (conns == (struct connection **) NULL) @@ -1414,12 +1552,37 @@ listen_and_process(const char *prog) krb5_klog_reopen(kdc_context); signal_requests_hup = 0; } - sstate.end_time.tv_sec = sstate.end_time.tv_usec = 0; + + if (network_reconfiguration_needed) { + krb5_klog_syslog(LOG_INFO, "network reconfiguration needed"); + /* It might be tidier to add a timer-callback interface to + the control loop here, but for this one use, it's not a + big deal. */ + err = getcurtime(&sstate.end_time); + if (err) { + com_err(prog, err, "while getting the time"); + continue; + } + sstate.end_time.tv_sec += 3; + netchanged = 1; + } else + sstate.end_time.tv_sec = sstate.end_time.tv_usec = 0; + err = krb5int_cm_call_select(&sstate, &sout, &sret); if (err) { com_err(prog, err, "while selecting for network input(1)"); continue; } + if (sret == 0 && netchanged) { + network_reconfiguration_needed = 0; + closedown_network(prog); + err = setup_network(prog); + if (err) { + com_err(prog, err, "while reinitializing network"); + return err; + } + netchanged = 0; + } if (sret == -1) { if (errno != EINTR) com_err(prog, errno, "while selecting for network input(2)"); @@ -1451,8 +1614,10 @@ closedown_network(const char *prog) return KDC5_NONET; FOREACH_ELT (connections, i, conn) { - if (conn->fd >= 0) + if (conn->fd >= 0) { + krb5_klog_syslog(LOG_INFO, "closing down fd %d", conn->fd); (void) close(conn->fd); + } DEL (connections, i); /* There may also be per-connection data in the tcp structure (tcp.buffer, tcp.response) that we're not freeing here.