#include "libbb.h"
#include <math.h>
#include <netinet/ip.h> /* For IPTOS_LOWDELAY definition */
+#include <sys/resource.h> /* setpriority */
#include <sys/timex.h>
#ifndef IPTOS_LOWDELAY
# define IPTOS_LOWDELAY 0x10
#define RETRY_INTERVAL 5 /* on error, retry in N secs */
#define RESPONSE_INTERVAL 15 /* wait for reply up to N secs */
#define INITIAL_SAMPLES 4 /* how many samples do we want for init */
+#define BAD_DELAY_GROWTH 4 /* drop packet if its delay grew by more than this */
/* Clock discipline parameters and constants */
typedef struct {
len_and_sockaddr *p_lsa;
char *p_dotted;
- /* when to send new query (if p_fd == -1)
- * or when receive times out (if p_fd >= 0): */
int p_fd;
int datapoint_idx;
uint32_t lastpkt_refid;
uint8_t lastpkt_status;
uint8_t lastpkt_stratum;
uint8_t reachable_bits;
+ /* when to send new query (if p_fd == -1)
+ * or when receive times out (if p_fd >= 0): */
double next_action_time;
double p_xmttime;
double lastpkt_recv_time;
llist_t *ntp_peers;
#if ENABLE_FEATURE_NTPD_SERVER
int listen_fd;
+# define G_listen_fd (G.listen_fd)
+#else
+# define G_listen_fd (-1)
#endif
unsigned verbose;
unsigned peer_cnt;
p->p_xmt_msg.m_xmttime.fractionl = random();
p->p_xmttime = gettime1900d();
+ /* Were doing it only if sendto worked, but
+ * loss of sync detection needs reachable_bits updated
+ * even if sending fails *locally*:
+ * "network is unreachable" because cable was pulled?
+ * We still need to declare "unsync" if this condition persists.
+ */
+ p->reachable_bits <<= 1;
+
if (do_sendto(p->p_fd, /*from:*/ NULL, /*to:*/ &p->p_lsa->u.sa, /*addrlen:*/ p->p_lsa->len,
&p->p_xmt_msg, NTP_MSGSIZE_NOAUTH) == -1
) {
close(p->p_fd);
p->p_fd = -1;
+ /*
+ * We know that we sent nothing.
+ * We can retry *soon* without fearing
+ * that we are flooding the peer.
+ */
set_next(p, RETRY_INTERVAL);
return;
}
- p->reachable_bits <<= 1;
set_next(p, RESPONSE_INTERVAL);
}
/* Note that there is no provision to prevent several run_scripts
- * to be done in quick succession. In fact, it happens rather often
+ * to be started in quick succession. In fact, it happens rather often
* if initial syncronization results in a step.
* You will see "step" and then "stratum" script runs, sometimes
* as close as only 0.002 seconds apart.
char *argv[3];
char *env1, *env2, *env3, *env4;
+ G.last_script_run = G.cur_time;
+
if (!G.script_name)
return;
free(env2);
free(env3);
free(env4);
-
- G.last_script_run = G.cur_time;
}
static NOINLINE void
VERB2 {
tval = tvc.tv_sec;
- strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", localtime(&tval));
+ strftime_YYYYMMDDHHMMSS(buf, sizeof(buf), &tval);
bb_error_msg("current time is %s.%06u", buf, (unsigned)tvc.tv_usec);
}
tval = tvn.tv_sec;
- strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", localtime(&tval));
+ strftime_YYYYMMDDHHMMSS(buf, sizeof(buf), &tval);
bb_error_msg("setting time to %s.%06u (offset %+fs)", buf, (unsigned)tvn.tv_usec, offset);
/* Correct various fields which contain time-relative values: */
+ /* Globals: */
+ G.cur_time += offset;
+ G.last_update_recv_time += offset;
+ G.last_script_run += offset;
+
/* p->lastpkt_recv_time, p->next_action_time and such: */
for (item = G.ntp_peers; item != NULL; item = item->link) {
peer_t *pp = (peer_t *) item->data;
//bb_error_msg("offset:%+f pp->next_action_time:%f -> %f",
// offset, pp->next_action_time, pp->next_action_time + offset);
pp->next_action_time += offset;
+ if (pp->p_fd >= 0) {
+ /* We wait for reply from this peer too.
+ * But due to step we are doing, reply's data is no longer
+ * useful (in fact, it'll be bogus). Stop waiting for it.
+ */
+ close(pp->p_fd);
+ pp->p_fd = -1;
+ set_next(pp, RETRY_INTERVAL);
+ }
}
- /* Globals: */
- G.cur_time += offset;
- G.last_update_recv_time += offset;
- G.last_script_run += offset;
}
ssize_t size;
msg_t msg;
double T1, T2, T3, T4;
+ double dv;
unsigned interval;
datapoint_t *datapoint;
peer_t *q;
) {
//TODO: always do this?
interval = retry_interval();
- goto set_next_and_close_sock;
+ goto set_next_and_ret;
}
xfunc_die();
}
if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE) {
bb_error_msg("malformed packet received from %s", p->p_dotted);
- goto bail;
+ return;
}
if (msg.m_orgtime.int_partl != p->p_xmt_msg.m_xmttime.int_partl
|| msg.m_orgtime.fractionl != p->p_xmt_msg.m_xmttime.fractionl
) {
- goto bail;
+ /* Somebody else's packet */
+ return;
}
+ /* We do not expect any more packets from this peer for now.
+ * Closing the socket informs kernel about it.
+ * We open a new socket when we send a new query.
+ */
+ close(p->p_fd);
+ p->p_fd = -1;
+
if ((msg.m_status & LI_ALARM) == LI_ALARM
|| msg.m_stratum == 0
|| msg.m_stratum > NTP_MAXSTRATUM
// TODO: stratum 0 responses may have commands in 32-bit m_refid field:
// "DENY", "RSTR" - peer does not like us at all
// "RATE" - peer is overloaded, reduce polling freq
- interval = poll_interval(0);
- bb_error_msg("reply from %s: not synced, next query in %us", p->p_dotted, interval);
- goto set_next_and_close_sock;
+ bb_error_msg("reply from %s: peer is unsynced", p->p_dotted);
+ goto pick_normal_interval;
}
// /* Verify valid root distance */
T4 = G.cur_time;
p->lastpkt_recv_time = T4;
-
VERB5 bb_error_msg("%s->lastpkt_recv_time=%f", p->p_dotted, p->lastpkt_recv_time);
- p->datapoint_idx = p->reachable_bits ? (p->datapoint_idx + 1) % NUM_DATAPOINTS : 0;
- datapoint = &p->filter_datapoint[p->datapoint_idx];
- datapoint->d_recv_time = T4;
- datapoint->d_offset = ((T2 - T1) + (T3 - T4)) / 2;
+
/* The delay calculation is a special case. In cases where the
* server and client clocks are running at different rates and
* with very fast networks, the delay can appear negative. In
* order to avoid violating the Principle of Least Astonishment,
* the delay is clamped not less than the system precision.
*/
+ dv = p->lastpkt_delay;
p->lastpkt_delay = (T4 - T1) - (T3 - T2);
if (p->lastpkt_delay < G_precision_sec)
p->lastpkt_delay = G_precision_sec;
+ /*
+ * If this packet's delay is much bigger than the last one,
+ * it's better to just ignore it than use its much less precise value.
+ */
+ if (p->reachable_bits && p->lastpkt_delay > dv * BAD_DELAY_GROWTH) {
+ bb_error_msg("reply from %s: delay %f is too high, ignoring", p->p_dotted, p->lastpkt_delay);
+ goto pick_normal_interval;
+ }
+
+ p->datapoint_idx = p->reachable_bits ? (p->datapoint_idx + 1) % NUM_DATAPOINTS : 0;
+ datapoint = &p->filter_datapoint[p->datapoint_idx];
+ datapoint->d_recv_time = T4;
+ datapoint->d_offset = ((T2 - T1) + (T3 - T4)) / 2;
datapoint->d_dispersion = LOG2D(msg.m_precision_exp) + G_precision_sec;
if (!p->reachable_bits) {
/* 1st datapoint ever - replicate offset in every element */
p->reachable_bits |= 1;
if ((MAX_VERBOSE && G.verbose) || (option_mask32 & OPT_w)) {
- bb_error_msg("reply from:%s offset:%+f delay:%f status:0x%02x strat:%d refid:0x%08x rootdelay:%f reach:0x%02x",
+ bb_error_msg("reply from %s: offset:%+f delay:%f status:0x%02x strat:%d refid:0x%08x rootdelay:%f reach:0x%02x",
p->p_dotted,
datapoint->d_offset,
p->lastpkt_delay,
}
/* Decide when to send new query for this peer */
+ pick_normal_interval:
interval = poll_interval(0);
- set_next_and_close_sock:
+ set_next_and_ret:
set_next(p, interval);
- /* We do not expect any more packets from this peer for now.
- * Closing the socket informs kernel about it.
- * We open a new socket when we send a new query.
- */
- close(p->p_fd);
- p->p_fd = -1;
- bail:
- return;
}
#if ENABLE_FEATURE_NTPD_SERVER
uint8_t query_status;
l_fixedpt_t query_xmttime;
- to = get_sock_lsa(G.listen_fd);
+ to = get_sock_lsa(G_listen_fd);
from = xzalloc(to->len);
- size = recv_from_to(G.listen_fd, &msg, sizeof(msg), MSG_DONTWAIT, from, &to->u.sa, to->len);
+ size = recv_from_to(G_listen_fd, &msg, sizeof(msg), MSG_DONTWAIT, from, &to->u.sa, to->len);
if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE) {
char *addr;
if (size < 0) {
/* Build a reply packet */
memset(&msg, 0, sizeof(msg));
- msg.m_status = G.stratum < MAXSTRAT ? G.ntp_status : LI_ALARM;
+ msg.m_status = G.stratum < MAXSTRAT ? (G.ntp_status & LI_MASK) : LI_ALARM;
msg.m_status |= (query_status & VERSION_MASK);
msg.m_status |= ((query_status & MODE_MASK) == MODE_CLIENT) ?
- MODE_SERVER : MODE_SYM_PAS;
+ MODE_SERVER : MODE_SYM_PAS;
msg.m_stratum = G.stratum;
msg.m_ppoll = G.poll_exp;
msg.m_precision_exp = G_precision_exp;
/* We reply from the local address packet was sent to,
* this makes to/from look swapped here: */
- do_sendto(G.listen_fd,
+ do_sendto(G_listen_fd,
/*from:*/ &to->u.sa, /*to:*/ from, /*addrlen:*/ to->len,
&msg, size);
logmode = LOGMODE_NONE;
}
#if ENABLE_FEATURE_NTPD_SERVER
- G.listen_fd = -1;
+ G_listen_fd = -1;
if (opts & OPT_l) {
- G.listen_fd = create_and_bind_dgram_or_die(NULL, 123);
- socket_want_pktinfo(G.listen_fd);
- setsockopt(G.listen_fd, IPPROTO_IP, IP_TOS, &const_IPTOS_LOWDELAY, sizeof(const_IPTOS_LOWDELAY));
+ G_listen_fd = create_and_bind_dgram_or_die(NULL, 123);
+ socket_want_pktinfo(G_listen_fd);
+ setsockopt(G_listen_fd, IPPROTO_IP, IP_TOS, &const_IPTOS_LOWDELAY, sizeof(const_IPTOS_LOWDELAY));
}
#endif
/* I hesitate to set -20 prio. -15 should be high enough for timekeeping */
*/
cnt = G.peer_cnt * (INITIAL_SAMPLES + 1);
+ write_pidfile(CONFIG_PID_FILE_PATH "/ntpd.pid");
+
while (!bb_got_signal) {
llist_t *item;
unsigned i, j;
i = 0;
#if ENABLE_FEATURE_NTPD_SERVER
- if (G.listen_fd != -1) {
- pfd[0].fd = G.listen_fd;
+ if (G_listen_fd != -1) {
+ pfd[0].fd = G_listen_fd;
pfd[0].events = POLLIN;
i++;
}
/* Here we may block */
VERB2 {
- if (i > (ENABLE_FEATURE_NTPD_SERVER && G.listen_fd != -1)) {
+ if (i > (ENABLE_FEATURE_NTPD_SERVER && G_listen_fd != -1)) {
/* We wait for at least one reply.
* Poll for it, without wasting time for message.
* Since replies often come under 1 second, this also
did_poll:
gettime1900d(); /* sets G.cur_time */
if (nfds <= 0) {
- if (G.script_name && G.cur_time - G.last_script_run > 11*60) {
+ if (!bb_got_signal /* poll wasn't interrupted by a signal */
+ && G.cur_time - G.last_script_run > 11*60
+ ) {
/* Useful for updating battery-backed RTC and such */
run_script("periodic", G.last_update_offset);
gettime1900d(); /* sets G.cur_time */
}
- continue;
+ goto check_unsync;
}
/* Process any received packets */
gettime1900d(); /* sets G.cur_time */
}
}
+
+ check_unsync:
+ if (G.ntp_peers && G.stratum != MAXSTRAT) {
+ for (item = G.ntp_peers; item != NULL; item = item->link) {
+ peer_t *p = (peer_t *) item->data;
+ if (p->reachable_bits)
+ goto have_reachable_peer;
+ }
+ /* No peer responded for last 8 packets, panic */
+ G.polladj_count = 0;
+ G.poll_exp = MINPOLL;
+ G.stratum = MAXSTRAT;
+ run_script("unsync", 0.0);
+ have_reachable_peer: ;
+ }
} /* while (!bb_got_signal) */
+ remove_pidfile(CONFIG_PID_FILE_PATH "/ntpd.pid");
kill_myself_with_sig(bb_got_signal);
}
if (pps_enable) {
if (!(pll_status & STA_PPSTIME))
report_event(EVNT_KERN,
- NULL, "PPS enabled");
+ NULL, "PPS enabled");
ntv.status |= STA_PPSTIME | STA_PPSFREQ;
} else {
if (pll_status & STA_PPSTIME)
report_event(EVNT_KERN,
- NULL, "PPS disabled");
- ntv.status &= ~(STA_PPSTIME |
- STA_PPSFREQ);
+ NULL, "PPS disabled");
+ ntv.status &= ~(STA_PPSTIME | STA_PPSFREQ);
}
if (sys_leap == LEAP_ADDSECOND)
ntv.status |= STA_INS;
if (ntp_adjtime(&ntv) == TIME_ERROR) {
if (!(ntv.status & STA_PPSSIGNAL))
report_event(EVNT_KERN, NULL,
- "PPS no signal");
+ "PPS no signal");
}
pll_status = ntv.status;
#ifdef STA_NANO