/* ntp_config.c
*
* This file contains the ntpd configuration code.
*
* Written By: Sachin Kamboj
* University of Delaware
* Newark, DE 19711
* Some parts borrowed from the older ntp_config.c
* Copyright (c) 2006
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef HAVE_NETINFO
# include <netinfo/ni.h>
#endif
/* Bug 2817 */
#if defined(HAVE_SYS_MMAN_H)
# include <sys/mman.h>
#endif
/*
* Poll Skew List
*/
static psl_item psl[17-3+1]; /* values for polls 3-17 */
/* To simplify the runtime code we */
/* don't want to have to special-case */
/* dealing with a default */
/* list of servers from command line for config_peers() */
int cmdline_server_count;
char ** cmdline_servers;
/* Current state of memory locking:
* -1: default
* 0: memory locking disabled
* 1: Memory locking enabled
*/
int cur_memlock = -1;
#ifdef HAVE_NETINFO
/*
* NetInfo configuration state
*/
struct netinfo_config_state {
void *domain; /* domain with config */
ni_id config_dir; /* ID config dir */
int prop_index; /* current property */
int val_index; /* current value */
char **val_list; /* value list */
};
#endif
struct REMOTE_CONFIG_INFO remote_config; /* Remote configuration buffer and
pointer info */
int old_config_style = 1; /* A boolean flag, which when set,
* indicates that the old configuration
* format with a newline at the end of
* every command is being used
*/
int cryptosw; /* crypto command called */
extern char *stats_drift_file; /* name of the driftfile */
/*
* declare an int pointer for each flag for quick testing without
* walking bc_list. If the pointer is consumed by libntp rather
* than ntpd, declare it in a libntp source file pointing to storage
* initialized with the appropriate value for other libntp clients, and
* redirect it to point into bc_list during ntpd startup.
*/
int *p_bcXXXX_enabled = &bc_list[0].enabled;
#endif
/* FUNCTION PROTOTYPES */
static void init_syntax_tree(config_tree *);
static void apply_enable_disable(attr_val_fifo *q, int enable);
#ifdef SAVECONFIG
/* Dump all trees */
int
dump_all_config_trees(
FILE *df,
int comment
)
{
config_tree * cfg_ptr;
int return_value;
time_t now = time(NULL);
struct tm tm = *localtime(&now);
/*
* For options without documentation we just output the name
* and its data value
*/
atrv = HEAD_PFIFO(ptree->vars);
for ( ; atrv != NULL; atrv = atrv->link) {
switch (atrv->type) {
#ifdef DEBUG
default:
fprintf(df, "\n# dump error:\n"
"# unknown vars type %d (%s) for %s\n",
atrv->type, token_name(atrv->type),
token_name(atrv->attr));
break;
#endif
case T_Double:
fprintf(df, "%s %s\n", keyword(atrv->attr),
normal_dtoa(atrv->value.d));
break;
case T_Integer:
fprintf(df, "%s %d\n", keyword(atrv->attr),
atrv->value.i);
break;
peer_node *
create_peer_node(
int hmode,
address_node * addr,
attr_val_fifo * options
)
{
peer_node *my_node;
attr_val *option;
int freenode;
int errflag = 0;
my_node = emalloc_zero(sizeof(*my_node));
/* Initialize node values to default */
my_node->peerversion = NTP_VERSION;
/* Now set the node to the read values */
my_node->host_mode = hmode;
my_node->addr = addr;
/*
* the options FIFO mixes items that will be saved in the
* peer_node as explicit members, such as minpoll, and
* those that are moved intact to the peer_node's peerflags
* FIFO. The options FIFO is consumed and reclaimed here.
*/
if (options != NULL)
CHECK_FIFO_CONSISTENCY(*options);
while (options != NULL) {
UNLINK_FIFO(option, *options, link);
if (NULL == option) {
free(options);
break;
}
freenode = 1;
/* Check the kind of option being set */
switch (option->attr) {
case T_Flag:
APPEND_G_FIFO(my_node->peerflags, option);
freenode = 0;
break;
case T_Minpoll:
if (option->value.i < NTP_MINPOLL ||
option->value.i > UCHAR_MAX) {
msyslog(LOG_INFO,
"minpoll: provided value (%d) is out of range [%d-%d])",
option->value.i, NTP_MINPOLL,
UCHAR_MAX);
my_node->minpoll = NTP_MINPOLL;
} else {
my_node->minpoll =
(u_char)option->value.u;
}
break;
case T_Maxpoll:
if (option->value.i < 0 ||
option->value.i > NTP_MAXPOLL) {
msyslog(LOG_INFO,
"maxpoll: provided value (%d) is out of range [0-%d])",
option->value.i, NTP_MAXPOLL);
my_node->maxpoll = NTP_MAXPOLL;
} else {
my_node->maxpoll =
(u_char)option->value.u;
}
break;
case T_Ttl:
if (is_refclk_addr(addr)) {
msyslog(LOG_ERR, "'ttl' does not apply for refclocks");
errflag = 1;
} else if (option->value.u >= MAX_TTL) {
msyslog(LOG_ERR, "ttl: invalid argument");
errflag = 1;
} else {
my_node->ttl = (u_char)option->value.u;
}
break;
case T_Mode:
if (is_refclk_addr(addr)) {
my_node->ttl = option->value.u;
} else {
msyslog(LOG_ERR, "'mode' does not apply for network peers");
errflag = 1;
}
break;
/*
* From the parser's perspective an association ID fits into
* its generic T_String definition of a name/address "address".
* We treat all valid 16-bit numbers as association IDs.
*/
for (u = 0, pch = (u_char*)addr->address; isdigit(*pch); ++pch) {
/* accumulate with overflow retention */
u = (10 * u + *pch - '0') | (u & 0xFF000000u);
}
static void
destroy_restrict_node(
restrict_node *my_node
)
{
/* With great care, free all the memory occupied by
* the restrict node
*/
destroy_address_node(my_node->addr);
destroy_address_node(my_node->mask);
destroy_attr_val_fifo(my_node->flag_tok_fifo);
free(my_node);
}
if (fifo != NULL) {
for (;;) {
UNLINK_FIFO(aon, *fifo, link);
if (aon == NULL)
break;
destroy_address_node(aon->addr);
destroy_attr_val_fifo(aon->options);
free(aon);
}
free(fifo);
}
}
setvar_node *
create_setvar_node(
char * var,
char * val,
int isdefault
)
{
setvar_node * my_node;
char * pch;
/* do not allow = in the variable name */
pch = strchr(var, '=');
if (NULL != pch)
*pch = '\0';
/* Now store the string into a setvar_node */
my_node = emalloc_zero(sizeof(*my_node));
my_node->var = var;
my_node->val = val;
my_node->isdefault = isdefault;
return my_node;
}
nic_rule_node *
create_nic_rule_node(
int match_class,
char *if_name, /* interface name or numeric address */
int action
)
{
nic_rule_node *my_node;
if (gai_err) {
fprintf(stderr, "ERROR!! Could not get a new address\n");
exit(1);
}
memcpy(final_addr, ptr->ai_addr, ptr->ai_addrlen);
fprintf(stderr, "Successful in setting ip address of simulated server to: %s\n",
stoa(final_addr));
freeaddrinfo(ptr);
/*
* Count the number of trusted keys to preallocate storage and
* size the hash table.
*/
count = 0;
my_val = HEAD_PFIFO(ptree->auth.trusted_key_list);
for (; my_val != NULL; my_val = my_val->link) {
if (T_Integer == my_val->type) {
first = my_val->value.i;
if (first > 1 && first <= NTP_MAXKEY)
count++;
} else {
REQUIRE(T_Intrange == my_val->type);
first = my_val->value.r.first;
last = my_val->value.r.last;
if (!(first > last || first < 1 ||
last > NTP_MAXKEY)) {
count += 1 + last - first;
}
}
}
auth_prealloc_symkeys(count);
/* Keys Command */
if (ptree->auth.keys)
getauthkeys(ptree->auth.keys);
/* Control Key Command */
if (ptree->auth.control_key)
ctl_auth_keyid = (keyid_t)ptree->auth.control_key;
/* Requested Key Command */
if (ptree->auth.request_key) {
DPRINTF(4, ("set info_auth_keyid to %08lx\n",
(u_long) ptree->auth.request_key));
info_auth_keyid = (keyid_t)ptree->auth.request_key;
}
/* Trusted Key Command */
my_val = HEAD_PFIFO(ptree->auth.trusted_key_list);
for (; my_val != NULL; my_val = my_val->link) {
if (T_Integer == my_val->type) {
first = my_val->value.i;
if (first >= 1 && first <= NTP_MAXKEY) {
authtrust(first, TRUE);
} else {
msyslog(LOG_NOTICE,
"Ignoring invalid trustedkey %d, min 1 max %d.",
first, NTP_MAXKEY);
}
} else {
first = my_val->value.r.first;
last = my_val->value.r.last;
if (first > last || first < 1 ||
last > NTP_MAXKEY) {
msyslog(LOG_NOTICE,
"Ignoring invalid trustedkey range %d ... %d, min 1 max %d.",
first, last, NTP_MAXKEY);
} else {
for (i = first; i <= last; i++) {
authtrust(i, TRUE);
}
}
}
}
static void
config_tos(
config_tree *ptree
)
{
char const improper_operation_msg[] =
" - daemon will not operate properly!";
attr_val * tos;
int item;
double val;
/* [Bug 2896] For the daemon to work properly it is essential
* that minsane < minclock <= maxclock.
*
* If either constraint is violated, the daemon will be or might
* become dysfunctional. Fixing the values is too fragile here,
* since three variables with interdependecies are involved. We
* just log an error but do not stop: This might be caused by
* remote config, and it might be fixed by remote config, too.
*/
int l_maxclock = sys_maxclock;
int l_minclock = sys_minclock;
int l_minsane = sys_minsane;
int l_floor = sys_floor;
int l_ceiling = sys_ceiling;
/* -*- phase one: inspect / sanitize the values */
tos = HEAD_PFIFO(ptree->orphan_cmds);
for (; tos != NULL; tos = tos->link) {
/* not all attributes are doubles (any more), so loading
* 'val' in all cases is not a good idea: It should be
* done as needed in every case processed here.
*/
switch(tos->attr) {
default:
break;
case T_Bcpollbstep:
val = tos->value.d;
if (val > 4) {
msyslog(LOG_WARNING,
"Using maximum tos bcpollbstep %d, %d requested",
4, (int)val);
tos->value.d = 4;
} else if (val < 0) {
msyslog(LOG_WARNING,
"Using minimum tos bcpollbstep %d, %d requested",
0, (int)val);
tos->value.d = 0;
}
break;
case T_Floor:
l_floor = (int)tos->value.d;
if (l_floor > STRATUM_UNSPEC - 1) {
msyslog(LOG_WARNING,
"Using maximum tos floor %d, %d requested",
STRATUM_UNSPEC - 1, l_floor);
tos->value.d = STRATUM_UNSPEC - 1;
}
else if (l_floor < 0) {
msyslog(LOG_WARNING,
"Using minimum tos floor %d, %d requested",
0, l_floor);
tos->value.d = 0;
}
l_floor = (int)tos->value.d;
break;
case T_Ceiling:
l_ceiling = (int)tos->value.d;
if (l_ceiling > STRATUM_UNSPEC - 1) {
msyslog(LOG_WARNING,
"Using maximum tos ceiling %d, %d requested",
STRATUM_UNSPEC - 1, l_ceiling);
tos->value.d = STRATUM_UNSPEC - 1;
}
else if (l_ceiling < 0) {
msyslog(LOG_WARNING,
"Using minimum tos ceiling %d, %d requested",
0, l_ceiling);
tos->value.d = 0;
}
l_ceiling = (int)tos->value.d;
break;
case T_Minclock:
val = tos->value.d;
if ((int)tos->value.d < 1)
tos->value.d = 1;
l_minclock = (int)tos->value.d;
break;
case T_Maxclock:
val = tos->value.d;
if ((int)tos->value.d < 1)
tos->value.d = 1;
l_maxclock = (int)tos->value.d;
break;
case T_Minsane:
val = tos->value.d;
if ((int)tos->value.d < 0)
tos->value.d = 0;
l_minsane = (int)tos->value.d;
break;
}
}
if ( ! (l_minsane < l_minclock && l_minclock <= l_maxclock)) {
msyslog(LOG_ERR, "Must have tos "
"minsane (%d) < minclock (%d) <= maxclock (%d)%s",
l_minsane, l_minclock, l_maxclock,
improper_operation_msg);
}
if (l_floor > l_ceiling) {
msyslog(LOG_ERR, "Must have tos "
"floor (%d) <= ceiling (%d)%s",
l_floor, l_ceiling, improper_operation_msg);
}
/* -*- phase two: forward the values to the protocol machinery */
tos = HEAD_PFIFO(ptree->orphan_cmds);
for (; tos != NULL; tos = tos->link) {
switch(tos->attr) {
/* Set the statistics directory */
if (ptree->stats_dir) {
stats_config(STATS_STATSDIR, ptree->stats_dir, TRUE);
}
/* NOTE:
* Calling filegen_get is brain dead. Doing a string
* comparison to find the relavant filegen structure is
* expensive.
*
* Through the parser, we already know which filegen is
* being specified. Hence, we should either store a
* pointer to the specified structure in the syntax tree
* or an index into a filegen array.
*
* Need to change the filegen code to reflect the above.
*/
#ifndef SIM
static void
config_access(
config_tree *ptree
)
{
static int warned_signd;
attr_val * my_opt;
restrict_node * my_node;
sockaddr_u addr;
sockaddr_u mask;
struct addrinfo hints;
struct addrinfo * ai_list;
struct addrinfo * pai;
int rc;
int/*BOOL*/ success;
int/*BOOL*/ restrict_default;
u_short rflags;
u_short mflags;
short ippeerlimit;
int range_err;
attr_val * atrv;
attr_val * dflt_psl_atr;
const char * signd_warning =
#ifdef HAVE_NTP_SIGND
"MS-SNTP signd operations currently block ntpd degrading service to all clients.\n";
#else
"mssntp restrict bit ignored, this ntpd was configured without --enable-ntp-signd.\n";
#endif
/* Configure the mru options */
my_opt = HEAD_PFIFO(ptree->mru_opts);
for (; my_opt != NULL; my_opt = my_opt->link) {
range_err = FALSE;
switch (my_opt->attr) {
case T_Incalloc:
if (0 <= my_opt->value.i)
mru_incalloc = my_opt->value.u;
else
range_err = TRUE;
break;
case T_Incmem:
if (0 <= my_opt->value.i)
mru_incalloc = (my_opt->value.u * 1024U)
/ sizeof(mon_entry);
else
range_err = TRUE;
break;
case T_Initalloc:
if (0 <= my_opt->value.i)
mru_initalloc = my_opt->value.u;
else
range_err = TRUE;
break;
case T_Initmem:
if (0 <= my_opt->value.i)
mru_initalloc = (my_opt->value.u * 1024U)
/ sizeof(mon_entry);
else
range_err = TRUE;
break;
case T_Mindepth:
if (0 <= my_opt->value.i)
mru_mindepth = my_opt->value.u;
else
range_err = TRUE;
break;
case T_Maxage:
mru_maxage = my_opt->value.i;
break;
case T_Maxdepth:
if (0 <= my_opt->value.i)
mru_maxdepth = my_opt->value.u;
else
mru_maxdepth = UINT_MAX;
break;
case T_Maxmem:
if (0 <= my_opt->value.i)
mru_maxdepth = (my_opt->value.u * 1024U) /
sizeof(mon_entry);
else
mru_maxdepth = UINT_MAX;
break;
default:
msyslog(LOG_ERR,
"Unknown mru option %s (%d)",
keyword(my_opt->attr), my_opt->attr);
exit(1);
}
if (range_err)
msyslog(LOG_ERR,
"mru %s %d out of range, ignored.",
keyword(my_opt->attr), my_opt->value.i);
}
/* Configure the discard options */
my_opt = HEAD_PFIFO(ptree->discard_opts);
for (; my_opt != NULL; my_opt = my_opt->link) {
switch (my_opt->attr) {
case T_Average:
if (0 <= my_opt->value.i &&
my_opt->value.i <= UCHAR_MAX)
ntp_minpoll = (u_char)my_opt->value.u;
else
msyslog(LOG_ERR,
"discard average %d out of range, ignored.",
my_opt->value.i);
break;
case T_Minimum:
ntp_minpkt = my_opt->value.i;
break;
ZERO_SOCK(&addr);
ai_list = NULL;
pai = NULL;
restrict_default = FALSE;
if (NULL == my_node->addr) {
ZERO_SOCK(&mask);
if (!(RESM_SOURCE & mflags)) {
/*
* The user specified a default rule
* without a -4 / -6 qualifier, add to
* both lists
*/
restrict_default = TRUE;
} else {
/* apply "restrict source ..." */
success = hack_restrict(RESTRICT_FLAGS,
NULL, NULL,
ippeerlimit,
mflags, rflags,
0);
if (!success) {
msyslog(LOG_ERR,
"unable to save restrict source");
}
continue;
}
} else {
/* Resolve the specified address */
AF(&addr) = (u_short)my_node->addr->type;
if (getnetnum(my_node->addr->address,
&addr, 1, t_UNK) != 1) {
/*
* Attempt a blocking lookup. This
* is in violation of the nonblocking
* design of ntpd's mainline code. The
* alternative of running without the
* restriction until the name resolved
* seems worse.
* Ideally some scheme could be used for
* restrict directives in the startup
* ntp.conf to delay starting up the
* protocol machinery until after all
* restrict hosts have been resolved.
*/
ai_list = NULL;
ZERO(hints);
hints.ai_protocol = IPPROTO_UDP;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_family = my_node->addr->type;
rc = getaddrinfo(my_node->addr->address,
"ntp", &hints,
&ai_list);
if (rc) {
msyslog(LOG_ERR,
"restrict: line %d col %d"
" address/host '%s' unusable.",
my_node->line_no,
my_node->column,
my_node->addr->address);
continue;
}
INSIST(ai_list != NULL);
pai = ai_list;
INSIST(pai->ai_addr != NULL);
INSIST(sizeof(addr) >= pai->ai_addrlen);
memcpy(&addr, pai->ai_addr,
pai->ai_addrlen);
INSIST(AF_INET == AF(&addr) ||
AF_INET6 == AF(&addr));
}
/* default to all-ones mask for single address */
SET_HOSTMASK(&mask, AF(&addr));
/* Ignore mask if addr from hostname [Bug 3872] */
if (NULL == ai_list && my_node->mask) {
ZERO_SOCK(&mask);
AF(&mask) = my_node->mask->type;
if (getnetnum(my_node->mask->address,
&mask, 1, t_MSK) != 1) {
msyslog(LOG_ERR,
"restrict: line %d col %d"
" mask '%s' unusable.",
my_node->line_no,
my_node->column,
my_node->mask->address);
continue;
}
}
}
/* Set the flags */
if (restrict_default) {
AF(&addr) = AF_INET;
AF(&mask) = AF_INET;
success = hack_restrict(
RESTRICT_FLAGS,
&addr,
&mask,
ippeerlimit,
mflags,
rflags,
0
);
if (!success) {
msyslog(LOG_ERR,
"unable to save %s %s restriction",
stoa(&addr), stoa(&mask));
}
AF(&addr) = AF_INET6;
AF(&mask) = AF_INET6;
}
/*
* pollskewlist
*/
atrv = HEAD_PFIFO(ptree->pollskewlist);
if (NULL == atrv) {
/* don't touch the poll skew list */
return;
}
ZERO(psl);
/*
* First, find the last default pollskewlist item.
*/
dflt_psl_atr = NULL;
for ( ; atrv != NULL; atrv = atrv->link) {
if (-1 == atrv->attr) { /* default */
dflt_psl_atr = atrv;
} else if ( atrv->attr < NTP_MINPOLL
|| atrv->attr > NTP_MAXPOLL) {
msyslog(LOG_ERR,
"Poll %d out of bounds [%d-%d] for pollskewlist",
atrv->attr, NTP_MINPOLL, NTP_MAXPOLL);
}
}
/* If we have a nonzero default, put it in all entries */
if ( dflt_psl_atr
&& ( 0 != dflt_psl_atr->value.r.first
|| 0 != dflt_psl_atr->value.r.last)) {
int i;
for (i = NTP_MINPOLL; i <= NTP_MAXPOLL; ++i) {
attrtopsl(i, dflt_psl_atr);
}
}
/* Finally, update the PSL with any explicit entries */
atrv = HEAD_PFIFO(ptree->pollskewlist);
for ( ; atrv != NULL; atrv = atrv->link) {
if (atrv->attr >= NTP_MINPOLL && atrv->attr <= NTP_MAXPOLL) {
attrtopsl(atrv->attr, atrv);
}
}
#if 0
int p;
msyslog(LOG_INFO, "Dumping PSL:");
for (p = NTP_MINPOLL; p <= NTP_MAXPOLL; ++p) {
psl_item psi;
case T_Memlock:
/* What if we HAVE_OPT(SAVECONFIGQUIT) ? */
if (HAVE_OPT( SAVECONFIGQUIT )) {
break;
}
if (rlimit_av->value.i == -1) {
# if defined(HAVE_MLOCKALL)
if (cur_memlock != 0) {
if (-1 == munlockall()) {
msyslog(LOG_ERR, "munlockall() failed: %m");
}
}
cur_memlock = 0;
# endif /* HAVE_MLOCKALL */
} else if (rlimit_av->value.i >= 0) {
#if defined(RLIMIT_MEMLOCK)
# if defined(HAVE_MLOCKALL)
if (cur_memlock != 1) {
if (-1 == mlockall(MCL_CURRENT|MCL_FUTURE)) {
msyslog(LOG_ERR, "mlockall() failed: %m");
}
}
# endif /* HAVE_MLOCKALL */
ntp_rlimit(RLIMIT_MEMLOCK,
(rlim_t)(rlimit_av->value.i * 1024 * 1024),
1024 * 1024,
"MB");
cur_memlock = 1;
#else
/* STDERR as well would be fine... */
msyslog(LOG_WARNING, "'rlimit memlock' specified but is not available on this system.");
#endif /* RLIMIT_MEMLOCK */
} else {
msyslog(LOG_WARNING, "'rlimit memlock' value of %d is unexpected!", rlimit_av->value.i);
}
break;
case T_Stacksize:
#if defined(RLIMIT_STACK)
ntp_rlimit(RLIMIT_STACK,
(rlim_t)(rlimit_av->value.i * 4096),
4096,
"4k");
#else
/* STDERR as well would be fine... */
msyslog(LOG_WARNING, "'rlimit stacksize' specified but is not available on this system.");
#endif /* RLIMIT_STACK */
break;
case T_Filenum:
#if defined(RLIMIT_NOFILE)
ntp_rlimit(RLIMIT_NOFILE,
(rlim_t)(rlimit_av->value.i),
1,
"");
#else
/* STDERR as well would be fine... */
msyslog(LOG_WARNING, "'rlimit filenum' specified but is not available on this system.");
#endif /* RLIMIT_NOFILE */
break;
case 0:
/*
* 0 is out of range for valid token T_...
* and in a nic_rules_node indicates the
* interface descriptor is either a name or
* address, stored in if_name in either case.
*/
INSIST(if_name != NULL);
pchSlash = strchr(if_name, '/');
if (pchSlash != NULL)
*pchSlash = '\0';
if (is_ip_address(if_name, AF_UNSPEC, &addr)) {
match_type = MATCH_IFADDR;
if (pchSlash != NULL
&& 1 == sscanf(pchSlash + 1, "%d",
&prefixlen)) {
addrbits = 8 *
SIZEOF_INADDR(AF(&addr));
prefixlen = max(-1, prefixlen);
prefixlen = min(prefixlen,
addrbits);
}
} else {
match_type = MATCH_IFNAME;
if (pchSlash != NULL)
*pchSlash = '/';
}
break;
case T_Ipv4:
match_type = MATCH_IPV4;
break;
case T_Ipv6:
match_type = MATCH_IPV6;
break;
case T_Wildcard:
match_type = MATCH_WILDCARD;
break;
}
i = 0;
sn = HEAD_PFIFO(ptree->phone);
for (; sn != NULL; sn = sn->link) {
/* need to leave array entry for NULL terminator */
if (i < COUNTOF(sys_phone) - 1) {
sys_phone[i++] = estrdup(sn->s);
sys_phone[i] = NULL;
} else {
msyslog(LOG_INFO,
"phone: Number of phone entries exceeds %zu. Ignoring phone %s...",
(COUNTOF(sys_phone) - 1), sn->s);
}
}
}
/* [Bug 3465] There is a built-in default for the TTLs. We must
* overwrite 'sys_ttlmax' if we change that preset, and leave it
* alone otherwise!
*/
curr_ttl = HEAD_PFIFO(ptree->ttl);
for (; curr_ttl != NULL; curr_ttl = curr_ttl->link) {
if (i < COUNTOF(sys_ttl))
sys_ttl[i++] = (u_char)curr_ttl->i;
else
msyslog(LOG_INFO,
"ttl: Number of TTL entries exceeds %zu. Ignoring TTL %d...",
COUNTOF(sys_ttl), curr_ttl->i);
}
if (0 != i) /* anything written back at all? */
sys_ttlmax = i - 1;
}
#endif /* !SIM */
curr_opt = HEAD_PFIFO(curr_trap->options);
for (; curr_opt != NULL; curr_opt = curr_opt->link) {
if (T_Port == curr_opt->attr) {
if (curr_opt->value.i < 1
|| curr_opt->value.i > USHRT_MAX) {
msyslog(LOG_ERR,
"invalid port number "
"%d, trap ignored",
curr_opt->value.i);
err_flag = 1;
}
port = (u_short)curr_opt->value.i;
}
else if (T_Interface == curr_opt->attr) {
/* Resolve the interface address */
ZERO_SOCK(&addr_sock);
if (getnetnum(curr_opt->value.s,
&addr_sock, 1, t_UNK) != 1) {
err_flag = 1;
break;
}
localaddr = findinterface(&addr_sock);
if (NULL == localaddr) {
msyslog(LOG_ERR,
"can't find interface with address %s",
stoa(&addr_sock));
err_flag = 1;
}
}
}
/* Now process the trap for the specified interface
* and port number
*/
if (!err_flag) {
if (!port)
port = TRAPPORT;
ZERO_SOCK(&peeraddr);
rc = getnetnum(curr_trap->addr->address,
&peeraddr, 1, t_UNK);
if (1 != rc) {
#ifndef WORKER
msyslog(LOG_ERR,
"trap: unable to use IP address %s.",
curr_trap->addr->address);
#else /* WORKER follows */
/*
* save context and hand it off
* for name resolution.
*/
ZERO(hints);
hints.ai_protocol = IPPROTO_UDP;
hints.ai_socktype = SOCK_DGRAM;
snprintf(port_text, sizeof(port_text),
"%u", port);
hints.ai_flags = Z_AI_NUMERICSERV;
pstp = emalloc_zero(sizeof(*pstp));
if (localaddr != NULL) {
hints.ai_family = localaddr->family;
pstp->ifaddr_nonnull = 1;
memcpy(&pstp->ifaddr,
&localaddr->sin,
sizeof(pstp->ifaddr));
}
rc = getaddrinfo_sometime(
curr_trap->addr->address,
port_text, &hints,
INITIAL_DNS_RETRY,
&trap_name_resolved,
pstp);
if (!rc)
msyslog(LOG_ERR,
"config_trap: getaddrinfo_sometime(%s,%s): %m",
curr_trap->addr->address,
port_text);
#endif /* WORKER */
continue;
}
/* port is at same location for v4 and v6 */
SET_PORT(&peeraddr, port);
/* Get the reference clock address and
* ensure that it is sane
*/
addr_node = curr_fudge->addr;
ZERO_SOCK(&addr_sock);
if (getnetnum(addr_node->address, &addr_sock, 1, t_REF)
!= 1) {
err_flag = 1;
msyslog(LOG_ERR,
"unrecognized fudge reference clock address %s, line ignored",
addr_node->address);
} else if (!ISREFCLOCKADR(&addr_sock)) {
err_flag = 1;
msyslog(LOG_ERR,
"inappropriate address %s for the fudge command, line ignored",
stoa(&addr_sock));
}
/* Parse all the options to the fudge command */
ZERO(clock_stat);
/* some things are not necessarily cleared by ZERO...*/
clock_stat.fudgeminjitter = 0.0;
clock_stat.fudgetime1 = 0.0;
clock_stat.fudgetime2 = 0.0;
curr_opt = HEAD_PFIFO(curr_fudge->options);
for (; curr_opt != NULL; curr_opt = curr_opt->link) {
switch (curr_opt->attr) {
case T_Time1:
clock_stat.haveflags |= CLK_HAVETIME1;
clock_stat.fudgetime1 = curr_opt->value.d;
break;
case T_Time2:
clock_stat.haveflags |= CLK_HAVETIME2;
clock_stat.fudgetime2 = curr_opt->value.d;
break;
case T_Stratum:
clock_stat.haveflags |= CLK_HAVEVAL1;
clock_stat.fudgeval1 = curr_opt->value.i;
break;
case T_Refid:
clock_stat.haveflags |= CLK_HAVEVAL2;
/*
* strncpy() does exactly what we want
* here, do not be tempted to replace
* it with strlcpy(), we want it to
* zero-fill refid's less than 4 chars
* because we're going to stuff it
* into a u_int32.
*/
strncpy(refidstr, curr_opt->value.s,
sizeof refidstr - 1);
memcpy(&clock_stat.fudgeval2, refidstr,
sizeof clock_stat.fudgeval2);
break;
case T_Flag1:
clock_stat.haveflags |= CLK_HAVEFLAG1;
if (curr_opt->value.i)
clock_stat.flags |= CLK_FLAG1;
else
clock_stat.flags &= ~CLK_FLAG1;
break;
case T_Flag2:
clock_stat.haveflags |= CLK_HAVEFLAG2;
if (curr_opt->value.i)
clock_stat.flags |= CLK_FLAG2;
else
clock_stat.flags &= ~CLK_FLAG2;
break;
case T_Flag3:
clock_stat.haveflags |= CLK_HAVEFLAG3;
if (curr_opt->value.i)
clock_stat.flags |= CLK_FLAG3;
else
clock_stat.flags &= ~CLK_FLAG3;
break;
case T_Flag4:
clock_stat.haveflags |= CLK_HAVEFLAG4;
if (curr_opt->value.i)
clock_stat.flags |= CLK_FLAG4;
else
clock_stat.flags &= ~CLK_FLAG4;
break;
case T_Minjitter:
clock_stat.haveflags |= CLK_HAVEMINJIT;
clock_stat.fudgeminjitter = curr_opt->value.d;
break;
curr_var = HEAD_PFIFO(ptree->vars);
for (; curr_var != NULL; curr_var = curr_var->link) {
/* Determine which variable to set and set it */
switch (curr_var->attr) {
case T_Broadcastdelay:
proto_config(PROTO_BROADDELAY, 0, curr_var->value.d, NULL);
break;
case T_Tick:
loop_config(LOOP_TICK, curr_var->value.d);
break;
case T_Driftfile:
if ('\0' == curr_var->value.s[0])
msyslog(LOG_INFO, "config: driftfile disabled");
stats_config(STATS_FREQ_FILE, curr_var->value.s, TRUE);
break;
case T_Dscp:
/* DSCP is in the upper 6 bits of the IP TOS/DS field */
qos = curr_var->value.i << 2;
break;
case T_Ident:
sys_ident = curr_var->value.s;
break;
case T_WanderThreshold: /* FALLTHROUGH */
case T_Nonvolatile:
wander_threshold = curr_var->value.d;
break;
case T_Leapfile:
stats_config(STATS_LEAP_FILE, curr_var->value.s, curr_var->flag);
break;
#ifndef SIM
/* Define a function to check if a resolved address is sane.
* If yes, return 1, else return 0;
*/
static int
is_sane_resolved_address(
sockaddr_u * peeraddr,
int hmode
)
{
if (!ISREFCLOCKADR(peeraddr) && ISBADADR(peeraddr)) {
msyslog(LOG_ERR,
"attempt to configure invalid address %s",
stoa(peeraddr));
return 0;
}
/*
* Shouldn't be able to specify:
* - multicast address for server/peer!
* - unicast address for manycastclient!
*/
if ((T_Server == hmode || T_Peer == hmode || T_Pool == hmode)
&& IS_MCAST(peeraddr)) {
msyslog(LOG_ERR,
"attempt to configure invalid address %s",
stoa(peeraddr));
return 0;
}
if (T_Manycastclient == hmode && !IS_MCAST(peeraddr)) {
msyslog(LOG_ERR,
"attempt to configure invalid address %s",
stoa(peeraddr));
return 0;
}
if (IS_IPV6(peeraddr) && !ipv6_works)
return 0;
/* Ok, all tests succeeded, now we can return 1 */
return 1;
}
static u_char
get_correct_host_mode(
int token
)
{
switch (token) {
case T_Server:
case T_Pool:
case T_Manycastclient:
return MODE_CLIENT;
case T_Peer:
return MODE_ACTIVE;
case T_Broadcast:
return MODE_BROADCAST;
default:
return 0;
}
}
/*
* peerflag_bits() get config_peers() peerflags value from a
* peer_node's queue of flag attr_val entries.
*/
static int
peerflag_bits(
peer_node *pn
)
{
int peerflags;
attr_val *option;
int hmode;
/* add servers named on the command line with iburst implied */
for (;
cmdline_server_count > 0;
cmdline_server_count--, cmdline_servers++) {
ZERO_SOCK(&peeraddr);
/*
* If we have a numeric address, we can safely
* proceed in the mainline with it. Otherwise, hand
* the hostname off to the blocking child.
*
* Note that if we're told to add the peer here, we
* do that regardless of ippeerlimit.
*/
if (is_ip_address(*cmdline_servers, AF_UNSPEC,
&peeraddr)) {
getaddrinfo_sometime_ex(*cmdline_servers,
"ntp", &hints,
INITIAL_DNS_RETRY,
&peer_name_resolved,
(void *)ctx, DNSFLAGS);
# else /* !WORKER follows */
msyslog(LOG_ERR,
"hostname %s can not be used, please use IP address instead.",
curr_peer->addr->address);
# endif
}
}
/* add associations from the configuration file */
curr_peer = HEAD_PFIFO(ptree->peers);
for (; curr_peer != NULL; curr_peer = curr_peer->link) {
ZERO_SOCK(&peeraddr);
/* Find the correct host-mode */
hmode = get_correct_host_mode(curr_peer->host_mode);
INSIST(hmode != 0);
if (T_Pool == curr_peer->host_mode) {
AF(&peeraddr) = curr_peer->addr->type;
peer_config(
&peeraddr,
curr_peer->addr->address,
NULL,
-1,
hmode,
curr_peer->peerversion,
curr_peer->minpoll,
curr_peer->maxpoll,
peerflag_bits(curr_peer),
curr_peer->ttl,
curr_peer->peerkey,
curr_peer->group);
/*
* If we have a numeric address, we can safely
* proceed in the mainline with it. Otherwise, hand
* the hostname off to the blocking child.
*/
} else if (is_ip_address(curr_peer->addr->address,
curr_peer->addr->type, &peeraddr)) {
if (rescode) {
free(ctx);
msyslog(LOG_ERR,
"giving up resolving host %s: %s (%d)",
name, gai_strerror(rescode), rescode);
return;
}
/* Loop to configure a single association */
for (; res != NULL; res = res->ai_next) {
memcpy(&peeraddr, res->ai_addr, res->ai_addrlen);
if (is_sane_resolved_address(&peeraddr,
ctx->host_mode)) {
NLOG(NLOG_SYSINFO) {
af = ctx->family;
fam_spec = (AF_INET6 == af)
? "(AAAA) "
: (AF_INET == af)
? "(A) "
: "";
msyslog(LOG_INFO, "DNS %s %s-> %s",
name, fam_spec,
stoa(&peeraddr));
}
/*
* peer_clear needs to know if this association was specified
* in the startup configuration file to set the next poll time.
*/
if (ctx->was_initializing) {
INSIST(!initializing);
initializing = TRUE;
}
curr_unpeer = HEAD_PFIFO(ptree->unpeers);
for (; curr_unpeer != NULL; curr_unpeer = curr_unpeer->link) {
/*
* If we have no address attached, assume we have to
* unpeer by AssocID.
*/
if (!curr_unpeer->addr) {
p = findpeerbyassoc(curr_unpeer->assocID);
if (p != NULL) {
msyslog(LOG_NOTICE, "unpeered %s",
stoa(&p->srcadr));
peer_clear(p, "GONE");
unpeer(p);
}
continue;
}
ZERO(peeraddr);
AF(&peeraddr) = curr_unpeer->addr->type;
name = curr_unpeer->addr->address;
rc = getnetnum(name, &peeraddr, 0, t_UNK);
/* Do we have a numeric address? */
if (rc > 0) {
DPRINTF(1, ("unpeer: searching for %s\n",
stoa(&peeraddr)));
p = findexistingpeer(&peeraddr, NULL, NULL, -1, 0, NULL);
if (p != NULL) {
msyslog(LOG_NOTICE, "unpeered %s",
stoa(&peeraddr));
peer_clear(p, "GONE");
unpeer(p);
}
continue;
}
/*
* It's not a numeric IP address, it's a hostname.
* Check for associations with a matching hostname.
*/
for (p = peer_list; p != NULL; p = p->p_link)
if (p->hostname != NULL)
if (!strcasecmp(p->hostname, name))
break;
if (p != NULL) {
msyslog(LOG_NOTICE, "unpeered %s", name);
peer_clear(p, "GONE");
unpeer(p);
}
/* Resolve the hostname to address(es). */
# ifdef WORKER
ZERO(hints);
hints.ai_family = curr_unpeer->addr->type;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
getaddrinfo_sometime(name, "ntp", &hints,
INITIAL_DNS_RETRY,
&unpeer_name_resolved, NULL);
# else /* !WORKER follows */
msyslog(LOG_ERR,
"hostname %s can not be used, please use IP address instead.",
name);
# endif
}
}
/* Check if a simulate block was found in the configuration code.
* If not, return an error and exit
*/
sim_n = HEAD_PFIFO(ptree->sim_details);
if (NULL == sim_n) {
fprintf(stderr, "ERROR!! I couldn't find a \"simulate\" block for configuring the simulator.\n");
fprintf(stderr, "\tCheck your configuration file.\n");
exit(1);
}
/* Process the initialization statements
* -------------------------------------
*/
init_stmt = HEAD_PFIFO(sim_n->init_opts);
for (; init_stmt != NULL; init_stmt = init_stmt->link) {
switch(init_stmt->attr) {
case T_Beep_Delay:
simulation.beep_delay = init_stmt->value.d;
break;
case T_Sim_Duration:
simulation.end_time = init_stmt->value.d;
break;
if (NULL == ptree->sim_details)
return;
sim_n = HEAD_PFIFO(ptree->sim_details);
free(ptree->sim_details);
ptree->sim_details = NULL;
if (NULL == sim_n)
return;
FREE_ATTR_VAL_FIFO(sim_n->init_opts);
for (;;) {
UNLINK_FIFO(serv_n, *sim_n->servers, link);
if (NULL == serv_n)
break;
free(serv_n->curr_script);
if (serv_n->script != NULL) {
for (;;) {
UNLINK_FIFO(script_n, *serv_n->script,
link);
if (script_n == NULL)
break;
free(script_n);
}
free(serv_n->script);
}
free(serv_n);
}
free(sim_n);
}
#endif /* FREE_CFG_T */
#endif /* SIM */
/* Define two different config functions. One for the daemon and the other for
* the simulator. The simulator ignores a lot of the standard ntpd configuration
* options
*/
#ifndef SIM
static void
config_ntpd(
config_tree *ptree,
int/*BOOL*/ input_from_files
)
{
/* [Bug 3435] check and esure clock sanity if configured from
* file and clock sanity parameters (-> basedate) are given. Do
* this ASAP, so we don't disturb the closed loop controller.
*/
if (input_from_files) {
if (config_tos_clock(ptree))
clamp_systime();
}
/*
* install a non default variable with this daemon version
*/
snprintf(line, sizeof(line), "daemon_version=\"%s\"", Version);
set_sys_var(line, strlen(line) + 1, RO);
/*
* Set up for the first time step to install a variable showing
* which syscall is being used to step.
*/
set_tod_using = &ntpd_set_tod_using;
getCmdOpts(argc, argv);
init_syntax_tree(&cfgt);
if (
!lex_init_stack(FindConfig(config_file), "r")
#ifdef HAVE_NETINFO
/* If there is no config_file, try NetInfo. */
&& check_netinfo && !(config_netinfo = get_netinfo_config())
#endif /* HAVE_NETINFO */
) {
msyslog(LOG_INFO, "getconfig: Couldn't open <%s>: %m", FindConfig(config_file));
#ifndef SYS_WINNT
io_open_sockets();
return;
#else
/* Under WinNT try alternate_config_file name, first NTP.CONF, then NTP.INI */
if (!lex_init_stack(FindConfig(alt_config_file), "r")) {
/*
* Broadcast clients can sometimes run without
* a configuration file.
*/
msyslog(LOG_INFO, "getconfig: Couldn't open <%s>: %m", FindConfig(alt_config_file));
io_open_sockets();
/*
* Keep all the configuration trees applied since startup in
* a list that can be used to dump the configuration back to
* a text file.
*/
ptree = emalloc(sizeof(*ptree));
memcpy(ptree, &cfgt, sizeof(*ptree));
ZERO(cfgt);
/* The actual configuration done depends on whether we are configuring the
* simulator or the daemon. Perform a check and call the appropriate
* function as needed.
*/
/*
* With configure --disable-saveconfig, there's no use keeping
* the config tree around after application, so free it.
*/
#ifndef SAVECONFIG
UNLINK_SLIST(punlinked, cfg_tree_history, ptree, link,
config_tree);
INSIST(punlinked == ptree);
free_config_tree(ptree);
#endif
}
/* Hack to disambiguate 'server' statements for refclocks and network peers.
* Please note the qualification 'hack'. It's just that.
*/
static int/*BOOL*/
is_refclk_addr(
const address_node * addr
)
{
return addr && addr->address && !strncmp(addr->address, "127.127.", 8);
}
/*
* gettokens_netinfo - return tokens from NetInfo
*/
static int
gettokens_netinfo (
struct netinfo_config_state *config,
char **tokenlist,
int *ntokens
)
{
int prop_index = config->prop_index;
int val_index = config->val_index;
char **val_list = config->val_list;
/*
* Iterate through each keyword and look for a property that matches it.
*/
again:
if (!val_list) {
for (; prop_index < COUNTOF(keywords); prop_index++)
{
ni_namelist namelist;
struct keyword current_prop = keywords[prop_index];
ni_index index;
/*
* For each value associated in the property, we're going to return
* a separate line. We squirrel away the values in the config state
* so the next time through, we don't need to do this lookup.
*/
NI_INIT(&namelist);
if (NI_OK == ni_lookupprop(config->domain,
&config->config_dir, current_prop.text,
&namelist)) {
/* Found the property, but it has no values */
if (namelist.ni_namelist_len == 0) continue;
/* No list; we're done here. */
if (!val_list)
return CONFIG_UNKNOWN;
/*
* We have a list of values for the current property.
* Iterate through them and return each in order.
*/
if (val_list[val_index]) {
int ntok = 1;
int quoted = 0;
char *tokens = val_list[val_index];
if (ISEOL(*tokens)) {
*tokens = '\0';
break;
} else { /* must be space */
*tokens++ = '\0';
while (ISSPACE(*tokens))
tokens++;
if (ISEOL(*tokens))
break;
}
}
if (ntok == MAXTOKENS) {
/* HMS: chomp it to lose the EOL? */
msyslog(LOG_ERR,
"gettokens_netinfo: too many tokens. Ignoring: %s",
tokens);
} else {
*ntokens = ntok + 1;
}
config->val_index++; /* HMS: Should this be in the 'else'? */
return keywords[prop_index].keytype;
}
/* We're done with the current property. */
prop_index = ++config->prop_index;
/*
* getnetnum - return a net number (this is crude, but careful)
*
* returns 1 for success, and mysteriously, 0 for most failures, and
* -1 if the address found is IPv6 and we believe IPv6 isn't working.
*/
static int
getnetnum(
const char *num,
sockaddr_u *addr,
int complain, /* ignored */
enum gnn_type a_type /* ignored */
)
{
REQUIRE(AF_UNSPEC == AF(addr) ||
AF_INET == AF(addr) ||
AF_INET6 == AF(addr));
#if defined(HAVE_SETRLIMIT)
void
ntp_rlimit(
int rl_what,
rlim_t rl_value,
int rl_scale,
const char * rl_sstr
)
{
struct rlimit rl;
switch (rl_what) {
# ifdef RLIMIT_MEMLOCK
case RLIMIT_MEMLOCK:
if (HAVE_OPT( SAVECONFIGQUIT )) {
break;
}
/*
* The default RLIMIT_MEMLOCK is very low on Linux systems.
* Unless we increase this limit malloc calls are likely to
* fail if we drop root privilege. To be useful the value
* has to be larger than the largest ntpd resident set size.
*/
DPRINTF(2, ("ntp_rlimit: MEMLOCK: %d %s\n",
(int)(rl_value / rl_scale), rl_sstr));
rl.rlim_cur = rl.rlim_max = rl_value;
if (setrlimit(RLIMIT_MEMLOCK, &rl) == -1)
msyslog(LOG_ERR, "Cannot set RLIMIT_MEMLOCK: %m");
break;
# endif /* RLIMIT_MEMLOCK */
# ifdef RLIMIT_NOFILE
case RLIMIT_NOFILE:
/*
* For large systems the default file descriptor limit may
* not be enough.
*/
DPRINTF(2, ("ntp_rlimit: NOFILE: %d %s\n",
(int)(rl_value / rl_scale), rl_sstr));
rl.rlim_cur = rl.rlim_max = rl_value;
if (setrlimit(RLIMIT_NOFILE, &rl) == -1)
msyslog(LOG_ERR, "Cannot set RLIMIT_NOFILE: %m");
break;
# endif /* RLIMIT_NOFILE */
# ifdef RLIMIT_STACK
case RLIMIT_STACK:
/*
* Provide a way to set the stack limit to something
* smaller, so that we don't lock a lot of unused
* stack memory.
*/
DPRINTF(2, ("ntp_rlimit: STACK: %d %s pages\n",
(int)(rl_value / rl_scale), rl_sstr));
if (-1 == getrlimit(RLIMIT_STACK, &rl)) {
msyslog(LOG_ERR, "getrlimit(RLIMIT_STACK) failed: %m");
} else {
if (rl_value > rl.rlim_max) {
msyslog(LOG_WARNING,
"ntp_rlimit: using maximum allowed stack limit %lu instead of %lu.",
(u_long)rl.rlim_max,
(u_long)rl_value);
rl_value = rl.rlim_max;
}
rl.rlim_cur = rl_value;
if (-1 == setrlimit(RLIMIT_STACK, &rl)) {
msyslog(LOG_DEBUG,
"ntp_rlimit: Cannot set RLIMIT_STACK: %m");
}
}
break;
# endif /* RLIMIT_STACK */