/* Output a long floating point value in hex in the style described above */ void l_fp_output( l_fp * ts, FILE * output ) { fprintf(output, "%s\n", prettydate(ts)); }
/* * wwvb_timer - called once per second by the transmit procedure */ static void wwvb_timer( int unit, struct peer *peer ) { register struct wwvbunit *up; struct refclockproc *pp; char pollchar; /* character sent to clock */ #ifdef DEBUG l_fp now; #endif /* * Time to poll the clock. The Spectracom clock responds to a * 'T' by returning a timecode in the format(s) specified above. * Note there is no checking on state, since this may not be the * only customer reading the clock. Only one customer need poll * the clock; all others just listen in. */ pp = peer->procptr; up = pp->unitptr; if (up->linect > 0) pollchar = 'R'; else pollchar = 'T'; if (write(pp->io.fd, &pollchar, 1) != 1) refclock_report(peer, CEVNT_FAULT); #ifdef DEBUG get_systime(&now); if (debug) printf("%c poll at %s\n", pollchar, prettydate(&now)); #endif #ifdef HAVE_PPSAPI if (up->ppsapi_lit && refclock_pps(peer, &up->atom, pp->sloppyclockflag) > 0) { up->pcount++, peer->flags |= FLAG_PPS; peer->precision = PPS_PRECISION; } #endif /* HAVE_PPSAPI */ }
/* * wwvb_receive - receive data from the serial interface */ static void wwvb_receive( struct recvbuf *rbufp ) { struct wwvbunit *up; struct refclockproc *pp; struct peer *peer; l_fp trtmp; /* arrival timestamp */ int tz; /* time zone */ int day, month; /* ddd conversion */ int temp; /* int temp */ char syncchar; /* synchronization indicator */ char qualchar; /* quality indicator */ char leapchar; /* leap indicator */ char dstchar; /* daylight/standard indicator */ char tmpchar; /* trashbin */ /* * Initialize pointers and read the timecode and timestamp */ peer = rbufp->recv_peer; pp = peer->procptr; up = pp->unitptr; temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp); /* * Note we get a buffer and timestamp for both a <cr> and <lf>, * but only the <cr> timestamp is retained. Note: in format 0 on * a Netclock/2 or upgraded 8170 the start bit is delayed 100 * +-50 us relative to the pps; however, on an unmodified 8170 * the start bit can be delayed up to 10 ms. In format 2 the * reading precision is only to the millisecond. Thus, unless * you have a PPS gadget and don't have to have the year, format * 0 provides the lowest jitter. * Save the timestamp of each <CR> in up->laststamp. Lines with * no characters occur for every <LF>, and for some <CR>s when * format 0 is used. Format 0 starts and ends each cycle with a * <CR><LF> pair, format 2 starts each cycle with its only pair. * The preceding <CR> is the on-time character for both formats. * The timestamp provided with non-empty lines corresponds to * the <CR> following the timecode, which is ultimately not used * with format 0 and is used for the following timecode for * format 2. */ if (temp == 0) { if (up->prev_eol_cr) { DPRINTF(2, ("wwvb: <LF> @ %s\n", prettydate(&trtmp))); } else { up->laststamp = trtmp; DPRINTF(2, ("wwvb: <CR> @ %s\n", prettydate(&trtmp))); } up->prev_eol_cr = !up->prev_eol_cr; return; } pp->lencode = temp; pp->lastrec = up->laststamp; up->laststamp = trtmp; up->prev_eol_cr = TRUE; DPRINTF(2, ("wwvb: code @ %s\n" " using %s minus one char\n", prettydate(&trtmp), prettydate(&pp->lastrec))); if (L_ISZERO(&pp->lastrec)) return; /* * We get down to business, check the timecode format and decode * its contents. This code uses the timecode length to determine * format 0, 2 or 3. If the timecode has invalid length or is * not in proper format, we declare bad format and exit. */ syncchar = qualchar = leapchar = dstchar = ' '; tz = 0; switch (pp->lencode) { case LENWWVB0: /* * Timecode format 0: "I ddd hh:mm:ss DTZ=nn" */ if (sscanf(pp->a_lastcode, "%c %3d %2d:%2d:%2d%c%cTZ=%2d", &syncchar, &pp->day, &pp->hour, &pp->minute, &pp->second, &tmpchar, &dstchar, &tz) == 8) { pp->nsec = 0; break; } goto bad_format; case LENWWVB2: /* * Timecode format 2: "IQyy ddd hh:mm:ss.mmm LD" */ if (sscanf(pp->a_lastcode, "%c%c %2d %3d %2d:%2d:%2d.%3ld %c", &syncchar, &qualchar, &pp->year, &pp->day, &pp->hour, &pp->minute, &pp->second, &pp->nsec, &leapchar) == 9) { pp->nsec *= 1000000; break; } goto bad_format; case LENWWVB3: /* * Timecode format 3: "0003I yyyymmdd hhmmss+0000SL#" * WARNING: Undocumented, and the on-time character # is * not yet handled correctly by this driver. It may be * as simple as compensating for an additional 1/960 s. */ if (sscanf(pp->a_lastcode, "0003%c %4d%2d%2d %2d%2d%2d+0000%c%c", &syncchar, &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second, &dstchar, &leapchar) == 8) { pp->day = ymd2yd(pp->year, month, day); pp->nsec = 0; break; } goto bad_format; default: bad_format: /* * Unknown format: If dumping internal table, record * stats; otherwise, declare bad format. */ if (up->linect > 0) { up->linect--; record_clock_stats(&peer->srcadr, pp->a_lastcode); } else { refclock_report(peer, CEVNT_BADREPLY); } return; } /* * Decode synchronization, quality and leap characters. If * unsynchronized, set the leap bits accordingly and exit. * Otherwise, set the leap bits according to the leap character. * Once synchronized, the dispersion depends only on the * quality character. */ switch (qualchar) { case ' ': pp->disp = .001; pp->lastref = pp->lastrec; break; case 'A': pp->disp = .01; break; case 'B': pp->disp = .1; break; case 'C': pp->disp = .5; break; case 'D': pp->disp = MAXDISPERSE; break; default: pp->disp = MAXDISPERSE; refclock_report(peer, CEVNT_BADREPLY); break; } if (syncchar != ' ') pp->leap = LEAP_NOTINSYNC; else if (leapchar == 'L') pp->leap = LEAP_ADDSECOND; else pp->leap = LEAP_NOWARNING; /* * Process the new sample in the median filter and determine the * timecode timestamp, but only if the PPS is not in control. */ #ifdef HAVE_PPSAPI up->tcount++; if (peer->flags & FLAG_PPS) return; #endif /* HAVE_PPSAPI */ if (!refclock_process_f(pp, pp->fudgetime2)) refclock_report(peer, CEVNT_BADTIME); }
/* ** Check if it's data for us and whether it's useable or not. ** ** If not, return a failure code so we can delete this server from our list ** and continue with another one. */ int process_pkt ( struct pkt *rpkt, sockaddr_u *sender, int pkt_len, int mode, struct pkt *spkt, const char * func_name ) { u_int key_id; struct key * pkt_key; int is_authentic; int mac_size; u_int exten_len; u_int32 * exten_end; u_int32 * packet_end; l_fp sent_xmt; l_fp resp_org; // key_id = 0; pkt_key = NULL; is_authentic = (HAVE_OPT(AUTHENTICATION)) ? 0 : -1; /* * Parse the extension field if present. We figure out whether * an extension field is present by measuring the MAC size. If * the number of words following the packet header is 0, no MAC * is present and the packet is not authenticated. If 1, the * packet is a crypto-NAK; if 3, the packet is authenticated * with DES; if 5, the packet is authenticated with MD5; if 6, * the packet is authenticated with SHA. If 2 or 4, the packet * is a runt and discarded forthwith. If greater than 6, an * extension field is present, so we subtract the length of the * field and go around again. */ if (pkt_len < (int)LEN_PKT_NOMAC || (pkt_len & 3) != 0) { msyslog(LOG_ERR, "%s: Incredible packet length: %d. Discarding.", func_name, pkt_len); return PACKET_UNUSEABLE; } /* HMS: the following needs a bit of work */ /* Note: pkt_len must be a multiple of 4 at this point! */ packet_end = (void*)((char*)rpkt + pkt_len); exten_end = skip_efields(rpkt->exten, packet_end); if (NULL == exten_end) { msyslog(LOG_ERR, "%s: Missing extension field. Discarding.", func_name); return PACKET_UNUSEABLE; } /* get size of MAC in cells; can be zero */ exten_len = (u_int)(packet_end - exten_end); /* deduce action required from remaining length */ switch (exten_len) { case 0: /* no Legacy MAC */ break; case 1: /* crypto NAK */ /* Only if the keyID is 0 and there were no EFs */ key_id = ntohl(*exten_end); printf("Crypto NAK = 0x%08x from %s\n", key_id, stoa(sender)); break; case 3: /* key ID + 3DES MAC -- unsupported! */ msyslog(LOG_ERR, "%s: Key ID + 3DES MAC is unsupported. Discarding.", func_name); return PACKET_UNUSEABLE; case 5: /* key ID + MD5 MAC */ case 6: /* key ID + SHA MAC */ /* ** Look for the key used by the server in the specified ** keyfile and if existent, fetch it or else leave the ** pointer untouched */ key_id = ntohl(*exten_end); get_key(key_id, &pkt_key); if (!pkt_key) { printf("unrecognized key ID = 0x%08x\n", key_id); break; } /* ** Seems like we've got a key with matching keyid. ** ** Generate a md5sum of the packet with the key from our ** keyfile and compare those md5sums. */ mac_size = exten_len << 2; if (!auth_md5(rpkt, pkt_len - mac_size, mac_size - 4, pkt_key)) { is_authentic = FALSE; break; } /* Yay! Things worked out! */ is_authentic = TRUE; TRACE(1, ("sntp %s: packet from %s authenticated using key id %d.\n", func_name, stoa(sender), key_id)); break; default: msyslog(LOG_ERR, "%s: Unexpected extension length: %d. Discarding.", func_name, exten_len); return PACKET_UNUSEABLE; } switch (is_authentic) { case -1: /* unknown */ break; case 0: /* not authentic */ return SERVER_AUTH_FAIL; break; case 1: /* authentic */ break; default: /* error */ break; } /* Check for server's ntp version */ if (PKT_VERSION(rpkt->li_vn_mode) < NTP_OLDVERSION || PKT_VERSION(rpkt->li_vn_mode) > NTP_VERSION) { msyslog(LOG_ERR, "%s: Packet shows wrong version (%d)", func_name, PKT_VERSION(rpkt->li_vn_mode)); return SERVER_UNUSEABLE; } /* We want a server to sync with */ if (PKT_MODE(rpkt->li_vn_mode) != mode && PKT_MODE(rpkt->li_vn_mode) != MODE_PASSIVE) { msyslog(LOG_ERR, "%s: mode %d stratum %d", func_name, PKT_MODE(rpkt->li_vn_mode), rpkt->stratum); return SERVER_UNUSEABLE; } /* Stratum is unspecified (0) check what's going on */ if (STRATUM_PKT_UNSPEC == rpkt->stratum) { char *ref_char; TRACE(1, ("%s: Stratum unspecified, going to check for KOD (stratum: %d)\n", func_name, rpkt->stratum)); ref_char = (char *) &rpkt->refid; TRACE(1, ("%s: Packet refid: %c%c%c%c\n", func_name, ref_char[0], ref_char[1], ref_char[2], ref_char[3])); /* If it's a KOD packet we'll just use the KOD information */ if (ref_char[0] != 'X') { if (strncmp(ref_char, "DENY", 4) == 0) return KOD_DEMOBILIZE; if (strncmp(ref_char, "RSTR", 4) == 0) return KOD_DEMOBILIZE; if (strncmp(ref_char, "RATE", 4) == 0) return KOD_RATE; /* ** There are other interesting kiss codes which ** might be interesting for authentication. */ } } /* If the server is not synced it's not really useable for us */ if (LEAP_NOTINSYNC == PKT_LEAP(rpkt->li_vn_mode)) { msyslog(LOG_ERR, "%s: %s not in sync, skipping this server", func_name, stoa(sender)); return SERVER_UNUSEABLE; } /* * Decode the org timestamp and make sure we're getting a response * to our last request, but only if we're not in broadcast mode. */ if (MODE_BROADCAST == mode) return pkt_len; if (!L_ISEQU(&rpkt->org, &spkt->xmt)) { NTOHL_FP(&rpkt->org, &resp_org); NTOHL_FP(&spkt->xmt, &sent_xmt); msyslog(LOG_ERR, "%s response org expected to match sent xmt", stoa(sender)); msyslog(LOG_ERR, "resp org: %s", prettydate(&resp_org)); msyslog(LOG_ERR, "sent xmt: %s", prettydate(&sent_xmt)); return PACKET_UNUSEABLE; } return pkt_len; }