PR-4108417.diff   [plain text]


diff -up -pr ../ntp-4.2.2/include/ntpd.h ./include/ntpd.h
--- ../ntp-4.2.2/include/ntpd.h	2006-06-06 13:16:20.000000000 -0700
+++ ./include/ntpd.h	2006-06-08 11:10:47.000000000 -0700
@@ -88,6 +88,7 @@ extern	void	wait_for_signal P((void));
 extern	void	unblock_io_and_alarm P((void));
 extern	void	block_io_and_alarm P((void));
 #endif
+extern	int	rebind_interfaces P((void));
 
 /* ntp_leap.c */
 extern	void	init_leap	P((void));
@@ -202,6 +203,7 @@ extern	void	init_timer	P((void));
 extern	void	reinit_timer	P((void));
 extern	void	timer		P((void));
 extern	void	timer_clr_stats P((void));
+extern	u_long	rebind_timer;
 #ifdef OPENSSL
 extern	char	*sys_hostname;
 extern	l_fp	sys_revoketime;
diff -up -pr ../ntp-4.2.2/ntpd/ntp_io.c ./ntpd/ntp_io.c
--- ../ntp-4.2.2/ntpd/ntp_io.c	2006-06-06 13:16:41.000000000 -0700
+++ ./ntpd/ntp_io.c	2006-06-08 11:11:34.000000000 -0700
@@ -590,6 +590,165 @@ convert_isc_if(isc_interface_t *isc_if, 
 		itf->flags |= INT_MULTICAST;
 
 }
