diff -up ntp-4.2.2p1/html/ntpd.html.listen ntp-4.2.2p1/html/ntpd.html --- ntp-4.2.2p1/html/ntpd.html.listen 2011-03-28 14:19:02.854051653 +0200 +++ ntp-4.2.2p1/html/ntpd.html 2011-03-28 16:48:42.676513083 +0200 @@ -34,7 +34,7 @@ </ul> <hr> <h4 id="synop">Synopsis</h4> - <tt>ntpd [ -46aAbdDgLnNqx ] [ -c <i>conffile</i> ] [ -f <i>driftfile</i> ] [ -i <i>jaildir</i> ] [ -k <i>keyfile</i> ] [ -l <i>logfile</i> ] [ -p <i>pidfile</i> ] [ -P <i>priority</i> ] [ -r <i>broadcastdelay</i> ] [ -s <i>statsdir</i> ] [ -t <i>key</i> ] [ -u <i>user</i>[:<i>group</i>] ] [ -v <i>variable</i> ] [ -V <i>variable</i> ]</tt> + <tt>ntpd [ -46aAbdDgLnNqx ] [ -c <i>conffile</i> ] [ -f <i>driftfile</i> ] [ -i <i>jaildir</i> ] [ -I <i>iface</i> ] [ -k <i>keyfile</i> ] [ -l <i>logfile</i> ] [ -p <i>pidfile</i> ] [ -P <i>priority</i> ] [ -r <i>broadcastdelay</i> ] [ -s <i>statsdir</i> ] [ -t <i>key</i> ] [ -u <i>user</i>[:<i>group</i>] ] [ -v <i>variable</i> ] [ -V <i>variable</i> ]</tt> <h4 id="descr">Description</h4> <p>The <tt>ntpd</tt> program is an operating system daemon which sets and maintains the system time of day in synchronism with Internet standard time servers. It is a complete implementation of the Network Time Protocol (NTP) version 4, but also retains compatibility with version 3, as defined by RFC-1305, and version 1 and 2, as defined by RFC-1059 and RFC-1119, respectively. <tt>ntpd</tt> does most computations in 64-bit floating point arithmetic and does relatively clumsy 64-bit fixed point operations only when necessary to preserve the ultimate precision, about 232 picoseconds. While the ultimate precision is not achievable with ordinary workstations and networks of today, it may be required with future gigahertz CPU clocks and gigabit LANs.</p> <h4 id="op">How NTP Operates</h4> @@ -88,6 +88,8 @@ <dd>Normally, <tt>ntpd</tt> exits with a message to the system log if the offset exceeds the panic threshold, which is 1000 s by default. This option allows the time to be set to any value without restriction; however, this can happen only once. If the threshold is exceeded after that, <tt>ntpd</tt> will exit with a message to the system log. This option can be used with the <tt>-q</tt> and <tt>-x</tt> options. See the <tt>tinker</tt> command for other options. <dt><tt>-i <i>jaildir</i></tt> <dd>Chroot the server to the directory <i>jaildir</i>. This option also implies that the server attempts to drop root privileges at startup (otherwise, chroot gives very little additional security), and it is only available if the OS supports to run the server without full root privileges. You may need to also specify a <tt>-u</tt> option. + <dt><tt>-I <i>iface</i></tt> + <dd>Listen on interface. This option may appear an unlimited number of times. <dt><tt>-k <i>keyfile</i></tt> <dd>Specify the name and path of the symmetric key file. This is the same operation as the <tt>keys <i>keyfile</i></tt> configuration command. <dt><tt>-l <i>logfile</i></tt> diff -up ntp-4.2.2p1/include/ntpd.h.listen ntp-4.2.2p1/include/ntpd.h --- ntp-4.2.2p1/include/ntpd.h.listen 2006-06-06 22:16:20.000000000 +0200 +++ ntp-4.2.2p1/include/ntpd.h 2011-03-25 15:55:03.848848253 +0100 @@ -73,6 +73,8 @@ extern void enable_broadcast P((struct extern void enable_multicast_if P((struct interface *, struct sockaddr_storage *)); extern void interface_dump P((struct interface *)); +extern void add_specific_interface P((const char *)); +extern void init_specific_interface P((void)); extern void init_io P((void)); extern void input_handler P((l_fp *)); extern void io_clr_stats P((void)); diff -up ntp-4.2.2p1/ntpd/cmd_args.c.listen ntp-4.2.2p1/ntpd/cmd_args.c --- ntp-4.2.2p1/ntpd/cmd_args.c.listen 2011-03-25 15:55:03.772863453 +0100 +++ ntp-4.2.2p1/ntpd/cmd_args.c 2011-03-25 15:55:03.848848253 +0100 @@ -19,9 +19,8 @@ extern char const *progname; extern int default_ai_family; int listen_to_virtual_ips = 1; -char *specific_interface = NULL; /* interface name or IP address to bind to */ -static const char *ntp_options = "46aAbB:c:C:dD:f:gHi:k:l:L:nNO:p:P:qr:s:S:t:T:W:u:v:V:xY:Z:-:"; +static const char *ntp_options = "46aAbB:c:C:dD:f:gHi:I:k:l:L:nNO:p:P:qr:s:S:t:T:W:u:v:V:xY:Z:-:"; #ifdef HAVE_NETINFO extern int check_netinfo; @@ -34,7 +33,7 @@ void ntpd_usage( void ) (void) fprintf(stderr, "\t\t[ -f drift_file ] [ -k key_file ] [ -l log_file ]\n"); (void) fprintf(stderr, "\t\t[ -p pid_file ] [ -r broadcast_delay ] [ -s stats_dir ]\n"); (void) fprintf(stderr, "\t\t[ -t trusted_key ] [ -v sys_var ] [ -V default_sysvar ]\n"); - (void) fprintf(stderr, "\t\t[ -L [ interface ] ]\n"); + (void) fprintf(stderr, "\t\t[ -L [ interface ] ] [ -I interface ]\n"); #if defined(HAVE_SCHED_SETSCHEDULER) (void) fprintf(stderr, "\t\t[ -P fixed_process_priority ]\n"); #endif @@ -77,6 +76,8 @@ getstartup( errflg = 0; progname = argv[0]; + init_specific_interface(); + /* * Decode argument list */ @@ -98,11 +99,16 @@ getstartup( ++errflg; break; #endif + case 'I': + add_specific_interface(ntp_optarg); + break; + case 'L': listen_to_virtual_ips = 0; if(ntp_optarg) - specific_interface = ntp_optarg; + add_specific_interface(ntp_optarg); break; + case 'l': { FILE *new_file; @@ -276,7 +282,12 @@ getCmdOpts( getauthkeys(ntp_optarg); break; + case 'I': /* already done at pre-scan */ + break; + case 'L': /* already done at pre-scan */ + break; + case 'l': /* already done at pre-scan */ break; diff -up ntp-4.2.2p1/ntpd/ntp_io.c.listen ntp-4.2.2p1/ntpd/ntp_io.c --- ntp-4.2.2p1/ntpd/ntp_io.c.listen 2011-03-25 15:55:03.833851254 +0100 +++ ntp-4.2.2p1/ntpd/ntp_io.c 2011-03-25 15:58:20.072595654 +0100 @@ -53,7 +53,14 @@ #endif /* IPv6 Support */ extern int listen_to_virtual_ips; -extern const char *specific_interface; + +/* interface names to listen on */ +struct specific_interface { + const char *name; + ISC_LINK(struct specific_interface) link; +}; + +ISC_LIST(struct specific_interface) specific_interface_list; #if defined(SYS_WINNT) #include <transmitbuff.h> @@ -203,8 +210,14 @@ 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)); int findlocalinterface P((struct sockaddr_storage *)); +int findclosestinterface P((struct sockaddr_storage *)); int findlocalcastinterface P((struct sockaddr_storage *, int)); +static void calc_addr_distance(struct sockaddr_storage *, + struct sockaddr_storage *, + struct sockaddr_storage *); +static int cmp_addr_distance(struct sockaddr_storage *, + struct sockaddr_storage *); /* * Routines to read the ntp packets */ @@ -577,11 +590,12 @@ address_okay(isc_interface_t *isc_if) { /* * Check if the interface is specified */ - if (specific_interface != NULL) { - if (strcasecmp(isc_if->name, specific_interface) == 0) - return (ISC_TRUE); - else - return (ISC_FALSE); + if (ISC_LIST_HEAD(specific_interface_list)) { + struct specific_interface *iface; + for (iface = ISC_LIST_HEAD(specific_interface_list); iface != NULL; iface = ISC_LIST_NEXT(iface, link)) + if (strcasecmp(isc_if->name, iface->name) == 0) + return (ISC_TRUE); + return (ISC_FALSE); } else { if (listen_to_virtual_ips == 0 && @@ -598,6 +612,24 @@ address_okay(isc_interface_t *isc_if) { */ return (ISC_TRUE); } + +void +add_specific_interface (const char *if_name) +{ + struct specific_interface *iface; + + iface = (struct specific_interface *)emalloc(sizeof(struct specific_interface)); + iface->name = if_name; + ISC_LINK_INIT(iface, link); + ISC_LIST_APPEND(specific_interface_list, iface, link); +} + +void +init_specific_interface (void) +{ + ISC_LIST_INIT(specific_interface_list); +} + void convert_isc_if(isc_interface_t *isc_if, struct interface *itf, u_short port) { @@ -2649,12 +2681,160 @@ findlocalinterface( break; } } - if (idx != -1) + + /* + * if we didn't find an exact match on saddr, find the closest + * available local address. This handles the case of the + * address suggested by the kernel being excluded by the user's + * -I and -L options to ntpd. + * See http://bugs.ntp.org/1184 and http://bugs.ntp.org/1683 + * for more background. + */ + if (idx == -1 && (ISC_LIST_HEAD(specific_interface_list) || !listen_to_virtual_ips)) { - return (idx); + idx = findclosestinterface(&saddr); + } + + return (idx); +} + +/* + * findclosestinterface + * + * If there are -I/--interface or -L/novirtualips command-line options, + * findlocalinterface() may + * find the kernel's preferred local address for a given peer address is + * administratively unavailable to ntpd, and punt to this routine's more + * expensive search. + * + * Find the numerically closest local address to the one connect() + * suggested. This matches an address on the same subnet first, as + * needed by Bug 1184, and provides a consistent choice if there are + * multiple feasible local addresses, regardless of the order ntpd + * enumerated them. + */ +int +findclosestinterface( + struct sockaddr_storage * addr + ) +{ + int i, winner; + struct sockaddr_storage addr_dist; + struct sockaddr_storage min_dist; + + winner = -1; + + for (i = nwilds; i < ninterfaces; i++) { + if (inter_list[i].ignore_packets || + addr->ss_family != inter_list[i].family || + inter_list[i].flags & INT_LOOPBACK) + continue; + + calc_addr_distance(&addr_dist, addr, &inter_list[i].sin); + if (-1 == winner || + -1 == cmp_addr_distance(&addr_dist, &min_dist)) { + min_dist = addr_dist; + winner = i; + } + } + + return winner; +} + + +/* + * calc_addr_distance - calculate the distance between two addresses, + * the absolute value of the difference between + * the addresses numerically, stored as an address. + */ + +#define AF(psau) ((psau)->ss_family) +#define IS_IPV4(psau) (AF_INET == AF(psau)) +#define IS_IPV6(psau) (AF_INET6 == AF(psau)) +#define SOCK_ADDR6(psau) (((struct sockaddr_in6 *)(psau))->sin6_addr) +#define NSRCADR6(psau) (SOCK_ADDR6(psau).s6_addr) +#define SET_ADDR4(psau, addr4) (NSRCADR(psau) = htonl(addr4)) + +static void +calc_addr_distance( + struct sockaddr_storage * dist, + struct sockaddr_storage * a1, + struct sockaddr_storage * a2 + ) +{ + u_int32 a1val; + u_int32 a2val; + u_int32 v4dist; + int found_greater; + int a1_greater; + int i; + + memset(dist, 0, sizeof(*dist)); + AF(dist) = AF(a1); + + /* v4 can be done a bit simpler */ + if (IS_IPV4(a1)) { + a1val = SRCADR(a1); + a2val = SRCADR(a2); + v4dist = (a1val > a2val) + ? a1val - a2val + : a2val - a1val; + SET_ADDR4(dist, v4dist); + + return; } - return (-1); + found_greater = FALSE; + a1_greater = FALSE; /* suppress pot. uninit. warning */ + for (i = 0; i < sizeof(NSRCADR6(a1)); i++) { + if (!found_greater && + NSRCADR6(a1)[i] != NSRCADR6(a2)[i]) { + found_greater = TRUE; + a1_greater = (NSRCADR6(a1)[i] > NSRCADR6(a2)[i]); + } + if (!found_greater) { + NSRCADR6(dist)[i] = 0; + } else { + if (a1_greater) + NSRCADR6(dist)[i] = NSRCADR6(a1)[i] - + NSRCADR6(a2)[i]; + else + NSRCADR6(dist)[i] = NSRCADR6(a2)[i] - + NSRCADR6(a1)[i]; + } + } +} + + +/* + * cmp_addr_distance - compare two address distances, returning -1, 0, + * 1 to indicate their relationship. + */ +static int +cmp_addr_distance( + struct sockaddr_storage * d1, + struct sockaddr_storage * d2 + ) +{ + int i; + + if (IS_IPV4(d1)) { + if (SRCADR(d1) < SRCADR(d2)) + return -1; + else if (SRCADR(d1) == SRCADR(d2)) + return 0; + else + return 1; + } + + for (i = 0; i < sizeof(NSRCADR6(d1)); i++) { + if (NSRCADR6(d1)[i] < NSRCADR6(d2)[i]) + return -1; + else if (NSRCADR6(d1)[i] > NSRCADR6(d2)[i]) + return 1; + } + + return 0; } /*