#define CS_MRU_MAXDEPTH 29
#define CS_MRU_MEM 30
#define CS_MRU_MAXMEM 31
+#define CS_KOD_SENT 32
+#define CS_MAX_NOSSL CS_KOD_SENT
#ifdef OPENSSL
-#define CS_FLAGS 32
-#define CS_HOST 33
-#define CS_PUBLIC 34
-#define CS_CERTIF 35
-#define CS_SIGNATURE 36
-#define CS_REVTIME 37
-#define CS_GROUP 38
-#define CS_DIGEST 39
+#define CS_FLAGS (1 + CS_MAX_NOSSL)
+#define CS_HOST (2 + CS_MAX_NOSSL)
+#define CS_PUBLIC (3 + CS_MAX_NOSSL)
+#define CS_CERTIF (4 + CS_MAX_NOSSL)
+#define CS_SIGNATURE (5 + CS_MAX_NOSSL)
+#define CS_REVTIME (6 + CS_MAX_NOSSL)
+#define CS_GROUP (7 + CS_MAX_NOSSL)
+#define CS_DIGEST (8 + CS_MAX_NOSSL)
#define CS_MAXCODE CS_DIGEST
#else
-#define CS_MAXCODE CS_MRU_MAXMEM
+#define CS_MAXCODE CS_MAX_NOSSL
#endif /* OPENSSL */
/*
{ CS_MRU_MAXDEPTH, RO, "mru_maxdepth" },/* 29 */
{ CS_MRU_MEM, RO, "mru_mem" }, /* 30 */
{ CS_MRU_MAXMEM, RO, "mru_maxmem" }, /* 31 */
+ { CS_KOD_SENT, RO, "kod_sent" }, /* 32 */
#ifdef OPENSSL
- { CS_FLAGS, RO, "flags" }, /* 32 */
- { CS_HOST, RO, "host" }, /* 33 */
- { CS_PUBLIC, RO, "update" }, /* 34 */
- { CS_CERTIF, RO, "cert" }, /* 35 */
- { CS_SIGNATURE, RO, "signature" }, /* 36 */
- { CS_REVTIME, RO, "until" }, /* 37 */
- { CS_GROUP, RO, "group" }, /* 38 */
- { CS_DIGEST, RO, "digest" }, /* 39 */
+ { CS_FLAGS, RO, "flags" }, /* 33 */
+ { CS_HOST, RO, "host" }, /* 34 */
+ { CS_PUBLIC, RO, "update" }, /* 35 */
+ { CS_CERTIF, RO, "cert" }, /* 36 */
+ { CS_SIGNATURE, RO, "signature" }, /* 37 */
+ { CS_REVTIME, RO, "until" }, /* 38 */
+ { CS_GROUP, RO, "group" }, /* 39 */
+ { CS_DIGEST, RO, "digest" }, /* 40 */
#endif /* OPENSSL */
- { 0, EOV, "" } /* 32/40 */
+ { 0, EOV, "" } /* 33/41 */
};
static struct ctl_var *ext_sys_var = (struct ctl_var *)0;
ctl_putuint(sys_var[varid].text, u);
break;
+ case CS_KOD_SENT:
+ ctl_putint(sys_var[varid].text, sys_kodsent);
+ break;
+
+
#ifdef OPENSSL
case CS_FLAGS:
if (crypto_flags)
* newer neighbor, fetch the supplied entry, and
* in that case the #.last timestamp can be zero.
* This enables fetching a single entry by IP
- address.
+ * address.
+ * mincount= (decimal) return entries with count >= mincount.
+ * resall= 0x-prefixed hex restrict bits which must all be
+ * lit for an MRU entry to be included.
+ * Has precedence over any resany=.
+ * resany= 0x-prefixed hex restrict bits, at least one of
+ * which must be list for an MRU entry to be
+ * included.
* 0.last= 0x-prefixed hex l_fp timestamp of newest entry
* which client previously received.
* 0.addr= text of newest entry's IP address and port,
)
{
const char limit_text[] = "limit";
+ const char mincount_text[] = "mincount";
+ const char resall_text[] = "resall";
+ const char resany_text[] = "resany";
+ const char resaxx_fmt[] = "0x%hx";
const char addr_fmt[] = "addr.%d";
const char last_fmt[] = "last.%d";
const char first_fmt[] = "first.%d";
const char rs_fmt[] = "rs.%d";
const char l_fp_hexfmt[] = "0x%08x.%08x";
u_int limit;
+ u_short resall;
+ u_short resany;
+ int mincount;
u_int count;
u_int ui;
u_int uf;
int priors;
u_short hash;
mon_entry * mon;
+ mon_entry * prior_mon;
l_fp now;
/*
*/
in_parms = NULL;
set_var(&in_parms, limit_text, sizeof(limit_text), 0);
+ set_var(&in_parms, mincount_text, sizeof(mincount_text), 0);
+ set_var(&in_parms, resall_text, sizeof(resall_text), 0);
+ set_var(&in_parms, resany_text, sizeof(resany_text), 0);
for (i = 0; i < COUNTOF(last); i++) {
snprintf(buf, sizeof(buf), last_fmt, i);
set_var(&in_parms, buf, strlen(buf) + 1, 0);
/* decode input parms */
limit = 0;
+ mincount = 0;
+ resall = 0;
+ resany = 0;
priors = 0;
memset(last, 0, sizeof(last));
memset(addr, 0, sizeof(addr));
!(EOV & v->flags)) {
if (!strcmp(limit_text, v->text)) {
- if (1 != sscanf(val, "%u", &limit))
- limit = 0;
+ sscanf(val, "%u", &limit);
+ } else if (!strcmp(mincount_text, v->text)) {
+ if (1 != sscanf(val, "%d", &mincount) ||
+ mincount < 0)
+ mincount = 0;
+ } else if (!strcmp(resall_text, v->text)) {
+ sscanf(val, resaxx_fmt, &resall);
+ } else if (!strcmp(resany_text, v->text)) {
+ sscanf(val, resaxx_fmt, &resany);
} else if (1 == sscanf(v->text, last_fmt, &i) &&
i < COUNTOF(last)) {
if (2 == sscanf(val, l_fp_hexfmt, &ui, &uf)) {
mon = PREV_DLIST(mon_mru_list, mon, mru);
} else /* start with the oldest */
mon = TAIL_DLIST(mon_mru_list, mru);
-
+
+ prior_mon = NULL;
for (count = 0;
count < limit && mon != NULL;
- count++, mon = PREV_DLIST(mon_mru_list, mon, mru)) {
+ mon = PREV_DLIST(mon_mru_list, mon, mru)) {
+
+ if (mon->count < mincount)
+ continue;
+ if (resall && (resall != (resall & mon->flags)))
+ continue;
+ if (resany && !(resany & mon->flags))
+ continue;
snprintf(tag, sizeof(tag), addr_fmt, count);
pch = sptoa(&mon->rmtadr);
snprintf(tag, sizeof(tag), rs_fmt, count);
ctl_puthex(tag, mon->flags);
+
+ count++;
+ prior_mon = mon;
}
/*
* If this batch completes the MRU list (has the most recent),
* say so explicitly.
*/
if (NULL == mon) {
- mon = HEAD_DLIST(mon_mru_list, mru);
- if (mon != NULL) {
- get_systime(&now);
- snprintf(buf, sizeof(buf), l_fp_hexfmt,
- now.l_ui, now.l_uf);
- ctl_putunqstr("now", buf, strlen(buf));
+ get_systime(&now);
+ snprintf(buf, sizeof(buf), l_fp_hexfmt,
+ now.l_ui, now.l_uf);
+ ctl_putunqstr("now", buf, strlen(buf));
+ if (prior_mon != NULL) {
snprintf(buf, sizeof(buf), l_fp_hexfmt,
- mon->last.l_ui, mon->last.l_uf);
+ prior_mon->last.l_ui,
+ prior_mon->last.l_uf);
ctl_putunqstr("last.newest", buf, strlen(buf));
}
}
version = PKT_VERSION(pkt->li_vn_mode);
mon = mon_hash[hash];
+ /*
+ * We keep track of all traffic for a given IP in one entry,
+ * otherwise cron'ed ntpdate or similar evades RES_LIMITED.
+ */
+
for (; mon != NULL; mon = mon->hash_next)
- if (ADDR_PORT_EQ(&mon->rmtadr, &rbufp->recv_srcadr))
+ if (SOCK_EQ(&mon->rmtadr, &rbufp->recv_srcadr))
break;
if (mon != NULL) {
L_SUB(&interval_fp, &mon->last);
interval = interval_fp.l_i;
mon->last = rbufp->recv_time;
+ NSRCPORT(&mon->rmtadr) = NSRCPORT(&rbufp->recv_srcadr);
mon->count++;
restrict_mask = flags;
mon->vn_mode = VN_MODE(version, mode);
* ntp.conf controls. Similarly for "mru initalloc" and "mru
* initmem", and for "mru incalloc" and "mru incmem".
*/
- if (NULL == mon_free && mru_alloc < mru_mindepth) {
- mon_getmoremem();
+ if (mru_entries < mru_mindepth) {
+ if (NULL == mon_free)
+ mon_getmoremem();
UNLINK_HEAD_SLIST(mon, mon_free, hash_next);
} else {
oldest = TAIL_DLIST(mon_mru_list, mru);
day = now.l_ui / 86400 + MJD_1900;
now.l_ui %= 86400;
if (sysstats.fp != NULL) {
- fprintf(sysstats.fp,
+ fprintf(sysstats.fp,
"%lu %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
day, ulfptoa(&now, 3), current_time - sys_stattime,
sys_received, sys_processed, sys_newversion,
static void config_from_file(struct parse *, FILE *);
static void mrulist (struct parse *, FILE *);
-
/*
* Commands we understand. Ntpdc imports this.
*/
{ "config-from-file", config_from_file, { NTP_STR, NO, NO, NO },
{ "<configuration filename>", "", "", "" },
"configure ntpd using the configuration filename" },
- { "mrulist", mrulist, { OPT|NTP_STR, NO, NO, NO },
- { "<IP address>", "", "", "" },
- "display the list of most recently seen source addresses" },
+ { "mrulist", mrulist, { OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR },
+ { "tag=value", "", "", "" },
+ "display the list of most recently seen source addresses, tags mincount=... resall=0x... resany=0x..." },
{ 0, 0, { NO, NO, NO, NO },
{ "-4|-6", "", "", "" }, "" }
};
};
static mru mru_list; /* listhead */
static mru **hash_table;
-static mru * add_mru (mru *);
+
+/*
+ * other static function prototypes
+ */
+static mru * add_mru(mru *);
+static int collect_mru_list(const char *, l_fp *);
/*
/*
- * add_mru add and entry to mru list, hash table, and allocate
+ * add_mru Add and entry to mru list, hash table, and allocate
* and return a replacement.
+ * This is a helper for collect_mru_list().
*/
static mru *
add_mru(
mru *unlinked;
int first_ts_matches;
+ if (debug)
+ fprintf(stderr,
+ "add_mru %08x.%08x c %d m %d v %d rest %x first %08x.%08x %s\n",
+ add->last.l_ui, add->last.l_uf, add->count,
+ (int)add->mode, (int)add->ver, (u_int)add->rs,
+ add->first.l_ui, add->first.l_uf,
+ sptoa(&add->addr));
+
hash = NTP_HASH_ADDR(&add->addr);
/* see if we have it among previously received entries */
for (mon = hash_table[hash]; mon != NULL; mon = mon->hlink)
break;
if (mon != NULL) {
first_ts_matches = L_ISEQU(&add->first, &mon->first);
- NTP_INSIST(first_ts_matches);
+ if (!first_ts_matches) {
+ fprintf(stderr,
+ "add_mru duplicate %s first ts mismatch %08x.%08x expected %08x.%08x\n",
+ sptoa(&add->addr), add->last.l_ui,
+ add->last.l_uf, mon->last.l_ui,
+ mon->last.l_uf);
+ exit(1);
+
+ }
UNLINK_DLIST(mon, mlink);
UNLINK_SLIST(unlinked, hash_table[hash], mon, hlink, mru);
NTP_INSIST(unlinked == mon);
}
LINK_DLIST(mru_list, add, mlink);
LINK_SLIST(hash_table[hash], add, hlink);
- if (debug)
- fprintf(stderr,
- "add_mru %08x.%08x c %d m %d v %d rest %x first %08x.%08x %s\n",
- add->last.l_ui, add->last.l_uf, add->count,
- (int)add->mode, (int)add->ver, (u_int)add->rs,
- add->first.l_ui, add->first.l_uf,
- sptoa(&add->addr));
if (NULL == mon)
mon = emalloc(sizeof(*mon));
memset(mon, 0, sizeof(*mon));
}
-static void
-mrulist(
- struct parse *pcmd,
- FILE *fp
- )
-{
- u_char got; /* MRU_GOT_* bits */
+/* MGOT macro is specific to collect_mru_list() */
#define MGOT(bit) \
do { \
got |= (bit); \
ci++; \
} \
} while (0)
+
+
+static int
+collect_mru_list(
+ const char *parms,
+ l_fp * pnow
+ )
+{
+ int c_mru_l_rc; /* this function's return code */
+ u_char got; /* MRU_GOT_* bits */
const char ts_fmt[] = "0x%08x.%08x";
size_t cb;
mru *mon;
int ci; /* client (our) index for validation */
int ri; /* request index (.# suffix) */
int mv;
- l_fp now;
l_fp newest;
l_fp last_older;
- l_fp interval;
sockaddr_u addr_older;
- double favgint;
- double flstint;
- int avgint;
- int lstint;
int have_now;
int have_addr_older;
int have_last_older;
u_short hash;
mru *unlinked;
+ c_mru_l_rc = FALSE;
list_complete = FALSE;
- INIT_DLIST(mru_list, mlink);
- cb = NTP_HASH_SIZE * sizeof(*hash_table);
- hash_table = emalloc(cb);
- memset(hash_table, 0, cb);
+ have_now = FALSE;
+ got = 0;
+ ri = 0;
cb = sizeof(*mon);
mon = emalloc(cb);
memset(mon, 0, cb);
- memset(&now, 0, sizeof(now));
+ memset(pnow, 0, sizeof(*pnow));
memset(&last_older, 0, sizeof(last_older));
- have_now = FALSE;
- got = 0;
- ri = 0;
limit = 2;
- snprintf(req_buf, sizeof(req_buf), "limit=%d", limit);
+ snprintf(req_buf, sizeof(req_buf), "limit=%d%s", limit, parms);
while (TRUE) {
if (debug)
fprintf(stderr, "READ_MRU: %s\n", req_buf);
+ // if (debug) !!!!!
+ fprintf(stderr, "attempting next %d\n", limit);
+
qres = doqueryex(CTL_OP_READ_MRU, 0, 0, strlen(req_buf),
req_buf, &rstatus, &rsize, &rdata, TRUE);
NTP_INSIST(unlinked == recent);
free(recent);
}
+ } else if (ERR_INCOMPLETE == qres) {
+ /*
+ * Reduce the number of rows to minimize effect
+ * of single lost packets.
+ */
+ limit = max(2, limit * 2 / 3);
} else if (qres) {
show_error_msg(qres, 0);
- return;
+ goto cleanup_return;
}
+ /*
+ * This is a cheap cop-out implementation of rawmode
+ * output for mrulist. A better approach would be to
+ * dump similar output after the list is collected by
+ * ntpq with a continuous sequence of indexes. This
+ * cheap approach has indexes resetting to zero for
+ * each query/response, and duplicates are not
+ * coalesced.
+ */
if (!qres && rawmode)
printvars(rsize, rdata, rstatus, TYPE_SYS, 1, stdout);
ci = 0;
if (1 != sscanf(tag, "first.%d", &si) ||
si != ci ||
2 != sscanf(val, ts_fmt,
- &mon->first.l_ui,
- &mon->first.l_uf))
+ &mon->first.l_ui,
+ &mon->first.l_uf))
goto nomatch;
MGOT(MRU_GOT_FIRST);
break;
case 'n':
if (strcmp(tag, "now") ||
- 2 != sscanf(val, ts_fmt, &now.l_ui,
- &now.l_uf))
+ 2 != sscanf(val, ts_fmt,
+ &pnow->l_ui,
+ &pnow->l_uf))
goto nomatch;
have_now = TRUE;
break;
/* ignore unknown tags */
}
}
-
+ if (have_now)
+ list_complete = TRUE;
if (list_complete) {
NTP_INSIST(0 == ri || have_addr_older);
break;
* up with other duties.
*/
sleep(1);
+ /*
+ * If there were no errors, increase the number of rows
+ * to a maximum of 3 * MAXFRAGS (the most packets ntpq
+ * can handle in one response), on the assumption that
+ * no less than 3 rows fit in each packet.
+ */
+ if (!qres)
+ limit = min(3 * MAXFRAGS, limit * 2);
/*
* prepare next query with as many address and last-seen
* timestamps as will fit in a single packet.
req = req_buf;
req_end = req_buf + sizeof(req_buf);
#define REQ_ROOM (req_end - req)
- snprintf(req, REQ_ROOM, "limit=%d", limit);
+ snprintf(req, REQ_ROOM, "limit=%d%s", limit, parms);
req += strlen(req);
for (ri = 0, recent = HEAD_DLIST(mru_list, mlink);
NTP_INSIST(ri > 0 || NULL == recent);
}
+ c_mru_l_rc = TRUE;
+
+cleanup_return:
+ if (mon != NULL)
+ free(mon);
+
+ return c_mru_l_rc;
+}
+
+
+/*
+ * mrulist - ntpq's mrulist command to fetch an arbitrarily large Most
+ * Recently Used (seen) remote address list from ntpd.
+ *
+ * Similar to ntpdc's monlist command, but not limited to a single
+ * request/response, and thereby not limited to a few hundred remote
+ * addresses.
+ *
+ * See ntpd/ntp_control.c read_mru_list() for comments on the way
+ * CTL_OP_READ_MRU is designed to be used.
+ */
+static void
+mrulist(
+ struct parse *pcmd,
+ FILE *fp
+ )
+{
+ const char mincount_eq[] = "mincount=";
+ const char resall_eq[] = "resall=";
+ const char resany_eq[] = "resany=";
+ char parms_buf[128];
+ char *parms;
+ char *arg;
+ size_t cb;
+ mru *recent;
+ l_fp now;
+ l_fp interval;
+ double favgint;
+ double flstint;
+ int avgint;
+ int lstint;
+ int i;
+
+ parms_buf[0] = '\0';
+ parms = parms_buf;
+ for (i = 0; i < pcmd->nargs; i++) {
+ arg = pcmd->argval[i].string;
+ if (arg != NULL) {
+ cb = strlen(arg) + 1;
+ if ((!strncmp(resall_eq, arg, sizeof(resall_eq)
+ - 1) || !strncmp(resany_eq, arg,
+ sizeof(resany_eq) - 1) || !strncmp(
+ mincount_eq, arg, sizeof(mincount_eq) - 1))
+ && parms + cb + 2 <= parms_buf +
+ sizeof(parms_buf)) {
+ memcpy(parms, ", ", 2);
+ parms += 2;
+ memcpy(parms, arg, cb);
+ parms += cb - 1;
+ } else
+ fprintf(stderr,
+ "ignoring unrecognized mrulist parameter: %s\n",
+ arg);
+ }
+ }
+ parms = parms_buf;
+
+ INIT_DLIST(mru_list, mlink);
+ cb = NTP_HASH_SIZE * sizeof(*hash_table);
+ hash_table = emalloc(cb);
+ memset(hash_table, 0, cb);
+
+ if (!collect_mru_list(parms, &now))
+ return;
+
/* display the results */
if (rawmode)
goto cleanup_return;
-
printf( "lstint avgint rstr m v count rport remote address\n"
"==============================================================================\n");
/* '-' x 78 */
ITER_DLIST_END()
cleanup_return:
- if (mon != NULL)
- free(mon);
ITER_DLIST_BEGIN(mru_list, recent, mlink, mru)
free(recent);
ITER_DLIST_END()
* Holds data returned from queries. Declare buffer long to be sure of
* alignment.
*/
-#define MAXFRAGS 64 /* maximum number of fragments */
#define DATASIZE (MAXFRAGS*480) /* maximum amount of data */
long pktdata[DATASIZE/sizeof(long)];
const char *chosts[MAXHOSTS];
#define ADDHOST(cp) if (numhosts < MAXHOSTS) chosts[numhosts++] = (cp)
-/*
- * Error codes for internal use
- */
-#define ERR_UNSPEC 256
-#define ERR_INCOMPLETE 257
-#define ERR_TIMEOUT 258
-#define ERR_TOOMUCH 259
-
/*
* Macro definitions we use
*/
}
if (n == numfrags) {
*rsize = offsets[numfrags-1] + counts[numfrags-1];
+ if (debug)
+ fprintf(stderr,
+ "%d packets reassembled into response\n",
+ numfrags);
return 0;
}
}
#define MAXARGS 4
/*
- * Flags for forming descriptors.
+ * Limit on packets in a single response
*/
+#define MAXFRAGS 64
+
+/*
+ * Error codes for internal use
+ */
+#define ERR_UNSPEC 256
+#define ERR_INCOMPLETE 257
+#define ERR_TIMEOUT 258
+#define ERR_TOOMUCH 259
+
/*
* Flags for forming descriptors.
*/
#endif /* !defined(STR_PROCESSOR) */
+#undef STRINGIZE
+
#define SIOCGIFFLAGS SIO_GET_INTERFACE_LIST /* used in ntp_io.c */
/*
* Below this line are includes which must happen after the bulk of