+
+static int
+find_isc_if(isc_interface_t *result_isc_if, char *name, int family,
+	    struct sockaddr_storage *addr) {
+	isc_mem_t *mctx = NULL;
+	isc_interfaceiter_t *iter = NULL;
+	isc_result_t result;
+
+	result = isc_interfaceiter_create(mctx, &iter);
+	if (result != ISC_R_SUCCESS)
+		return (result);
+
+	for (result = isc_interfaceiter_first(iter);
+	     result == ISC_R_SUCCESS;
+	     result = isc_interfaceiter_next(iter))
+	{
+		result = isc_interfaceiter_current(iter, result_isc_if);
+		if (result != ISC_R_SUCCESS)
+			break;
+		if (strcmp(result_isc_if->name, name))
+			continue;
+		if (result_isc_if->af != family)
+			continue;
+		if (SOCKCMP(&result_isc_if->address.type.in, addr))
+			continue;
+		break;	/* found it */
+	}
+	isc_interfaceiter_destroy(&iter);
+	return result;
+}
+
+static int
+disable_interface(struct interface *inter)
+{
+	int fd = inter->fd;
+	if (fd != INVALID_SOCKET) {
+		msyslog(LOG_INFO, "Interface %s %s unavailable", inter->name, stoa(&inter->sin));
+		inter->fd = INVALID_SOCKET;
+		if (inter->bfd == fd)
+			inter->bfd = INVALID_SOCKET;
+		BLOCKIO();
+		close_socket(fd);
+		UNBLOCKIO();
+		if (inter->bfd != INVALID_SOCKET) {
+			fd = inter->bfd;
+			inter->bfd = INVALID_SOCKET;
+			BLOCKIO();
+			close_socket(fd);
+			UNBLOCKIO();
+		}
+		inter->flags = 0;
+		inter->num_mcast = inter->received = inter->sent = inter->notsent = 0;
+		inter->ignore_packets = ISC_TRUE;
+		return 1;
+	}
+	return 0;
+}
+
+/* Call when sendto has EADDRNOTAVAIL */
+static int
+rebind_interface(struct interface *inter, isc_interface_t *isc_if, int *changed) {
+	isc_interface_t isc_if_local;
+	int fd;
+
+	if ((inter == any_interface) || /* never kill wildcards */
+	    (inter == any6_interface))
+		return 0;
+	if (isc_if == NULL) {
+		isc_if = &isc_if_local;
+		if (ISC_R_SUCCESS != find_isc_if(isc_if, inter->name, inter->family, &inter->sin)) {
+			/* Interface is gone */
+			if (disable_interface(inter))
+				*changed = TRUE; /* rebind all clients */
+			return 0;
+		}
+	}
+	/* Did state or IP address change? */
+	if (((inter->family == AF_INET) &&
+	    memcmp(&(((struct sockaddr_in*)&inter->sin)->sin_addr),
+		    &(isc_if->address.type.in),
+		   sizeof(struct in_addr))) ||
+	    ((inter->family == AF_INET6) && 
+	     memcmp(&(((struct sockaddr_in6 *)&inter->sin)->sin6_addr),
+		    &(isc_if->address.type.in6),
+		    sizeof(struct in6_addr)))) {
+		*changed = TRUE;
+		msyslog(LOG_DEBUG, "Address %s is going away", stoa(&inter->sin));
+	}
+	if (((isc_if->flags & INTERFACE_F_UP)!=0) != ((inter->flags & INT_UP)!=0)) {
+		*changed = TRUE;
+		msyslog(LOG_DEBUG, "Interface state toggled");
+	}
+	if (*changed) {
+		inter->flags = 0;
+		convert_isc_if(isc_if, inter, htons(NTP_PORT));
+		/*
+		 * Calculate the address hash for each interface address.
+		 */
+		inter->addr_refid = addr2refid(&inter->sin);
+		fd = inter->fd;
+		if (fd != INVALID_SOCKET) {
+			msyslog(LOG_DEBUG, "Closing socket for interface %s", isc_if->name);
+			inter->fd = INVALID_SOCKET;
+			if (fd == inter->bfd)
+				inter->bfd = INVALID_SOCKET;
+			BLOCKIO();
+			close_socket(fd);
+			UNBLOCKIO();
+		}
+		if (inter->bfd != INVALID_SOCKET) {
+			fd = inter->bfd;
+			inter->bfd = INVALID_SOCKET;
+			BLOCKIO();
+			close_socket(inter->bfd);
+			UNBLOCKIO();
+		}
+		if (inter->flags & INT_UP) {
+			set_reuseaddr(1);
+			inter->fd = open_socket(&inter->sin, inter->flags, 0, inter, inter->ifindex);
+			set_reuseaddr(0);
+			if (inter->fd != INVALID_SOCKET) {
+				inter->ignore_packets = ISC_FALSE;
+				msyslog(LOG_INFO, "Listening on interface %s, %s#%d %s",
+					inter->name,
+					stoa((&inter->sin)),
+					NTP_PORT,
+					(inter->ignore_packets == ISC_FALSE) ?
+					"Enabled": "Disabled");
+			} else {
+				msyslog(LOG_ERR, "Interface %s %s failed to bind", inter->name, stoa(&inter->sin));
+				return 1;
+			}
+		} else {
+			msyslog(LOG_ERR, "%s: should not get here %s:%d", __FUNCTION__, __FILE__, __LINE__);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/* refresh interface of peers using inter */
+static void
+rebind_peers(struct interface *inter)
+{
+	struct peer *peer, *next_peer;
+	int n;
+
+	for (n = 0; n < NTP_HASH_SIZE; n++) {
+		for (peer = peer_hash[n]; peer != 0; peer = next_peer) {
+			next_peer = peer->next;
+			if (peer->dstadr == inter || inter == 0) { /* inter==0 rebind all peers */
+				char *src = stoa(&peer->srcadr); /* stoa cycles thru buffers */
+				peer->dstadr = findinterface(&peer->srcadr);
+				msyslog(LOG_DEBUG, "Peer %s using interface %s %s", src, peer->dstadr->name, stoa(&peer->dstadr->sin));
+			}
+		}
+	}
+}
+
 /*
  * create_sockets - create a socket for each interface plus a default
  *			socket for when we don't know where to send
@@ -1977,6 +2136,28 @@ sendpkt(
 
 			netsyslog(LOG_ERR, "sendto(%s) (fd=%d): %m",
 				  stoa(dest), inter->fd);
+#ifdef __APPLE__
+			switch (errno) { /* interface probably changed address or disconnected */
+			case ENETDOWN:
+				/* just ignore */
+				break;
+
+			case EADDRNOTAVAIL:
+			case EHOSTUNREACH:
+			case ENETUNREACH:
+			{
+				int changed = FALSE;
+				rebind_interface(inter, NULL, &changed);
+				if (changed)
+					rebind_peers(inter); /* refresh all peers using this interface */
+				break;
+			}
+
+			default:
+				msyslog(LOG_DEBUG, "rebind_interface not triggered on errno %d", errno);
+				break;
+			}
+#endif	/* __APPLE__ */
 		}
 	}
 	else
