diff options
Diffstat (limited to 'ntpd/ntp_io.c')
| -rw-r--r-- | ntpd/ntp_io.c | 4110 | 
1 files changed, 2883 insertions, 1227 deletions
| diff --git a/ntpd/ntp_io.c b/ntpd/ntp_io.c index 9f2acea8bb35d..faa46461f484e 100644 --- a/ntpd/ntp_io.c +++ b/ntpd/ntp_io.c @@ -12,9 +12,10 @@  #include "ntp_io.h"  #include "iosignal.h"  #include "ntp_refclock.h" -#include "ntp_if.h"  #include "ntp_stdlib.h" +#include "ntp_request.h"  #include "ntp.h" +#include "ntp_unixtime.h"  /* Don't include ISC's version of IPv6 variables and structures */  #define ISC_IPV6_H 1 @@ -31,28 +32,53 @@  #ifdef HAVE_SYS_PARAM_H  # include <sys/param.h>  #endif /* HAVE_SYS_PARAM_H */ -#ifdef HAVE_NETINET_IN_H -# include <netinet/in.h> -#endif -#ifdef HAVE_NETINET_IN_SYSTM_H -# include <netinet/in_systm.h> -#else /* Some old linux systems at least have in_system.h instead. */ -# ifdef HAVE_NETINET_IN_SYSTEM_H -#  include <netinet/in_system.h> -# endif -#endif /* HAVE_NETINET_IN_SYSTM_H */ -#ifdef HAVE_NETINET_IP_H -# include <netinet/ip.h> -#endif  #ifdef HAVE_SYS_IOCTL_H  # include <sys/ioctl.h>  #endif  #ifdef HAVE_SYS_SOCKIO_H	/* UXPV: SIOC* #defines (Frank Vance <fvance@waii.com>) */  # include <sys/sockio.h>  #endif -#include <arpa/inet.h> +#ifdef HAVE_SYS_UIO_H +# include <sys/uio.h> +#endif + +/* + * setsockopt does not always have the same arg declaration + * across all platforms. If it's not defined we make it empty + */ + +#ifndef SETSOCKOPT_ARG_CAST +#define SETSOCKOPT_ARG_CAST +#endif + +/*  + * Set up some macros to look for IPv6 and IPv6 multicast + */ + +#if defined(ISC_PLATFORM_HAVEIPV6) && !defined(DISABLE_IPV6) + +#define INCLUDE_IPV6_SUPPORT + +#if defined(INCLUDE_IPV6_SUPPORT) && defined(IPV6_JOIN_GROUP) && defined(IPV6_LEAVE_GROUP) +#define INCLUDE_IPV6_MULTICAST_SUPPORT + +#endif	/* IPV6 Multicast Support */ +#endif  /* IPv6 Support */  extern int listen_to_virtual_ips; +extern const char *specific_interface; + +#if defined(SO_TIMESTAMP) && defined(SCM_TIMESTAMP) +#if defined(CMSG_FIRSTHDR) +#define HAVE_TIMESTAMP +#define USE_TIMESTAMP_CMSG +#ifndef TIMESTAMP_CTLMSGBUF_SIZE +#define TIMESTAMP_CTLMSGBUF_SIZE 1536 /* moderate default */ +#endif +#else +/* fill in for old/other timestamp interfaces */ +#endif +#endif  #if defined(SYS_WINNT)  #include <transmitbuff.h> @@ -106,12 +132,10 @@ u_long io_timereset;		/* time counters were reset */  struct interface *any_interface;	/* default ipv4 interface */  struct interface *any6_interface;	/* default ipv6 interface */  struct interface *loopback_interface;	/* loopback ipv4 interface */ -struct interface *loopback6_interface;	/* loopback ipv6 interface */ -struct interface inter_list[MAXINTERFACES]; /* Interface list */ +  int ninterfaces;			/* Total number of interfaces */ -int nwilds;				/* Total number of wildcard intefaces */ -int wildipv4 = -1;			/* Index into inter_list for IPv4 wildcard */ -int wildipv6 = -1;			/* Index into inter_list for IPv6 wildcard */ + +volatile int disable_dynamic_updates;   /* when set to != 0 dynamic updates won't happen */  #ifdef REFCLOCK  /* @@ -141,43 +165,118 @@ static	struct refclockio *refio;   */  fd_set activefds;  int maxactivefd; +/* + * bit alternating value to detect verified interfaces during an update cycle + */ +static  u_char          sys_interphase = 0; -static	int create_sockets	P((u_short)); -static	SOCKET	open_socket	P((struct sockaddr_storage *, int, int)); -static	void	close_socket	P((SOCKET)); -#ifdef REFCLOCK -static	void	close_file	P((SOCKET)); +static  struct interface *new_interface P((struct interface *)); +static  void add_interface P((struct interface *)); +static  int update_interfaces P((u_short, interface_receiver_t, void *)); +static  void remove_interface P((struct interface *)); +static  struct interface *create_interface P((u_short, struct interface *)); + +static int	move_fd		P((SOCKET)); + +/* + * Multicast functions + */ +static	isc_boolean_t	addr_ismulticast	 P((struct sockaddr_storage *)); +/* + * Not all platforms support multicast + */ +#ifdef MCAST +static	isc_boolean_t	socket_multicast_enable	 P((struct interface *, int, struct sockaddr_storage *)); +static	isc_boolean_t	socket_multicast_disable P((struct interface *, struct sockaddr_storage *)); +#endif + +#ifdef DEBUG +static void print_interface	P((struct interface *, char *, char *)); +#define DPRINT_INTERFACE(_LVL_, _ARGS_) do { if (debug >= (_LVL_)) { print_interface _ARGS_; } } while (0) +#else +#define DPRINT_INTERFACE(_LVL_, _ARGS_)  #endif -static	char *	fdbits		P((int, fd_set *)); -static	void	set_reuseaddr	P((int));  typedef struct vsock vsock_t; +enum desc_type { FD_TYPE_SOCKET, FD_TYPE_FILE };  struct vsock {  	SOCKET				fd; +	enum desc_type                  type;  	ISC_LINK(vsock_t)		link;  }; -ISC_LIST(vsock_t)	sockets_list; +#if !defined(HAVE_IO_COMPLETION_PORT) && defined(HAS_ROUTING_SOCKET) +/* + * async notification processing (e. g. routing sockets) + */ +/* + * support for receiving data on fd that is not a refclock or a socket + * like e. g. routing sockets + */ +struct asyncio_reader { +	SOCKET fd;		                    /* fd to be read */ +	void  *data;		                    /* possibly local data */ +	void (*receiver)(struct asyncio_reader *);  /* input handler */ +	ISC_LINK(struct asyncio_reader) link;       /* the list this is being kept in */ +}; + +ISC_LIST(struct asyncio_reader) asyncio_reader_list; + +static void delete_asyncio_reader P((struct asyncio_reader *)); +static struct asyncio_reader *new_asyncio_reader P((void)); +static void add_asyncio_reader P((struct asyncio_reader *, enum desc_type)); +static void remove_asyncio_reader P((struct asyncio_reader *)); + +#endif /* !defined(HAVE_IO_COMPLETION_PORT) && defined(HAS_ROUTING_SOCKET) */ + +static void init_async_notifications P((void)); + +static	int create_sockets	P((u_short)); +static	SOCKET	open_socket	P((struct sockaddr_storage *, int, int, struct interface *)); +static	char *	fdbits		P((int, fd_set *)); +static	void	set_reuseaddr	P((int)); +static	isc_boolean_t	socket_broadcast_enable	 P((struct interface *, SOCKET, struct sockaddr_storage *)); +static	isc_boolean_t	socket_broadcast_disable P((struct interface *, struct sockaddr_storage *)); + +ISC_LIST(vsock_t)	fd_list;  typedef struct remaddr remaddr_t;  struct remaddr { -      struct sockaddr_storage        addr; -      int                            if_index; -      ISC_LINK(remaddr_t)              link; +      struct sockaddr_storage	 addr; +      struct interface               *interface; +      ISC_LINK(remaddr_t)	 link;  };  ISC_LIST(remaddr_t)       remoteaddr_list; -void	add_socket_to_list	P((SOCKET)); -void	delete_socket_from_list	P((SOCKET)); -void	add_addr_to_list	P((struct sockaddr_storage *, int)); -void	delete_addr_from_list	P((struct sockaddr_storage *)); -int     find_addr_in_list P((struct sockaddr_storage *)); -int	create_wildcards	P((u_short)); -isc_boolean_t address_okay	P((isc_interface_t *)); -void	convert_isc_if		P((isc_interface_t *, struct interface *, u_short)); +ISC_LIST(struct interface)     inter_list; + +static struct interface *wildipv4 = NULL; +static struct interface *wildipv6 = NULL; + +static void	add_fd_to_list	P((SOCKET, enum desc_type)); +static void	close_and_delete_fd_from_list	P((SOCKET)); +static void	add_addr_to_list	P((struct sockaddr_storage *, struct interface *)); +static void	delete_addr_from_list	P((struct sockaddr_storage *)); +static struct interface *find_addr_in_list	P((struct sockaddr_storage *)); +static struct interface *find_flagged_addr_in_list P((struct sockaddr_storage *, int)); +static void	create_wildcards	P((u_short)); +static isc_boolean_t	address_okay	P((struct interface *)); +static void		convert_isc_if		P((isc_interface_t *, struct interface *, u_short)); +static void	delete_interface_from_list	P((struct interface *)); +static struct interface *getinterface	P((struct sockaddr_storage *, int)); +static struct interface *findlocalinterface	P((struct sockaddr_storage *, int)); +static struct interface *findlocalcastinterface	P((struct sockaddr_storage *, int)); + +/* + * Routines to read the ntp packets + */ +#if !defined(HAVE_IO_COMPLETION_PORT) +static inline int     read_network_packet	P((SOCKET, struct interface *, l_fp)); +static inline int     read_refclock_packet	P((SOCKET, struct refclockio *, l_fp)); +#endif  #ifdef SYS_WINNT  /* @@ -209,6 +308,168 @@ connection_reset_fix(SOCKET fd) {  		return (ISC_R_UNEXPECTED);  }  #endif + +/* + * on Unix systems the stdio library typically + * makes use of file descriptors in the lower + * integer range. stdio usually will make use + * of the file descriptor in the range of + * [0..FOPEN_MAX) + * in order to keep this range clean for socket + * file descriptors we attempt to move them above + * FOPEM_MAX. This is not as easy as it sounds as + * FOPEN_MAX changes from implementation to implementation + * and may exceed to current file decriptor limits. + * We are using following strategy: + * - keep a current socket fd boundary initialized with + *   max(0, min(getdtablesize() - FD_CHUNK, FOPEN_MAX)) + * - attempt to move the descriptor to the boundary or + *   above. + *   - if that fails and boundary > 0 set boundary + *     to min(0, socket_fd_boundary - FD_CHUNK) + *     -> retry + *     if failure and boundary == 0 return old fd + *   - on success close old fd return new fd + * + * effects: + *   - fds will be moved above the socket fd boundary + *     if at all possible. + *   - the socket boundary will be reduced until + *     allocation is possible or 0 is reached - at this + *     point the algrithm will be disabled + */ +static int move_fd(SOCKET fd) +{ +#if !defined(SYS_WINNT) && defined(F_DUPFD) +#ifndef FD_CHUNK +#define FD_CHUNK	10 +#endif +/* + * number of fds we would like to have for + * stdio FILE* available. + * we can pick a "low" number as our use of + * FILE* is limited to log files and temporarily + * to data and config files. Except for log files + * we don't keep the other FILE* open beyond the + * scope of the function that opened it. + */ +#ifndef FD_PREFERRED_SOCKBOUNDARY +#define FD_PREFERRED_SOCKBOUNDARY 48 +#endif + +#ifndef HAVE_GETDTABLESIZE +/* + * if we have no idea about the max fd value set up things + * so we will start at FOPEN_MAX + */ +#define getdtablesize() (FOPEN_MAX+FD_CHUNK) +#endif + +#ifndef FOPEN_MAX +#define FOPEN_MAX	20	/* assume that for the lack of anything better */ +#endif +	static SOCKET socket_boundary = -1; +	SOCKET newfd; + +	/* +	 * check whether boundary has be set up +	 * already +	 */ +	if (socket_boundary == -1) { +		socket_boundary = max(0, min(getdtablesize() - FD_CHUNK,  +					     min(FOPEN_MAX, FD_PREFERRED_SOCKBOUNDARY))); +#ifdef DEBUG +		msyslog(LOG_DEBUG, "ntp_io: estimated max descriptors: %d, initial socket boundary: %d", +			getdtablesize(), socket_boundary); +#endif +	} + +	/* +	 * Leave a space for stdio to work in. potentially moving the +	 * socket_boundary lower until allocation succeeds. +	 */ +	do { +		if (fd >= 0 && fd < socket_boundary) { +			/* inside reserved range: attempt to move fd */ +			newfd = fcntl(fd, F_DUPFD, socket_boundary); +			 +			if (newfd != -1) { +				/* success: drop the old one - return the new one */ +				(void)close(fd); +				return (newfd); +			} +		} else { +			/* outside reserved range: no work - return the original one */ +			return (fd); +		} +		socket_boundary = max(0, socket_boundary - FD_CHUNK); +#ifdef DEBUG +		msyslog(LOG_DEBUG, "ntp_io: selecting new socket boundary: %d", +			socket_boundary); +#endif +	} while (socket_boundary > 0); +#endif /* !defined(SYS_WINNT) && defined(F_DUPFD) */ +	return (fd); +} + +#ifdef DEBUG_TIMING +/* + * collect timing information for various processing + * paths. currently we only pass then on to the file + * for later processing. this could also do histogram + * based analysis in other to reduce the load (and skew) + * dur to the file output + */ +void +collect_timing(struct recvbuf *rb, const char *tag, int count, l_fp *dts) +{ +	char buf[2048]; + +	snprintf(buf, sizeof(buf), "%s %d %s %s",  +		 (rb != NULL) ?  +		 ((rb->dstadr) ? stoa(&rb->recv_srcadr) : "-REFCLOCK-") : "-", +		 count, lfptoa(dts, 9), tag); +	record_timing_stats(buf); +} +#endif +   +/* + * About dynamic interfaces, sockets, reception and more... + * + * the code solves following tasks: + * + *   - keep a current list of active interfaces in order + *     to bind to to the interface address on NTP_PORT so that + *     all wild and specific bindings for NTP_PORT are taken by ntpd + *     to avoid other daemons messing with the time or sockets. + *   - all interfaces keep a list of peers that are referencing  + *     the interface in order to quickly re-assign the peers to + *     new interface in case an interface is deleted (=> gone from system or + *     down) + *   - have a preconfigured socket ready with the right local address + *     for transmission and reception + *   - have an address list for all destination addresses used within ntpd + *     to find the "right" preconfigured socket. + *   - facilitate updating the internal interface list with respect to + *     the current kernel state + * + * special issues: + * + *   - mapping of multicast addresses to the interface affected is not always + *     one to one - especially on hosts with multiple interfaces + *     the code here currently allocates a separate interface entry for those + *     multicast addresses + *     iff it is able to bind to a *new* socket with the multicast address (flags |= MCASTIF) + *     in case of failure the multicast address is bound to an existing interface. + *   - on some systems it is perfectly legal to assign the same address to + *     multiple interfaces. Therefore this code does not keep a list of interfaces + *     but a list of interfaces that represent a unique address as determined by the kernel + *     by the procedure in findlocalinterface. Thus it is perfectly legal to see only + *     one representative of a group of real interfaces if they share the same address. + *  + * Frank Kardel 20050910 + */ +  /*   * init_io - initialize I/O data structures and call socket creation routine   */ @@ -216,6 +477,11 @@ void  init_io(void)  {  #ifdef SYS_WINNT +	if (!Win32InitSockets()) +	{ +		netsyslog(LOG_ERR, "No useable winsock.dll: %m"); +		exit(1); +	}  	init_transmitbuff();  #endif /* SYS_WINNT */ @@ -230,28 +496,27 @@ init_io(void)  	handler_calls = handler_pkts = 0;  	io_timereset = 0;  	loopback_interface = NULL; -	loopback6_interface = NULL; +	any_interface = NULL; +	any6_interface = NULL;  #ifdef REFCLOCK -	refio = 0; +	refio = NULL;  #endif  #if defined(HAVE_SIGNALED_IO)  	(void) set_signal();  #endif -#ifdef SYS_WINNT -	if (!Win32InitSockets()) -	{ -		netsyslog(LOG_ERR, "No useable winsock.dll: %m"); -		exit(1); -	} -#endif /* SYS_WINNT */ +	ISC_LIST_INIT(fd_list); -	ISC_LIST_INIT(sockets_list); +#if !defined(HAVE_IO_COMPLETION_PORT) && defined(HAS_ROUTING_SOCKET) +	ISC_LIST_INIT(asyncio_reader_list); +#endif          ISC_LIST_INIT(remoteaddr_list); +	ISC_LIST_INIT(inter_list); +  	/*  	 * Create the sockets  	 */ @@ -259,95 +524,436 @@ init_io(void)  	(void) create_sockets(htons(NTP_PORT));  	UNBLOCKIO(); +	init_async_notifications(); + +	DPRINTF(3, ("init_io: maxactivefd %d\n", maxactivefd)); +} +  #ifdef DEBUG -	if (debug) -	    printf("init_io: maxactivefd %d\n", maxactivefd); +/* + * function to dump the contents of the interface structure + * for debugging use only. + */ +void +interface_dump(struct interface *itf) +{ +	u_char* cp; +	int i; +	/* Limit the size of the sockaddr_storage hex dump */ +	int maxsize = min(32, sizeof(struct sockaddr_storage)); + +	printf("Dumping interface: %p\n", itf); +	printf("fd = %d\n", itf->fd); +	printf("bfd = %d\n", itf->bfd); +	printf("sin = %s,\n", stoa(&(itf->sin))); +	cp = (u_char*) &(itf->sin); +	for(i = 0; i < maxsize; i++) +	{ +		printf("%02x", *cp++); +		if((i+1)%4 == 0) +			printf(" "); +	} +	printf("\n"); +	printf("bcast = %s,\n", stoa(&(itf->bcast))); +	cp = (u_char*) &(itf->bcast); +	for(i = 0; i < maxsize; i++) +	{ +		printf("%02x", *cp++); +		if((i+1)%4 == 0) +			printf(" "); +	} +	printf("\n"); +	printf("mask = %s,\n", stoa(&(itf->mask))); +	cp = (u_char*) &(itf->mask); +	for(i = 0; i < maxsize; i++) +	{ +		printf("%02x", *cp++); +		if((i+1)%4 == 0) +			printf(" "); +	} +	printf("\n"); +	printf("name = %s\n", itf->name); +	printf("flags = 0x%08x\n", itf->flags); +	printf("last_ttl = %d\n", itf->last_ttl); +	printf("addr_refid = %08x\n", itf->addr_refid); +	printf("num_mcast = %d\n", itf->num_mcast); +	printf("received = %ld\n", itf->received); +	printf("sent = %ld\n", itf->sent); +	printf("notsent = %ld\n", itf->notsent); +	printf("ifindex = %u\n", itf->ifindex); +	printf("scopeid = %u\n", itf->scopeid); +	printf("peercnt = %u\n", itf->peercnt); +	printf("phase = %u\n", itf->phase); +} + +/* + * print_interface - helper to output debug information + */ +static void +print_interface(struct interface *iface, char *pfx, char *sfx) +{ +	printf("%sinterface #%d: fd=%d, bfd=%d, name=%s, flags=0x%x, scope=%d, ifindex=%d", +	       pfx, +	       iface->ifnum, +	       iface->fd, +	       iface->bfd, +	       iface->name, +	       iface->flags, +	       iface->scopeid, +	       iface->ifindex); +	/* Leave these as three printf calls. */ +	printf(", sin=%s", +	       stoa((&iface->sin))); +	if (iface->flags & INT_BROADCAST) +		printf(", bcast=%s,", +		       stoa((&iface->bcast))); +	if (iface->family == AF_INET) +	  printf(", mask=%s", +		 stoa((&iface->mask))); +	printf(", %s:%s", iface->ignore_packets == ISC_FALSE ? "Enabled" : "Disabled", sfx); +	if (debug > 4)	/* in-depth debugging only */ +		interface_dump(iface); +} +  #endif + +#if !defined(HAVE_IO_COMPLETION_PORT) && defined(HAS_ROUTING_SOCKET) +/* + * create an asyncio_reader structure + */ +static struct asyncio_reader * +new_asyncio_reader() +{ +	struct asyncio_reader *reader; + +	reader = (struct asyncio_reader *)emalloc(sizeof(struct asyncio_reader)); + +	memset((char *)reader, 0, sizeof(*reader)); +	ISC_LINK_INIT(reader, link); +	reader->fd = INVALID_SOCKET; +	return reader;  } -int -create_wildcards(u_short port) { +/* + * delete a reader + */ +static void +delete_asyncio_reader(struct asyncio_reader *reader) +{ +	free(reader); +} -	int idx = 0; +/* + * add asynchio_reader + */ +static void +add_asyncio_reader(struct asyncio_reader *reader, enum desc_type type) +{ +	ISC_LIST_APPEND(asyncio_reader_list, reader, link); +	add_fd_to_list(reader->fd, type); +} +	 +/* + * remove asynchio_reader + */ +static void +remove_asyncio_reader(struct asyncio_reader *reader) +{ +	ISC_LIST_UNLINK_TYPE(asyncio_reader_list, reader, link, struct asyncio_reader); + +	if (reader->fd != INVALID_SOCKET) +		close_and_delete_fd_from_list(reader->fd); + +	reader->fd = INVALID_SOCKET; +} +#endif /* !defined(HAVE_IO_COMPLETION_PORT) && defined(HAS_ROUTING_SOCKET) */ + +/* + * interface list enumerator - visitor pattern + */ +void +interface_enumerate(interface_receiver_t receiver, void *data) +{ +	interface_info_t ifi; +        struct interface *interf; + +	ifi.action = IFS_EXISTS; +	 +	for (interf = ISC_LIST_HEAD(inter_list); +	     interf != NULL; +	     interf = ISC_LIST_NEXT(interf, link)) { +		ifi.interface = interf; +		receiver(data, &ifi); +	} +} + +/* + * do standard initialization of interface structure + */ +static void +init_interface(struct interface *interface) +{ +	memset((char *)interface, 0, sizeof(struct interface)); +	ISC_LINK_INIT(interface, link); +	ISC_LIST_INIT(interface->peers); +	interface->fd = INVALID_SOCKET; +	interface->bfd = INVALID_SOCKET; +	interface->num_mcast = 0; +	interface->received = 0; +	interface->sent = 0; +	interface->notsent = 0; +	interface->peercnt = 0; +	interface->phase = sys_interphase; +} + +/* + * create new interface structure initialize from + * template structure or via standard initialization + * function + */ +static struct interface * +new_interface(struct interface *interface) +{ +	static u_int sys_ifnum = 0; + +	struct interface *iface = (struct interface *)emalloc(sizeof(struct interface)); + +	if (interface != NULL) +	{ +		memcpy((char*)iface, (char*)interface, sizeof(*interface)); +	} +	else +	{ +		init_interface(iface); +	} + +	iface->ifnum = sys_ifnum++;  /* count every new instance of an interface in the system */ +	iface->starttime = current_time; + +	return iface; +} + +/* + * return interface storage into free memory pool + */ +static void +delete_interface(struct interface *interface) +{ +	free(interface); +} + +/* + * link interface into list of known interfaces + */ +static void +add_interface(struct interface *interface) +{  	/* -	 * create pseudo-interface with wildcard IPv4 address +	 * Calculate the address hash  	 */ -	inter_list[idx].sin.ss_family = AF_INET; -	((struct sockaddr_in*)&inter_list[idx].sin)->sin_addr.s_addr = htonl(INADDR_ANY); -	((struct sockaddr_in*)&inter_list[idx].sin)->sin_port = port; -	(void) strncpy(inter_list[idx].name, "wildcard", sizeof(inter_list[idx].name)); -	inter_list[idx].mask.ss_family = AF_INET; -	((struct sockaddr_in*)&inter_list[idx].mask)->sin_addr.s_addr = htonl(~(u_int32)0); -	inter_list[idx].bfd = INVALID_SOCKET; -	inter_list[idx].num_mcast = 0; -	inter_list[idx].received = 0; -	inter_list[idx].sent = 0; -	inter_list[idx].notsent = 0; -	inter_list[idx].flags = INT_BROADCAST; -	any_interface = &inter_list[idx]; -#if defined(MCAST) +	interface->addr_refid = addr2refid(&interface->sin); +	 +	ISC_LIST_APPEND(inter_list, interface, link); +	ninterfaces++; +} + +/* + * remove interface from known interface list and clean up + * associated resources + */ +static void +remove_interface(struct interface *interface) +{ +	struct sockaddr_storage resmask; + +	ISC_LIST_UNLINK_TYPE(inter_list, interface, link, struct interface); + +	delete_interface_from_list(interface); +   +	if (interface->fd != INVALID_SOCKET)  +	{ +		msyslog(LOG_INFO, "Deleting interface #%d %s, %s#%d, interface stats: received=%ld, sent=%ld, dropped=%ld, active_time=%ld secs", +			interface->ifnum, +			interface->name, +			stoa((&interface->sin)), +			NTP_PORT,  /* XXX should extract port from sin structure */ +			interface->received, +			interface->sent, +			interface->notsent, +			current_time - interface->starttime); + +		close_and_delete_fd_from_list(interface->fd); +	} +   +	if (interface->bfd != INVALID_SOCKET)  +	{ +		msyslog(LOG_INFO, "Deleting interface #%d %s, broadcast address %s#%d", +			interface->ifnum, +			interface->name, +			stoa((&interface->bcast)), +			(u_short) NTP_PORT);  /* XXX extract port from sin structure */ +		close_and_delete_fd_from_list(interface->bfd); +	} + +	ninterfaces--; +	ntp_monclearinterface(interface); + +	/* remove restrict interface entry */ + +	/* +	 * Blacklist bound interface address +	 */ +	SET_HOSTMASK(&resmask, interface->sin.ss_family); +	hack_restrict(RESTRICT_REMOVEIF, &interface->sin, &resmask, +		      RESM_NTPONLY|RESM_INTERFACE, RES_IGNORE); +} + +static void +list_if_listening(struct interface *interface, u_short port) +{ +	msyslog(LOG_INFO, "Listening on interface #%d %s, %s#%d %s", +		interface->ifnum, +		interface->name, +		stoa((&interface->sin)), +		ntohs( (u_short) port), +		(interface->ignore_packets == ISC_FALSE) ? +		"Enabled": "Disabled"); +} + +static void +create_wildcards(u_short port) { +	isc_boolean_t okipv4 = ISC_TRUE;  	/* -	 * enable possible multicast reception on the broadcast socket +	 * create pseudo-interface with wildcard IPv4 address  	 */ -	inter_list[idx].bcast.ss_family = AF_INET; -	((struct sockaddr_in*)&inter_list[idx].bcast)->sin_port = port; -	((struct sockaddr_in*)&inter_list[idx].bcast)->sin_addr.s_addr = htonl(INADDR_ANY); +#ifdef IPV6_V6ONLY +	if(isc_net_probeipv4() != ISC_R_SUCCESS) +		okipv4 = ISC_FALSE; +#endif + +	if(okipv4 == ISC_TRUE) { +	        struct interface *interface = new_interface(NULL); + +		interface->family = AF_INET; +		interface->sin.ss_family = AF_INET; +		((struct sockaddr_in*)&interface->sin)->sin_addr.s_addr = htonl(INADDR_ANY); +		((struct sockaddr_in*)&interface->sin)->sin_port = port; +		(void) strncpy(interface->name, "wildcard", sizeof(interface->name)); +		interface->mask.ss_family = AF_INET; +		((struct sockaddr_in*)&interface->mask)->sin_addr.s_addr = htonl(~(u_int32)0); +		interface->flags = INT_BROADCAST | INT_UP | INT_WILDCARD; +		interface->ignore_packets = ISC_TRUE; +#if defined(MCAST) +		/* +		 * enable possible multicast reception on the broadcast socket +		 */ +		interface->bcast.ss_family = AF_INET; +		((struct sockaddr_in*)&interface->bcast)->sin_port = port; +		((struct sockaddr_in*)&interface->bcast)->sin_addr.s_addr = htonl(INADDR_ANY);  #endif /* MCAST */ -	wildipv4 = idx; -	idx++; +		interface->fd = open_socket(&interface->sin, +				 interface->flags, 1, interface); + +		if (interface->fd != INVALID_SOCKET) { +			wildipv4 = interface; +			any_interface = interface; +			 +			add_addr_to_list(&interface->sin, interface); +			add_interface(interface); +			list_if_listening(interface, port); +		} else { +			msyslog(LOG_ERR, "unable to bind to wildcard socket address %s - another process may be running - EXITING", +				stoa((&interface->sin))); +			exit(1); +		} +	} -#ifdef HAVE_IPV6 +#ifdef INCLUDE_IPV6_SUPPORT  	/*  	 * create pseudo-interface with wildcard IPv6 address  	 */  	if (isc_net_probeipv6() == ISC_R_SUCCESS) { -		inter_list[idx].sin.ss_family = AF_INET6; -		((struct sockaddr_in6*)&inter_list[idx].sin)->sin6_addr = in6addr_any; -		((struct sockaddr_in6*)&inter_list[idx].sin)->sin6_port = port; -		(void) strncpy(inter_list[idx].name, "wildcard", sizeof(inter_list[idx].name)); -		inter_list[idx].mask.ss_family = AF_INET6; -		memset(&((struct sockaddr_in6*)&inter_list[idx].mask)->sin6_addr.s6_addr, 0xff, sizeof(struct in6_addr)); -		inter_list[idx].bfd = INVALID_SOCKET; -		inter_list[idx].num_mcast = 0; -		inter_list[idx].received = 0; -		inter_list[idx].sent = 0; -		inter_list[idx].notsent = 0; -		inter_list[idx].flags = 0; -		any6_interface = &inter_list[idx]; -		wildipv6 = idx; -		idx++; +	        struct interface *interface = new_interface(NULL); + +		interface->family = AF_INET6; +		interface->sin.ss_family = AF_INET6; +		((struct sockaddr_in6*)&interface->sin)->sin6_addr = in6addr_any; + 		((struct sockaddr_in6*)&interface->sin)->sin6_port = port; +# ifdef ISC_PLATFORM_HAVESCOPEID + 		((struct sockaddr_in6*)&interface->sin)->sin6_scope_id = 0; +# endif +		(void) strncpy(interface->name, "wildcard", sizeof(interface->name)); +		interface->mask.ss_family = AF_INET6; +		memset(&((struct sockaddr_in6*)&interface->mask)->sin6_addr.s6_addr, 0xff, sizeof(struct in6_addr)); +		interface->flags = INT_UP | INT_WILDCARD; +		interface->ignore_packets = ISC_TRUE; + +		interface->fd = open_socket(&interface->sin, +				 interface->flags, 1, interface); + +		if (interface->fd != INVALID_SOCKET) { +			wildipv6 = interface; +			any6_interface = interface; +			add_addr_to_list(&interface->sin, interface); +			add_interface(interface); +			list_if_listening(interface, port); +		} else { +			msyslog(LOG_ERR, "unable to bind to wildcard socket address %s - another process may be running - EXITING", +				stoa((&interface->sin))); +			exit(1); +		}  	}  #endif -	return (idx);  } -isc_boolean_t -address_okay(isc_interface_t *isc_if) { -#ifdef DEBUG -	if (debug > 2) -	    printf("address_okay: listen Virtual: %d, IF name: %s, Up Flag: %d\n",  -		    listen_to_virtual_ips, isc_if->name, (isc_if->flags & INTERFACE_F_UP)); -#endif +static isc_boolean_t +address_okay(struct interface *iface) { -	if (listen_to_virtual_ips == 0  && (strchr(isc_if->name, (int)':') != NULL)) -		return (ISC_FALSE); +	DPRINTF(4, ("address_okay: listen Virtual: %d, IF name: %s\n",  +		    listen_to_virtual_ips, iface->name)); -	/* XXXPDM This should be fixed later, but since we may not have set -	 * the UP flag, we at least get to use the interface. -	 * The UP flag is not always set so we don't do this right now. +	/* +	 * Always allow the loopback  	 */ -/*	if ((isc_if->flags & INTERFACE_F_UP) == 0) -		return (ISC_FALSE); -*/ +	if((iface->flags & INT_LOOPBACK) != 0) { +		DPRINTF(4, ("address_okay: loopback - OK\n")); +		return (ISC_TRUE); +	} + +	/* +	 * Check if the interface is specified +	 */ +	if (specific_interface != NULL) { +		if (strcasecmp(iface->name, specific_interface) == 0) { +			DPRINTF(4, ("address_okay: specific interface name matched - OK\n")); +			return (ISC_TRUE); +		} else { +			DPRINTF(4, ("address_okay: specific interface name NOT matched - FAIL\n")); +			return (ISC_FALSE); +		} +	} +	else { +		if (listen_to_virtual_ips == 0  &&  +		    (strchr(iface->name, (int)':') != NULL)) { +			DPRINTF(4, ("address_okay: virtual ip/alias - FAIL\n")); +			return (ISC_FALSE); +		} +	} + +	DPRINTF(4, ("address_okay: OK\n"));  	return (ISC_TRUE);  } -void -convert_isc_if(isc_interface_t *isc_if, struct interface *itf, u_short port) { + +static void +convert_isc_if(isc_interface_t *isc_if, struct interface *itf, u_short port) +{ +	itf->scopeid = 0; +	itf->family = (short) isc_if->af; +	strcpy(itf->name, isc_if->name);  	if(isc_if->af == AF_INET) {  		itf->sin.ss_family = (u_short) isc_if->af; -		strcpy(itf->name, isc_if->name);  		memcpy(&(((struct sockaddr_in*)&itf->sin)->sin_addr),  		       &(isc_if->address.type.in),  		       sizeof(struct in_addr)); @@ -367,35 +973,31 @@ convert_isc_if(isc_interface_t *isc_if, struct interface *itf, u_short port) {  		       &(isc_if->netmask.type.in),  		       sizeof(struct in_addr));  		((struct sockaddr_in*)&itf->mask)->sin_port = port; - -		if (((isc_if->flags & INTERFACE_F_LOOPBACK) != 0) && (loopback_interface == NULL)) -		{ -			loopback_interface = itf; -		}  	} -#ifdef HAVE_IPV6 +#ifdef INCLUDE_IPV6_SUPPORT  	else if (isc_if->af == AF_INET6) {  		itf->sin.ss_family = (u_short) isc_if->af; -		strcpy(itf->name, isc_if->name);  		memcpy(&(((struct sockaddr_in6 *)&itf->sin)->sin6_addr),  		       &(isc_if->address.type.in6), -		       sizeof(struct in6_addr)); +		       sizeof(((struct sockaddr_in6 *)&itf->sin)->sin6_addr));  		((struct sockaddr_in6 *)&itf->sin)->sin6_port = port; +#ifdef ISC_PLATFORM_HAVESCOPEID +		((struct sockaddr_in6 *)&itf->sin)->sin6_scope_id = isc_netaddr_getzone(&isc_if->address); +		itf->scopeid = isc_netaddr_getzone(&isc_if->address); +#endif  		itf->mask.ss_family = itf->sin.ss_family;  		memcpy(&(((struct sockaddr_in6 *)&itf->mask)->sin6_addr),  		       &(isc_if->netmask.type.in6),  		       sizeof(struct in6_addr));  		((struct sockaddr_in6 *)&itf->mask)->sin6_port = port; - -		if (((isc_if->flags & INTERFACE_F_LOOPBACK) != 0) && (loopback6_interface == NULL)) -		{ -			loopback6_interface = itf; -		} +		/* Copy the interface index */ +		itf->ifindex = isc_if->ifindex;  	} -#endif /* HAVE_IPV6 */ +#endif /* INCLUDE_IPV6_SUPPORT */ + -		/* Process the rest of the flags */ +	/* Process the rest of the flags */  	if((isc_if->flags & INTERFACE_F_UP) != 0)  		itf->flags |= INT_UP; @@ -403,57 +1005,228 @@ convert_isc_if(isc_interface_t *isc_if, struct interface *itf, u_short port) {  		itf->flags |= INT_LOOPBACK;  	if((isc_if->flags & INTERFACE_F_POINTTOPOINT) != 0)  		itf->flags |= INT_PPP; +	if((isc_if->flags & INTERFACE_F_MULTICAST) != 0) +		itf->flags |= INT_MULTICAST; +  } +  /* - * create_sockets - create a socket for each interface plus a default - *			socket for when we don't know where to send + * refresh_interface + * + * some OSes have been observed to keep + * cached routes even when more specific routes + * become available. + * this can be mitigated by re-binding + * the socket.   */  static int -create_sockets( -	u_short port +refresh_interface(struct interface * interface) +{ +#ifdef  OS_MISSES_SPECIFIC_ROUTE_UPDATES +	if (interface->fd != INVALID_SOCKET) +	{ +		close_and_delete_fd_from_list(interface->fd); +		interface->fd = open_socket(&interface->sin, +					    interface->flags, 0, interface); +		 /* +		  * reset TTL indication so TTL is is set again  +		  * next time around +		  */ +		interface->last_ttl = 0; +		return interface->fd != INVALID_SOCKET; +	} +	else +	{ +		return 0;	/* invalid sockets are not refreshable */ +	} +#else /* !OS_MISSES_SPECIFIC_ROUTE_UPDATES */ +	return interface->fd != INVALID_SOCKET; +#endif /* !OS_MISSES_SPECIFIC_ROUTE_UPDATES */ +} + +/* + * interface_update - externally callable update function + */ +void +interface_update(interface_receiver_t receiver, void *data) +{ +	if (!disable_dynamic_updates) { +		int new_interface_found; + +		BLOCKIO(); +		new_interface_found = update_interfaces(htons(NTP_PORT), receiver, data); +		UNBLOCKIO(); + +		if (new_interface_found) { +#ifdef DEBUG +			msyslog(LOG_DEBUG, "new interface(s) found: waking up resolver"); +#endif +#ifdef SYS_WINNT +			/* wake up the resolver thread */ +			if (ResolverEventHandle != NULL) +				SetEvent(ResolverEventHandle); +#else +			/* write any single byte to the pipe to wake up the resolver process */ +			write( resolver_pipe_fd[1], &new_interface_found, 1 ); +#endif +		} +	} +} + +/* + * find out if a given interface structure contains + * a wildcard address + */ +static int +is_wildcard_addr(struct sockaddr_storage *sas) +{ +	if (sas->ss_family == AF_INET && +	    ((struct sockaddr_in*)sas)->sin_addr.s_addr == htonl(INADDR_ANY)) +		return 1; + +#ifdef INCLUDE_IPV6_SUPPORT +	if (sas->ss_family == AF_INET6 && +	    memcmp(&((struct sockaddr_in6*)sas)->sin6_addr, &in6addr_any, +		   sizeof(in6addr_any) == 0)) +		return 1; +#endif + +	return 0; +} + +#ifdef OS_NEEDS_REUSEADDR_FOR_IFADDRBIND +/* + * enable/disable re-use of wildcard address socket + */ +static void +set_wildcard_reuse(int family, int on) +{ +	int onvalue = 1; +	int offvalue = 0; +	int *onoff; +	SOCKET fd = INVALID_SOCKET; + +	onoff = on ? &onvalue : &offvalue; + +	switch (family) { +	case AF_INET: +		if (any_interface) { +			fd = any_interface->fd; +		} +		break; + +#ifdef INCLUDE_IPV6_SUPPORT +	case AF_INET6: +		if (any6_interface) { +			fd = any6_interface->fd; +		} +		break; +#endif /* !INCLUDE_IPV6_SUPPORT */ +	} + +	if (fd != INVALID_SOCKET) { +		if (setsockopt(fd, SOL_SOCKET, +			       SO_REUSEADDR, (char *)onoff, +			       sizeof(*onoff))) { +			netsyslog(LOG_ERR, "set_wildcard_reuse: setsockopt(SO_REUSEADDR, %s) failed: %m", *onoff ? "on" : "off"); +		} +		DPRINTF(4, ("set SO_REUSEADDR to %s on %s\n", *onoff ? "ON" : "OFF", +			    stoa((family == AF_INET) ? +				  &any_interface->sin : &any6_interface->sin))); +	} +} +#endif /* OS_NEEDS_REUSEADDR_FOR_IFADDRBIND */ + +/* + * update_interface strategy + * + * toggle configuration phase + * + * Phase 1: + * forall currently existing interfaces + *   if address is known: + *       drop socket - rebind again + * + *   if address is NOT known: + *     attempt to create a new interface entry + * + * Phase 2: + * forall currently known non MCAST and WILDCARD interfaces + *   if interface does not match configuration phase (not seen in phase 1): + *     remove interface from known interface list + *     forall peers associated with this interface + *       disconnect peer from this interface + * + * Phase 3: + *   attempt to re-assign interfaces to peers + * + */ + +static int +update_interfaces( +	u_short port, +	interface_receiver_t receiver, +	void *data  	)  { -	struct sockaddr_storage resmask; -	int i; +	interface_info_t ifi;  	isc_mem_t *mctx = NULL;  	isc_interfaceiter_t *iter = NULL;  	isc_boolean_t scan_ipv4 = ISC_FALSE;  	isc_boolean_t scan_ipv6 = ISC_FALSE;  	isc_result_t result; -	int idx = 0; +	int new_interface_found = 0; -#ifdef DEBUG -	if (debug) -	    printf("create_sockets(%d)\n", ntohs( (u_short) port)); -#endif +	DPRINTF(3, ("update_interfaces(%d)\n", ntohs( (u_short) port))); +#ifdef INCLUDE_IPV6_SUPPORT  	if (isc_net_probeipv6() == ISC_R_SUCCESS)  		scan_ipv6 = ISC_TRUE; -#ifdef HAVE_IPV6 +#if defined(DEBUG)  	else -		netsyslog(LOG_ERR, "no IPv6 interfaces found"); +		if(debug) +			netsyslog(LOG_ERR, "no IPv6 interfaces found"); +#endif +#endif +	if (isc_net_probeipv6() == ISC_R_SUCCESS) +		scan_ipv6 = ISC_TRUE; +#if defined(ISC_PLATFORM_HAVEIPV6) && defined(DEBUG) +	else +		if(debug) +			netsyslog(LOG_ERR, "no IPv6 interfaces found");  #endif  	if (isc_net_probeipv4() == ISC_R_SUCCESS)  		scan_ipv4 = ISC_TRUE; +#ifdef DEBUG  	else -		netsyslog(LOG_ERR, "no IPv4 interfaces found"); - -	nwilds = create_wildcards(port); -	idx = nwilds; +		if(debug) +			netsyslog(LOG_ERR, "no IPv4 interfaces found"); +#endif +	/* +	 * phase one - scan interfaces +	 * - create those that are not found +	 * - update those that are found +	 */  	result = isc_interfaceiter_create(mctx, &iter); +  	if (result != ISC_R_SUCCESS) -		return (result); +		return 0; +	sys_interphase ^= 0x1;	/* toggle system phase for finding untouched (to be deleted) interfaces */ +	  	for (result = isc_interfaceiter_first(iter);  	     result == ISC_R_SUCCESS;  	     result = isc_interfaceiter_next(iter))  	{  		isc_interface_t isc_if; -		unsigned int family;  - +		unsigned int family; +		struct interface interface; +		struct interface *iface; +		  		result = isc_interfaceiter_current(iter, &isc_if); +  		if (result != ISC_R_SUCCESS)  			break; @@ -466,152 +1239,266 @@ create_sockets(  		if (scan_ipv6 == ISC_FALSE && family == AF_INET6)  			continue; -		/* Check to see if we are going to use the interface */ -		if (address_okay(&isc_if) == ISC_TRUE) { -			convert_isc_if(&isc_if, &inter_list[idx], port); -			inter_list[idx].fd = INVALID_SOCKET; -			inter_list[idx].bfd = INVALID_SOCKET; -			inter_list[idx].num_mcast = 0; -			inter_list[idx].received = 0; -			inter_list[idx].sent = 0; -			inter_list[idx].notsent = 0; -			idx++; +		/* +		 * create prototype +		 */ +		init_interface(&interface); + +		convert_isc_if(&isc_if, &interface, port); + +		/*  +		 * Check to see if we are going to use the interface +		 * If we don't use it we mark it to drop any packet +		 * received but we still must create the socket and +		 * bind to it. This prevents other apps binding to it +		 * and potentially causing problems with more than one +		 * process fiddling with the clock +		 */ +		if (address_okay(&interface) == ISC_TRUE) { +			interface.ignore_packets = ISC_FALSE; +		} +		else { +			interface.ignore_packets = ISC_TRUE; +		} + +		DPRINT_INTERFACE(4, (&interface, "examining ", "\n")); + +		if (!(interface.flags & INT_UP))  { /* interfaces must be UP to be usable */ +			DPRINTF(4, ("skipping interface %s (%s) - DOWN\n", interface.name, stoa(&interface.sin))); +			continue; +		} + +		/* +		 * skip any interfaces UP and bound to a wildcard +		 * address - some dhcp clients produce that in the +		 * wild +		 */ +		if (is_wildcard_addr(&interface.sin)) +			continue; + +		/* +		 * map to local *address* in order +		 * to map all duplicate interfaces to an interface structure +		 * with the appropriate socket (our name space is +		 * (ip-address) - NOT (interface name, ip-address)) +		 */ +		iface = getinterface(&interface.sin, INT_WILDCARD); +		 +		if (iface && refresh_interface(iface))  +		{ +			/* +			 * found existing and up to date interface - mark present +			 */ + +			iface->phase = sys_interphase; +			DPRINT_INTERFACE(4, (iface, "updating ", " present\n")); +			ifi.action = IFS_EXISTS; +			ifi.interface = iface; +			if (receiver) +				receiver(data, &ifi); +		} +		else +		{ +			/* +			 * this is new or refreshing failed - add to our interface list +			 * if refreshing failed we will delete the interface structure in +			 * phase 2 as the interface was not marked current. We can bind to +			 * the address as the refresh code already closed the offending socket +			 */ +			 +			iface = create_interface(port, &interface); + +			if (iface) +			{ +				ifi.action = IFS_CREATED; +				ifi.interface = iface; +				if (receiver) +					receiver(data, &ifi); + +				new_interface_found = 1; + +				DPRINT_INTERFACE(3, (iface, "updating ", " new - created\n")); +			} +			else +			{ +				DPRINT_INTERFACE(3, (&interface, "updating ", " new - creation FAILED")); +			 +				msyslog(LOG_INFO, "failed to initialize interface for address %s", stoa(&interface.sin)); +				continue; +			}  		}  	} +  	isc_interfaceiter_destroy(&iter); -	ninterfaces = idx;  	/* -	 * I/O Completion Ports don't care about the select and FD_SET +	 * phase 2 - delete gone interfaces - reassigning peers to other interfaces  	 */ -#ifndef HAVE_IO_COMPLETION_PORT -	maxactivefd = 0; -	FD_ZERO(&activefds); -#endif -	for (i = 0; i < ninterfaces; i++) { -		inter_list[i].fd = open_socket(&inter_list[i].sin, -		    inter_list[i].flags & INT_BROADCAST, 0); -		if (inter_list[i].bfd != INVALID_SOCKET) -			msyslog(LOG_INFO, "Listening on interface %s, %s#%d", -				inter_list[i].name, -				stoa((&inter_list[i].sin)), -				NTP_PORT); -		if ((inter_list[i].flags & INT_BROADCAST) && -		     inter_list[i].bfd != INVALID_SOCKET) -			msyslog(LOG_INFO, "Listening on broadcast address %s#%d", -				stoa((&inter_list[i].bcast)), -				NTP_PORT); -#if defined (HAVE_IO_COMPLETION_PORT) -		if (inter_list[i].fd != INVALID_SOCKET) { -			io_completion_port_add_socket(inter_list[i].fd, &inter_list[i]); +	{ +		struct interface *interf = ISC_LIST_HEAD(inter_list); + +		while (interf != NULL) +		{ +			struct interface *next = ISC_LIST_NEXT(interf, link); +			   +			if (!(interf->flags & (INT_WILDCARD|INT_MCASTIF))) { +				/* +				 * if phase does not match sys_phase this interface was not +				 * enumerated during interface scan - so it is gone and +				 * will be deleted here unless it is solely an MCAST/WILDCARD interface +				 */ +				if (interf->phase != sys_interphase) { +					struct peer *peer; +					DPRINT_INTERFACE(3, (interf, "updating ", "GONE - deleting\n")); +					remove_interface(interf); + +					ifi.action = IFS_DELETED; +					ifi.interface = interf; +					if (receiver) +						receiver(data, &ifi); + +					peer = ISC_LIST_HEAD(interf->peers); +					/* +					 * disconnect peer from deleted interface +					 */ +					while (peer != NULL) { +						struct peer *npeer = ISC_LIST_NEXT(peer, ilink); +						 +						/* +						 * this one just lost it's interface +						 */ +						set_peerdstadr(peer, NULL); +	 +						peer = npeer; +					} + +					/* +					 * update globals in case we lose  +					 * a loopback interface +					 */ +					if (interf == loopback_interface) +						loopback_interface = NULL; + +					delete_interface(interf); +				} +			} +			interf = next;  		} -#endif  	}  	/* -	 * Now that we have opened all the sockets, turn off the reuse -	 * flag for security. +	 * phase 3 - re-configure as the world has changed if necessary  	 */ -	set_reuseaddr(0); +	refresh_all_peerinterfaces(); +	return new_interface_found; +} + +/* + * create_sockets - create a socket for each interface plus a default + *			socket for when we don't know where to send + */ +static int +create_sockets( +	u_short port +	) +{ +#ifndef HAVE_IO_COMPLETION_PORT  	/* -	 * Blacklist all bound interface addresses -	 * Wildcard interfaces are ignored. +	 * I/O Completion Ports don't care about the select and FD_SET  	 */ +	maxactivefd = 0; +	FD_ZERO(&activefds); +#endif -	for (i = nwilds; i < ninterfaces; i++) { -		SET_HOSTMASK(&resmask, inter_list[i].sin.ss_family); -		hack_restrict(RESTRICT_FLAGS, &inter_list[i].sin, &resmask, -		    RESM_NTPONLY|RESM_INTERFACE, RES_IGNORE); -	} +	DPRINTF(2, ("create_sockets(%d)\n", ntohs( (u_short) port))); +	create_wildcards(port); + +	update_interfaces(port, NULL, NULL); +	  	/* -	 * Calculate the address hash for each interface address. +	 * Now that we have opened all the sockets, turn off the reuse +	 * flag for security.  	 */ -	for (i = 0; i < ninterfaces; i++) { -		inter_list[i].addr_refid = addr2refid(&inter_list[i].sin); -	} +	set_reuseaddr(0); +	DPRINTF(2, ("create_sockets: Total interfaces = %d\n", ninterfaces)); -#ifdef DEBUG -	if (debug > 1) { -		printf("create_sockets: ninterfaces=%d\n", ninterfaces); -		for (i = 0; i < ninterfaces; i++) { -			printf("interface %d:  fd=%d,  bfd=%d,  name=%.8s,  flags=0x%x\n", -			       i, -			       inter_list[i].fd, -			       inter_list[i].bfd, -			       inter_list[i].name, -			       inter_list[i].flags); -			/* Leave these as three printf calls. */ -			printf("              sin=%s", -			       stoa((&inter_list[i].sin))); -			if (inter_list[i].flags & INT_BROADCAST) -			    printf("  bcast=%s,", -				   stoa((&inter_list[i].bcast))); -			printf("  mask=%s\n", -			       stoa((&inter_list[i].mask))); -		} -	} -#endif  	return ninterfaces;  }  /* - * io_setbclient - open the broadcast client sockets + * create_interface - create a new interface for a given prototype + *		      binding the socket.   */ -void -io_setbclient(void) +static struct interface * +create_interface( +		 u_short port, +		 struct interface *iface +		 )  { -	int i; +	struct sockaddr_storage resmask; +	struct interface *interface; -#ifdef OPEN_BCAST_SOCKET -	set_reuseaddr(1); -#endif -	for (i = nwilds; i < ninterfaces; i++) { -		/* Only IPv4 addresses are valid for broadcast */ -		if (inter_list[i].bcast.ss_family != AF_INET) -			continue; +	DPRINTF(2, ("create_interface(%s#%d)\n", stoa(&iface->sin), ntohs( (u_short) port))); -		/* Is this a broadcast address? */ -		if (!(inter_list[i].flags & INT_BROADCAST)) -			continue; +	/* build an interface */ +	interface = new_interface(iface); +	 +	/* +	 * create socket +	 */ +	interface->fd = open_socket(&interface->sin, +				 interface->flags, 0, interface); + +	if (interface->fd != INVALID_SOCKET) +		list_if_listening(interface, port); + +	if ((interface->flags & INT_BROADCAST) && +	    interface->bfd != INVALID_SOCKET) +	  msyslog(LOG_INFO, "Listening on broadcast address %s#%d", +		  stoa((&interface->bcast)), +		  ntohs( (u_short) port)); + +	if (interface->fd == INVALID_SOCKET && +	    interface->bfd == INVALID_SOCKET) { +		msyslog(LOG_ERR, "unable to create socket on %s (%d) for %s#%d", +			interface->name, +			interface->ifnum, +			stoa((&interface->sin)), +			ntohs( (u_short) port)); +		delete_interface(interface); +		return NULL; +	} +	 +        /* +	 * Blacklist bound interface address +	 */ +	 +	SET_HOSTMASK(&resmask, interface->sin.ss_family); +	hack_restrict(RESTRICT_FLAGS, &interface->sin, &resmask, +		      RESM_NTPONLY|RESM_INTERFACE, RES_IGNORE); +	   +	/* +	 * set globals with the first found +	 * loopback interface of the appropriate class +	 */ +	if ((loopback_interface == NULL) && +	    (interface->family == AF_INET) && +	    ((interface->flags & INT_LOOPBACK) != 0)) +	{ +		loopback_interface = interface; +	} -		/* Do we already have the broadcast address open? */ -		if (inter_list[i].flags & INT_BCASTOPEN) -			continue; +	/* +	 * put into our interface list +	 */ +	add_addr_to_list(&interface->sin, interface); +	add_interface(interface); -#ifdef	SYS_SOLARIS -		inter_list[i].bcast.sin_addr.s_addr = htonl(INADDR_ANY); -#endif -#ifdef OPEN_BCAST_SOCKET /* Was: !SYS_DOMAINOS && !SYS_LINUX */ -		inter_list[i].bfd = open_socket(&inter_list[i].bcast, -		    INT_BROADCAST, 1); -		if (inter_list[i].bfd != INVALID_SOCKET) { -			inter_list[i].flags |= INT_BCASTOPEN; -#if defined (HAVE_IO_COMPLETION_PORT) -			io_completion_port_add_socket(inter_list[i].bfd, &inter_list[i]); -#endif -		} -#ifdef DEBUG -		if (debug) { -			if (inter_list[i].bfd != INVALID_SOCKET) -				printf("io_setbclient: Opened broadcast client on interface %d, socket: %d\n", -				i, inter_list[i].bfd); -			else -				printf("io_setbclient: Unable to Open broadcast client on interface %d\n", -				i); -		} -#endif -#endif -	} -#ifdef OPEN_BCAST_SOCKET -	set_reuseaddr(0); -#endif -#ifdef DEBUG -	if (debug) -		printf("io_setbclient: Opened broadcast clients\n"); -#endif +	DPRINT_INTERFACE(2, (interface, "created ", "\n")); +	return interface;  }  /* @@ -621,15 +1508,22 @@ io_setbclient(void)   */  static void  set_reuseaddr(int flag) { -	int i; - -	for (i=0; i < ninterfaces; i++) { +        struct interface *interf; + +	for (interf = ISC_LIST_HEAD(inter_list); +	     interf != NULL; +	     interf = ISC_LIST_NEXT(interf, link)) { +	        if (interf->flags & INT_WILDCARD) +		        continue; +	    		/* -		 * if inter_list[ n ].fd  is -1, we might have a adapter +		 * if interf->fd  is INVALID_SOCKET, we might have a adapter  		 * configured but not present  		 */ -		if (inter_list[i].fd != INVALID_SOCKET) { -			if (setsockopt(inter_list[i].fd, SOL_SOCKET, +		DPRINTF(4, ("setting SO_REUSEADDR on %.16s@%s to %s\n", interf->name, stoa(&interf->sin), flag ? "on" : "off")); +		 +		if (interf->fd != INVALID_SOCKET) { +			if (setsockopt(interf->fd, SOL_SOCKET,  					SO_REUSEADDR, (char *)&flag,  					sizeof(flag))) {  				netsyslog(LOG_ERR, "set_reuseaddr: setsockopt(SO_REUSEADDR, %s) failed: %m", flag ? "on" : "off"); @@ -638,203 +1532,422 @@ set_reuseaddr(int flag) {  	}  } -  /* - * io_multicast_add() - add multicast group address + * This is just a wrapper around an internal function so we can + * make other changes as necessary later on   */  void -io_multicast_add( -	struct sockaddr_storage addr -	) +enable_broadcast(struct interface *iface, struct sockaddr_storage *baddr)  { -#ifdef MCAST -	struct ip_mreq mreq; -	int i = ninterfaces;	/* Use the next interface */ -	u_int32 haddr = ntohl(((struct sockaddr_in*)&addr)->sin_addr.s_addr); -	struct in_addr iaddr; -	SOCKET s; -	struct sockaddr_in *sinp; +#ifdef SO_BROADCAST +	socket_broadcast_enable(iface, iface->fd, baddr); +#endif +} -#ifdef HAVE_IPV6 -	struct ipv6_mreq mreq6; -	struct in6_addr iaddr6; -	struct sockaddr_in6 *sin6p; -#endif /* HAVE_IPV6 */ +#ifdef OPEN_BCAST_SOCKET  +/* + * Enable a broadcast address to a given socket + * The socket is in the inter_list all we need to do is enable + * broadcasting. It is not this function's job to select the socket + */ +static isc_boolean_t +socket_broadcast_enable(struct interface *iface, SOCKET fd, struct sockaddr_storage *maddr) +{ +#ifdef SO_BROADCAST +	int on = 1; -	switch (addr.ss_family) +	if (maddr->ss_family == AF_INET) +	{ +		/* if this interface can support broadcast, set SO_BROADCAST */ +		if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, +			       (char *)&on, sizeof(on))) +		{ +			netsyslog(LOG_ERR, "setsockopt(SO_BROADCAST) enable failure on address %s: %m", +				stoa(maddr)); +		} +#ifdef DEBUG +		else if (debug > 1) { +			printf("Broadcast enabled on socket %d for address %s\n", +				fd, stoa(maddr)); +		} +#endif +	} +	iface->flags |= INT_BCASTOPEN; +	return ISC_TRUE; +#else +	return ISC_FALSE; +#endif /* SO_BROADCAST */ +} + +/* + * Remove a broadcast address from a given socket + * The socket is in the inter_list all we need to do is disable + * broadcasting. It is not this function's job to select the socket + */ +static isc_boolean_t +socket_broadcast_disable(struct interface *iface, struct sockaddr_storage *maddr) +{ +#ifdef SO_BROADCAST +	int off = 0;	/* This seems to be OK as an int */ + +	if (maddr->ss_family == AF_INET) +	{ +		if (setsockopt(iface->fd, SOL_SOCKET, SO_BROADCAST, +			       (char *)&off, sizeof(off))) +		{ +			netsyslog(LOG_ERR, "setsockopt(SO_BROADCAST) disable failure on address %s: %m", +				stoa(maddr)); +		} +	} +	iface->flags &= ~INT_BCASTOPEN; +	return ISC_TRUE; +#else +	return ISC_FALSE; +#endif /* SO_BROADCAST */ +} + +#endif /* OPEN_BCAST_SOCKET */ +/* + * Check to see if the address is a multicast address + */ +static isc_boolean_t +addr_ismulticast(struct sockaddr_storage *maddr) +{ +	switch (maddr->ss_family)  	{  	case AF_INET : -		iaddr = (((struct sockaddr_in*)&addr)->sin_addr); -		if (!IN_CLASSD(haddr)) { -			netsyslog(LOG_ERR, -			"multicast address %s not class D", -				inet_ntoa(iaddr)); -			return; +		if (!IN_CLASSD(ntohl(((struct sockaddr_in*)maddr)->sin_addr.s_addr))) { +			DPRINTF(4, ("multicast address %s not class D\n", stoa(maddr))); +			return (ISC_FALSE);  		} -		for (i = nwilds; i < ninterfaces; i++) { -			 /* Be sure it's the correct family */ -                        if (inter_list[i].sin.ss_family != AF_INET) -                                continue; -			/* Already have this address */ -			if (SOCKCMP(&inter_list[i].sin, &addr)) -				return; -			/* found a free slot */ -			if (SOCKNUL(&inter_list[i].sin) && -			inter_list[i].fd <= 0 && inter_list[i].bfd <= 0 && -			inter_list[i].flags == 0) -			break; +		else +		{ +			return (ISC_TRUE);  		} -		sinp = (struct sockaddr_in*)&(inter_list[i].sin); -		memset((char *)&mreq, 0, sizeof(mreq)); -		memset((char *)&inter_list[i], 0, sizeof(struct interface)); -		sinp->sin_family = AF_INET; -		sinp->sin_addr = iaddr; -		sinp->sin_port = htons(NTP_PORT); -		/* -		* Try opening a socket for the specified class D address. This -		* works under SunOS 4.x, but not OSF1 .. :-( -		*/ -		set_reuseaddr(1); -		s = open_socket((struct sockaddr_storage*)sinp, 0, 1); -		set_reuseaddr(0); -		if (s == INVALID_SOCKET) { -			memset((char *)&inter_list[i], 0, sizeof(struct interface)); -			if (wildipv4 >= 0) { -				i = wildipv4; -				/* HACK ! -- stuff in an address */ -				inter_list[i].bcast = addr; -				netsyslog(LOG_ERR, -				"...multicast address %s using wildcard socket", -				inet_ntoa(iaddr)); -			} else { -				netsyslog(LOG_ERR, -				"No wildcard socket available to use for address %s", -				inet_ntoa(iaddr)); -				return; -			} -		} else { -			inter_list[i].fd = s; -			inter_list[i].bfd = INVALID_SOCKET; -			(void) strncpy(inter_list[i].name, "multicast", -			sizeof(inter_list[i].name)); -			((struct sockaddr_in*)&inter_list[i].mask)->sin_addr.s_addr = htonl(~(u_int32)0); -#if defined (HAVE_IO_COMPLETION_PORT) -			io_completion_port_add_socket(inter_list[i].fd, &inter_list[i]); -#endif +	case AF_INET6 : +#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT +		if (!IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)maddr)->sin6_addr)) { +			DPRINTF(4, ("address %s not IPv6 multicast address\n", stoa(maddr))); +			return (ISC_FALSE); +		} +		else +		{ +			return (ISC_TRUE);  		} +/* + * If we don't have IPV6 support any IPV6 address is not multicast + */ +#else +		return (ISC_FALSE); +#endif +	/* +	 * Never valid +	 */ +	default: +		return (ISC_FALSE); +	} +} + +/* + * Multicast servers need to set the appropriate Multicast interface + * socket option in order for it to know which interface to use for + * send the multicast packet. + */ +void +enable_multicast_if(struct interface *iface, struct sockaddr_storage *maddr) +{ +#ifdef MCAST +	/*u_char*/ TYPEOF_IP_MULTICAST_LOOP off = 0; + +	switch (maddr->ss_family) +	{ +	case AF_INET: +		if (setsockopt(iface->fd, IPPROTO_IP, IP_MULTICAST_IF, +		   (char *)&(((struct sockaddr_in*)&iface->sin)->sin_addr.s_addr), +		    sizeof(struct in_addr)) == -1) { +			netsyslog(LOG_ERR, +			"setsockopt IP_MULTICAST_IF failure: %m on socket %d, addr %s for multicast address %s", +			iface->fd, stoa(&iface->sin), stoa(maddr)); +			return; +		} +#ifdef IP_MULTICAST_LOOP  		/* -		* enable reception of multicast packets -		*/ -		mreq.imr_multiaddr = iaddr; -		mreq.imr_interface.s_addr = htonl(INADDR_ANY); -		if (setsockopt(inter_list[i].fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, -		(char *)&mreq, sizeof(mreq)) == -1) +		 * Don't send back to itself, but allow it to fail to set it +		 */ +		if (setsockopt(iface->fd, IPPROTO_IP, IP_MULTICAST_LOOP, +		       SETSOCKOPT_ARG_CAST &off, sizeof(off)) == -1) {  			netsyslog(LOG_ERR, -			"setsockopt IP_ADD_MEMBERSHIP fails: %m for %x / %x (%s)", -			mreq.imr_multiaddr.s_addr, -			mreq.imr_interface.s_addr, inet_ntoa(iaddr)); -		inter_list[i].flags |= INT_MULTICAST; -		inter_list[i].num_mcast++; -		if (i >= ninterfaces) -			ninterfaces = i+1; - -                add_addr_to_list(&addr, i); +			"setsockopt IP_MULTICAST_LOOP failure: %m on socket %d, addr %s for multicast address %s", +			iface->fd, stoa(&iface->sin), stoa(maddr)); +		} +#endif +	DPRINTF(4, ("Added IPv4 multicast interface on socket %d, addr %s for multicast address %s\n", +			    iface->fd, stoa(&iface->sin), +			    stoa(maddr)));  		break; -#ifdef HAVE_IPV6 -	case AF_INET6 : - -		iaddr6 = ((struct sockaddr_in6*)&addr)->sin6_addr; -		if (!IN6_IS_ADDR_MULTICAST(&iaddr6)) { +	case AF_INET6: +#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT +		if (setsockopt(iface->fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, +		    &iface->scopeid, sizeof(iface->scopeid)) == -1) {  			netsyslog(LOG_ERR, -			    "address %s not IPv6 multicast address", -				stoa(&addr)); +			"setsockopt IPV6_MULTICAST_IF failure: %m on socket %d, addr %s, scope %d for multicast address %s", +			iface->fd, stoa(&iface->sin), iface->scopeid, +			stoa(maddr));  			return;  		} -		for (i = nwilds; i < ninterfaces; i++) { -			/* Be sure it's the correct family */ -			if(inter_list[i].sin.ss_family != AF_INET6) -				continue; -			/* Already have this address */ -			if (SOCKCMP(&inter_list[i].sin, &addr)) -				return; -			/* found a free slot */ -			if (SOCKNUL(&inter_list[i].sin) && -			    inter_list[i].fd <= 0 && inter_list[i].bfd <= 0 && -			    inter_list[i].flags == 0) -			break; +#ifdef IPV6_MULTICAST_LOOP +		/* +		 * Don't send back to itself, but allow it to fail to set it +		 */ +		if (setsockopt(iface->fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, +		       &off, sizeof(off)) == -1) { +			netsyslog(LOG_ERR, +			"setsockopt IP_MULTICAST_LOOP failure: %m on socket %d, addr %s for multicast address %s", +			iface->fd, stoa(&iface->sin), stoa(maddr));  		} -		sin6p = (struct sockaddr_in6*)&inter_list[i].sin; -		memset((char *)&mreq6, 0, sizeof(mreq6)); -		memset((char *)&inter_list[i], 0, sizeof(struct interface)); -		sin6p->sin6_family = AF_INET6; -		sin6p->sin6_addr = iaddr6; -		sin6p->sin6_port = htons(NTP_PORT); +#endif +		DPRINTF(4, ("Added IPv6 multicast interface on socket %d, addr %s, scope %d for multicast address %s\n", +			    iface->fd,  stoa(&iface->sin), iface->scopeid, +			    stoa(maddr))); +		break; +#else +		return; +#endif	/* INCLUDE_IPV6_MULTICAST_SUPPORT */ +	} +	return; +#endif +} + +/* + * Add a multicast address to a given socket + * The socket is in the inter_list all we need to do is enable + * multicasting. It is not this function's job to select the socket + */ +static isc_boolean_t +socket_multicast_enable(struct interface *iface, int lscope, struct sockaddr_storage *maddr) +{ +#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT +	struct ipv6_mreq mreq6; +	struct in6_addr iaddr6; +#endif /* INCLUDE_IPV6_MULTICAST_SUPPORT */ + +	struct ip_mreq mreq; + +	if (find_addr_in_list(maddr)) { +		DPRINTF(4, ("socket_multicast_enable(%s): already enabled\n", stoa(maddr))); +		return ISC_TRUE; +	} + +	switch (maddr->ss_family) +	{ +	case AF_INET: +		memset((char *)&mreq, 0, sizeof(mreq)); +		mreq.imr_multiaddr = (((struct sockaddr_in*)maddr)->sin_addr); +		mreq.imr_interface.s_addr = htonl(INADDR_ANY); +		if (setsockopt(iface->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, +			(char *)&mreq, sizeof(mreq)) == -1) { +			netsyslog(LOG_ERR, +			"setsockopt IP_ADD_MEMBERSHIP failure: %m on socket %d, addr %s for %x / %x (%s)", +			iface->fd, stoa(&iface->sin), +			mreq.imr_multiaddr.s_addr, +			mreq.imr_interface.s_addr, stoa(maddr)); +			return ISC_FALSE; +		} +		DPRINTF(4, ("Added IPv4 multicast membership on socket %d, addr %s for %x / %x (%s)\n", +			    iface->fd, stoa(&iface->sin), +			    mreq.imr_multiaddr.s_addr, +			    mreq.imr_interface.s_addr, stoa(maddr))); +		break; +	case AF_INET6: +#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT  		/* -		 * Try opening a socket for the specified class D address. This -		 * works under SunOS 4.x, but not OSF1 .. :-( +		 * Enable reception of multicast packets +		 * If the address is link-local we can get the interface index +		 * from the scope id. Don't do this for other types of multicast +		 * addresses. For now let the kernel figure it out.  		 */ -		set_reuseaddr(1); -		s = open_socket((struct sockaddr_storage*)sin6p, 0, 1); -		set_reuseaddr(0); -		if(s == INVALID_SOCKET){ -			memset((char *)&inter_list[i], 0, sizeof(struct interface)); -			if (wildipv6 >= 0) { -				i = wildipv6; -				/* HACK ! -- stuff in an address */ -				inter_list[i].bcast = addr; -				netsyslog(LOG_ERR, -				 "...multicast address %s using wildcard socket", -				 stoa(&addr)); -			} else { -				netsyslog(LOG_ERR, -				"No wildcard socket available to use for address %s", -				stoa(&addr)); -				return; -			} -		} else { -			inter_list[i].fd = s; -			inter_list[i].bfd = INVALID_SOCKET; -			(void)strncpy(inter_list[i].name, "multicast", -			   sizeof(inter_list[i].name)); -			memset(&(((struct sockaddr_in6*)&inter_list[i].mask)->sin6_addr), 1, sizeof(struct in6_addr)); -#if defined (HAVE_IO_COMPLETION_PORT) -			io_completion_port_add_socket(inter_list[i].fd, &inter_list[i]); -#endif +		memset((char *)&mreq6, 0, sizeof(mreq6)); +		iaddr6 = ((struct sockaddr_in6*)maddr)->sin6_addr; +		mreq6.ipv6mr_multiaddr = iaddr6; +		mreq6.ipv6mr_interface = lscope; + +		if (setsockopt(iface->fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, +			(char *)&mreq6, sizeof(mreq6)) == -1) { +			netsyslog(LOG_ERR, +			 "setsockopt IPV6_JOIN_GROUP failure: %m on socket %d, addr %s for interface %d(%s)", +			iface->fd, stoa(&iface->sin), +			mreq6.ipv6mr_interface, stoa(maddr)); +			return ISC_FALSE;  		} +		DPRINTF(4, ("Added IPv6 multicast group on socket %d, addr %s for interface %d(%s)\n", +			    iface->fd, stoa(&iface->sin), +			    mreq6.ipv6mr_interface, stoa(maddr))); +		break; +#else +		return ISC_FALSE; +#endif	/* INCLUDE_IPV6_MULTICAST_SUPPORT */ +	} +	iface->flags |= INT_MCASTOPEN; +	iface->num_mcast++; +	add_addr_to_list(maddr, iface); +	return ISC_TRUE; +} + +/* + * Remove a multicast address from a given socket + * The socket is in the inter_list all we need to do is disable + * multicasting. It is not this function's job to select the socket + */ +static isc_boolean_t +socket_multicast_disable(struct interface *iface, struct sockaddr_storage *maddr) +{ +#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT +	struct ipv6_mreq mreq6; +	struct in6_addr iaddr6; +#endif /* INCLUDE_IPV6_MULTICAST_SUPPORT */ + +	struct ip_mreq mreq; +	memset((char *)&mreq, 0, sizeof(mreq)); + +	if (find_addr_in_list(maddr) == NULL) { +		DPRINTF(4, ("socket_multicast_disable(%s): not enabled\n", stoa(maddr))); +		return ISC_TRUE; +	} +	switch (maddr->ss_family) +	{ +	case AF_INET: +		mreq.imr_multiaddr = (((struct sockaddr_in*)&maddr)->sin_addr); +		mreq.imr_interface.s_addr = ((struct sockaddr_in*)&iface->sin)->sin_addr.s_addr; +		if (setsockopt(iface->fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, +			(char *)&mreq, sizeof(mreq)) == -1) { +			netsyslog(LOG_ERR, +			"setsockopt IP_DROP_MEMBERSHIP failure: %m on socket %d, addr %s for %x / %x (%s)", +			iface->fd, stoa(&iface->sin), +			mreq.imr_multiaddr.s_addr, +			mreq.imr_interface.s_addr, stoa(maddr)); +			return ISC_FALSE; +		} +		break; +	case AF_INET6: +#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT  		/* -		 * enable reception of multicast packets +		 * Disable reception of multicast packets +		 * If the address is link-local we can get the interface index +		 * from the scope id. Don't do this for other types of multicast +		 * addresses. For now let the kernel figure it out.  		 */ +		iaddr6 = ((struct sockaddr_in6*)&maddr)->sin6_addr;  		mreq6.ipv6mr_multiaddr = iaddr6; -		mreq6.ipv6mr_interface = 0; -		if(setsockopt(inter_list[i].fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, -		   (char *)&mreq6, sizeof(mreq6)) == -1) +		mreq6.ipv6mr_interface = iface->scopeid; + +		if (setsockopt(iface->fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, +			(char *)&mreq6, sizeof(mreq6)) == -1) {  			netsyslog(LOG_ERR, -			 "setsockopt IPV6_JOIN_GROUP fails: %m on interface %d(%s)", -			 mreq6.ipv6mr_interface, stoa(&addr)); -		inter_list[i].flags |= INT_MULTICAST; -		inter_list[i].num_mcast++; -		if(i >= ninterfaces) -			ninterfaces = i+1; - -                add_addr_to_list(&addr, i); +			"setsockopt IPV6_LEAVE_GROUP failure: %m on socket %d, addr %s for %d(%s)", +			iface->fd, stoa(&iface->sin), +			mreq6.ipv6mr_interface, stoa(maddr)); +			return ISC_FALSE; +		}  		break; -#endif /* HAVE_IPV6 */ +#else +		return ISC_FALSE; +#endif	/* INCLUDE_IPV6_MULTICAST_SUPPORT */ + +	} +	iface->num_mcast--; +	if (iface->num_mcast <= 0) { +                iface->num_mcast = 0; +		iface->flags &= ~INT_MCASTOPEN;  	} +	return ISC_TRUE; +} + +/* + * io_setbclient - open the broadcast client sockets + */ +void +io_setbclient(void) +{ +#ifdef OPEN_BCAST_SOCKET  +        struct interface *interf; +	int nif = 0; +	isc_boolean_t jstatus;  +	SOCKET fd; + +	set_reuseaddr(1); + +	for (interf = ISC_LIST_HEAD(inter_list); +	     interf != NULL; +	     interf = ISC_LIST_NEXT(interf, link)) { +	        if (interf->flags & INT_WILDCARD) +		        continue; +	   +		/* use only allowed addresses */ +		if (interf->ignore_packets == ISC_TRUE) +			continue; +		/* Only IPv4 addresses are valid for broadcast */ +		if (interf->sin.ss_family != AF_INET) +			continue; +		/* Is this a broadcast address? */ +		if (!(interf->flags & INT_BROADCAST)) +			continue; + +		/* Skip the loopback addresses */ +		if (interf->flags & INT_LOOPBACK) +			continue; + +		/* Do we already have the broadcast address open? */ +		if (interf->flags & INT_BCASTOPEN) { +		/* account for already open interfaces to aviod misleading warning below */ +			nif++; +			continue; +		} + +		/* +		 * Try to open the broadcast address +		 */ +		interf->family = AF_INET; +		interf->bfd = open_socket(&interf->bcast, +				    INT_BROADCAST, 0, interf); + +		 /* +		 * If we succeeded then we use it otherwise +		 * enable the underlying address +		 */ +		if (interf->bfd == INVALID_SOCKET) { +			fd = interf->fd; +		} +		else { +			fd = interf->bfd; +		} + +		/* Enable Broadcast on socket */ +		jstatus = socket_broadcast_enable(interf, fd, &interf->sin); +		if (jstatus == ISC_TRUE) +		{ +			nif++; +			netsyslog(LOG_INFO,"io_setbclient: Opened broadcast client on interface #%d %s, socket: %d", +				  interf->ifnum, interf->name, fd); +			interf->addr_refid = addr2refid(&interf->sin); +		} +	} +	set_reuseaddr(0);  #ifdef DEBUG  	if (debug) -		printf("io_multicast_add %s\n", stoa(&addr)); +		if (nif > 0) +			printf("io_setbclient: Opened broadcast clients\n"); +#endif +	if (nif == 0) +		netsyslog(LOG_ERR, "Unable to listen for broadcasts, no broadcast interfaces available"); +#else +	netsyslog(LOG_ERR, "io_setbclient: Broadcast Client disabled by build");  #endif -#else /* MCAST */ -	netsyslog(LOG_ERR, -	    "cannot add multicast address %s as no MCAST support", -	    stoa(&addr)); -#endif /* MCAST */  }  /* @@ -843,139 +1956,225 @@ io_multicast_add(  void  io_unsetbclient(void)  { -	int i; +        struct interface *interf; +	isc_boolean_t lstatus; -	for (i = nwilds; i < ninterfaces; i++) +	for (interf = ISC_LIST_HEAD(inter_list); +	     interf != NULL; +	     interf = ISC_LIST_NEXT(interf, link))  	{ -		if (!(inter_list[i].flags & INT_BCASTOPEN)) +	        if (interf->flags & INT_WILDCARD) +		    continue; +	   +		if (!(interf->flags & INT_BCASTOPEN))  		    continue; -		close_socket(inter_list[i].bfd); -		inter_list[i].bfd = INVALID_SOCKET; -		inter_list[i].flags &= ~INT_BCASTOPEN; +		lstatus = socket_broadcast_disable(interf, &interf->sin);  	}  } -  /* - * io_multicast_del() - delete multicast group address + * io_multicast_add() - add multicast group address   */  void -io_multicast_del( +io_multicast_add(  	struct sockaddr_storage addr  	)  {  #ifdef MCAST -	int i; -	struct ip_mreq mreq; -	u_int32 haddr; - -#ifdef HAVE_IPV6 -	struct ipv6_mreq mreq6; -	struct in6_addr haddr6; -#endif /* HAVE_IPV6 */ +        struct interface *interface, *iface; +	int lscope = 0; +	 +	/* +	 * Check to see if this is a multicast address +	 */ +	if (addr_ismulticast(&addr) == ISC_FALSE) +		return; -	switch (addr.ss_family) +	/* If we already have it we can just return */ +	if (find_flagged_addr_in_list(&addr, INT_MCASTOPEN|INT_MCASTIF) != NULL)  	{ -	case AF_INET : +		netsyslog(LOG_INFO, "Duplicate request found for multicast address %s", +			stoa(&addr)); +		return; +	} -		haddr = ntohl(((struct sockaddr_in*)&addr)->sin_addr.s_addr); +#ifndef MULTICAST_NONEWSOCKET +	interface = new_interface(NULL); +	 +	/* +	 * Open a new socket for the multicast address +	 */ +	interface->sin.ss_family = addr.ss_family; +	interface->family = addr.ss_family; -		if (!IN_CLASSD(haddr)) -		{ +	switch(addr.ss_family) { +	case AF_INET: +		memcpy(&(((struct sockaddr_in *)&interface->sin)->sin_addr), +		       &(((struct sockaddr_in*)&addr)->sin_addr), +		       sizeof(struct in_addr)); +		((struct sockaddr_in*)&interface->sin)->sin_port = htons(NTP_PORT); +		memset(&((struct sockaddr_in*)&interface->mask)->sin_addr.s_addr, 0xff, sizeof(struct in_addr)); +		break; +	case AF_INET6: +#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT +		memcpy(&(((struct sockaddr_in6 *)&interface->sin)->sin6_addr), +		       &((struct sockaddr_in6*)&addr)->sin6_addr, +		       sizeof(struct in6_addr)); +		((struct sockaddr_in6*)&interface->sin)->sin6_port = htons(NTP_PORT); +#ifdef ISC_PLATFORM_HAVESCOPEID +		((struct sockaddr_in6*)&interface->sin)->sin6_scope_id = ((struct sockaddr_in6*)&addr)->sin6_scope_id; +#endif +		memset(&((struct sockaddr_in6*)&interface->mask)->sin6_addr.s6_addr, 0xff, sizeof(struct in6_addr)); +#endif +		iface = findlocalcastinterface(&addr, INT_MULTICAST); +		if (iface) { +# ifdef ISC_PLATFORM_HAVESCOPEID +			lscope = ((struct sockaddr_in6*)&iface->sin)->sin6_scope_id; +# endif +			DPRINTF(4, ("Found interface #%d %s, scope: %d for address %s\n", iface->ifnum, iface->name, lscope, stoa(&addr))); +		} +		break; +	} +		 +	set_reuseaddr(1); +	interface->bfd = INVALID_SOCKET; +	interface->fd = open_socket(&interface->sin, +			    INT_MULTICAST, 0, interface); + +	if (interface->fd != INVALID_SOCKET) +	{ +		interface->bfd = INVALID_SOCKET; +		interface->ignore_packets = ISC_FALSE; +		interface->flags |= INT_MCASTIF; +		 +		(void) strncpy(interface->name, "multicast", +			sizeof(interface->name)); +		((struct sockaddr_in*)&interface->mask)->sin_addr.s_addr = +						htonl(~(u_int32)0); +		DPRINT_INTERFACE(2, (interface, "multicast add ", "\n")); +		/* socket_multicast_enable() will add this address to the addresslist */ +		add_interface(interface); +		list_if_listening(interface, htons(NTP_PORT)); +	} +	else +	{ +	        delete_interface(interface);  /* re-use existing interface */ +		interface = NULL; +		if (addr.ss_family == AF_INET) +			interface = wildipv4; +		else if (addr.ss_family == AF_INET6) +			interface = wildipv6; + +		if (interface != NULL) { +			/* HACK ! -- stuff in an address */ +			interface->bcast = addr; +			netsyslog(LOG_ERR, +			 "...multicast address %s using wildcard interface #%d %s", +				  stoa(&addr), interface->ifnum, interface->name); +		} else {  			netsyslog(LOG_ERR, -				 "invalid multicast address %s", stoa(&addr)); +			"No multicast socket available to use for address %s", +			stoa(&addr));  			return;  		} +	} +#else +	/* +	 * For the case where we can't use a separate socket +	 */ +	interface = findlocalcastinterface(&addr, INT_MULTICAST); +	/* +	 * If we don't have a valid socket, just return +	 */ +	if (!interface) +	{ +		netsyslog(LOG_ERR, +		"Cannot add multicast address %s: Cannot find slot", +		stoa(&addr)); +		return; +	} + +#endif +	{ +		isc_boolean_t jstatus; +		jstatus = socket_multicast_enable(interface, lscope, &addr); +	 +		if (jstatus == ISC_TRUE) +			netsyslog(LOG_INFO, "Added Multicast Listener %s on interface #%d %s\n", stoa(&addr), interface->ifnum, interface->name); +		else +			netsyslog(LOG_ERR, "Failed to add Multicast Listener %s\n", stoa(&addr)); +	} +#else /* MCAST */ +	netsyslog(LOG_ERR, +		  "Cannot add multicast address %s: no Multicast support", +		  stoa(&addr)); +#endif /* MCAST */ +	return; +} + +/* + * io_multicast_del() - delete multicast group address + */ +void +io_multicast_del( +	struct sockaddr_storage addr +	) +{ +#ifdef MCAST +        struct interface *interface; +	isc_boolean_t lstatus; + +	/* +	 * Check to see if this is a multicast address +	 */ +	if (addr_ismulticast(&addr) == ISC_FALSE) +	{ +		netsyslog(LOG_ERR, +			 "invalid multicast address %s", stoa(&addr)); +		return; +	} +	switch (addr.ss_family) +	{ +	case AF_INET :  		/* -		* Disable reception of multicast packets -		*/ -		mreq.imr_multiaddr = ((struct sockaddr_in*)&addr)->sin_addr; -		mreq.imr_interface.s_addr = htonl(INADDR_ANY); -		for (i = 0; i < ninterfaces; i++) -		{ -			/* Be sure it's the correct family */ -			if (inter_list[i].sin.ss_family != AF_INET) -				continue; -			if (!(inter_list[i].flags & INT_MULTICAST)) -				continue; -			if (!(inter_list[i].fd < 0)) -				continue; -			if (!SOCKCMP(&addr, &inter_list[i].sin)) -				continue; -			if (i != wildipv4) -			{ -				/* we have an explicit fd, so we can close it */ -				close_socket(inter_list[i].fd); -				memset((char *)&inter_list[i], 0, sizeof(struct interface)); -				inter_list[i].fd = INVALID_SOCKET; -				inter_list[i].bfd = INVALID_SOCKET; -			} -			else -			{ -				/* We are sharing "any address" port :-(  Don't close it! */ -				if (setsockopt(inter_list[i].fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, -					(char *)&mreq, sizeof(mreq)) == -1) -				netsyslog(LOG_ERR, "setsockopt IP_DROP_MEMBERSHIP fails on address: %s %m", -					stoa(&addr)); -				inter_list[i].num_mcast--; -				/* If there are none left negate the Multicast flag */ -				if(inter_list[i].num_mcast == 0) -					inter_list[i].flags &= ~INT_MULTICAST; -			} +		 * Disable reception of multicast packets +		 */ +		interface = find_flagged_addr_in_list(&addr, INT_MCASTOPEN); +		while ( interface != NULL) { +			lstatus = socket_multicast_disable(interface, &addr); +			interface = find_flagged_addr_in_list(&addr, INT_MCASTOPEN);  		}  		break; -#ifdef HAVE_IPV6 +#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT  	case AF_INET6 : -		haddr6 = ((struct sockaddr_in6*)&addr)->sin6_addr; - -		if (!IN6_IS_ADDR_MULTICAST(&haddr6)) -		{ -			netsyslog(LOG_ERR, -				"invalid multicast address %s", stoa(&addr)); -			return; -		} -  		/* -		* Disable reception of multicast packets -		*/ -		mreq6.ipv6mr_multiaddr = ((struct sockaddr_in6*)&addr)->sin6_addr; -		mreq6.ipv6mr_interface = 0; -		for (i = 0; i < ninterfaces; i++) +		 * Disable reception of multicast packets +		 */ +		for (interface = ISC_LIST_HEAD(inter_list); +		     interface != NULL; +		     interface = ISC_LIST_NEXT(interface, link))  		{ +                        if (interface->flags & INT_WILDCARD) +			        continue; +	    			/* Be sure it's the correct family */ -			if (inter_list[i].sin.ss_family != AF_INET6) +			if (interface->sin.ss_family != AF_INET6)  				continue; -			if (!(inter_list[i].flags & INT_MULTICAST)) +			if (!(interface->flags & INT_MCASTOPEN))  				continue; -			if (!(inter_list[i].fd < 0)) +			if (!(interface->fd < 0))  				continue; -			if (!SOCKCMP(&addr, &inter_list[i].sin)) +			if (!SOCKCMP(&addr, &interface->sin))  				continue; -			if (i != wildipv6) -			{ -				/* we have an explicit fd, so we can close it */ -				close_socket(inter_list[i].fd); -				memset((char *)&inter_list[i], 0, sizeof(struct interface)); -				inter_list[i].fd = INVALID_SOCKET; -				inter_list[i].bfd = INVALID_SOCKET; -			} -			else -			{ -				/* We are sharing "any address" port :-(  Don't close it! */ -				if (setsockopt(inter_list[i].fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, -					(char *)&mreq6, sizeof(mreq6)) == -1) -				netsyslog(LOG_ERR, "setsockopt IP_DROP_MEMBERSHIP fails on address %s: %m", -					stoa(&addr)); -				/* If there are none left negate the Multicast flag */ -				if(inter_list[i].num_mcast == 0) -					inter_list[i].flags &= ~INT_MULTICAST; -			} +			lstatus = socket_multicast_disable(interface, &addr);  		}  		break; -#endif /* HAVE_IPV6 */ +#endif /* INCLUDE_IPV6_MULTICAST_SUPPORT */ +  	}/* switch */ +          delete_addr_from_list(&addr);  #else /* not MCAST */ @@ -983,6 +2182,76 @@ io_multicast_del(  #endif /* not MCAST */  } +/* + * init_nonblocking_io() - set up descriptor to be non blocking + */ +static void init_nonblocking_io(SOCKET fd) +{ +	/* +	 * set non-blocking, +	 */ + +#ifdef USE_FIONBIO +	/* in vxWorks we use FIONBIO, but the others are defined for old systems, so +	 * all hell breaks loose if we leave them defined +	 */ +#undef O_NONBLOCK +#undef FNDELAY +#undef O_NDELAY +#endif + +#if defined(O_NONBLOCK) /* POSIX */ +	if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) +	{ +		netsyslog(LOG_ERR, "fcntl(O_NONBLOCK) fails on fd #%d: %m", +			fd); +		exit(1); +		/*NOTREACHED*/ +	} +#elif defined(FNDELAY) +	if (fcntl(fd, F_SETFL, FNDELAY) < 0) +	{ +		netsyslog(LOG_ERR, "fcntl(FNDELAY) fails on fd #%d: %m", +			fd); +		exit(1); +		/*NOTREACHED*/ +	} +#elif defined(O_NDELAY) /* generally the same as FNDELAY */ +	if (fcntl(fd, F_SETFL, O_NDELAY) < 0) +	{ +		netsyslog(LOG_ERR, "fcntl(O_NDELAY) fails on fd #%d: %m", +			fd); +		exit(1); +		/*NOTREACHED*/ +	} +#elif defined(FIONBIO) +	{ +		int on = 1; +# if defined(SYS_WINNT) + +		if (ioctlsocket(fd,FIONBIO,(u_long *) &on) == SOCKET_ERROR) +# else +		if (ioctl(fd,FIONBIO,&on) < 0) +# endif +		{ +			netsyslog(LOG_ERR, "ioctl(FIONBIO) fails on fd #%d: %m", +				fd); +			exit(1); +			/*NOTREACHED*/ +		} +	} +#elif defined(FIOSNBIO) +	if (ioctl(fd,FIOSNBIO,&on) < 0) +	{ +		netsyslog(LOG_ERR, "ioctl(FIOSNBIO) fails on fd #%d: %m", +			fd); +		exit(1); +		/*NOTREACHED*/ +	} +#else +# include "Bletch: Need non-blocking I/O!" +#endif +}  /*   * open_socket - open a socket, returning the file descriptor @@ -992,12 +2261,14 @@ static SOCKET  open_socket(  	struct sockaddr_storage *addr,  	int flags, -	int turn_off_reuse +	int turn_off_reuse, +	struct interface *interf  	)  {  	int errval;  	SOCKET fd; -	int on = 1, off = 0; +	int on = 1, off = 0;	/* int is OK for REUSEADR per */ +				/* http://www.kohala.com/start/mcast.api.txt */  #if defined(IPTOS_LOWDELAY) && defined(IPPROTO_IP) && defined(IP_TOS)  	int tos;  #endif /* IPTOS_LOWDELAY && IPPROTO_IP && IP_TOS */ @@ -1009,70 +2280,84 @@ open_socket(  #ifndef SYS_WINNT  	if (  (fd = socket(addr->ss_family, SOCK_DGRAM, 0)) < 0) {  		errval = errno; -		if(addr->ss_family == AF_INET) -			netsyslog(LOG_ERR, "socket(AF_INET, SOCK_DGRAM, 0) failed on address %s: %m", -				stoa(addr)); -		else if(addr->ss_family == AF_INET6) -			netsyslog(LOG_ERR, "socket(AF_INET6, SOCK_DGRAM, 0) failed on address %s: %m", -				stoa(addr)); -		if (errval == EPROTONOSUPPORT || errval == EAFNOSUPPORT || -		    errval == EPFNOSUPPORT) -			return (INVALID_SOCKET); -		exit(1); -		/*NOTREACHED*/ -	}  #else  	if (  (fd = socket(addr->ss_family, SOCK_DGRAM, 0)) == INVALID_SOCKET) {  		errval = WSAGetLastError(); +#endif  		if(addr->ss_family == AF_INET)  			netsyslog(LOG_ERR, "socket(AF_INET, SOCK_DGRAM, 0) failed on address %s: %m",  				stoa(addr));  		else if(addr->ss_family == AF_INET6)  			netsyslog(LOG_ERR, "socket(AF_INET6, SOCK_DGRAM, 0) failed on address %s: %m",  				stoa(addr)); +#ifndef SYS_WINNT +		if (errval == EPROTONOSUPPORT || errval == EAFNOSUPPORT || +		    errval == EPFNOSUPPORT) +#else  		if (errval == WSAEPROTONOSUPPORT || errval == WSAEAFNOSUPPORT ||  		    errval == WSAEPFNOSUPPORT) +#endif  			return (INVALID_SOCKET); +		msyslog(LOG_ERR, "unexpected error code %d (not PROTONOSUPPORT|AFNOSUPPORT|FPNOSUPPORT) - exiting", errval);  		exit(1);  		/*NOTREACHED*/  	} +#ifdef SYS_WINNT  	if (connection_reset_fix(fd) != ISC_R_SUCCESS) {  		netsyslog(LOG_ERR, "connection_reset_fix(fd) failed on address %s: %m",  			stoa(addr));  	} -  #endif /* SYS_WINNT */ -	/* set SO_REUSEADDR since we will be binding the same port -	   number on each interface */ +	/* +	 * Fixup the file descriptor for some systems +	 * See bug #530 for details of the issue. +	 */ +	fd = move_fd(fd); + +	/* +	 * set SO_REUSEADDR since we will be binding the same port +	 * number on each interface according to flag +	 */  	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, -		       (char *)&on, sizeof(on))) +		       turn_off_reuse ? (char *)&off : (char *)&on, sizeof(on)))  	{ -		netsyslog(LOG_ERR, "setsockopt SO_REUSEADDR on fails on address %s: %m", -			stoa(addr)); +		netsyslog(LOG_ERR, "setsockopt SO_REUSEADDR %s on fails on address %s: %m", +			turn_off_reuse ? "off" : "on", stoa(addr)); + +		closesocket(fd); + +		return INVALID_SOCKET;  	} +	/* +	 * IPv4 specific options go here +	 */ +	if (addr->ss_family == AF_INET) {  #if defined(IPTOS_LOWDELAY) && defined(IPPROTO_IP) && defined(IP_TOS)  	/* set IP_TOS to minimize packet delay */ -	tos = IPTOS_LOWDELAY; -	if (addr->ss_family == AF_INET) +		tos = IPTOS_LOWDELAY;  		if (setsockopt(fd, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(tos)) < 0)  		{  			netsyslog(LOG_ERR, "setsockopt IPTOS_LOWDELAY on fails on address %s: %m",  				stoa(addr));  		} +#endif /* IPTOS_LOWDELAY && IPPROTO_IP && IP_TOS */ +	} +	/* +	 * IPv6 specific options go here +	 */ +        if (addr->ss_family == AF_INET6) {  #if defined(IPV6_V6ONLY) -        if (addr->ss_family == AF_INET6)                  if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY,                  	(char*)&on, sizeof(on)))                  {                  	netsyslog(LOG_ERR, "setsockopt IPV6_V6ONLY on fails on address %s: %m",  				stoa(addr));  		} -#else /* IPV6_V6ONLY */ +#endif /* IPV6_V6ONLY */  #if defined(IPV6_BINDV6ONLY) -        if (addr->ss_family == AF_INET6)                  if (setsockopt(fd, IPPROTO_IPV6, IPV6_BINDV6ONLY,                  	(char*)&on, sizeof(on)))                  { @@ -1081,238 +2366,119 @@ open_socket(  			    stoa(addr));  		}  #endif /* IPV6_BINDV6ONLY */ -#endif /* IPV6_V6ONLY */ - -#endif /* IPTOS_LOWDELAY && IPPROTO_IP && IP_TOS */ +	} +#ifdef OS_NEEDS_REUSEADDR_FOR_IFADDRBIND  	/* -	 * bind the local address. +	 * some OSes don't allow binding to more specific +	 * addresses if a wildcard address already bound +	 * to the port and SO_REUSEADDR is not set  	 */ -	if (bind(fd, (struct sockaddr *)addr, SOCKLEN(addr)) < 0) { -		char buff[160]; - -		if(addr->ss_family == AF_INET) -			sprintf(buff, -				"bind() fd %d, family %d, port %d, addr %s, in_classd=%d flags=%d fails: %%m", -				fd, addr->ss_family, (int)ntohs(((struct sockaddr_in*)addr)->sin_port), -				stoa(addr), -				IN_CLASSD(ntohl(((struct sockaddr_in*)addr)->sin_addr.s_addr)), flags); -		else if(addr->ss_family == AF_INET6) -		                sprintf(buff, -                                "bind() fd %d, family %d, port %d, addr %s, in6_is_addr_multicast=%d flags=%d fails: %%m", -                                fd, addr->ss_family, (int)ntohs(((struct sockaddr_in6*)addr)->sin6_port), -                                stoa(addr), -                                IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)addr)->sin6_addr), flags); -		else return INVALID_SOCKET; - -		netsyslog(LOG_ERR, buff); -		closesocket(fd); - -		/* -		 * soft fail if opening a multicast address -		 */ - 		if(addr->ss_family == AF_INET){ -			if(IN_CLASSD(ntohl(((struct sockaddr_in*)addr)->sin_addr.s_addr))) -				return (INVALID_SOCKET); -		} -		else { -			if(IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)addr)->sin6_addr)) -				return (INVALID_SOCKET); -		} -#if 0 -		exit(1); -#else -		return INVALID_SOCKET; -#endif +	if (!is_wildcard_addr(addr)) { +		set_wildcard_reuse(addr->ss_family, 1);  	} -#ifdef DEBUG -	if (debug) -	    printf("bind() fd %d, family %d, port %d, addr %s, flags=%d\n", -		   fd, -		   addr->ss_family, -		   (int)ntohs(((struct sockaddr_in*)addr)->sin_port), -		   stoa(addr), -		   flags);  #endif  	/* -	 * I/O Completion Ports don't care about the select and FD_SET -	 */ -#ifndef HAVE_IO_COMPLETION_PORT -	if (fd > maxactivefd) -	    maxactivefd = fd; -	FD_SET(fd, &activefds); -#endif -	add_socket_to_list(fd); -	/* -	 * set non-blocking, +	 * bind the local address.  	 */ +	errval = bind(fd, (struct sockaddr *)addr, SOCKLEN(addr)); -#ifdef USE_FIONBIO -	/* in vxWorks we use FIONBIO, but the others are defined for old systems, so -	 * all hell breaks loose if we leave them defined +#ifdef OS_NEEDS_REUSEADDR_FOR_IFADDRBIND +	/* +	 * some OSes don't allow binding to more specific +	 * addresses if a wildcard address already bound +	 * to the port and REUSE_ADDR is not set  	 */ -#undef O_NONBLOCK -#undef FNDELAY -#undef O_NDELAY +	if (!is_wildcard_addr(addr)) { +		set_wildcard_reuse(addr->ss_family, 0); +	}  #endif -#if defined(O_NONBLOCK) /* POSIX */ -	if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) -	{ -		netsyslog(LOG_ERR, "fcntl(O_NONBLOCK) fails on address %s: %m", -			stoa(addr)); -		exit(1); -		/*NOTREACHED*/ -	} -#elif defined(FNDELAY) -	if (fcntl(fd, F_SETFL, FNDELAY) < 0) -	{ -		netsyslog(LOG_ERR, "fcntl(FNDELAY) fails on address %s: %m", -			stoa(addr)); -		exit(1); -		/*NOTREACHED*/ -	} -#elif defined(O_NDELAY) /* generally the same as FNDELAY */ -	if (fcntl(fd, F_SETFL, O_NDELAY) < 0) -	{ -		netsyslog(LOG_ERR, "fcntl(O_NDELAY) fails on address %s: %m", -			stoa(addr)); -		exit(1); -		/*NOTREACHED*/ -	} -#elif defined(FIONBIO) -# if defined(VMS) -		if (ioctl(fd,FIONBIO,&on) < 0) -# elif defined(SYS_WINNT) -		if (ioctlsocket(fd,FIONBIO,(u_long *) &on) == SOCKET_ERROR) +	if (errval < 0) { +		/* +		 * Don't log this under all conditions +		 */ +		if (turn_off_reuse == 0 +#ifdef DEBUG +		    || debug > 1 +#endif +			) { +			if (addr->ss_family == AF_INET) +				netsyslog(LOG_ERR, +					  "bind() fd %d, family %d, port %d, addr %s, in_classd=%d flags=0x%x fails: %m", +					  fd, addr->ss_family, (int)ntohs(((struct sockaddr_in*)addr)->sin_port), +					  stoa(addr), +					  IN_CLASSD(ntohl(((struct sockaddr_in*)addr)->sin_addr.s_addr)), flags); +#ifdef INCLUDE_IPV6_SUPPORT +			else if (addr->ss_family == AF_INET6) +		                netsyslog(LOG_ERR, +					  "bind() fd %d, family %d, port %d, scope %d, addr %s, in6_is_addr_multicast=%d flags=0x%x fails: %m", +					  fd, addr->ss_family, (int)ntohs(((struct sockaddr_in6*)addr)->sin6_port), +# ifdef ISC_PLATFORM_HAVESCOPEID +					  ((struct sockaddr_in6*)addr)->sin6_scope_id  # else -		if (ioctl(fd,FIONBIO,&on) < 0) +					  -1  # endif -	{ -		netsyslog(LOG_ERR, "ioctl(FIONBIO) fails on address %s: %m", -			stoa(addr)); -		exit(1); -		/*NOTREACHED*/ +					  , stoa(addr), +					  IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)addr)->sin6_addr), flags); +#endif +		} + +		closesocket(fd); +		 +		return INVALID_SOCKET;  	} -#elif defined(FIOSNBIO) -	if (ioctl(fd,FIOSNBIO,&on) < 0) + +#ifdef HAVE_TIMESTAMP  	{ -		netsyslog(LOG_ERR, "ioctl(FIOSNBIO) fails on address %s: %m", -			stoa(addr)); -		exit(1); -		/*NOTREACHED*/ -	} -#else -# include "Bletch: Need non-blocking I/O!" +		if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, +			       (char*)&on, sizeof(on))) +		{ +			netsyslog(LOG_DEBUG, +				  "setsockopt SO_TIMESTAMP on fails on address %s: %m", +				  stoa(addr)); +		} +#ifdef DEBUG +		else +		{ +			DPRINTF(4, ("setsockopt SO_TIMESTAMP enabled on fd %d address %s\n", fd, stoa(addr))); +		} +#endif +	}	  #endif +	DPRINTF(4, ("bind() fd %d, family %d, port %d, addr %s, flags=0x%x\n", +		   fd, +		   addr->ss_family, +		   (int)ntohs(((struct sockaddr_in*)addr)->sin_port), +		   stoa(addr), +		    flags)); +	init_nonblocking_io(fd); +	  #ifdef HAVE_SIGNALED_IO  	init_socket_sig(fd);  #endif /* not HAVE_SIGNALED_IO */ -	/* -	 *	Turn off the SO_REUSEADDR socket option.  It apparently -	 *	causes heartburn on systems with multicast IP installed. -	 *	On normal systems it only gets looked at when the address -	 *	is being bound anyway.. -	 */ -	if (turn_off_reuse) -	    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, -			   (char *)&off, sizeof(off))) -	    { -		    netsyslog(LOG_ERR, "setsockopt SO_REUSEADDR off fails on address %s: %m", -			    stoa(addr)); -	    } - -#ifdef SO_BROADCAST -	/* if this interface can support broadcast, set SO_BROADCAST */ -	if (flags & INT_BROADCAST) -	{ -		if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, -			       (char *)&on, sizeof(on))) -		{ -			netsyslog(LOG_ERR, "setsockopt(SO_BROADCAST) on address %s: %m", -				stoa(addr)); -		} -	} -#endif /* SO_BROADCAST */ +	add_fd_to_list(fd, FD_TYPE_SOCKET);  #if !defined(SYS_WINNT) && !defined(VMS) -# ifdef DEBUG -	if (debug > 1) -	    printf("flags for fd %d: 0%o\n", fd, -		   fcntl(fd, F_GETFL, 0)); -# endif +	DPRINTF(4, ("flags for fd %d: 0x%x\n", fd, +		    fcntl(fd, F_GETFL, 0)));  #endif /* SYS_WINNT || VMS */ -	return fd; -} - - -/* - * close_socket - close a socket and remove from the activefd list - */ -static void -close_socket( -	     SOCKET fd -	) -{ -	SOCKET i, newmax; - -	(void) closesocket(fd); - -	/* -	 * I/O Completion Ports don't care about select and fd_set -	 */ -#ifndef HAVE_IO_COMPLETION_PORT -	FD_CLR( (u_int) fd, &activefds); - -	if (fd == maxactivefd) { -		newmax = 0; -		for (i = 0; i < maxactivefd; i++) -			if (FD_ISSET(i, &activefds)) -				newmax = i; -		maxactivefd = newmax; -	} -#endif -	delete_socket_from_list(fd); - -} - - +#if defined (HAVE_IO_COMPLETION_PORT)  /* - * close_file - close a file and remove from the activefd list - * added 1/31/1997 Greg Schueman for Windows NT portability + * Add the socket to the completion port   */ -#ifdef REFCLOCK -static void -close_file( -	SOCKET fd -	) -{ -	int i, newmax; - -	(void) close(fd); -	/* -	 * I/O Completion Ports don't care about select and fd_set -	 */ -#ifndef HAVE_IO_COMPLETION_PORT -	FD_CLR( (u_int) fd, &activefds); - -	if (fd == maxactivefd) { -		newmax = 0; -		for (i = 0; i < maxactivefd; i++) -			if (FD_ISSET(i, &activefds)) -				newmax = i; -		maxactivefd = newmax; +	if (io_completion_port_add_socket(fd, interf)) +	{ +		msyslog(LOG_ERR, "unable to set up io completion port - EXITING"); +		exit(1);  	}  #endif -	delete_socket_from_list(fd); - +	return fd;  } -#endif -  /* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */  /* @@ -1343,87 +2509,98 @@ sendpkt(  		struct	in_addr addr;  	}; -#ifdef HAVE_IPV6 +#ifdef INCLUDE_IPV6_SUPPORT  	struct cache6 {  		u_short port;  		struct in6_addr addr;  	}; -#endif /* HAVE_IPV6 */ +#endif /* INCLUDE_IPV6_SUPPORT */ +  #ifndef ERRORCACHESIZE  #define ERRORCACHESIZE 8  #endif  #if ERRORCACHESIZE > 0  	static struct cache badaddrs[ERRORCACHESIZE]; -#ifdef HAVE_IPV6 +#ifdef INCLUDE_IPV6_SUPPORT  	static struct cache6 badaddrs6[ERRORCACHESIZE]; -#endif /* HAVE_IPV6 */ +#endif /* INCLUDE_IPV6_SUPPORT */  #else  #define badaddrs ((struct cache *)0)		/* Only used in empty loops! */ -#ifdef HAVE_IPV6 +#ifdef INCLUDE_IPV6_SUPPORT  #define badaddrs6 ((struct cache6 *)0)		/* Only used in empty loops! */ -#endif /* HAVE_IPV6 */ +#endif /* INCLUDE_IPV6_SUPPORT */  #endif  #ifdef DEBUG  	if (debug > 1) -	    printf("%ssendpkt(fd=%d dst=%s, src=%s, ttl=%d, len=%d)\n", -		   (ttl >= 0) ? "\tMCAST\t*****" : "", -		   inter->fd, stoa(dest), -		   stoa(&inter->sin), ttl, len); +	  { +	    if (inter != NULL)  +	      { +		printf("%ssendpkt(fd=%d dst=%s, src=%s, ttl=%d, len=%d)\n", +		       (ttl > 0) ? "\tMCAST\t***** " : "", +		       inter->fd, stoa(dest), +		       stoa(&inter->sin), ttl, len); +	      } +	    else +	      { +		printf("%ssendpkt(dst=%s, ttl=%d, len=%d): no interface - IGNORED\n", +		       (ttl > 0) ? "\tMCAST\t***** " : "", +		       stoa(dest), +		       ttl, len); +	      } +	  }  #endif +	if (inter == NULL)	/* unbound peer - drop request and wait for better network conditions */ +	  return; +	  #ifdef MCAST -	switch (inter->sin.ss_family) { - -	case AF_INET : - +	/* +	 * for the moment we use the bcast option to set multicast ttl +	 */ +	if (ttl > 0 && ttl != inter->last_ttl) { +		  		/* -		* for the moment we use the bcast option to set multicast ttl -		*/ -		if (ttl > 0 && ttl != inter->last_ttl) { +		 * set the multicast ttl for outgoing packets +		 */ +		int rtc; +		 +		switch (inter->sin.ss_family) { +			 +		case AF_INET : +		{ +			u_char mttl = (u_char) ttl; -			/* -			* set the multicast ttl for outgoing packets -			*/ -			if (setsockopt(inter->fd, IPPROTO_IP, IP_MULTICAST_TTL, -				(char *) &ttl, sizeof(ttl)) != 0) { -				netsyslog(LOG_ERR, "setsockopt IP_MULTICAST_TTL fails on address %s: %m", -					stoa(&inter->sin)); -			} -			else -   				inter->last_ttl = ttl; +			rtc = setsockopt(inter->fd, IPPROTO_IP, IP_MULTICAST_TTL, +					 (const void *) &mttl, sizeof(mttl)); +			break;  		} -		break; - -#ifdef HAVE_IPV6 -	case AF_INET6 : +			 +#ifdef INCLUDE_IPV6_SUPPORT +		case AF_INET6 : +		{ +			u_int ittl = (u_char) ttl; -	 	/* -		 * for the moment we use the bcast option to set -		 * multicast max hops -		 */ -        	if (ttl > 0 && ttl != inter->last_ttl) { - -                	/* -                 	* set the multicast ttl for outgoing packets -                 	*/ -                	if (setsockopt(inter->fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, -                    	&ttl, sizeof(ttl)) == -1) -	                        netsyslog(LOG_ERR, "setsockopt IP_MULTICAST_TTL fails on address %s: %m", -					stoa(&inter->sin)); -                	else -	                        inter->last_ttl = ttl; -	        } -	        break; -#endif /* HAVE_IPV6 */ - -	default : -		exit(1); +			rtc = setsockopt(inter->fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, +					 (const void *) &ittl, sizeof(ittl)); +			break; +		} +#endif /* INCLUDE_IPV6_SUPPORT */ +		default:	/* just NOP if not supported */ +			rtc = 0; +			break; +		} +		 +		if (rtc != 0) { +			netsyslog(LOG_ERR, "setsockopt IP_MULTICAST_TTL/IPV6_MULTICAST_HOPS fails on address %s: %m", +				  stoa(&inter->sin)); +		} +		else +			inter->last_ttl = ttl;  	} -  #endif /* MCAST */  	for (slot = ERRORCACHESIZE; --slot >= 0; ) @@ -1432,14 +2609,13 @@ sendpkt(  				badaddrs[slot].addr.s_addr == ((struct sockaddr_in*)dest)->sin_addr.s_addr)  			break;  		} -#ifdef HAVE_IPV6 +#ifdef INCLUDE_IPV6_SUPPORT  		else if (dest->ss_family == AF_INET6) {  			if (badaddrs6[slot].port == ((struct sockaddr_in6*)dest)->sin6_port &&  				badaddrs6[slot].addr.s6_addr == ((struct sockaddr_in6*)dest)->sin6_addr.s6_addr)  			break;  		} -#endif /* HAVE_IPV6 */ -		else exit(1);  /* address family not supported yet */ +#endif /* INCLUDE_IPV6_SUPPORT */  #if defined(HAVE_IO_COMPLETION_PORT)          err = io_completion_port_sendto(inter, pkt, len, dest); @@ -1479,7 +2655,7 @@ sendpkt(  					}  				break; -#ifdef HAVE_IPV6 +#ifdef INCLUDE_IPV6_SUPPORT  			case AF_INET6 :  				for (slot = ERRORCACHESIZE; --slot >= 0; ) @@ -1490,13 +2666,13 @@ sendpkt(                                      		break;                              		}                  		break; -#endif /* HAVE_IPV6 */ - -			default : -				exit(1); +#endif /* INCLUDE_IPV6_SUPPORT */ +			default:  /* don't care if not supported */ +				break;  			} -			netsyslog(LOG_ERR, "sendto(%s): %m", stoa(dest)); +			netsyslog(LOG_ERR, "sendto(%s) (fd=%d): %m", +				  stoa(dest), inter->fd);  		}  	}  	else @@ -1513,11 +2689,13 @@ sendpkt(  			case AF_INET :  				badaddrs[slot].port = 0;  				break; -#ifdef HAVE_IPV6 +#ifdef INCLUDE_IPV6_SUPPORT  			case AF_INET6 :  				badaddrs6[slot].port = 0;  				break; -#endif /* HAVE_IPV6 */ +#endif /* INCLUDE_IPV6_SUPPORT */ +			default:  /* don't care if not supported */ +				break;  			}  		}  	} @@ -1550,363 +2728,506 @@ fdbits(  }  /* - * input_handler - receive packets asynchronously + * Routine to read the refclock packets for a specific interface + * Return the number of bytes read. That way we know if we should + * read it again or go on to the next one if no bytes returned   */ -void -input_handler( -	l_fp *cts -	) +static inline int +read_refclock_packet(SOCKET fd, struct refclockio *rp, l_fp ts)  { -	register int i, n; +	int i; +	int buflen;  	register struct recvbuf *rb; -	register int doing; -	register SOCKET fd; -	struct timeval tvzero; -	int fromlen; -	l_fp ts;			/* Timestamp at BOselect() gob */ -	l_fp ts_e;			/* Timestamp at EOselect() gob */ -	fd_set fds; -	int select_count = 0; -	static int handler_count = 0; -	++handler_count; -	if (handler_count != 1) -	    msyslog(LOG_ERR, "input_handler: handler_count is %d!", handler_count); -	handler_calls++; -	ts = *cts; +	rb = get_free_recv_buffer(); -	for (;;) +	if (rb == NULL)  	{  		/* -		 * Do a poll to see who has data +		 * No buffer space available - just drop the packet  		 */ +		char buf[RX_BUFF_SIZE]; + +		buflen = read(fd, buf, sizeof buf); +		packets_dropped++; +		return (buflen); +	} -		fds = activefds; -		tvzero.tv_sec = tvzero.tv_usec = 0; +	i = (rp->datalen == 0 +	    || rp->datalen > sizeof(rb->recv_space)) +	    ? sizeof(rb->recv_space) : rp->datalen; +	buflen = read(fd, (char *)&rb->recv_space, (unsigned)i); +	if (buflen < 0) +	{ +		if (errno != EINTR && errno != EAGAIN) { +			netsyslog(LOG_ERR, "clock read fd %d: %m", fd); +		} +		freerecvbuf(rb); +		return (buflen); +	} + +	/* +	 * Got one. Mark how and when it got here, +	 * put it on the full list and do bookkeeping. +	 */ +	rb->recv_length = buflen; +	rb->recv_srcclock = rp->srcclock; +	rb->dstadr = 0; +	rb->fd = fd; +	rb->recv_time = ts; +	rb->receiver = rp->clock_recv; + +	if (rp->io_input) +	{  		/* -		 * If we have something to do, freeze a timestamp. -		 * See below for the other cases (nothing (left) to do or error) +		 * have direct input routine for refclocks  		 */ -		while (0 < (n = select(maxactivefd+1, &fds, (fd_set *)0, (fd_set *)0, &tvzero))) +		if (rp->io_input(rb) == 0)  		{ -			++select_count; -			++handler_pkts; - -#ifdef REFCLOCK  			/* -			 * Check out the reference clocks first, if any +			 * data was consumed - nothing to pass up +			 * into block input machine  			 */ -			if (refio != 0) -			{ -				register struct refclockio *rp; +			freerecvbuf(rb); +			return (buflen); +		} +	} +	 +	add_full_recv_buffer(rb); -				for (rp = refio; rp != 0 && n > 0; rp = rp->next) -				{ -					fd = rp->fd; -					if (FD_ISSET(fd, &fds)) -					{ -						n--; -						if (free_recvbuffs() == 0) -						{ -							char buf[RX_BUFF_SIZE]; - -							(void) read(fd, buf, sizeof buf); -							packets_dropped++; -							goto select_again; -						} - -						rb = get_free_recv_buffer(); - -						i = (rp->datalen == 0 -						     || rp->datalen > sizeof(rb->recv_space)) -						    ? sizeof(rb->recv_space) : rp->datalen; -						rb->recv_length = -						    read(fd, (char *)&rb->recv_space, (unsigned)i); - -						if (rb->recv_length == -1) -						{ -							netsyslog(LOG_ERR, "clock read fd %d: %m", fd); -							freerecvbuf(rb); -							goto select_again; -						} +	rp->recvcount++; +	packets_received++; +	return (buflen); +} -						/* -						 * Got one.  Mark how -						 * and when it got here, -						 * put it on the full -						 * list and do -						 * bookkeeping. -						 */ -						rb->recv_srcclock = rp->srcclock; -						rb->dstadr = 0; -						rb->fd = fd; -						rb->recv_time = ts; -						rb->receiver = rp->clock_recv; - -						if (rp->io_input) -						{ -							/* -							 * have direct -							 * input routine -							 * for refclocks -							 */ -							if (rp->io_input(rb) == 0) -							{ -								/* -								 * data -								 * was -								 * consumed -								 * - -								 * nothing -								 * to -								 * pass -								 * up -								 * into -								 * block -								 * input -								 * machine -								 */ -								freerecvbuf(rb); -#if 1 -								goto select_again; -#else -								continue; +#ifdef HAVE_TIMESTAMP +/* + * extract timestamps from control message buffer + */ +static l_fp +	fetch_timestamp(struct recvbuf *rb, struct msghdr *msghdr, l_fp ts) +{ +#ifdef USE_TIMESTAMP_CMSG +	struct cmsghdr *cmsghdr; + +	cmsghdr = CMSG_FIRSTHDR(msghdr); +	while (cmsghdr != NULL) { +		switch (cmsghdr->cmsg_type) +		{ +		case SCM_TIMESTAMP: +		{ +			struct timeval *tvp = (struct timeval *)CMSG_DATA(cmsghdr); +			double dtemp; +			l_fp nts; +			DPRINTF(4, ("fetch_timestamp: system network time stamp: %ld.%06ld\n", tvp->tv_sec, tvp->tv_usec)); +			nts.l_i = tvp->tv_sec + JAN_1970; +			dtemp = tvp->tv_usec / 1e6; + + 			/* fuzz lower bits not covered by precision */ + 			if (sys_precision != 0) + 				dtemp += (ntp_random() / FRAC - .5) / (1 << + 								       -sys_precision); + +			nts.l_uf = (u_int32)(dtemp*FRAC); +#ifdef DEBUG_TIMING +			{ +				l_fp dts = ts; +				L_SUB(&dts, &nts); +				collect_timing(rb, "input processing delay", 1, &dts); +				DPRINTF(4, ("fetch_timestamp: timestamp delta: %s (incl. prec fuzz)\n", lfptoa(&dts, 9))); +			} +#endif +			ts = nts;  /* network time stamp */ +			break; +		} +		default: +			DPRINTF(4, ("fetch_timestamp: skipping control message 0x%x\n", cmsghdr->cmsg_type)); +			break; +		} +		cmsghdr = CMSG_NXTHDR(msghdr, cmsghdr); +	} +#endif +	return ts; +}  #endif -							} -						} -						add_full_recv_buffer(rb); +/* + * Routine to read the network NTP packets for a specific interface + * Return the number of bytes read. That way we know if we should + * read it again or go on to the next one if no bytes returned + */ +static inline int +read_network_packet(SOCKET fd, struct interface *itf, l_fp ts) +{ +	GETSOCKNAME_SOCKLEN_TYPE fromlen; +	int buflen; +	register struct recvbuf *rb; +#ifdef HAVE_TIMESTAMP +	struct msghdr msghdr; +	struct iovec iovec; +	char control[TIMESTAMP_CTLMSGBUF_SIZE];	/* pick up control messages */ +#endif -						rp->recvcount++; -						packets_received++; -					} -				} -			} -#endif /* REFCLOCK */ +	/* +	 * Get a buffer and read the frame.  If we +	 * haven't got a buffer, or this is received +	 * on a disallowed socket, just dump the +	 * packet. +	 */ -			/* -			 * Loop through the interfaces looking for data -			 * to read. -			 */ -			for (i = ninterfaces - 1; (i >= 0) && (n > 0); i--) -			{ -				for (doing = 0; (doing < 2) && (n > 0); doing++) -				{ -					if (doing == 0) -					{ -						fd = inter_list[i].fd; -					} -					else -					{ -						if (!(inter_list[i].flags & INT_BCASTOPEN)) -						    break; -						fd = inter_list[i].bfd; -					} -					if (fd < 0) continue; -					if (FD_ISSET(fd, &fds)) -					{ -						n--; +	rb = get_free_recv_buffer(); -						/* -						 * Get a buffer and read -						 * the frame.  If we -						 * haven't got a buffer, -						 * or this is received -						 * on the wild card -						 * socket, just dump the -						 * packet. -						 */ -						if ( -#ifdef UDP_WILDCARD_DELIVERY -				/* -				 * these guys manage to put properly addressed -				 * packets into the wildcard queue -				 */ -							(free_recvbuffs() == 0) -#else -							((i == wildipv4) || (i == wildipv6)|| -							(free_recvbuffs() == 0)) -#endif -							) +	if (rb == NULL || itf->ignore_packets == ISC_TRUE)  	{  		char buf[RX_BUFF_SIZE];  		struct sockaddr_storage from; - -		fromlen = sizeof from; -		(void) recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr*)&from, &fromlen); -#ifdef DEBUG -		if (debug) -		    printf("%s on %d(%lu) fd=%d from %s\n", -			   (i) ? "drop" : "ignore", -			   i, free_recvbuffs(), fd, -			   stoa(&from)); -#endif -		if (i == wildipv4 || i == wildipv6) -		    packets_ignored++; +		if (rb != NULL) +			freerecvbuf(rb); + +		fromlen = sizeof(from); +		buflen = recvfrom(fd, buf, sizeof(buf), 0, +				(struct sockaddr*)&from, &fromlen); +		DPRINTF(4, ("%s on (%lu) fd=%d from %s\n", +			(itf->ignore_packets == ISC_TRUE) ? "ignore" : "drop", +			free_recvbuffs(), fd, +			stoa(&from))); +		if (itf->ignore_packets == ISC_TRUE) +			packets_ignored++;  		else -		    packets_dropped++; -		goto select_again; +			packets_dropped++; +		return (buflen);  	} -	rb = get_free_recv_buffer(); -  	fromlen = sizeof(struct sockaddr_storage); + +#ifndef HAVE_TIMESTAMP  	rb->recv_length = recvfrom(fd, -				   (char *)&rb->recv_space, -				   sizeof(rb->recv_space), 0, -				   (struct sockaddr *)&rb->recv_srcadr, -				   &fromlen); -	if (rb->recv_length == 0 -#ifdef EWOULDBLOCK -		 || errno==EWOULDBLOCK +			  (char *)&rb->recv_space, +			   sizeof(rb->recv_space), 0, +			   (struct sockaddr *)&rb->recv_srcadr, +			   &fromlen); +#else +	iovec.iov_base        = (void *)&rb->recv_space; +	iovec.iov_len         = sizeof(rb->recv_space); +	msghdr.msg_name       = (void *)&rb->recv_srcadr; +	msghdr.msg_namelen    = sizeof(rb->recv_srcadr); +	msghdr.msg_iov        = &iovec; +	msghdr.msg_iovlen     = 1; +	msghdr.msg_control    = (void *)&control; +	msghdr.msg_controllen = sizeof(control); +	msghdr.msg_flags      = 0; +	rb->recv_length       = recvmsg(fd, &msghdr, 0);  #endif + +	buflen = rb->recv_length; + +	if (buflen == 0 || (buflen == -1 &&  +	    (errno==EWOULDBLOCK  #ifdef EAGAIN -		 || errno==EAGAIN +	   || errno==EAGAIN  #endif -		 ) { +	 ))) {  		freerecvbuf(rb); -	    continue; +		return (buflen);  	} -	else if (rb->recv_length < 0) +	else if (buflen < 0)  	{  		netsyslog(LOG_ERR, "recvfrom(%s) fd=%d: %m", - 			stoa(&rb->recv_srcadr), fd); -#ifdef DEBUG -		if (debug) -		    printf("input_handler: fd=%d dropped (bad recvfrom)\n", fd); -#endif +		stoa(&rb->recv_srcadr), fd); +		DPRINTF(5, ("read_network_packet: fd=%d dropped (bad recvfrom)\n", fd));  		freerecvbuf(rb); -		continue; +		return (buflen);  	} +  #ifdef DEBUG  	if (debug > 2) {  		if(rb->recv_srcadr.ss_family == AF_INET) -			printf("input_handler: if=%d fd=%d length %d from %08lx %s\n", -		   		i, fd, rb->recv_length, +			printf("read_network_packet: fd=%d length %d from %08lx %s\n", +				fd, buflen,  				(u_long)ntohl(((struct sockaddr_in*)&rb->recv_srcadr)->sin_addr.s_addr) &  				0x00000000ffffffff, -			   	stoa(&rb->recv_srcadr)); +				stoa(&rb->recv_srcadr));  		else -			printf("input_handler: if=%d fd=%d length %d from %s\n", -				i, fd, rb->recv_length, +			printf("read_network_packet: fd=%d length %d from %s\n", +				fd, buflen,  				stoa(&rb->recv_srcadr)); -        } +	}  #endif  	/*  	 * Got one.  Mark how and when it got here,  	 * put it on the full list and do bookkeeping.  	 */ -	rb->dstadr = &inter_list[i]; +	rb->dstadr = itf;  	rb->fd = fd; +#ifdef HAVE_TIMESTAMP +	ts = fetch_timestamp(rb, &msghdr, ts);  /* pick up a network time stamp if possible */ +#endif  	rb->recv_time = ts;  	rb->receiver = receive;  	add_full_recv_buffer(rb); -	inter_list[i].received++; +	itf->received++;  	packets_received++; -	goto select_again; -					} -					/* Check more interfaces */ -				} -			} -		select_again:; -			/* -			 * Done everything from that select.  Poll again. -			 */ -		} +	return (buflen); +} + +/* + * input_handler - receive packets asynchronously + */ +void +input_handler( +	l_fp *cts +	) +{ + +	int buflen; +	int n; +	int doing; +	SOCKET fd; +	struct timeval tvzero; +	l_fp ts;			/* Timestamp at BOselect() gob */ +#ifdef DEBUG_TIMING +	l_fp ts_e;			/* Timestamp at EOselect() gob */ +#endif +	fd_set fds; +	int select_count = 0; +	struct interface *interface; +#if defined(HAS_ROUTING_SOCKET) +	struct asyncio_reader *asyncio_reader; +#endif + +	handler_calls++; + +	/* +	 * If we have something to do, freeze a timestamp. +	 * See below for the other cases (nothing (left) to do or error) +	 */ +	ts = *cts; + +	/* +	 * Do a poll to see who has data +	 */ + +	fds = activefds; +	tvzero.tv_sec = tvzero.tv_usec = 0; +	n = select(maxactivefd+1, &fds, (fd_set *)0, (fd_set *)0, &tvzero); + +	/* +	 * If there are no packets waiting just return +	 */ +	if (n < 0) +	{ +		int err = errno;  		/* -		 * If nothing more to do, try again. -		 * If nothing to do, just return. -		 * If an error occurred, complain and return. +		 * extended FAU debugging output  		 */ -		if (n == 0) +		if (err != EINTR) +		    netsyslog(LOG_ERR, +			      "select(%d, %s, 0L, 0L, &0.0) error: %m", +			      maxactivefd+1, +			      fdbits(maxactivefd, &activefds)); +		if (err == EBADF) { +			int j, b; +			fds = activefds; +			for (j = 0; j <= maxactivefd; j++) +			    if ((FD_ISSET(j, &fds) && (read(j, &b, 0) == -1))) +				netsyslog(LOG_ERR, "Bad file descriptor %d", j); +		} +		return; +	} +	else if (n == 0) +		return; + +	++handler_pkts; + +#ifdef REFCLOCK +	/* +	 * Check out the reference clocks first, if any +	 */ + +	if (refio != NULL) +	{ +		register struct refclockio *rp; + +		for (rp = refio; rp != NULL; rp = rp->next)  		{ -			if (select_count == 0) /* We really had nothing to do */ +			fd = rp->fd; + +			if (FD_ISSET(fd, &fds))  			{ -				if (debug) -				    netsyslog(LOG_DEBUG, "input_handler: select() returned 0"); -				--handler_count; -				return; -			} -			/* We've done our work */ -			get_systime(&ts_e); -			/* -			 * (ts_e - ts) is the amount of time we spent -			 * processing this gob of file descriptors.  Log -			 * it. -			 */ -			L_SUB(&ts_e, &ts); -			if (debug > 3) -			    netsyslog(LOG_INFO, "input_handler: Processed a gob of fd's in %s msec", lfptoms(&ts_e, 6)); +				do { +					++select_count; +					buflen = read_refclock_packet(fd, rp, ts); +				} while (buflen > 0); -			/* just bail. */ -			--handler_count; -			return; -		} -		else if (n == -1) -		{ -			int err = errno; +			} /* End if (FD_ISSET(fd, &fds)) */ +		} /* End for (rp = refio; rp != 0 && n > 0; rp = rp->next) */ +	} /* End if (refio != 0) */ -			/* -			 * extended FAU debugging output -			 */ -			if (err != EINTR) -			    netsyslog(LOG_ERR, -				      "select(%d, %s, 0L, 0L, &0.0) error: %m", -				      maxactivefd+1, -				      fdbits(maxactivefd, &activefds)); -			if (err == EBADF) { -				int j, b; - -				fds = activefds; -				for (j = 0; j <= maxactivefd; j++) -				    if ( -					    (FD_ISSET(j, &fds) && (read(j, &b, 0) == -1)) -					    ) -					netsyslog(LOG_ERR, "Bad file descriptor %d", j); +#endif /* REFCLOCK */ + +	/* +	 * Loop through the interfaces looking for data to read. +	 */ +	for (interface = ISC_LIST_TAIL(inter_list); +	     interface != NULL; +	     interface = ISC_LIST_PREV(interface, link)) +	{ +		for (doing = 0; (doing < 2); doing++) +		{ +			if (doing == 0) +			{ +				fd = interface->fd;  			} -			--handler_count; -			return; +			else +			{ +				if (!(interface->flags & INT_BCASTOPEN)) +				    break; +				fd = interface->bfd; +			} +			if (fd < 0) continue; +			if (FD_ISSET(fd, &fds)) +			{ +				do { +					++select_count; +					buflen = read_network_packet(fd, interface, ts); +				} while (buflen > 0); +			} +		/* Check more interfaces */  		}  	} -	msyslog(LOG_ERR, "input_handler: fell out of infinite for(;;) loop!"); -	--handler_count; + +#ifdef HAS_ROUTING_SOCKET +	/* +	 * scan list of asyncio readers - currently only used for routing sockets +	 */ +	asyncio_reader = ISC_LIST_TAIL(asyncio_reader_list); + +	while (asyncio_reader != NULL) +	{ +	        struct asyncio_reader *next = ISC_LIST_PREV(asyncio_reader, link); +		if (FD_ISSET(asyncio_reader->fd, &fds)) { +			++select_count; +			asyncio_reader->receiver(asyncio_reader); +		} +		asyncio_reader = next; +	} +#endif /* HAS_ROUTING_SOCKET */ +	 +	/* +	 * Done everything from that select. +	 */ + +	/* +	 * If nothing to do, just return. +	 * If an error occurred, complain and return. +	 */ +	if (select_count == 0) /* We really had nothing to do */ +	{ +#ifdef DEBUG +		if (debug) +		    netsyslog(LOG_DEBUG, "input_handler: select() returned 0"); +#endif +		return; +	} +		/* We've done our work */ +#ifdef DEBUG_TIMING +	get_systime(&ts_e); +	/* +	 * (ts_e - ts) is the amount of time we spent +	 * processing this gob of file descriptors.  Log +	 * it. +	 */ +	L_SUB(&ts_e, &ts); +	collect_timing(NULL, "input handler", 1, &ts_e); +	if (debug > 3) +	    netsyslog(LOG_INFO, "input_handler: Processed a gob of fd's in %s msec", lfptoms(&ts_e, 6)); +#endif +	/* just bail. */  	return;  }  #endif +  /* - * findinterface - find interface corresponding to address + * findinterface - find local interface corresponding to address   */  struct interface *  findinterface(  	struct sockaddr_storage *addr  	)  { +	struct interface *interface; +	 +	interface = findlocalinterface(addr, INT_WILDCARD); + +	if (interface == NULL) +	{ +		DPRINTF(4, ("Found no interface for address %s - returning wildcard\n", +			    stoa(addr))); + +		return (ANY_INTERFACE_CHOOSE(addr)); +	} +	else +	{ +		DPRINTF(4, ("Found interface #%d %s for address %s\n", +			    interface->ifnum, interface->name, stoa(addr))); + +		return (interface); +	} +} + +/* + * findlocalinterface - find local interface index corresponding to address + * + * This code attempts to find the local sending address for an outgoing + * address by connecting a new socket to destinationaddress:NTP_PORT + * and reading the sockname of the resulting connect. + * the complicated sequence simulates the routing table lookup + * for to first hop without duplicating any of the routing logic into + * ntpd. preferably we would have used an API call - but its not there - + * so this is the best we can do here short of duplicating to entire routing + * logic in ntpd which would be a silly and really unportable thing to do. + * + */ +static struct interface * +findlocalinterface( +	struct sockaddr_storage *addr, +	int flags +	) +{  	SOCKET s; -	int rtn, i; +	int rtn;  	struct sockaddr_storage saddr; -	int saddrlen = SOCKLEN(addr); -	/* -	 * This is considerably hoke. We open a socket, connect to it -	 * and slap a getsockname() on it. If anything breaks, as it -	 * probably will in some j-random knockoff, we just return the -	 * wildcard interface. -	 */ +	GETSOCKNAME_SOCKLEN_TYPE saddrlen = SOCKLEN(addr); +	struct interface *iface; + +	DPRINTF(4, ("Finding interface for addr %s in list of addresses\n", +		    stoa(addr));) +  	memset(&saddr, 0, sizeof(saddr));  	saddr.ss_family = addr->ss_family; -	if(addr->ss_family == AF_INET) +	if(addr->ss_family == AF_INET) {  		memcpy(&((struct sockaddr_in*)&saddr)->sin_addr, &((struct sockaddr_in*)addr)->sin_addr, sizeof(struct in_addr)); -	else if(addr->ss_family == AF_INET6) -		memcpy(&((struct sockaddr_in6*)&saddr)->sin6_addr, &((struct sockaddr_in6*)addr)->sin6_addr, sizeof(struct in6_addr)); -	((struct sockaddr_in*)&saddr)->sin_port = htons(2000); +		((struct sockaddr_in*)&saddr)->sin_port = htons(NTP_PORT); +	} +#ifdef INCLUDE_IPV6_SUPPORT +	else if(addr->ss_family == AF_INET6) { + 		memcpy(&((struct sockaddr_in6*)&saddr)->sin6_addr, &((struct sockaddr_in6*)addr)->sin6_addr, sizeof(struct in6_addr)); +		((struct sockaddr_in6*)&saddr)->sin6_port = htons(NTP_PORT); +# ifdef ISC_PLATFORM_HAVESCOPEID +		((struct sockaddr_in6*)&saddr)->sin6_scope_id = ((struct sockaddr_in6*)addr)->sin6_scope_id; +# endif +	} +#endif +	  	s = socket(addr->ss_family, SOCK_DGRAM, 0);  	if (s == INVALID_SOCKET) -		return ANY_INTERFACE_CHOOSE(addr); +		return NULL;  	rtn = connect(s, (struct sockaddr *)&saddr, SOCKLEN(&saddr));  #ifndef SYS_WINNT @@ -1916,7 +3237,7 @@ findinterface(  #endif  	{  		closesocket(s); -		return ANY_INTERFACE_CHOOSE(addr); +		return NULL;  	}  	rtn = getsockname(s, (struct sockaddr *)&saddr, &saddrlen); @@ -1926,21 +3247,132 @@ findinterface(  #else  	if (rtn == SOCKET_ERROR)  #endif -		return ANY_INTERFACE_CHOOSE(addr); +		return NULL; -	for (i = 0; i < ninterfaces; i++) { -		/* -		* First look if is the the correct family -		*/ -		if(inter_list[i].sin.ss_family != saddr.ss_family) -	  		continue; -		/* -		 * We match the unicast address only. -		 */ -		if (SOCKCMP(&inter_list[i].sin, &saddr)) -			return (&inter_list[i]); +	DPRINTF(4, ("findlocalinterface: kernel maps %s to %s\n", stoa(addr), stoa(&saddr))); +	 +	iface = getinterface(&saddr, flags); + +	/* Don't both with ignore interfaces */ +	if (iface != NULL && iface->ignore_packets == ISC_TRUE) +	{ +		return NULL; +	} +	else +	{ +		return iface; +	} +} + +/* + * fetch an interface structure the matches the + * address is has the given flags not set + */ +static struct interface * +getinterface(struct sockaddr_storage *addr, int flags) +{ +	struct interface *interface = find_addr_in_list(addr); + +	if (interface != NULL && interface->flags & flags) +	{ +		return NULL; +	} +	else +	{ +		return interface; +	} +} + +/* + * findlocalcastinterface - find local *cast interface index corresponding to address + * depending on the flags passed + */ +static struct interface * +findlocalcastinterface( +	struct sockaddr_storage *addr, int flags +	) +{ +	struct interface *interface; +	struct interface *nif = NULL; +#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT +	isc_boolean_t want_linklocal; +#endif  + +	/* +	 * see how kernel maps the mcast address +	 */ +        nif = findlocalinterface(addr, 0); + +	if (nif) { +		DPRINTF(2, ("findlocalcastinterface: kernel recommends interface #%d %s\n", nif->ifnum, nif->name)); +		return nif; +	} + +#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT +	want_linklocal = ISC_FALSE;  +	if (addr_ismulticast(addr) && flags == INT_MULTICAST) +	{ +		if (IN6_IS_ADDR_MC_LINKLOCAL(&((struct sockaddr_in6*)addr)->sin6_addr)) +		{ +			want_linklocal = ISC_TRUE; +		} +		else if (IN6_IS_ADDR_MC_SITELOCAL(&((struct sockaddr_in6*)addr)->sin6_addr)) +		{ +			want_linklocal = ISC_TRUE; +		} +	} +#endif + +	for (interface = ISC_LIST_HEAD(inter_list); +	     interface != NULL; +	     interface = ISC_LIST_NEXT(interface, link))  +	  { +		/* use only allowed addresses */ +		if (interface->ignore_packets == ISC_TRUE) +			continue; + +		/* Skip the loopback and wildcard addresses */ +		if (interface->flags & (INT_LOOPBACK|INT_WILDCARD)) +			continue; + +		/* Skip if different family */ +		if(interface->sin.ss_family != addr->ss_family) +			continue; + +		/* Is this it one of these based on flags? */ +		if (!(interface->flags & flags)) +			continue; + +		/* for IPv6 multicast check the address for linklocal */ +#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT +		if (flags == INT_MULTICAST && interface->sin.ss_family == AF_INET6 && +		   (IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6*)&interface->sin)->sin6_addr)) +		   && want_linklocal == ISC_TRUE) +		{ +			nif = interface; +			break; +		} +		/* If we want a linklocal address and this isn't it, skip */\ +		if (want_linklocal == ISC_TRUE) +			continue; +#endif +		/* Otherwise just look for the flag */ +		if((interface->flags & flags)) +		{ +			nif = interface; +			break; +		}  	} -	return ANY_INTERFACE_CHOOSE(addr); +#ifdef DEBUG +	if (debug > 2)  +	{ +		if (nif) +			printf("findlocalcastinterface: found interface #%d %s\n", nif->ifnum, nif->name); +		else +			printf("findlocalcastinterface: no interface found for %s flags 0x%x\n", stoa(addr), flags); +	} +#endif +	return (nif);  }  /* @@ -1952,43 +3384,94 @@ findbcastinter(  	)  {  #if !defined(MPE) && (defined(SIOCGIFCONF) || defined(SYS_WINNT)) -	register int i; +        struct interface *interface; +	 -	i = find_addr_in_list(addr); -	if(i >= 0) -	     return (&inter_list[i]); +	DPRINTF(4, ("Finding broadcast/multicast interface for addr %s in list of addresses\n", +		    stoa(addr))); -	for (i = 0; i < ninterfaces; i++) { +	interface = findlocalinterface(addr, INT_LOOPBACK|INT_WILDCARD); +	 +	if (interface != NULL) +	{ +		DPRINTF(4, ("Found bcast-/mcast- interface index #%d %s\n", interface->ifnum, interface->name)); +		return interface; +	} + +	/* plan B - try to find something reasonable in our lists in case kernel lookup doesn't help */ + +	for (interface = ISC_LIST_HEAD(inter_list); +	     interface != NULL; +	     interface = ISC_LIST_NEXT(interface, link))  +	{ +	        if (interface->flags & INT_WILDCARD) +		        continue; +		 +		/* Don't bother with ignored interfaces */ +		if (interface->ignore_packets == ISC_TRUE) +			continue; +		  		/* -		* First look if this is the correct family -		*/ -		if(inter_list[i].sin.ss_family != addr->ss_family) +		 * First look if this is the correct family +		 */ +		if(interface->sin.ss_family != addr->ss_family)  	  		continue; + +		/* Skip the loopback addresses */ +		if (interface->flags & INT_LOOPBACK) +			continue; + +		/* +		 * If we are looking to match a multicast address grab it. +		 */ +		if (addr_ismulticast(addr) == ISC_TRUE && interface->flags & INT_MULTICAST) +		{ +#ifdef INCLUDE_IPV6_SUPPORT +			if(addr->ss_family == AF_INET6) { +				/* Only use link-local address for link-scope mcast */ +				if(IN6_IS_ADDR_MC_LINKLOCAL(&((struct sockaddr_in6*)addr)->sin6_addr) && +				  !IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6*)&interface->sin)->sin6_addr)) { +					continue; +				} +			} +#endif +			break; +		} +  		/*  		 * We match only those interfaces marked as  		 * broadcastable and either the explicit broadcast  		 * address or the network portion of the IP address.  		 * Sloppy.  		 */ -		if (!(inter_list[i].flags & INT_BROADCAST)) -			continue;  		if(addr->ss_family == AF_INET) { -			if (SOCKCMP(&inter_list[i].bcast, addr)) -				return (&inter_list[i]); -			if ((NSRCADR(&inter_list[i].sin) & -				NSRCADR(&inter_list[i].mask)) == (NSRCADR(addr) & -			    	NSRCADR(&inter_list[i].mask))) -				return (&inter_list[i]); +			if (SOCKCMP(&interface->bcast, addr)) { +				break; +			} +			if ((NSRCADR(&interface->sin) & +			     NSRCADR(&interface->mask)) == (NSRCADR(addr) & +							    NSRCADR(&interface->mask))) +				break;  		} +#ifdef INCLUDE_IPV6_SUPPORT  		else if(addr->ss_family == AF_INET6) { -			if (SOCKCMP(&inter_list[i].bcast, addr)) -				return (&inter_list[i]); -			if (SOCKCMP(netof(&inter_list[i].sin), netof(addr))) -				return (&inter_list[i]); -		     } +			if (SOCKCMP(&interface->bcast, addr)) { +				break; +			} +			if (SOCKCMP(netof(&interface->sin), netof(addr))) { +				break; +			} +		} +#endif  	}  #endif /* SIOCGIFCONF */ - 	return ANY_INTERFACE_CHOOSE(addr); +	if (interface == NULL) { +		DPRINTF(4, ("No bcast interface found for %s\n", stoa(addr))); +		return ANY_INTERFACE_CHOOSE(addr); +	} else { +		DPRINTF(4, ("Found bcast-/mcast- interface index #%d %s\n", interface->ifnum, interface->name)); +		return interface; +	}  } @@ -2012,36 +3495,6 @@ io_clr_stats(void)  #ifdef REFCLOCK  /* - * This is a hack so that I don't have to fool with these ioctls in the - * pps driver ... we are already non-blocking and turn on SIGIO thru - * another mechanisim - */ -int -io_addclock_simple( -	struct refclockio *rio -	) -{ -	BLOCKIO(); -	/* -	 * Stuff the I/O structure in the list and mark the descriptor -	 * in use.	There is a harmless (I hope) race condition here. -	 */ -	rio->next = refio; -	refio = rio; - -	/* -	 * I/O Completion Ports don't care about select and fd_set -	 */ -#ifndef HAVE_IO_COMPLETION_PORT -	if (rio->fd > maxactivefd) -	    maxactivefd = rio->fd; -	FD_SET(rio->fd, &activefds); -#endif -	UNBLOCKIO(); -	return 1; -} - -/*   * io_addclock - add a reference clock to the list and arrange that we   *				 get SIGIO interrupts from it.   */ @@ -2056,33 +3509,31 @@ io_addclock(  	 * in use.	There is a harmless (I hope) race condition here.  	 */  	rio->next = refio; -	refio = rio;  # ifdef HAVE_SIGNALED_IO  	if (init_clock_sig(rio))  	{ -		refio = rio->next;  		UNBLOCKIO();  		return 0;  	}  # elif defined(HAVE_IO_COMPLETION_PORT)  	if (io_completion_port_add_clock_io(rio))  	{ -		add_socket_to_list(rio->fd); -		refio = rio->next;  		UNBLOCKIO();  		return 0;  	}  # endif  	/* -	 * I/O Completion Ports don't care about select and fd_set +	 * enqueue  	 */ -#ifndef HAVE_IO_COMPLETION_PORT -	if (rio->fd > maxactivefd) -	    maxactivefd = rio->fd; -	FD_SET(rio->fd, &activefds); -#endif +	refio = rio; + +        /* +	 * register fd +	 */ +	add_fd_to_list(rio->fd, FD_TYPE_FILE); +  	UNBLOCKIO();  	return 1;  } @@ -2095,6 +3546,7 @@ io_closeclock(  	struct refclockio *rio  	)  { +	BLOCKIO();  	/*  	 * Remove structure from the list  	 */ @@ -2106,20 +3558,15 @@ io_closeclock(  	{  		register struct refclockio *rp; -		for (rp = refio; rp != 0; rp = rp->next) +		for (rp = refio; rp != NULL; rp = rp->next)  		    if (rp->next == rio)  		    {  			    rp->next = rio->next;  			    break;  		    } -		if (rp == 0) -		{ -			/* -			 * Internal error.	Report it. -			 */ -			msyslog(LOG_ERR, -				"internal error: refclockio structure not found"); +		if (rp == NULL) { +			UNBLOCKIO();  			return;  		}  	} @@ -2127,26 +3574,11 @@ io_closeclock(  	/*  	 * Close the descriptor.  	 */ -	close_file(rio->fd); +	close_and_delete_fd_from_list(rio->fd); +	UNBLOCKIO();  }  #endif	/* REFCLOCK */ -	/* -	 * I/O Completion Ports don't care about select and fd_set -	 */ -#ifndef HAVE_IO_COMPLETION_PORT -void -kill_asyncio( -	int startfd -	) -{ -	SOCKET i; - -	BLOCKIO(); -	for (i = startfd; i <= maxactivefd; i++) -	    (void)close_socket(i); -} -#else  /*   * On NT a SOCKET is an unsigned int so we cannot possibly keep it in   * an array. So we use one of the ISC_LIST functions to hold the @@ -2160,58 +3592,119 @@ kill_asyncio(int startfd)  	BLOCKIO(); -	lsock = ISC_LIST_HEAD(sockets_list); +	lsock = ISC_LIST_HEAD(fd_list);  	while (lsock != NULL) { +		/* +		 * careful here - list is being dismantled while +		 * we scan it - setting next here insures that +		 * we are able to correctly scan the list +		 */  		next = ISC_LIST_NEXT(lsock, link); -		close_socket(lsock->fd); +		/* +		 * will remove socket from list +		 */ +		close_and_delete_fd_from_list(lsock->fd);  		lsock = next;  	} +	UNBLOCKIO();  } -#endif +  /*   * Add and delete functions for the list of open sockets   */ -void -add_socket_to_list(SOCKET fd){ -	vsock_t *lsock = malloc(sizeof(vsock_t)); +static void +add_fd_to_list(SOCKET fd, enum desc_type type) { +	vsock_t *lsock = (vsock_t *)emalloc(sizeof(vsock_t));  	lsock->fd = fd; +	lsock->type = type; -	ISC_LIST_APPEND(sockets_list, lsock, link); +	ISC_LIST_APPEND(fd_list, lsock, link); +	/* +	 * I/O Completion Ports don't care about the select and FD_SET +	 */ +#ifndef HAVE_IO_COMPLETION_PORT +	/* +	 * keep activefds in sync +	 */ +	if (fd > maxactivefd) +	    maxactivefd = fd; +	FD_SET( (u_int)fd, &activefds); +#endif  } -void -delete_socket_from_list(SOCKET fd) { + +static void +close_and_delete_fd_from_list(SOCKET fd) {  	vsock_t *next; -	vsock_t *lsock = ISC_LIST_HEAD(sockets_list); +	vsock_t *lsock = ISC_LIST_HEAD(fd_list);  	while(lsock != NULL) {  		next = ISC_LIST_NEXT(lsock, link);  		if(lsock->fd == fd) { -			ISC_LIST_DEQUEUE(sockets_list, lsock, link); +			ISC_LIST_DEQUEUE_TYPE(fd_list, lsock, link, vsock_t); + +			switch (lsock->type) { +			case FD_TYPE_SOCKET: +#ifdef SYS_WINNT +				closesocket(lsock->fd); +				break; +#endif +			case FD_TYPE_FILE: +				(void) close(lsock->fd); +				break; +			default: +				msyslog(LOG_ERR, "internal error - illegal descriptor type %d - EXITING", (int)lsock->type); +				exit(1); +			} +  			free(lsock); +			/* +			 * I/O Completion Ports don't care about select and fd_set +			 */ +#ifndef HAVE_IO_COMPLETION_PORT +			/* +			 * remove from activefds +			 */ +			FD_CLR( (u_int) fd, &activefds); +			 +			if (fd == maxactivefd) { +				int i, newmax = 0; +				for (i = 0; i < maxactivefd; i++) +					if (FD_ISSET(i, &activefds)) +						newmax = i; +				maxactivefd = newmax; +			} +#endif  			break;  		} -		else -			lsock = next; +		lsock = next;  	}  } -void -add_addr_to_list(struct sockaddr_storage *addr, int if_index){ -	remaddr_t *laddr = malloc(sizeof(remaddr_t)); -	memcpy(&laddr->addr, addr, sizeof(addr)); -	laddr->if_index = if_index; -	ISC_LIST_APPEND(remoteaddr_list, laddr, link); +static void +add_addr_to_list(struct sockaddr_storage *addr, struct interface *interface){  #ifdef DEBUG -	if (debug) -	    printf("Added addr %s to list of addresses\n", -		   stoa(addr)); +	if (find_addr_in_list(addr) == NULL) { +#endif +		/* not there yet - add to list */ +		remaddr_t *laddr = (remaddr_t *)emalloc(sizeof(remaddr_t)); +		memcpy(&laddr->addr, addr, sizeof(struct sockaddr_storage)); +		laddr->interface = interface; +		 +		ISC_LIST_APPEND(remoteaddr_list, laddr, link); +		 +		DPRINTF(4, ("Added addr %s to list of addresses\n", +			    stoa(addr))); +#ifdef DEBUG +	} else { +		DPRINTF(4, ("WARNING: Attempt to add duplicate addr %s to address list\n", +			    stoa(addr))); +	}  #endif - -  } -void + +static void  delete_addr_from_list(struct sockaddr_storage *addr) {  	remaddr_t *next; @@ -2220,38 +3713,201 @@ delete_addr_from_list(struct sockaddr_storage *addr) {  	while(laddr != NULL) {  		next = ISC_LIST_NEXT(laddr, link);  		if(SOCKCMP(&laddr->addr, addr)) { -			ISC_LIST_DEQUEUE(remoteaddr_list, laddr, link); +			ISC_LIST_DEQUEUE_TYPE(remoteaddr_list, laddr, link, remaddr_t); +			DPRINTF(4, ("Deleted addr %s from list of addresses\n", +				    stoa(addr)));  			free(laddr);  			break;  		} -		else -			laddr = next; +		laddr = next;  	} -#ifdef DEBUG -	if (debug) -	    printf("Deleted addr %s from list of addresses\n", -		   stoa(addr)); -#endif  } -int + +static void +delete_interface_from_list(struct interface *iface) { +	remaddr_t *next; +	remaddr_t *laddr = ISC_LIST_HEAD(remoteaddr_list); + +	while(laddr != NULL) { +		next = ISC_LIST_NEXT(laddr, link); +		if (laddr->interface == iface) { +			ISC_LIST_DEQUEUE_TYPE(remoteaddr_list, laddr, link, remaddr_t); +			DPRINTF(4, ("Deleted addr %s for interface #%d %s from list of addresses\n", +				    stoa(&laddr->addr), iface->ifnum, iface->name)); +			free(laddr); +		} +		laddr = next; +	} +} + +static struct interface *  find_addr_in_list(struct sockaddr_storage *addr) {  	remaddr_t *next;  	remaddr_t *laddr = ISC_LIST_HEAD(remoteaddr_list); -#ifdef DEBUG -	if (debug) -	    printf("Finding addr %s in list of addresses\n", -		   stoa(addr)); -#endif +	DPRINTF(4, ("Searching for addr %s in list of addresses - ", +		    stoa(addr)));  	while(laddr != NULL) {  		next = ISC_LIST_NEXT(laddr, link);  		if(SOCKCMP(&laddr->addr, addr)) { -			return (laddr->if_index); +			DPRINTF(4, ("FOUND\n")); +			return laddr->interface; +		} +		else +			laddr = next; +	} +	DPRINTF(4, ("NOT FOUND\n")); +	return NULL; /* Not found */ +} + +/* + * Find the given address with the associated flag in the list + */ +static struct interface * +find_flagged_addr_in_list(struct sockaddr_storage *addr, int flag) { + +	remaddr_t *next; +	remaddr_t *laddr = ISC_LIST_HEAD(remoteaddr_list); +	DPRINTF(4, ("Finding addr %s in list of addresses\n", +		    stoa(addr))); + +	while(laddr != NULL) { +		next = ISC_LIST_NEXT(laddr, link); +		if(SOCKCMP(&laddr->addr, addr) && (laddr->interface->flags & flag)) { +			return laddr->interface;  			break;  		}  		else  			laddr = next;  	} -	return (-1); /* Not found */ +	return NULL; /* Not found */ +} + +#ifdef HAS_ROUTING_SOCKET +#include <net/route.h> + +#ifndef UPDATE_GRACE +#define UPDATE_GRACE	2	/* wait UPDATE_GRACE seconds before scanning */ +#endif + +static void +process_routing_msgs(struct asyncio_reader *reader) +{ +	char buffer[5120]; +	char *p = buffer; + +	int cnt; +	 +	if (disable_dynamic_updates) { +		/* +		 * discard ourselves if we are not need any more +		 * usually happens when running unprivileged +		 */ +		remove_asyncio_reader(reader); +		delete_asyncio_reader(reader); +		return; +	} + +	cnt = read(reader->fd, buffer, sizeof(buffer)); +	 +	if (cnt < 0) { +		msyslog(LOG_ERR, "i/o error on routing socket %m - disabling"); +		remove_asyncio_reader(reader); +		delete_asyncio_reader(reader); +		return; +	} + +	/* +	 * process routing message +	 */ +	while ((p + sizeof(struct rt_msghdr)) <= (buffer + cnt)) +	{ +		struct rt_msghdr *rtm; +		 +		rtm = (struct rt_msghdr *)p; +		if (rtm->rtm_version != RTM_VERSION) { +			msyslog(LOG_ERR, "version mismatch on routing socket %m - disabling"); +			remove_asyncio_reader(reader); +			delete_asyncio_reader(reader); +			return; +		} +		 +		switch (rtm->rtm_type) { +#ifdef RTM_NEWADDR +		case RTM_NEWADDR: +#endif +#ifdef RTM_DELADDR +		case RTM_DELADDR: +#endif +#ifdef RTM_ADD +		case RTM_ADD: +#endif +#ifdef RTM_DELETE +		case RTM_DELETE: +#endif +#ifdef RTM_REDIRECT +		case RTM_REDIRECT: +#endif +#ifdef RTM_CHANGE +		case RTM_CHANGE: +#endif +#ifdef RTM_LOSING +		case RTM_LOSING: +#endif +#ifdef RTM_IFINFO +		case RTM_IFINFO: +#endif +#ifdef RTM_IFANNOUNCE +		case RTM_IFANNOUNCE: +#endif +			/* +			 * we are keen on new and deleted addresses and if an interface goes up and down or routing changes +			 */ +			DPRINTF(3, ("routing message op = %d: scheduling interface update\n", rtm->rtm_type)); +			timer_interfacetimeout(current_time + UPDATE_GRACE); +			break; +		default: +			/* +			 * the rest doesn't bother us. +			 */ +			DPRINTF(4, ("routing message op = %d: ignored\n", rtm->rtm_type)); +			break; +		} +		p += rtm->rtm_msglen; +	} +} + +/* + * set up routing notifications + */ +static void +init_async_notifications() +{ +	struct asyncio_reader *reader; +	int fd = socket(PF_ROUTE, SOCK_RAW, 0); +	 +	if (fd >= 0) { +		fd = move_fd(fd); +		init_nonblocking_io(fd); +#if defined(HAVE_SIGNALED_IO) +		init_socket_sig(fd); +#endif /* HAVE_SIGNALED_IO */ +		 +		reader = new_asyncio_reader(); + +		reader->fd = fd; +		reader->receiver = process_routing_msgs; +		 +		add_asyncio_reader(reader, FD_TYPE_SOCKET); +		msyslog(LOG_INFO, "Listening on routing socket on fd #%d for interface updates", fd); +	} else { +		msyslog(LOG_ERR, "unable to open routing socket (%m) - using polled interface update"); +	} +} +#else +static void +init_async_notifications() +{  } +#endif | 
