testing over WiFi and a lossy go6.net UDP tunnel.
bk: 4ba2f8e1FmMPWvksME6yKwGxWZ7sNw
#define NETINFO_CONFIG_DIR "/config/ntp"
#endif
+/* ntpq -c mrulist rows per request limit in ntpd */
+#define MRU_ROW_LIMIT 256
#endif /* NTP_H */
const u_char *str
)
{
- const u_char *cp;
int len;
- cp = str;
- len = strlen((const char *)cp);
- if (len == 0)
+ len = strlen((const char *)str);
+ if (0 == len)
return 0;
- MD5auth_setkey(keyno, keytype, str, (int)strlen((const char *)str));
+ MD5auth_setkey(keyno, keytype, str, len);
return 1;
}
}
free_varlist(in_parms);
in_parms = NULL;
- if (!(0 < limit && limit <= 256)) {
- ctl_error(CERR_BADFMT);
+ if (!(0 < limit && limit <= MRU_ROW_LIMIT)) {
+ ctl_error(CERR_BADVALUE);
return;
}
l_fp * pnow
)
{
+ static int ntpd_row_limit = MRU_ROW_LIMIT;
int c_mru_l_rc; /* this function's return code */
u_char got; /* MRU_GOT_* bits */
const char ts_fmt[] = "0x%08x.%08x";
l_fp last_older;
sockaddr_u addr_older;
int have_now;
- int have_addr_older;
+ int have_addr_older;
int have_last_older;
u_short hash;
mru *unlinked;
memset(pnow, 0, sizeof(*pnow));
memset(&last_older, 0, sizeof(last_older));
- limit = 32;
+ limit = min(3 * MAXFRAGS, ntpd_row_limit);
snprintf(req_buf, sizeof(req_buf), "limit=%d%s", limit, parms);
while (TRUE) {
NTP_INSIST(unlinked == recent);
free(recent);
}
+ } else if (CERR_BADVALUE == qres /* temp -> */ || CERR_BADFMT == qres /* <- temp */) {
+ /* ntpd has lower cap on row limit */
+ ntpd_row_limit--;
+ limit = min(limit, ntpd_row_limit);
} else if (ERR_INCOMPLETE == qres) {
/*
* Reduce the number of rows to minimize effect
* 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.
+ * no less than 3 rows fit in each packet, capped at
+ * our best guess at the server's row limit.
*/
if (!qres)
- limit = min(3 * MAXFRAGS, limit * 3 / 2);
+ limit = min3(3 * MAXFRAGS, ntpd_row_limit,
+ max(limit + 1, limit * 33 / 32));
/*
* prepare next query with as many address and last-seen
* timestamps as will fit in a single packet.
* Default values we use.
*/
#define DEFHOST "localhost" /* default host name */
-#define DEFTIMEOUT (5) /* 5 second time out */
-#define DEFSTIMEOUT (2) /* 2 second time out after first */
+#define DEFTIMEOUT 5 /* wait 5 seconds for 1st pkt */
+#define DEFSTIMEOUT 3 /* and 3 more for each additional */
+/*
+ * Requests are automatically retried once, so total timeout with no
+ * response is a bit over 2 * DEFTIMEOUT, or 10 seconds. At the other
+ * extreme, a request eliciting 32 packets of responses each for some
+ * reason nearly DEFSTIMEOUT seconds after the prior in that series,
+ * with a single packet dropped, would take around 32 * DEFSTIMEOUT, or
+ * 93 seconds to fail each of two times, or 186 seconds.
+ * Some commands involve a series of requests, such as "peers" and
+ * "mrulist", so the cumulative timeouts are even longer for those.
+ */
#define DEFDELAY 0x51EB852 /* 20 milliseconds, l_fp fraction */
#define LENHOSTNAME 256 /* host name is 256 characters long */
#define MAXCMDS 100 /* maximum commands on cmd line */
/*
* Loop until we have an error or a complete response. Nearly all
- * aths to loop again use continue.
+ * code paths to loop again use continue.
*/
for (;;) {
if (numfrags == 0)
- tvo = tvout;
+ tvo = tvout;
else
- tvo = tvsout;
+ tvo = tvsout;
FD_SET(sockfd, &fds);
- n = select(sockfd+1, &fds, (fd_set *)0, (fd_set *)0, &tvo);
+ n = select(sockfd + 1, &fds, NULL, NULL, &tvo);
if (n == -1) {
warning("select fails", "", "");
*/
if (numfrags == 0) {
if (timeo)
- (void) fprintf(stderr,
- "%s: timed out, nothing received\n",
- currenthost);
+ fprintf(stderr,
+ "%s: timed out, nothing received\n",
+ currenthost);
return ERR_TIMEOUT;
} else {
if (timeo)
- (void) fprintf(stderr,
+ fprintf(stderr,
"%s: timed out with incomplete data\n",
currenthost);
if (debug) {
- printf("Received fragments:\n");
+ fprintf(stderr,
+ "ERR_INCOMPLETE: Received fragments:\n");
for (n = 0; n < numfrags; n++)
- printf("%4d %d\n", offsets[n],
- counts[n]);
- if (seenlastfrag)
- printf("last fragment received\n");
- else
- printf("last fragment not received\n");
+ fprintf(stderr,
+ "%2d: %5d %5d\t%3d octets\n",
+ n, offsets[n],
+ offsets[n] +
+ counts[n],
+ counts[n]);
+ fprintf(stderr,
+ "last fragment %sreceived\n",
+ (seenlastfrag)
+ ? ""
+ : "not ");
}
return ERR_INCOMPLETE;
}
#define MAXARGS 4
/*
- * Limit on packets in a single response
+ * Limit on packets in a single response. Increasing this value to
+ * 96 will marginally speed "mrulist" operation on lossless networks
+ * but it has been observed to cause loss on WiFi networks and with
+ * an IPv6 go6.net tunnel over UDP. That loss causes the request
+ * row limit to be cut in half, and it grows back very slowly to
+ * ensure forward progress is made and loss isn't triggered too quickly
+ * afterward. While the lossless case gains only marginally with
+ * MAXFRAGS == 96, the lossy case is a lot slower due to the repeated
+ * timeouts. Empirally, MAXFRAGS == 32 avoids most of the routine loss
+ * on both the WiFi and UDP v6 tunnel tests and seems a good compromise.
+ * This suggests some device in the path has a limit of 32 ~512 byte UDP
+ * packets in queue.
+ * Lowering MAXFRAGS may help with particularly lossy networks, but some
+ * ntpq commands may rely on the longtime value of 24 implicitly,
+ * assuming a single multipacket response will be large enough for any
+ * needs. In contrast, the "mrulist" command is implemented as a series
+ * of requests and multipacket responses to each.
*/
-#define MAXFRAGS 64
+#define MAXFRAGS 32
/*
* Error codes for internal use
#define HAVE_LIMITS_H 1
#define HAVE_STRDUP
-#define HAVE_STRCHR
+#define HAVE_STRCHR /* for libopts */
#define HAVE_FCNTL_H 1
#define HAVE_SYS_RESOURCE_H
#define HAVE_BSD_NICE /* emulate BSD setpriority() */