@@ -2111,7 +2292,7 @@ read_refclock_packet(SOCKET fd, struct r
 static inline int
 read_network_packet(SOCKET fd, struct interface *itf, l_fp ts)
 {
-	int fromlen;
+	socklen_t fromlen;
 	int buflen;
 	register struct recvbuf *rb;
 
@@ -2397,7 +2578,7 @@ findlocalinterface(
 	SOCKET s;
 	int rtn, i, idx;
 	struct sockaddr_storage saddr;
-	int saddrlen = SOCKLEN(addr);
+	socklen_t saddrlen = SOCKLEN(addr);
 #ifdef DEBUG
 	if (debug>2)
 	    printf("Finding interface for addr %s in list of addresses\n",
@@ -2957,3 +3138,146 @@ find_flagged_addr_in_list(struct sockadd
 	}
 	return (-1); /* Not found */
 }
+
+static int
+add_interface(isc_interface_t *new_isc_if)
+{
+	/* Any free slots? */
+	int i;
+	struct interface *itf = inter_list+nwilds;
+	for (i = nwilds; i < ninterfaces; i++, itf++) {
+		if (itf->fd == INVALID_SOCKET &&
+		    itf->bfd == INVALID_SOCKET)
+			break;
+	}
+	if (i >= MAXINTERFACES) {
+		msyslog(LOG_ERR, "Too many interfaces %d", i);
+		return 1;
+	} else {
+		struct sockaddr_storage resmask;
+		memset(itf, 0, sizeof(*itf));
+		itf->fd = INVALID_SOCKET;
+		itf->bfd = INVALID_SOCKET;
+		convert_isc_if(new_isc_if, itf, htons(NTP_PORT));
+		itf->addr_refid = addr2refid(&itf->sin);
+		itf->ignore_packets = !address_okay(new_isc_if);
+		if (i == ninterfaces) {
+			ninterfaces++;
+		}
+		SET_HOSTMASK(&resmask, itf->sin.ss_family);
+		hack_restrict(RESTRICT_FLAGS, &itf->sin, &resmask,
+			      RESM_NTPONLY|RESM_INTERFACE, RES_IGNORE);
+		if (itf->flags & INT_UP) {
+			itf->ignore_packets = FALSE;
+			set_reuseaddr(1);
+			itf->fd = open_socket(&itf->sin, itf->flags, 0, itf, itf->ifindex);
+			set_reuseaddr(0);
+			if (itf->fd != INVALID_SOCKET) {
+				itf->ignore_packets = ISC_FALSE;
+				msyslog(LOG_INFO, "Listening on interface %s, %s#%d %s",
+					itf->name,
+					stoa((&itf->sin)),
+					NTP_PORT,
+					(itf->ignore_packets == ISC_FALSE) ?
+					"Enabled": "Disabled");
+				return 0;
+			} else {
+				msyslog(LOG_ERR, "Interface %s %s failed to bind", itf->name, stoa((&itf->sin)));
+				return 1;
+			}
+		} else {
+			msyslog(LOG_ERR, "Error: Adding down interface %s %s",
+				itf->name, stoa((&itf->sin)));
+			itf->ignore_packets = TRUE;
+		}
+		return 0;
+	}
+}
+
+int
+rebind_interfaces(void)
+{
+	int changed = FALSE;
+	isc_mem_t *mctx = NULL;
+	isc_interfaceiter_t *iter = NULL;
+	int adds = 0;
+	isc_interface_t *isc_if_add = NULL;
+	isc_result_t result;
+	int i;
+
+	result = isc_interfaceiter_create(mctx, &iter);
+	if (result != ISC_R_SUCCESS)
+		return (result);
+
+	int *still_alive = (int *)calloc(sizeof(int), ninterfaces);
+	if (!still_alive)
+		return -1;
+
+	for (result = isc_interfaceiter_first(iter);
+	     result == ISC_R_SUCCESS;
+	     result = isc_interfaceiter_next(iter))
+	{
+		isc_interface_t isc_if;
+
+		result = isc_interfaceiter_current(iter, &isc_if);
+		if (result != ISC_R_SUCCESS) {
+			msyslog(LOG_ERR, "isc_interfaceiter_current failed");
+			break;
+		}
+
+		if (0 == (isc_if.flags & INTERFACE_F_UP)) /* delete disabled interfaces */
+			continue;
+		/* First process known interfaces */
+		for (i = nwilds; i < ninterfaces; i++) { /* skip wilds */
+			if (inter_list[i].fd == INVALID_SOCKET)
+				continue;
+			if (isc_if.af != inter_list[i].family)
+				continue;
+			if (strcmp(isc_if.name, inter_list[i].name))
+				continue;
+			if ((inter_list[i].family == AF_INET) &&
+			    memcmp(&(((struct sockaddr_in*)&inter_list[i].sin)->sin_addr),
+				   &(isc_if.address.type.in),
+				   sizeof(struct in_addr)))
+				continue;
+			if ((inter_list[i].family == AF_INET6) && 
+			    memcmp(&(((struct sockaddr_in6 *)&inter_list[i].sin)->sin6_addr),
+				   &(isc_if.address.type.in6),
+				   sizeof(struct in6_addr)))
+				continue;
+			
+			int c = FALSE;
+			rebind_interface(inter_list+i, &isc_if, &c);
+			changed |= c;
+			still_alive[i] = TRUE; /* don't disable this interface */
+			break;
+		}
+		if (0 == (isc_if.flags & INTERFACE_F_UP))
+			continue; /* down interfaces are not useful */
+
+		if (i == ninterfaces) {	/* new interface/address */
+			changed = TRUE;
+			isc_if_add = reallocf(isc_if_add, sizeof(isc_interface_t) * (adds+1));
+			if (isc_if_add) {
+				isc_if_add[adds] = isc_if;
+				adds++;
+			}
+		}
+	}
+	isc_interfaceiter_destroy(&iter);
+
+	for (i = nwilds; i < ninterfaces; i++) {
+		if (!still_alive[i])
+			if (disable_interface(inter_list+i))
+				changed = TRUE;
+	}
+	free(still_alive);
+	for (i = 0; i < adds; i++) {
+		add_interface(isc_if_add+i);
+	}
+	realloc(isc_if_add, 0);
+
+	if (changed)
+		rebind_peers(0);
+	return result;
+}
diff -up -pr ../ntp-4.2.2/ntpd/ntp_proto.c ./ntpd/ntp_proto.c
--- ../ntp-4.2.2/ntpd/ntp_proto.c	2006-06-06 13:16:43.000000000 -0700
+++ ./ntpd/ntp_proto.c	2006-06-08 11:10:47.000000000 -0700
@@ -2053,6 +2053,7 @@ clock_select(void)
 				    msyslog(LOG_INFO,
 				    "no servers reachable");
 				report_event(EVNT_PEERSTCHG, NULL);
+				rebind_interfaces();
 			}
 		}
 	}
