Hi all, I just finished the NTP client/server applet. Would it be possible to merge it to the main repository, please?
Comments are welcomed. Regards, Adam -- Adam Tkac
>From 85b59ab5e4ef83cc04e02995841167ab85e6bd77 Mon Sep 17 00:00:00 2001 From: Adam Tkac <[email protected]> Date: Tue, 3 Nov 2009 17:26:33 +0100 Subject: [PATCH] Implement the NTPD applet. --- include/applets.h | 1 + include/usage.h | 10 + networking/Config.in | 14 + networking/Kbuild | 1 + networking/ntpd.c | 976 ++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1002 insertions(+), 0 deletions(-) create mode 100644 networking/ntpd.c diff --git a/include/applets.h b/include/applets.h index 15ccf9c..a2d1e95 100644 --- a/include/applets.h +++ b/include/applets.h @@ -285,6 +285,7 @@ IF_NICE(APPLET(nice, _BB_DIR_BIN, _BB_SUID_DROP)) IF_NMETER(APPLET(nmeter, _BB_DIR_USR_BIN, _BB_SUID_DROP)) IF_NOHUP(APPLET(nohup, _BB_DIR_USR_BIN, _BB_SUID_DROP)) IF_NSLOOKUP(APPLET(nslookup, _BB_DIR_USR_BIN, _BB_SUID_DROP)) +IF_NTPD(APPLET(ntpd, _BB_DIR_USR_SBIN, _BB_SUID_DROP)) IF_OD(APPLET(od, _BB_DIR_USR_BIN, _BB_SUID_DROP)) IF_OPENVT(APPLET(openvt, _BB_DIR_USR_BIN, _BB_SUID_DROP)) //IF_PARSE(APPLET(parse, _BB_DIR_USR_BIN, _BB_SUID_DROP)) diff --git a/include/usage.h b/include/usage.h index eae9650..0b41a16 100644 --- a/include/usage.h +++ b/include/usage.h @@ -3210,6 +3210,16 @@ "Name: debian\n" \ "Address: 127.0.0.1\n" +#define ntpd_trivial_usage \ + "[-f] [-l] [-p peer_address] [-s]" +#define ntpd_full_usage "\n\n" \ + "NTP client/server\n" \ + "\nOptions:" \ + "\n -f Run on foreground, not as a daemon" \ + "\n -l Listen port 123" \ + "\n -p addr Query NTP server(s) listenning on <addr>" \ + "\n -s Set system date even if it is too wrong" + #define od_trivial_usage \ "[-aBbcDdeFfHhIiLlOovXx] " IF_DESKTOP("[-t TYPE] ") "[FILE]" #define od_full_usage "\n\n" \ diff --git a/networking/Config.in b/networking/Config.in index 5f1b6f6..3d29622 100644 --- a/networking/Config.in +++ b/networking/Config.in @@ -661,6 +661,20 @@ config NSLOOKUP help nslookup is a tool to query Internet name servers. +config NTPD + bool "ntpd" + default n + help + The NTP client/server daemon. + +config FEATURE_NTPD_SERVER + bool "Make ntpd usable as a NTP server" + default y + depends on NTPD + help + Make ntpd usable as a NTP server. If you disable this option + ntpd will be usable only as a NTP client. + config PING bool "ping" default n diff --git a/networking/Kbuild b/networking/Kbuild index 866d42f..b0765bc 100644 --- a/networking/Kbuild +++ b/networking/Kbuild @@ -27,6 +27,7 @@ lib-$(CONFIG_NAMEIF) += nameif.o lib-$(CONFIG_NC) += nc.o lib-$(CONFIG_NETSTAT) += netstat.o lib-$(CONFIG_NSLOOKUP) += nslookup.o +lib-$(CONFIG_NTPD) += ntpd.o lib-$(CONFIG_PING) += ping.o lib-$(CONFIG_PING6) += ping.o lib-$(CONFIG_PSCAN) += pscan.o diff --git a/networking/ntpd.c b/networking/ntpd.c new file mode 100644 index 0000000..dd010c9 --- /dev/null +++ b/networking/ntpd.c @@ -0,0 +1,976 @@ +/* + * NTP client/server, based on OpenNTPD 3.9p1 + * + * Author: Adam Tkac <[email protected]> + * + * Licensed under GPLv2, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" +#include <netinet/ip.h> /* For IPTOS_LOWDELAY definition */ + +#ifndef IP_PKTINFO +#error "Sorry, your kernel has to support IP_PKTINFO" +#endif + +#define INTERVAL_QUERY_NORMAL 30 /* sync to peers every n secs */ +#define INTERVAL_QUERY_PATHETIC 60 +#define INTERVAL_QUERY_AGRESSIVE 5 + +#define TRUSTLEVEL_BADPEER 6 +#define TRUSTLEVEL_PATHETIC 2 +#define TRUSTLEVEL_AGRESSIVE 8 +#define TRUSTLEVEL_MAX 10 + +#define QSCALE_OFF_MIN 0.05 +#define QSCALE_OFF_MAX 0.50 + +#define QUERYTIME_MAX 15 /* single query might take n secs max */ +#define OFFSET_ARRAY_SIZE 8 +#define SETTIME_MIN_OFFSET 180 /* min offset for settime at start */ +#define SETTIME_TIMEOUT 15 /* max seconds to wait with -s */ + +/* Style borrowed from NTP ref/tcpdump and updated for SNTPv4 (RFC2030). */ + +/* + * RFC Section 3 + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Integer Part | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Fraction Part | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Integer Part | Fraction Part | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ +typedef struct { + uint32_t int_partl; + uint32_t fractionl; +} l_fixedpt_t; + +typedef struct { + uint16_t int_parts; + uint16_t fractions; +} s_fixedpt_t; + +#define NTP_DIGESTSIZE 16 +#define NTP_MSGSIZE_NOAUTH 48 +#define NTP_MSGSIZE (NTP_MSGSIZE_NOAUTH + 4 + NTP_DIGESTSIZE) + +typedef struct { + uint8_t status; /* status of local clock and leap info */ + uint8_t stratum; /* Stratum level */ + uint8_t ppoll; /* poll value */ + int8_t precision; + s_fixedpt_t rootdelay; + s_fixedpt_t dispersion; + uint32_t refid; + l_fixedpt_t reftime; + l_fixedpt_t orgtime; + l_fixedpt_t rectime; + l_fixedpt_t xmttime; + uint32_t keyid; + uint8_t digest[NTP_DIGESTSIZE]; +} ntp_msg_t; + +typedef struct { + int fd; + ntp_msg_t msg; + double xmttime; +} ntp_query_t; + +enum { + NTP_VERSION = 4, + NTP_MAXSTRATUM = 15, + /* Leap Second Codes (high order two bits) */ + LI_NOWARNING = (0 << 6), /* no warning */ + LI_PLUSSEC = (1 << 6), /* add a second (61 seconds) */ + LI_MINUSSEC = (2 << 6), /* minus a second (59 seconds) */ + LI_ALARM = (3 << 6), /* alarm condition */ + + /* Status Masks */ + MODE_MASK = (7 << 0), + VERSION_MASK = (7 << 3), + LI_MASK = (3 << 6), + + /* Mode values */ + MODE_RES0 = 0, /* reserved */ + MODE_SYM_ACT = 1, /* symmetric active */ + MODE_SYM_PAS = 2, /* symmetric passive */ + MODE_CLIENT = 3, /* client */ + MODE_SERVER = 4, /* server */ + MODE_BROADCAST = 5, /* broadcast */ + MODE_RES1 = 6, /* reserved for NTP control message */ + MODE_RES2 = 7 /* reserved for private use */ +}; + +#define JAN_1970 2208988800UL /* 1970 - 1900 in seconds */ + + +enum client_state { + STATE_NONE, + STATE_QUERY_SENT, + STATE_REPLY_RECEIVED +}; + +typedef struct { + double rootdelay; + double rootdispersion; + double reftime; + uint32_t refid; + uint32_t refid4; + uint8_t synced; + uint8_t leap; + int8_t precision; + uint8_t poll; + uint8_t stratum; +} ntp_status_t; + +typedef struct { + ntp_status_t status; + double offset; + double delay; + double error; + time_t rcvd; + uint8_t good; +} ntp_offset_t; + +typedef struct { + len_and_sockaddr *lsa; + ntp_query_t query; + ntp_offset_t reply[OFFSET_ARRAY_SIZE]; + ntp_offset_t update; + enum client_state state; + time_t next; + time_t deadline; + uint8_t shift; + uint8_t trustlevel; +} ntp_peer_t; + +typedef struct { +#if ENABLE_FEATURE_NTPD_SERVER + int listen_fd; +#endif + llist_t *ntp_peers; + ntp_status_t status; + uint8_t settime; + uint8_t foreground; + uint32_t scale; + uint8_t firstadj; + smallint peer_cnt; +} ntpd_conf_t; + +static void ntp_main(void); + +static ntpd_conf_t *G; +volatile sig_atomic_t quit = 0; + +static void +set_next(ntp_peer_t *p, time_t t) +{ + p->next = time(NULL) + t; + p->deadline = 0; +} + +static void +add_peers(const char *s) +{ + struct addrinfo hints, *res0, *res; + int error; + ntp_peer_t *p; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; /* DUMMY */ + error = getaddrinfo(s, NULL, &hints, &res0); + if (error) + bb_error_msg_and_die("DNS resolution failed for " + "peer %s", optarg); + + for (res = res0; res != NULL; res = res->ai_next) { + if (res->ai_family != AF_INET +#if ENABLE_FEATURE_IPV6 + && res->ai_family != AF_INET6 +#endif + ) + continue; + + p = xzalloc(sizeof(*p)); + p->lsa = xzalloc(sizeof(*p->lsa)); + p->lsa->len = +#if ENABLE_FEATURE_IPV6 + res->ai_family == AF_INET6 ? + sizeof(struct sockaddr_in6) : +#endif + sizeof(struct sockaddr_in); + + memcpy(&p->lsa->u.sa, res->ai_addr, p->lsa->len); + p->lsa->u.sa.sa_family = res->ai_family; + set_nport(p->lsa, htons(123)); + + p->query.fd = -1; + p->query.msg.status = MODE_CLIENT | (NTP_VERSION << 3); + p->state = STATE_NONE; + p->trustlevel = TRUSTLEVEL_PATHETIC; + p->query.fd = -1; + set_next(p, 0); + + llist_add_to(&G->ntp_peers, p); + G->peer_cnt++; + } + freeaddrinfo(res0); +} + +static double +gettime(void) +{ + struct timeval tv; + + if (gettimeofday(&tv, NULL) == -1) + bb_error_msg_and_die("gettimeofday"); + + return (tv.tv_sec + JAN_1970 + 1.0e-6 * tv.tv_usec); +} + + +static void +d_to_tv(double d, struct timeval *tv) +{ + tv->tv_sec = (long)d; + tv->tv_usec = (d - tv->tv_sec) * 1000000; +} + +static double +lfp_to_d(l_fixedpt_t lfp) +{ + double ret; + + lfp.int_partl = ntohl(lfp.int_partl); + lfp.fractionl = ntohl(lfp.fractionl); + + ret = (double)(lfp.int_partl) + ((double)lfp.fractionl / UINT_MAX); + + return (ret); +} + +#if ENABLE_FEATURE_NTPD_SERVER +static l_fixedpt_t +d_to_lfp(double d) +{ + l_fixedpt_t lfp; + + lfp.int_partl = htonl((uint32_t)d); + lfp.fractionl = htonl((uint32_t)((d - (u_int32_t)d) * UINT_MAX)); + + return (lfp); +} +#endif + +static double +sfp_to_d(s_fixedpt_t sfp) +{ + double ret; + + sfp.int_parts = ntohs(sfp.int_parts); + sfp.fractions = ntohs(sfp.fractions); + + ret = (double)(sfp.int_parts) + ((double)sfp.fractions / USHRT_MAX); + + return (ret); +} + +#if ENABLE_FEATURE_NTPD_SERVER +static s_fixedpt_t +d_to_sfp(double d) +{ + s_fixedpt_t sfp; + + sfp.int_parts = htons((uint16_t)d); + sfp.fractions = htons((uint16_t)((d - (u_int16_t)d) * USHRT_MAX)); + + return (sfp); +} +#endif + +enum { + OPT_f = (1 << 0), + OPT_l = (1 << 1), + OPT_p = (1 << 2), + OPT_s = (1 << 3) +}; + +int ntpd_main(int argc UNUSED_PARAM, char *argv[]) MAIN_EXTERNALLY_VISIBLE; +int ntpd_main(int argc UNUSED_PARAM, char *argv[]) +{ + ntpd_conf_t conf; +#if ENABLE_FEATURE_NTPD_SERVER + int tos = IPTOS_LOWDELAY; +#endif + smallint opts; + llist_t *peers = NULL; + + G = &conf; + + memset(&conf, 0, sizeof(conf)); + + logmode = LOGMODE_STDIO; + tzset(); + +#if ENABLE_FEATURE_NTPD_SERVER + conf.listen_fd = -1; +#endif + conf.firstadj = 1; + + opt_complementary = "p::"; + + opts = getopt32(argv, "f" IF_FEATURE_NTPD_SERVER("l") "p:s", &peers); + + if (opts & OPT_f) + conf.foreground = 1; +#if ENABLE_FEATURE_NTPD_SERVER + if (opts & OPT_l) { + conf.listen_fd = create_and_bind_dgram_or_die("0.0.0.0", 123); + socket_want_pktinfo(conf.listen_fd); + setsockopt(conf.listen_fd, IPPROTO_IP, IP_TOS, &tos, + sizeof(tos)); + } +#endif + while (peers) + add_peers(llist_pop(&peers)); + if (opts & OPT_s) + conf.settime = 1; + + if (getuid()) { + fprintf(stderr, "ntpd: need root privileges\n"); + exit(1); + } + + if (!conf.foreground) { + logmode = LOGMODE_NONE; + bb_daemonize(DAEMON_DEVNULL_STDIO); + } + + ntp_main(); + + return (0); +} + +static void +set_deadline(ntp_peer_t *p, time_t t) +{ + p->deadline = time(NULL) + t; + p->next = 0; +} + +static time_t +error_interval(void) +{ + time_t interval, r; + + interval = INTERVAL_QUERY_PATHETIC * QSCALE_OFF_MAX / QSCALE_OFF_MIN; + r = random() % (interval / 10); + return (interval + r); +} + +static int +sendmsg_wrap(int fd, const len_and_sockaddr *from, const len_and_sockaddr *to, + ntp_msg_t *msg, ssize_t len) +{ + ssize_t ret; + + if (from == NULL) { + ret = sendto(fd, msg, len, 0, &to->u.sa, to->len); + } else { + ret = send_to_from(fd, msg, len, 0, &to->u.sa, &from->u.sa, + to->len); + } + + if (ret != len) { + bb_error_msg("ntp_sendmsg failed"); + return (-1); + } + + return (0); +} +static int +client_query(ntp_peer_t *p) +{ + int tos = IPTOS_LOWDELAY; + + if (p->query.fd == -1) { + p->query.fd = xsocket(p->lsa->u.sa.sa_family, SOCK_DGRAM, 0); +#if ENABLE_FEATURE_IPV6 + if (p->lsa->u.sa.sa_family == AF_INET) +#endif + setsockopt(p->query.fd, IPPROTO_IP, IP_TOS, &tos, + sizeof(tos)); + } + + /* + * Send out a random 64-bit number as our transmit time. The NTP + * server will copy said number into the originate field on the + * response that it sends us. This is totally legal per the SNTP spec. + * + * The impact of this is two fold: we no longer send out the current + * system time for the world to see (which may aid an attacker), and + * it gives us a (not very secure) way of knowing that we're not + * getting spoofed by an attacker that can't capture our traffic + * but can spoof packets from the NTP server we're communicating with. + * + * Save the real transmit timestamp locally. + */ + + p->query.msg.xmttime.int_partl = random(); + p->query.msg.xmttime.fractionl = random(); + p->query.xmttime = gettime(); + + if (sendmsg_wrap(p->query.fd, NULL, p->lsa, + &p->query.msg, NTP_MSGSIZE_NOAUTH) == -1) { + set_next(p, INTERVAL_QUERY_PATHETIC); + return (-1); + } + + p->state = STATE_QUERY_SENT; + set_deadline(p, QUERYTIME_MAX); + + return (0); +} + +static int +offset_compare(const void *aa, const void *bb) +{ + const ntp_peer_t * const *a; + const ntp_peer_t * const *b; + + a = aa; + b = bb; + + if ((*a)->update.offset < (*b)->update.offset) + return (-1); + else if ((*a)->update.offset > (*b)->update.offset) + return (1); + else + return (0); +} + +static void +update_scale(double offset) +{ + if (offset < 0) + offset = -offset; + + if (offset > QSCALE_OFF_MAX) + G->scale = 1; + else if (offset < QSCALE_OFF_MIN) + G->scale = QSCALE_OFF_MAX / QSCALE_OFF_MIN; + else + G->scale = QSCALE_OFF_MAX / offset; +} + +static void +adjtime_wrap(void) +{ + ntp_peer_t *p; + int offset_cnt = 0, i = 0; + ntp_peer_t **peers; + double offset_median; + llist_t *item; + len_and_sockaddr *lsa; + struct timeval tv, olddelta; + + + for (item = G->ntp_peers; item != NULL; item = item->link) { + p = (ntp_peer_t *) item->data; + if (p->trustlevel < TRUSTLEVEL_BADPEER) + continue; + if (!p->update.good) + return; + offset_cnt++; + } + + peers = xzalloc(sizeof(ntp_peer_t *) * offset_cnt); + for (item = G->ntp_peers; item != NULL; item = item->link) { + p = (ntp_peer_t *) item->data; + if (p->trustlevel < TRUSTLEVEL_BADPEER) + continue; + peers[i++] = p; + } + + qsort(peers, offset_cnt, sizeof(ntp_peer_t *), offset_compare); + + if (offset_cnt > 0) { + if (offset_cnt > 1 && offset_cnt % 2 == 0) { + offset_median = + (peers[offset_cnt / 2 - 1]->update.offset + + peers[offset_cnt / 2]->update.offset) / 2; + G->status.rootdelay = + (peers[offset_cnt / 2 - 1]->update.delay + + peers[offset_cnt / 2]->update.delay) / 2; + G->status.stratum = MAX( + peers[offset_cnt / 2 - 1]->update.status.stratum, + peers[offset_cnt / 2]->update.status.stratum); + } else { + offset_median = peers[offset_cnt / 2]->update.offset; + G->status.rootdelay = + peers[offset_cnt / 2]->update.delay; + G->status.stratum = + peers[offset_cnt / 2]->update.status.stratum; + } + G->status.leap = peers[offset_cnt / 2]->update.status.leap; + + bb_info_msg("adjusting local clock by %fs", offset_median); + + d_to_tv(offset_median, &tv); + if (adjtime(&tv, &olddelta) == -1) + bb_error_msg("adjtime failed"); + else if (!G->firstadj && olddelta.tv_sec == 0 && + olddelta.tv_usec == 0 && !G->status.synced) { + bb_info_msg("clock synced"); + G->status.synced = 1; + } else if (G->status.synced) { + bb_info_msg("clock unsynced"); + G->status.synced = 0; + } + + G->firstadj = 0; + G->status.reftime = gettime(); + G->status.stratum++; /* one more than selected peer */ + update_scale(offset_median); + + G->status.refid4 = + peers[offset_cnt / 2]->update.status.refid4; + + lsa = peers[offset_cnt / 2]->lsa; + G->status.refid = +#if ENABLE_FEATURE_IPV6 + lsa->u.sa.sa_family == AF_INET6 ? + G->status.refid4 : +#endif + lsa->u.sin.sin_addr.s_addr; + } + + free(peers); + + for (item = G->ntp_peers; item != NULL; item = item->link) { + p = (ntp_peer_t *) item->data; + p->update.good = 0; + } +} + +static void +settime(double offset) +{ + ntp_peer_t *p; + llist_t *item; + struct timeval tv, curtime; + char buf[80]; + time_t tval; + +#if 0 + if (!G->settime) + return; +#endif + + /* if the offset is small, don't call settimeofday */ + if (offset < SETTIME_MIN_OFFSET && offset > -SETTIME_MIN_OFFSET) + return; + + if (gettimeofday(&curtime, NULL) == -1) { + bb_error_msg("gettimeofday"); + return; + } + + d_to_tv(offset, &tv); + curtime.tv_usec += tv.tv_usec + 1000000; + curtime.tv_sec += tv.tv_sec - 1 + (curtime.tv_usec / 1000000); + curtime.tv_usec %= 1000000; + + if (settimeofday(&curtime, NULL) == -1) { + bb_error_msg("settimeofday"); + return; + } + + G->settime = 0; + + tval = curtime.tv_sec; + strftime(buf, sizeof(buf), "%a %b %e %H:%M:%S %Z %Y", + localtime(&tval)); + + /* Do we want to print message below to system log when daemonized? */ + bb_info_msg("set local clock to %s (offset %fs)", buf, offset); + + for (item = G->ntp_peers; item != NULL; item = item->link) { + p = (ntp_peer_t *) item->data; + if (p->next) + p->next -= offset; + if (p->deadline) + p->deadline -= offset; + } +} + +static void +client_update(ntp_peer_t *p) +{ + int i, best = 0, good = 0; + + /* + * clock filter + * find the offset which arrived with the lowest delay + * use that as the peer update + * invalidate it and all older ones + */ + + for (i = 0; good == 0 && i < OFFSET_ARRAY_SIZE; i++) + if (p->reply[i].good) { + good++; + best = i; + } + + for (; i < OFFSET_ARRAY_SIZE; i++) + if (p->reply[i].good) { + good++; + if (p->reply[i].delay < p->reply[best].delay) + best = i; + } + + if (good < 8) + return; + + memcpy(&p->update, &p->reply[best], sizeof(p->update)); + adjtime_wrap(); + + for (i = 0; i < OFFSET_ARRAY_SIZE; i++) + if (p->reply[i].rcvd <= p->reply[best].rcvd) + p->reply[i].good = 0; +} + +static time_t +scale_interval(time_t requested) +{ + time_t interval, r; + + interval = requested * G->scale; + r = random() % MAX(5, interval / 10); + return (interval + r); +} + +static void +client_dispatch(ntp_peer_t *p) +{ + char *addr; + ssize_t size; + ntp_msg_t msg; + double T1, T2, T3, T4; + time_t interval; + ntp_offset_t *offset; + + addr = xmalloc_sockaddr2dotted_noport(&p->lsa->u.sa); + + if ((size = recvfrom(p->query.fd, &msg, sizeof(msg), 0, + NULL, NULL)) == -1) { + if (errno == EHOSTUNREACH || errno == EHOSTDOWN || + errno == ENETUNREACH || errno == ENETDOWN || + errno == ECONNREFUSED || errno == EADDRNOTAVAIL) { + bb_error_msg("recvfrom failed %s", addr); + set_next(p, error_interval()); + return; + } else + bb_error_msg_and_die("recvfrom"); + } + + T4 = gettime(); + + if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE) { + bb_error_msg("malformed packet received from %s", addr); + goto bail; + } + + if (msg.orgtime.int_partl != p->query.msg.xmttime.int_partl || + msg.orgtime.fractionl != p->query.msg.xmttime.fractionl) + goto bail; + + if ((msg.status & LI_ALARM) == LI_ALARM || msg.stratum == 0 || + msg.stratum > NTP_MAXSTRATUM) { + interval = error_interval(); + bb_info_msg("reply from %s: not synced, next query %ds", addr, + (int) interval); + goto bail; + } + + /* + * From RFC 2030 (with a correction to the delay math): + * + * Timestamp Name ID When Generated + * ------------------------------------------------------------ + * Originate Timestamp T1 time request sent by client + * Receive Timestamp T2 time request received by server + * Transmit Timestamp T3 time reply sent by server + * Destination Timestamp T4 time reply received by client + * + * The roundtrip delay d and local clock offset t are defined as + * + * d = (T4 - T1) - (T3 - T2) t = ((T2 - T1) + (T3 - T4)) / 2. + */ + + T1 = p->query.xmttime; + T2 = lfp_to_d(msg.rectime); + T3 = lfp_to_d(msg.xmttime); + + offset = &p->reply[p->shift]; + + offset->offset = ((T2 - T1) + (T3 - T4)) / 2; + offset->delay = (T4 - T1) - (T3 - T2); + if (offset->delay < 0) { + interval = error_interval(); + set_next(p, interval); + bb_info_msg("reply from %s: negative delay %f", addr, + p->reply[p->shift].delay); + goto bail; + } + offset->error = (T2 - T1) - (T3 - T4); + offset->rcvd = time(NULL); + offset->good = 1; + + offset->status.leap = (msg.status & LI_MASK); + offset->status.precision = msg.precision; + offset->status.rootdelay = sfp_to_d(msg.rootdelay); + offset->status.rootdispersion = sfp_to_d(msg.dispersion); + offset->status.refid = ntohl(msg.refid); + offset->status.refid4 = msg.xmttime.fractionl; + offset->status.reftime = lfp_to_d(msg.reftime); + offset->status.poll = msg.ppoll; + offset->status.stratum = msg.stratum; + + if (p->trustlevel < TRUSTLEVEL_PATHETIC) + interval = scale_interval(INTERVAL_QUERY_PATHETIC); + else if (p->trustlevel < TRUSTLEVEL_AGRESSIVE) + interval = scale_interval(INTERVAL_QUERY_AGRESSIVE); + else + interval = scale_interval(INTERVAL_QUERY_NORMAL); + + set_next(p, interval); + p->state = STATE_REPLY_RECEIVED; + + /* every received reply which we do not discard increases trust */ + if (p->trustlevel < TRUSTLEVEL_MAX) { + if (p->trustlevel < TRUSTLEVEL_BADPEER && + p->trustlevel + 1 >= TRUSTLEVEL_BADPEER) + bb_info_msg("peer %s now valid", addr); + p->trustlevel++; + } + + bb_info_msg("reply from %s: offset %f delay %f, next query %ds", addr, + offset->offset, offset->delay, (int) interval); + + client_update(p); + if (!G->settime) + settime(offset->offset); + + if (++p->shift >= OFFSET_ARRAY_SIZE) + p->shift = 0; + +bail: + free(addr); +} + +#if ENABLE_FEATURE_NTPD_SERVER +static void +server_dispatch(int fd) +{ + ssize_t size; + uint8_t version; + double rectime; + len_and_sockaddr *from, *to; + ntp_msg_t query, reply; + char *addr; + + to = get_sock_lsa(G->listen_fd); + from = xzalloc(LSA_LEN_SIZE + to->len); + + if ((size = recv_from_to(fd, &query, sizeof(query), 0, + &from->u.sa, &to->u.sa, to->len)) == -1) + bb_error_msg_and_die("recv_from_to"); + + rectime = gettime(); + + from->len = +#if ENABLE_FEATURE_IPV6 + (from->u.sa.sa_family == AF_INET6) ? + sizeof(struct sockaddr_in6) : +#endif + sizeof(struct sockaddr_in); + + addr = xmalloc_sockaddr2dotted_noport(&from->u.sa); + + if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE) { + bb_error_msg("malformed packet received from %s", addr); + goto bail; + } + + version = (query.status & VERSION_MASK) >> 3; + + memset(&reply, 0, sizeof(reply)); + reply.status = G->status.synced ? G->status.leap : LI_ALARM; + reply.status |= (query.status & VERSION_MASK); + reply.status |= ((query.status & MODE_MASK) == MODE_CLIENT) ? + MODE_SERVER : MODE_SYM_PAS; + reply.stratum = G->status.stratum; + reply.ppoll = query.ppoll; + reply.precision = G->status.precision; + reply.rectime = d_to_lfp(rectime); + reply.reftime = d_to_lfp(G->status.reftime); + reply.xmttime = d_to_lfp(gettime()); + reply.orgtime = query.xmttime; + reply.rootdelay = d_to_sfp(G->status.rootdelay); + reply.refid = (version > 3) ? G->status.refid4 : G->status.refid; + + sendmsg_wrap(fd, to, from, &reply, size); + +bail: + free(to); + free(from); + free(addr); +} +#endif + +static void +sighdlr(int sig) +{ + switch (sig) { + case SIGINT: + case SIGTERM: + quit = 1; + break; + } +} + +static void +ntp_main(void) +{ + int a, b, nfds, i, j, idx_peers, timeout; + uint new_cnt, sent_cnt, trial_cnt; + struct pollfd *pfd = NULL; + ntp_peer_t *p, **idx2peer = NULL; + time_t nextaction; + llist_t *item; + + bb_signals(SIGTERM|SIGINT, sighdlr); + bb_signals(SIGPIPE|SIGHUP, SIG_IGN); + +#if 0 + struct timespec tp; + + We can use sys_clock_getres but assuming 10ms tick should be fine. + clock_getres(CLOCK_REALTIME, &tp); + tp.tv_sec = 0; + tp.tv_nsec = 10000000; + + b = 1000000000 / tp.tv_nsec; /* convert to Hz */ +#else + b = 100; /* b = 1000000000/10000000 = 100 */ +#endif + for (a = 0; b > 1; a--, b >>= 1) + ; + G->status.precision = a; + G->scale = 1; + + idx2peer = xzalloc(sizeof(void *) * G->peer_cnt); + new_cnt = G->peer_cnt; + +#if ENABLE_FEATURE_NTPD_SERVER + if (G->listen_fd != -1) + new_cnt++; +#endif + + pfd = xzalloc(sizeof(struct pollfd) * new_cnt); + + while (quit == 0) { + nextaction = time(NULL) + 3600; + + i = 0; + +#if ENABLE_FEATURE_NTPD_SERVER + if (G->listen_fd != -1) { + pfd[0].fd = G->listen_fd; + pfd[0].events = POLLIN; + i++; + } +#endif + + idx_peers = i; + sent_cnt = trial_cnt = 0; + for (item = G->ntp_peers; item != NULL; item = item->link) { + p = (ntp_peer_t *) item->data; + + if (p->next > 0 && p->next <= time(NULL)) { + trial_cnt++; + if (client_query(p) == 0) + sent_cnt++; + } + if (p->next > 0 && p->next < nextaction) + nextaction = p->next; + + if (p->deadline > 0 && p->deadline < nextaction) + nextaction = p->deadline; + if (p->deadline > 0 && p->deadline <= time(NULL)) { + char *addr = xmalloc_sockaddr2dotted_noport( + &p->lsa->u.sa); + + timeout = error_interval(); + bb_info_msg("no reply from %s received in time, " + "next query %ds", addr, timeout); + if (p->trustlevel >= TRUSTLEVEL_BADPEER && + (p->trustlevel /= 2) < TRUSTLEVEL_BADPEER) + bb_info_msg("peer %s now invalid", addr); + free(addr); + + set_next(p, timeout); + } + + if (p->state == STATE_QUERY_SENT) { + pfd[i].fd = p->query.fd; + pfd[i].events = POLLIN; + idx2peer[i - idx_peers] = p; + i++; + } + } + + if (G->settime && + ((trial_cnt > 0 && sent_cnt == 0) || G->peer_cnt == 0)) + settime(0); /* no good peers, don't wait */ + + timeout = nextaction - time(NULL); + if (timeout < 0) + timeout = 0; +#if 0 + do { +#endif + nfds = poll(pfd, i, timeout * 1000); +#if 0 + } while (nfds == -1 && errno == ENOMEM); +#endif + +#if ENABLE_FEATURE_NTPD_SERVER + for (j = 0; nfds > 0 && j < idx_peers; j++) + if (pfd[j].revents & (POLLIN|POLLERR)) { + nfds--; + server_dispatch(pfd[j].fd); + } +#endif + + for (; nfds > 0 && j < i; j++) + if (pfd[j].revents & (POLLIN|POLLERR)) { + nfds--; + client_dispatch(idx2peer[j - idx_peers]); + } + } + + _exit(0); +} + -- 1.6.5.2
_______________________________________________ busybox mailing list [email protected] http://lists.busybox.net/mailman/listinfo/busybox
