]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/ntp/ntpd/refclock_zyfer.c
Fix multiple denial of service in ntpd.
[FreeBSD/FreeBSD.git] / contrib / ntp / ntpd / refclock_zyfer.c
1 /*
2  * refclock_zyfer - clock driver for the Zyfer GPSTarplus Clock
3  *
4  * Harlan Stenn, Jan 2002
5  */
6
7 #ifdef HAVE_CONFIG_H
8 #include <config.h>
9 #endif
10
11 #if defined(REFCLOCK) && defined(CLOCK_ZYFER)
12
13 #include "ntpd.h"
14 #include "ntp_io.h"
15 #include "ntp_refclock.h"
16 #include "ntp_stdlib.h"
17 #include "ntp_unixtime.h"
18 #include "ntp_calgps.h"
19
20 #include <stdio.h>
21 #include <ctype.h>
22
23 #if defined(HAVE_TERMIOS_H)
24 # include <termios.h>
25 #elif defined(HAVE_SYS_TERMIOS_H)
26 # include <sys/termios.h>
27 #endif
28 #ifdef HAVE_SYS_PPSCLOCK_H
29 # include <sys/ppsclock.h>
30 #endif
31
32 /*
33  * This driver provides support for the TOD serial port of a Zyfer GPStarplus.
34  * This clock also provides PPS as well as IRIG outputs.
35  * Precision is limited by the serial driver, etc.
36  *
37  * If I was really brave I'd hack/generalize the serial driver to deal
38  * with arbitrary on-time characters.  This clock *begins* the stream with
39  * `!`, the on-time character, and the string is *not* EOL-terminated.
40  *
41  * Configure the beast for 9600, 8N1.  While I see leap-second stuff
42  * in the documentation, the published specs on the TOD format only show
43  * the seconds going to '59'.  I see no leap warning in the TOD format.
44  *
45  * The clock sends the following message once per second:
46  *
47  *      !TIME,2002,017,07,59,32,2,4,1
48  *            YYYY DDD HH MM SS m T O
49  *
50  *      !               On-time character
51  *      YYYY            Year
52  *      DDD     001-366 Day of Year
53  *      HH      00-23   Hour
54  *      MM      00-59   Minute
55  *      SS      00-59   Second (probably 00-60)
56  *      m       1-5     Time Mode:
57  *                      1 = GPS time
58  *                      2 = UTC time
59  *                      3 = LGPS time (Local GPS)
60  *                      4 = LUTC time (Local UTC)
61  *                      5 = Manual time
62  *      T       4-9     Time Figure Of Merit:
63  *                      4         x <= 1us
64  *                      5   1us < x <= 10 us
65  *                      6  10us < x <= 100us
66  *                      7 100us < x <= 1ms
67  *                      8   1ms < x <= 10ms
68  *                      9  10ms < x
69  *      O       0-4     Operation Mode:
70  *                      0 Warm-up
71  *                      1 Time Locked
72  *                      2 Coasting
73  *                      3 Recovering
74  *                      4 Manual
75  *
76  */
77
78 /*
79  * Interface definitions
80  */
81 #define DEVICE          "/dev/zyfer%d" /* device name and unit */
82 #define SPEED232        B9600   /* uart speed (9600 baud) */
83 #define PRECISION       (-20)   /* precision assumed (about 1 us) */
84 #define REFID           "GPS\0" /* reference ID */
85 #define DESCRIPTION     "Zyfer GPStarplus" /* WRU */
86
87 #define LENZYFER        29      /* timecode length */
88
89 /*
90  * Unit control structure
91  */
92 struct zyferunit {
93         u_char  Rcvbuf[LENZYFER + 1];
94         u_char  polled;         /* poll message flag */
95         int     pollcnt;
96         l_fp    tstamp;         /* timestamp of last poll */
97         int     Rcvptr;
98 };
99
100 /*
101  * Function prototypes
102  */
103 static  int     zyfer_start     (int, struct peer *);
104 static  void    zyfer_shutdown  (int, struct peer *);
105 static  void    zyfer_receive   (struct recvbuf *);
106 static  void    zyfer_poll      (int, struct peer *);
107
108 /*
109  * Transfer vector
110  */
111 struct  refclock refclock_zyfer = {
112         zyfer_start,            /* start up driver */
113         zyfer_shutdown,         /* shut down driver */
114         zyfer_poll,             /* transmit poll message */
115         noentry,                /* not used (old zyfer_control) */
116         noentry,                /* initialize driver (not used) */
117         noentry,                /* not used (old zyfer_buginfo) */
118         NOFLAGS                 /* not used */
119 };
120
121
122 /*
123  * zyfer_start - open the devices and initialize data for processing
124  */
125 static int
126 zyfer_start(
127         int unit,
128         struct peer *peer
129         )
130 {
131         register struct zyferunit *up;
132         struct refclockproc *pp;
133         int fd;
134         char device[20];
135
136         /*
137          * Open serial port.
138          * Something like LDISC_ACTS that looked for ! would be nice...
139          */
140         snprintf(device, sizeof(device), DEVICE, unit);
141         fd = refclock_open(device, SPEED232, LDISC_RAW);
142         if (fd <= 0)
143                 return (0);
144
145         msyslog(LOG_NOTICE, "zyfer(%d) fd: %d dev <%s>", unit, fd, device);
146
147         /*
148          * Allocate and initialize unit structure
149          */
150         up = emalloc(sizeof(struct zyferunit));
151         memset(up, 0, sizeof(struct zyferunit));
152         pp = peer->procptr;
153         pp->io.clock_recv = zyfer_receive;
154         pp->io.srcclock = peer;
155         pp->io.datalen = 0;
156         pp->io.fd = fd;
157         if (!io_addclock(&pp->io)) {
158                 close(fd);
159                 pp->io.fd = -1;
160                 free(up);
161                 return (0);
162         }
163         pp->unitptr = up;
164
165         /*
166          * Initialize miscellaneous variables
167          */
168         peer->precision = PRECISION;
169         pp->clockdesc = DESCRIPTION;
170         memcpy((char *)&pp->refid, REFID, 4);
171         up->pollcnt = 2;
172         up->polled = 0;         /* May not be needed... */
173
174         return (1);
175 }
176
177
178 /*
179  * zyfer_shutdown - shut down the clock
180  */
181 static void
182 zyfer_shutdown(
183         int unit,
184         struct peer *peer
185         )
186 {
187         register struct zyferunit *up;
188         struct refclockproc *pp;
189
190         pp = peer->procptr;
191         up = pp->unitptr;
192         if (pp->io.fd != -1)
193                 io_closeclock(&pp->io);
194         if (up != NULL)
195                 free(up);
196 }
197
198
199 /*
200  * zyfer_receive - receive data from the serial interface
201  */
202 static void
203 zyfer_receive(
204         struct recvbuf *rbufp
205         )
206 {
207         register struct zyferunit *up;
208         struct refclockproc *pp;
209         struct peer *peer;
210         int tmode;              /* Time mode */
211         int tfom;               /* Time Figure Of Merit */
212         int omode;              /* Operation mode */
213         u_char *p;
214
215         TCivilDate      tsdoy;
216         TNtpDatum       tsntp;
217         l_fp            tfrac;
218         
219         peer = rbufp->recv_peer;
220         pp = peer->procptr;
221         up = pp->unitptr;
222         p = (u_char *) &rbufp->recv_space;
223         /*
224          * If lencode is 0:
225          * - if *rbufp->recv_space is !
226          * - - call refclock_gtlin to get things going
227          * - else flush
228          * else stuff it on the end of lastcode
229          * If we don't have LENZYFER bytes
230          * - wait for more data
231          * Crack the beast, and if it's OK, process it.
232          *
233          * We use refclock_gtlin() because we might use LDISC_CLK.
234          *
235          * Under FreeBSD, we get the ! followed by two 14-byte packets.
236          */
237
238         if (pp->lencode >= LENZYFER)
239                 pp->lencode = 0;
240
241         if (!pp->lencode) {
242                 if (*p == '!')
243                         pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode,
244                                                      BMAX, &pp->lastrec);
245                 else
246                         return;
247         } else {
248                 memcpy(pp->a_lastcode + pp->lencode, p, rbufp->recv_length);
249                 pp->lencode += rbufp->recv_length;
250                 pp->a_lastcode[pp->lencode] = '\0';
251         }
252
253         if (pp->lencode < LENZYFER)
254                 return;
255
256         record_clock_stats(&peer->srcadr, pp->a_lastcode);
257
258         /*
259          * We get down to business, check the timecode format and decode
260          * its contents. If the timecode has invalid length or is not in
261          * proper format, we declare bad format and exit.
262          */
263
264         if (pp->lencode != LENZYFER) {
265                 refclock_report(peer, CEVNT_BADTIME);
266                 return;
267         }
268
269         /*
270          * Timecode sample: "!TIME,2002,017,07,59,32,2,4,1"
271          */
272         if (sscanf(pp->a_lastcode, "!TIME,%4d,%3d,%2d,%2d,%2d,%d,%d,%d",
273                    &pp->year, &pp->day, &pp->hour, &pp->minute, &pp->second,
274                    &tmode, &tfom, &omode) != 8) {
275                 refclock_report(peer, CEVNT_BADREPLY);
276                 return;
277         }
278
279         if (tmode != 2) {
280                 refclock_report(peer, CEVNT_BADTIME);
281                 return;
282         }
283
284         /* Should we make sure tfom is 4? */
285
286         if (omode != 1) {
287                 pp->leap = LEAP_NOTINSYNC;
288                 return;
289         }
290
291         /* treat GPS input as subject to era warps */
292         ZERO(tsdoy);
293         ZERO(tfrac);
294
295         tsdoy.year    = pp->year;
296         tsdoy.yearday = pp->day;
297         tsdoy.hour    = pp->hour;
298         tsdoy.minute  = pp->minute;
299         tsdoy.second  = pp->second;
300         
301         /* note: We kept 'month' and 'monthday' zero above. That forces
302          * day-of-year based calculation now:
303          */
304         tsntp = gpsntp_from_calendar(&tsdoy, tfrac);
305         tfrac = ntpfp_from_ntpdatum(&tsntp);
306         refclock_process_offset(pp, tfrac, pp->lastrec, pp->fudgetime1);
307
308         /*
309          * Good place for record_clock_stats()
310          */
311         up->pollcnt = 2;
312
313         if (up->polled) {
314                 up->polled = 0;
315                 refclock_receive(peer);
316         }
317 }
318
319
320 /*
321  * zyfer_poll - called by the transmit procedure
322  */
323 static void
324 zyfer_poll(
325         int unit,
326         struct peer *peer
327         )
328 {
329         register struct zyferunit *up;
330         struct refclockproc *pp;
331
332         /*
333          * We don't really do anything here, except arm the receiving
334          * side to capture a sample and check for timeouts.
335          */
336         pp = peer->procptr;
337         up = pp->unitptr;
338         if (!up->pollcnt)
339                 refclock_report(peer, CEVNT_TIMEOUT);
340         else
341                 up->pollcnt--;
342         pp->polls++;
343         up->polled = 1;
344 }
345
346 #else
347 int refclock_zyfer_bs;
348 #endif /* REFCLOCK */