diff -up -pr ../ntp-4.2.2/ntpd/ntp_timer.c ./ntpd/ntp_timer.c
--- ../ntp-4.2.2/ntpd/ntp_timer.c	2006-06-06 13:16:46.000000000 -0700
+++ ./ntpd/ntp_timer.c	2006-06-08 11:10:47.000000000 -0700
@@ -46,6 +46,7 @@ static	u_long adjust_timer;		/* second t
 static	u_long keys_timer;		/* minute timer */
 static	u_long stats_timer;		/* stats timer */
 static	u_long huffpuff_timer;		/* huff-n'-puff timer */
+u_long rebind_timer;
 #ifdef OPENSSL
 static	u_long revoke_timer;		/* keys revoke timer */
 u_char	sys_revoke = KEY_REVOKE;	/* keys revoke timeout (log2 s) */
@@ -154,7 +155,7 @@ init_timer(void)
 	timer_overflows = 0;
 	timer_xmtcalls = 0;
 	timer_timereset = 0;
-
+	rebind_timer = 0;
 #if !defined(SYS_WINNT)
 	/*
 	 * Set up the alarm interrupt.	The first comes 2**EVENT_TIMEOUT
@@ -343,6 +344,10 @@ timer(void)
 		  write_stats();
 	     stats_timer += stats_write_period;
 	}
+	if (rebind_timer != 0 && rebind_timer <= current_time) {
+		rebind_timer = 0;
+		rebind_interfaces();
+	}
 }
 
 
diff -up -pr ../ntp-4.2.2/ntpd/ntpd.c ./ntpd/ntpd.c
--- ../ntp-4.2.2/ntpd/ntpd.c	2006-06-06 13:16:46.000000000 -0700
+++ ./ntpd/ntpd.c	2006-06-08 11:10:47.000000000 -0700
@@ -114,6 +114,9 @@
 #endif
 #endif
 
+#ifdef __APPLE__
+#include <notify.h>
+#endif	/* __APPLE__ */
 /*
  * Signals we catch for debugging.	If not debugging we ignore them.
  */
