static void process_pps( peerT * const peer , json_ctx * const jctx , const l_fp * const rtime) { clockprocT * const pp = peer->procptr; gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; struct timespec ts; errno = 0; ts.tv_sec = (time_t)json_object_lookup_int( jctx, 0, "clock_sec"); if (up->fl_nsec) ts.tv_nsec = json_object_lookup_int( jctx, 0, "clock_nsec"); else ts.tv_nsec = json_object_lookup_int( jctx, 0, "clock_musec") * 1000; if (0 != errno) goto fail; up->pps_local = *rtime; /* get fudged receive time */ up->pps_recvt = tspec_stamp_to_lfp(ts); L_SUB(&up->pps_recvt, &up->pps_fudge); /* map to next full second as reference time stamp */ up->pps_stamp = up->pps_recvt; L_ADDUF(&up->pps_stamp, 0x80000000u); up->pps_stamp.l_uf = 0; pp->lastrec = up->pps_stamp; DPRINTF(2, ("GPSD_JSON(%d): process_pps, stamp='%s', recvt='%s'\n", up->unit, gmprettydate(&up->pps_stamp), gmprettydate(&up->pps_recvt))); /* When we have a time pulse, clear the TPV flag: the * PPS is only valid for the >NEXT< TPV value! */ up->fl_pps = -1; up->fl_tpv = 0; return; fail: DPRINTF(2, ("GPSD_JSON(%d): process_pps FAILED, nsec=%d stamp='%s', recvt='%s'\n", up->unit, up->fl_nsec, gmprettydate(&up->pps_stamp), gmprettydate(&up->pps_recvt))); up->tc_breply += 1; }
/* * ntp_monitor - record stats about this packet * * Returns supplied restriction flags, with RES_LIMITED and RES_KOD * cleared unless the packet should not be responded to normally * (RES_LIMITED) and possibly should trigger a KoD response (RES_KOD). * The returned flags are saved in the MRU entry, so that it reflects * whether the last packet from that source triggered rate limiting, * and if so, possible KoD response. This implies you can not tell * whether a given address is eligible for rate limiting/KoD from the * monlist restrict bits, only whether or not the last packet triggered * such responses. ntpdc -c reslist lets you see whether RES_LIMITED * or RES_KOD is lit for a particular address before ntp_monitor()'s * typical dousing. */ u_short ntp_monitor( struct recvbuf *rbufp, u_short flags ) { l_fp interval_fp; struct pkt * pkt; mon_entry * mon; mon_entry * oldest; int oldest_age; u_int hash; u_short restrict_mask; u_char mode; u_char version; int interval; int head; /* headway increment */ int leak; /* new headway */ int limit; /* average threshold */ REQUIRE(rbufp != NULL); if (mon_enabled == MON_OFF) return ~(RES_LIMITED | RES_KOD) & flags; pkt = &rbufp->recv_pkt; hash = MON_HASH(&rbufp->recv_srcadr); mode = PKT_MODE(pkt->li_vn_mode); 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 (SOCK_EQ(&mon->rmtadr, &rbufp->recv_srcadr)) break; if (mon != NULL) { interval_fp = rbufp->recv_time; L_SUB(&interval_fp, &mon->last); /* add one-half second to round up */ L_ADDUF(&interval_fp, 0x80000000); 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); /* Shuffle to the head of the MRU list. */ UNLINK_DLIST(mon, mru); LINK_DLIST(mon_mru_list, mon, mru); /* * At this point the most recent arrival is first in the * MRU list. Decrease the counter by the headway, but * not less than zero. */ mon->leak -= interval; mon->leak = max(0, mon->leak); head = 1 << ntp_minpoll; leak = mon->leak + head; limit = NTP_SHIFT * head; DPRINTF(2, ("MRU: interval %d headway %d limit %d\n", interval, leak, limit)); /* * If the minimum and average thresholds are not * exceeded, douse the RES_LIMITED and RES_KOD bits and * increase the counter by the headway increment. Note * that we give a 1-s grace for the minimum threshold * and a 2-s grace for the headway increment. If one or * both thresholds are exceeded and the old counter is * less than the average threshold, set the counter to * the average threshold plus the increment and leave * the RES_LIMITED and RES_KOD bits lit. Otherwise, * leave the counter alone and douse the RES_KOD bit. * This rate-limits the KoDs to no less than the average * headway. */ if (interval + 1 >= ntp_minpkt && leak < limit) { mon->leak = leak - 2; restrict_mask &= ~(RES_LIMITED | RES_KOD); } else if (mon->leak < limit) mon->leak = limit + head; else restrict_mask &= ~RES_KOD; mon->flags = restrict_mask; return mon->flags; } /* * If we got here, this is the first we've heard of this * guy. Get him some memory, either from the free list * or from the tail of the MRU list. * * The following ntp.conf "mru" knobs come into play determining * the depth (or count) of the MRU list: * - mru_mindepth ("mru mindepth") is a floor beneath which * entries are kept without regard to their age. The * default is 600 which matches the longtime implementation * limit on the total number of entries. * - mru_maxage ("mru maxage") is a ceiling on the age in * seconds of entries. Entries older than this are * reclaimed once mon_mindepth is exceeded. 64s default. * Note that entries older than this can easily survive * as they are reclaimed only as needed. * - mru_maxdepth ("mru maxdepth") is a hard limit on the * number of entries. * - "mru maxmem" sets mru_maxdepth to the number of entries * which fit in the given number of kilobytes. The default is * 1024, or 1 megabyte. * - mru_initalloc ("mru initalloc" sets the count of the * initial allocation of MRU entries. * - "mru initmem" sets mru_initalloc in units of kilobytes. * The default is 4. * - mru_incalloc ("mru incalloc" sets the number of entries to * allocate on-demand each time the free list is empty. * - "mru incmem" sets mru_incalloc in units of kilobytes. * The default is 4. * Whichever of "mru maxmem" or "mru maxdepth" occurs last in * ntp.conf controls. Similarly for "mru initalloc" and "mru * initmem", and for "mru incalloc" and "mru incmem". */ 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); oldest_age = 0; /* silence uninit warning */ if (oldest != NULL) { interval_fp = rbufp->recv_time; L_SUB(&interval_fp, &oldest->last); /* add one-half second to round up */ L_ADDUF(&interval_fp, 0x80000000); oldest_age = interval_fp.l_i; } /* note -1 is legal for mru_maxage (disables) */ if (oldest != NULL && mru_maxage < oldest_age) { mon_reclaim_entry(oldest); mon = oldest; } else if (mon_free != NULL || mru_alloc < mru_maxdepth) { if (NULL == mon_free) mon_getmoremem(); UNLINK_HEAD_SLIST(mon, mon_free, hash_next); /* Preempt from the MRU list if old enough. */ } else if (ntp_random() / (2. * FRAC) > (double)oldest_age / mon_age) { return ~(RES_LIMITED | RES_KOD) & flags; } else { mon_reclaim_entry(oldest); mon = oldest; } } INSIST(mon != NULL); /* * Got one, initialize it */ mru_entries++; mru_peakentries = max(mru_peakentries, mru_entries); mon->last = rbufp->recv_time; mon->first = mon->last; mon->count = 1; mon->flags = ~(RES_LIMITED | RES_KOD) & flags; mon->leak = 0; memcpy(&mon->rmtadr, &rbufp->recv_srcadr, sizeof(mon->rmtadr)); mon->vn_mode = VN_MODE(version, mode); mon->lcladr = rbufp->dstadr; mon->cast_flags = (u_char)(((rbufp->dstadr->flags & INT_MCASTOPEN) && rbufp->fd == mon->lcladr->fd) ? MDF_MCAST : rbufp->fd == mon->lcladr->bfd ? MDF_BCAST : MDF_UCAST); /* * Drop him into front of the hash table. Also put him on top of * the MRU list. */ LINK_SLIST(mon_hash[hash], mon, hash_next); LINK_DLIST(mon_mru_list, mon, mru); return mon->flags; }
/* * request - send a configuration request to the server, wait for a response */ static int request( struct conf_peer *conf ) { struct sock_timeval tvout; struct req_pkt reqpkt; size_t req_len; size_t total_len; /* req_len plus keyid & digest */ fd_set fdset; l_fp ts; char * pch; char * pchEnd; l_fp * pts; keyid_t *pkeyid; int n; #ifdef SYS_WINNT HANDLE hReadWriteEvent = NULL; BOOL ret; DWORD NumberOfBytesWritten, NumberOfBytesRead, dwWait; OVERLAPPED overlap; #endif /* SYS_WINNT */ checkparent(); /* make sure our guy is still running */ if (sockfd == INVALID_SOCKET) openntp(); #ifdef SYS_WINNT hReadWriteEvent = CreateEvent(NULL, FALSE, FALSE, NULL); #endif /* SYS_WINNT */ /* * Try to clear out any previously received traffic so it * doesn't fool us. Note the socket is nonblocking. */ tvout.tv_sec = 0; tvout.tv_usec = 0; FD_ZERO(&fdset); FD_SET(sockfd, &fdset); while (select(sockfd + 1, &fdset, (fd_set *)0, (fd_set *)0, &tvout) > 0) { recv(sockfd, (char *)&reqpkt, sizeof(reqpkt), 0); FD_ZERO(&fdset); FD_SET(sockfd, &fdset); } /* * Make up a request packet with the configuration info */ memset(&reqpkt, 0, sizeof(reqpkt)); reqpkt.rm_vn_mode = RM_VN_MODE(0, 0, 0); reqpkt.auth_seq = AUTH_SEQ(1, 0); /* authenticated, no seq */ reqpkt.implementation = IMPL_XNTPD; /* local implementation */ reqpkt.request = REQ_CONFIG; /* configure a new peer */ reqpkt.err_nitems = ERR_NITEMS(0, 1); /* one item */ reqpkt.mbz_itemsize = MBZ_ITEMSIZE(sizeof(*conf)); /* Make sure mbz_itemsize <= sizeof reqpkt.data */ if (sizeof(*conf) > sizeof(reqpkt.data)) { msyslog(LOG_ERR, "Bletch: conf_peer is too big for reqpkt.data!"); resolver_exit(1); } memcpy(reqpkt.data, conf, sizeof(*conf)); if (sys_authenticate && req_hashlen > 16) { pch = reqpkt.data; /* 32-bit alignment */ pch += (sizeof(*conf) + 3) & ~3; pts = (void *)pch; pkeyid = (void *)(pts + 1); pchEnd = (void *)pkeyid; req_len = pchEnd - (char *)&reqpkt; pchEnd = (void *)(pkeyid + 1); pchEnd += req_hashlen; total_len = pchEnd - (char *)&reqpkt; if (total_len > sizeof(reqpkt)) { msyslog(LOG_ERR, "intres total_len %u limit is %u (%u octet digest)\n", total_len, sizeof(reqpkt), req_hashlen); resolver_exit(1); } } else { pts = &reqpkt.tstamp; pkeyid = &reqpkt.keyid; req_len = REQ_LEN_NOMAC; } *pkeyid = htonl(req_keyid); get_systime(&ts); L_ADDUF(&ts, SKEWTIME); HTONL_FP(&ts, pts); if (sys_authenticate) { n = authencrypt(req_keyid, (void *)&reqpkt, req_len); if ((size_t)n != req_hashlen + sizeof(reqpkt.keyid)) { msyslog(LOG_ERR, "intres maclen %d expected %u\n", n, req_hashlen + sizeof(reqpkt.keyid)); resolver_exit(1); } req_len += n; } /* * Done. Send it. */ #ifndef SYS_WINNT n = send(sockfd, (char *)&reqpkt, req_len, 0); if (n < 0) { msyslog(LOG_ERR, "send to NTP server failed: %m"); return 0; /* maybe should exit */ } #else /* In the NT world, documentation seems to indicate that there * exist _write and _read routines that can be used to do blocking * I/O on sockets. Problem is these routines require a socket * handle obtained through the _open_osf_handle C run-time API * of which there is no explanation in the documentation. We need * nonblocking write's and read's anyway for our purpose here. * We're therefore forced to deviate a little bit from the Unix * model here and use the ReadFile and WriteFile Win32 I/O API's * on the socket */ overlap.Offset = overlap.OffsetHigh = (DWORD)0; overlap.hEvent = hReadWriteEvent; ret = WriteFile((HANDLE)sockfd, (char *)&reqpkt, req_len, NULL, (LPOVERLAPPED)&overlap); if ((ret == FALSE) && (GetLastError() != ERROR_IO_PENDING)) { msyslog(LOG_ERR, "send to NTP server failed: %m"); return 0; } dwWait = WaitForSingleObject(hReadWriteEvent, (DWORD) TIMEOUT_SEC * 1000); if ((dwWait == WAIT_FAILED) || (dwWait == WAIT_TIMEOUT)) { if (dwWait == WAIT_FAILED) msyslog(LOG_ERR, "WaitForSingleObject failed: %m"); return 0; } if (!GetOverlappedResult((HANDLE)sockfd, (LPOVERLAPPED)&overlap, (LPDWORD)&NumberOfBytesWritten, FALSE)) { msyslog(LOG_ERR, "GetOverlappedResult for WriteFile fails: %m"); return 0; } #endif /* SYS_WINNT */ /* * Wait for a response. A weakness of the mode 7 protocol used * is that there is no way to associate a response with a * particular request, i.e. the response to this configuration * request is indistinguishable from that to any other. I should * fix this some day. In any event, the time out is fairly * pessimistic to make sure that if an answer is coming back * at all, we get it. */ for (;;) { FD_ZERO(&fdset); FD_SET(sockfd, &fdset); tvout.tv_sec = TIMEOUT_SEC; tvout.tv_usec = TIMEOUT_USEC; n = select(sockfd + 1, &fdset, (fd_set *)0, (fd_set *)0, &tvout); if (n < 0) { if (errno != EINTR) msyslog(LOG_ERR, "select() fails: %m"); return 0; } else if (n == 0) { #ifdef DEBUG if (debug) msyslog(LOG_INFO, "ntp_intres select() returned 0."); #endif return 0; } #ifndef SYS_WINNT n = recv(sockfd, (char *)&reqpkt, sizeof(reqpkt), 0); if (n <= 0) { if (n < 0) { msyslog(LOG_ERR, "recv() fails: %m"); return 0; } continue; } #else /* Overlapped I/O used on non-blocking sockets on Windows NT */ ret = ReadFile((HANDLE)sockfd, (char *)&reqpkt, sizeof(reqpkt), NULL, (LPOVERLAPPED)&overlap); if ((ret == FALSE) && (GetLastError() != ERROR_IO_PENDING)) { msyslog(LOG_ERR, "ReadFile() fails: %m"); return 0; } dwWait = WaitForSingleObject(hReadWriteEvent, (DWORD) TIMEOUT_SEC * 1000); if ((dwWait == WAIT_FAILED) || (dwWait == WAIT_TIMEOUT)) { if (dwWait == WAIT_FAILED) { msyslog(LOG_ERR, "WaitForSingleObject for ReadFile fails: %m"); return 0; } continue; } if (!GetOverlappedResult((HANDLE)sockfd, (LPOVERLAPPED)&overlap, (LPDWORD)&NumberOfBytesRead, FALSE)) { msyslog(LOG_ERR, "GetOverlappedResult fails: %m"); return 0; } n = NumberOfBytesRead; #endif /* SYS_WINNT */ /* * Got one. Check through to make sure it is what * we expect. */ if (n < RESP_HEADER_SIZE) { msyslog(LOG_ERR, "received runt response (%d octets)", n); continue; } if (!ISRESPONSE(reqpkt.rm_vn_mode)) { #ifdef DEBUG if (debug > 1) msyslog(LOG_INFO, "received non-response packet"); #endif continue; } if (ISMORE(reqpkt.rm_vn_mode)) { #ifdef DEBUG if (debug > 1) msyslog(LOG_INFO, "received fragmented packet"); #endif continue; } if ( ( (INFO_VERSION(reqpkt.rm_vn_mode) < 2) || (INFO_VERSION(reqpkt.rm_vn_mode) > NTP_VERSION)) || INFO_MODE(reqpkt.rm_vn_mode) != MODE_PRIVATE) { #ifdef DEBUG if (debug > 1) msyslog(LOG_INFO, "version (%d/%d) or mode (%d/%d) incorrect", INFO_VERSION(reqpkt.rm_vn_mode), NTP_VERSION, INFO_MODE(reqpkt.rm_vn_mode), MODE_PRIVATE); #endif continue; } if (INFO_SEQ(reqpkt.auth_seq) != 0) { #ifdef DEBUG if (debug > 1) msyslog(LOG_INFO, "nonzero sequence number (%d)", INFO_SEQ(reqpkt.auth_seq)); #endif continue; } if (reqpkt.implementation != IMPL_XNTPD || reqpkt.request != REQ_CONFIG) { #ifdef DEBUG if (debug > 1) msyslog(LOG_INFO, "implementation (%d) or request (%d) incorrect", reqpkt.implementation, reqpkt.request); #endif continue; } if (INFO_NITEMS(reqpkt.err_nitems) != 0 || INFO_MBZ(reqpkt.mbz_itemsize) != 0 || INFO_ITEMSIZE(reqpkt.mbz_itemsize) != 0) { #ifdef DEBUG if (debug > 1) msyslog(LOG_INFO, "nitems (%d) mbz (%d) or itemsize (%d) nonzero", INFO_NITEMS(reqpkt.err_nitems), INFO_MBZ(reqpkt.mbz_itemsize), INFO_ITEMSIZE(reqpkt.mbz_itemsize)); #endif continue; } n = INFO_ERR(reqpkt.err_nitems); switch (n) { case INFO_OKAY: /* success */ return 1; case INFO_ERR_NODATA: /* * newpeer() refused duplicate association, no * point in retrying so call it success. */ return 1; case INFO_ERR_IMPL: msyslog(LOG_ERR, "ntp_intres.request: implementation mismatch"); return 0; case INFO_ERR_REQ: msyslog(LOG_ERR, "ntp_intres.request: request unknown"); return 0; case INFO_ERR_FMT: msyslog(LOG_ERR, "ntp_intres.request: format error"); return 0; case INFO_ERR_AUTH: msyslog(LOG_ERR, "ntp_intres.request: permission denied"); return 0; default: msyslog(LOG_ERR, "ntp_intres.request: unknown error code %d", n); return 0; } } }