@@ -149,6 +152,7 @@ int priority_done = 2;		/* 0 - Set prior
  * Debugging flag
  */
 volatile int debug;
+volatile int info_flag;
 
 /*
  * Set the processing not to be in the forground
@@ -206,6 +210,7 @@ static	RETSIGTYPE	lessdebug	P((int));
 static	RETSIGTYPE	no_debug	P((int));
 #endif	/* not DEBUG */
 
+static RETSIGTYPE info P((int));
 int 		ntpdmain		P((int, char **));
 static void	set_process_priority	P((void));
 static void init_logging P((char *));
@@ -707,6 +712,11 @@ ntpdmain(
 	(void) signal_no_reset(SIGPIPE, SIG_IGN);
 #endif	/* SIGPIPE */
 
+#ifdef __APPLE__
+	int token;
+	(void) signal_no_reset(SIGINFO, info);
+	notify_register_signal("com.apple.system.config.network_change", SIGINFO, &token);
+#endif	/* __APPLE__ */
 	/*
 	 * Call the init_ routines to initialize the data structures.
 	 */
@@ -895,6 +905,11 @@ getgroup:	
 			alarm_flag = 0;
 		}
 
+		if (info_flag) {
+			info_flag = 0;
+			/* 6 is too short for ipv6 duplicate address detection */
+			rebind_timer = current_time + 10; /* let all changes settle down */
+		}
 		if (!was_alarmed && has_full_recv_buffer() == ISC_FALSE)
 		{
 			/*
@@ -1080,3 +1095,11 @@ no_debug(
 }
 #endif  /* not SYS_WINNT */
 #endif	/* not DEBUG */
+
+static RETSIGTYPE
+info(
+	int sig
+	)
+{
+	info_flag = 1;
+}
diff -up -pr ../ntp-4.2.2/ntpdc/nl.pl ./ntpdc/nl.pl
--- ../ntp-4.2.2/ntpdc/nl.pl	2006-06-06 14:11:24.000000000 -0700
+++ ./ntpdc/nl.pl	2006-06-08 11:10:47.000000000 -0700
@@ -1,4 +1,4 @@
-#! /usr/local/bin/perl -w
+#! /usr/bin/perl -w
 
 $found = 0;
 $last = 0;