]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/ntp/ntpd/refclock_bancomm.c
Update ntpd to 4.2.8p13 to fix authenticated denial of service.
[FreeBSD/FreeBSD.git] / contrib / ntp / ntpd / refclock_bancomm.c
1 /* refclock_bancomm.c - clock driver for the  Datum/Bancomm bc635VME 
2  * Time and Frequency Processor. It requires the BANCOMM bc635VME/
3  * bc350VXI Time and Frequency Processor Module Driver for SunOS4.x 
4  * and SunOS5.x UNIX Systems. It has been tested on a UltraSparc 
5  * IIi-cEngine running Solaris 2.6.
6  * 
7  * Author(s):   Ganesh Ramasivan & Gary Cliff, Computing Devices Canada,
8  *              Ottawa, Canada
9  *
10  * Date:        July 1999
11  *
12  * Note(s):     The refclock type has been defined as 16.
13  *
14  *              This program has been modelled after the Bancomm driver
15  *              originally written by R. Schmidt of Time Service, U.S. 
16  *              Naval Observatory for a HP-UX machine. Since the original
17  *              authors no longer plan to maintain this code, all 
18  *              references to the HP-UX vme2 driver subsystem bave been
19  *              removed. Functions vme_report_event(), vme_receive(), 
20  *              vme_control() and vme_buginfo() have been deleted because
21  *              they are no longer being used.
22  *
23  *      04/28/2005 Rob Neal 
24  *              Modified to add support for Symmetricom bc637PCI-U Time & 
25  *              Frequency Processor. 
26  *      2/21/2007 Ali Ghorashi
27  *              Modified to add support for Symmetricom bc637PCI-U Time & 
28  *              Frequency Processor on Solaris.
29  *              Tested on Solaris 10 with a bc635 card.
30  *
31  *              Card bus type (VME/VXI or PCI) and environment are specified via the
32  *              "mode" keyword on the server command in ntp.conf.
33  *              server 127.127.16.u prefer mode M
34  *              where u is the id (usually 0) of the entry in /dev (/dev/stfp0)
35  *      
36  *              and M is one of the following modes: 
37  *              1               : FreeBSD PCI 635/637.
38  *              2               : Linux or Windows PCI 635/637.
39  *              3               : Solaris PCI 635/637
40  *              not specified, or other number: 
41  *                              : Assumed to be VME/VXI legacy Bancomm card on Solaris.
42  *              Linux and Windows platforms require Symmetricoms' proprietary driver
43  *              for the TFP card.
44  *              Solaris requires Symmetricom's driver and its header file (freely distributed) to 
45  *              be installed and running.
46  */
47
48 #ifdef HAVE_CONFIG_H
49 #include <config.h>
50 #endif
51
52 #if defined(REFCLOCK) && defined(CLOCK_BANC) 
53
54 #include "ntpd.h"
55 #include "ntp_io.h"
56 #include "ntp_refclock.h"
57 #include "ntp_unixtime.h"
58 #include "ntp_stdlib.h"
59
60 #include <stdio.h>
61 #include <syslog.h>
62 #include <ctype.h>
63 #ifdef HAVE_SYS_IOCTL_H
64 # include <sys/ioctl.h>
65 #endif
66
67 struct btfp_time                /* Structure for reading 5 time words   */
68                                 /* in one ioctl(2) operation.           */
69 {
70         unsigned short btfp_time[5];  /* Time words 0,1,2,3, and 4. (16bit)*/
71 };
72 /* SunOS5 ioctl commands definitions.*/
73 #define BTFPIOC            ( 'b'<< 8 )
74 #define IOCIO( l, n )      ( BTFPIOC | n )
75 #define IOCIOR( l, n, s )  ( BTFPIOC | n )
76 #define IOCIORN( l, n, s ) ( BTFPIOC | n )
77 #define IOCIOWN( l, n, s ) ( BTFPIOC | n )
78
79 /***** Simple ioctl commands *****/
80 #define RUNLOCK         IOCIOR(b, 19, int )  /* Release Capture Lockout */
81 #define RCR0            IOCIOR(b, 22, int )  /* Read control register zero.*/
82 #define WCR0            IOCIOWN(b, 23, int)  /* Write control register zero*/
83 /***** Compound ioctl commands *****/
84
85 /* Read all 5 time words in one call.   */
86 #if defined(__FreeBSD__) 
87 # define READTIME       _IOR('u', 5, struct btfp_time )
88 #else
89 # define READTIME       IOCIORN(b, 32, sizeof( struct btfp_time ))
90 #endif 
91
92 /* Solaris specific section */
93 struct  stfp_tm {
94         int32_t tm_sec; 
95         int32_t tm_min;
96         int32_t tm_hour;
97         int32_t tm_mday;
98         int32_t tm_mon;
99         int32_t tm_year;
100         int32_t tm_wday;
101         int32_t tm_yday;
102         int32_t tm_isdst;
103 };
104
105 struct stfp_time {
106         struct stfp_tm  tm;
107         int32_t         usec;                   /* usec 0 - 999999 */
108         int32_t         hnsec;                  /* hnsec 0 - 9 (hundreds of nsecs) */
109         int32_t         status;
110 };
111
112 #define SELTIMEFORMAT   2       
113 #       define TIME_DECIMAL 0
114 #       define TIME_BINARY      1
115
116 #if defined(__sun__)
117 #undef  READTIME
118 #define READTIME                9
119 #endif /** __sun___ **/
120 /* end solaris specific section */
121
122 struct vmedate {                           /* structure returned by get_vmetime.c */
123         unsigned short year;
124         unsigned short day;
125         unsigned short hr;
126         unsigned short mn;
127         unsigned short sec;
128         long frac;
129         unsigned short status;
130 };
131
132 typedef void *SYMMT_PCI_HANDLE;
133
134 /*
135  * VME interface parameters. 
136  */
137 #define VMEPRECISION    (-21)   /* precision assumed (1 us) */
138 #define USNOREFID       "BTFP"  /* or whatever */
139 #define VMEREFID        "BTFP"  /* reference id */
140 #define VMEDESCRIPTION  "Bancomm bc635 TFP" /* who we are */
141 #define VMEHSREFID      0x7f7f1000 /* 127.127.16.00 refid hi strata */
142 /* clock type 16 is used here  */
143 #define GMT             0       /* hour offset from Greenwich */
144
145 /*
146  * Imported from ntp_timer module
147  */
148 extern u_long current_time;     /* current time(s) */
149
150 /*
151  * VME unit control structure.
152  * Changes made to vmeunit structure. Most members are now available in the 
153  * new refclockproc structure in ntp_refclock.h - 07/99 - Ganesh Ramasivan
154  */
155 struct vmeunit {
156         struct vmedate vmedata; /* data returned from vme read */
157         u_long lasttime;        /* last time clock heard from */
158 };
159
160 /*
161  * Function prototypes
162  */
163 static  int     vme_start       (int, struct peer *);
164 static  void    vme_shutdown    (int, struct peer *);
165 static  void    vme_receive     (struct recvbuf *);
166 static  void    vme_poll        (int unit, struct peer *);
167 struct vmedate *get_datumtime(struct vmedate *);        
168 void    tvme_fill(struct vmedate *, uint32_t btm[2]);
169 void    stfp_time2tvme(struct vmedate *time_vme, struct stfp_time *stfp);
170 static const char *get_devicename(int n);
171
172 /* [Bug 3558] and [Bug 1674]  perlinger@ntp.org says:
173  *
174  * bcReadBinTime() is defined to use two DWORD pointers on Windows and
175  * Linux in the BANCOMM SDK.  DWORD is of course Windows-specific
176  * (*shudder*), and it is defined as 'unsigned long' under
177  * Linux/Unix. (*sigh*)
178  *
179  * This creates quite some headache.  The size of 'unsigned long' is
180  * platform/compiler/memory-model dependent (LP32 vs LP64 vs LLP64),
181  * while the card itself always creates 32bit time stamps.  What a
182  * bummer.  And DWORD has tendency to contain 64bit on Win64 (which is
183  * why we have a DWORD32 defined on Win64) so it can be used as
184  * substitute for 'UINT_PTR' in Windows API headers.  I won't even try
185  * to comment on that, because anything I have to say will not be civil.
186  *
187  * We work around this by possibly using a wrapper function that makes
188  * the necessary conversions/casts.  It might be a bit tricky to
189  * maintain the conditional logic below, but any lingering disease needs
190  * constant care to avoid a breakout.
191  */
192 #if defined(__linux__)
193   typedef unsigned long bcBinTimeT;
194 # if SIZEOF_LONG == 4
195 #   define safeReadBinTime bcReadBinTime
196 # endif
197 #elif defined(SYS_WINNT)
198   typedef DWORD bcBinTimeT;
199 # if !defined(_WIN64) || _WIN64 == 0
200 #   define safeReadBinTime bcReadBinTime
201 # endif
202 #else
203   typedef uint32_t bcBinTimeT;
204 # define safeReadBinTime bcReadBinTime
205 #endif
206
207 /*
208  * Define the bc*() functions as weak so we can compile/link without them.
209  * Only clients with the card will have the proprietary vendor device driver
210  * and interface library needed for use on Linux/Windows platforms.
211  */
212 extern uint32_t __attribute__ ((weak)) bcReadBinTime(SYMMT_PCI_HANDLE, bcBinTimeT*, bcBinTimeT*, uint8_t*);
213 extern SYMMT_PCI_HANDLE __attribute__ ((weak)) bcStartPci(void);
214 extern void __attribute__ ((weak)) bcStopPci(SYMMT_PCI_HANDLE);
215
216 /* This is the conversion wrapper for the long/DWORD/uint32_t clash in
217  * reading binary times.
218  */
219 #ifndef safeReadBinTime
220 static uint32_t
221 safeReadBinTime(
222         SYMMT_PCI_HANDLE hnd,
223         uint32_t        *pt1,
224         uint32_t        *pt2,
225         uint8_t         *p3
226         )
227 {
228         bcBinTimeT t1, t2;
229         uint32_t   rc;
230
231         rc = bcReadBinTime(hnd, &t1, &t2, p3);
232         if (rc != 0) {
233                 *pt1 = (uint32_t)t1;
234                 *pt2 = (uint32_t)t2;
235         }
236         return rc;
237 }
238 #endif /* !defined(safeReadBinTime) */
239
240 /*
241  * Transfer vector
242  */
243 struct  refclock refclock_bancomm = {
244         vme_start,              /* start up driver */
245         vme_shutdown,           /* shut down driver */
246         vme_poll,               /* transmit poll message */
247         noentry,                /* not used (old vme_control) */
248         noentry,                /* initialize driver */ 
249         noentry,                /* not used (old vme_buginfo) */ 
250         NOFLAGS                 /* not used */
251 };
252
253 int fd_vme;  /* file descriptor for ioctls */
254 int regvalue;
255 int tfp_type;   /* mode selector, indicate platform and driver interface */
256 SYMMT_PCI_HANDLE stfp_handle;
257
258 /* This helper function returns the device name based on the platform we
259  * are running on and the device number.
260  *
261  * Uses a static buffer, so the result is valid only to the next call of
262  * this function!
263  */
264 static const char*
265 get_devicename(int n)
266 {
267         
268 #   if defined(__sun__)
269         static const char * const template ="/dev/stfp%d";
270 #   else
271         static const char * const template ="/dev/btfp%d";
272 #   endif
273         static char namebuf[20];
274         
275         snprintf(namebuf, sizeof(namebuf), template, n);
276         namebuf[sizeof(namebuf)-1] = '\0'; /* paranoia rulez! */
277         return namebuf;
278 }
279
280 /*
281  * vme_start - open the VME device and initialize data for processing
282  */
283 static int
284 vme_start(
285         int unit,
286         struct peer *peer
287         )
288 {
289         register struct vmeunit *vme;
290         struct refclockproc *pp;
291         int dummy;
292         char vmedev[20];
293         
294         tfp_type = (int)(peer->ttl);
295         switch (tfp_type) {             
296                 case 1:
297                 case 3:
298                         break;
299                 case 2:
300                         stfp_handle = bcStartPci();     /* init the card in lin/win */
301                         break;
302                 default:
303                         break;
304         }
305         /*
306          * Open VME device
307          */
308 #ifdef DEBUG
309
310         printf("Opening DATUM DEVICE %s\n",get_devicename(peer->refclkunit));
311 #endif
312         if ( (fd_vme = open(get_devicename(peer->refclkunit), O_RDWR)) < 0) {
313                 msyslog(LOG_ERR, "vme_start: failed open of %s: %m", vmedev);
314                 return (0);
315         }
316         else  { 
317                 switch (tfp_type) {
318                         case 1: break;
319                         case 2: break;
320                         case 3:break;
321                         default: 
322                                 /* Release capture lockout in case it was set before. */
323                                 if( ioctl( fd_vme, RUNLOCK, &dummy ) )
324                                 msyslog(LOG_ERR, "vme_start: RUNLOCK failed %m");
325
326                                 regvalue = 0; /* More esoteric stuff to do... */
327                                 if( ioctl( fd_vme, WCR0, &regvalue ) )
328                                 msyslog(LOG_ERR, "vme_start: WCR0 failed %m");
329                                 break;
330                 }
331         }
332
333         /*
334          * Allocate unit structure
335          */
336         vme = emalloc_zero(sizeof(struct vmeunit));
337
338
339         /*
340          * Set up the structures
341          */
342         pp = peer->procptr;
343         pp->unitptr = vme;
344         pp->timestarted = current_time;
345
346         pp->io.clock_recv = vme_receive;
347         pp->io.srcclock = peer;
348         pp->io.datalen = 0;
349         pp->io.fd = fd_vme;
350         /* shouldn't there be an io_addclock() call? */
351
352         /*
353          * All done.  Initialize a few random peer variables, then
354          * return success. Note that root delay and root dispersion are
355          * always zero for this clock.
356          */
357         peer->precision = VMEPRECISION;
358         memcpy(&pp->refid, USNOREFID,4);
359         return (1);
360 }
361
362
363 /*
364  * vme_shutdown - shut down a VME clock
365  */
366 static void
367 vme_shutdown(
368         int unit, 
369         struct peer *peer
370         )
371 {
372         register struct vmeunit *vme;
373         struct refclockproc *pp;
374
375         /*
376          * Tell the I/O module to turn us off.  We're history.
377          */
378         pp = peer->procptr;
379         vme = pp->unitptr;
380         io_closeclock(&pp->io);
381         pp->unitptr = NULL;
382         if (NULL != vme)
383                 free(vme);
384         if (tfp_type == 2)
385                 bcStopPci(stfp_handle); 
386 }
387
388
389 /*
390  * vme_receive - receive data from the VME device.
391  *
392  * Note: This interface would be interrupt-driven. We don't use that
393  * now, but include a dummy routine for possible future adventures.
394  */
395 static void
396 vme_receive(
397         struct recvbuf *rbufp
398         )
399 {
400 }
401
402
403 /*
404  * vme_poll - called by the transmit procedure
405  */
406 static void
407 vme_poll(
408         int unit,
409         struct peer *peer
410         )
411 {
412         struct vmedate *tptr; 
413         struct vmeunit *vme;
414         struct refclockproc *pp;
415         time_t tloc;
416         struct tm *tadr;
417         
418         pp = peer->procptr;      
419         vme = pp->unitptr;        /* Here is the structure */
420
421         tptr = &vme->vmedata; 
422         if ((tptr = get_datumtime(tptr)) == NULL ) {
423                 refclock_report(peer, CEVNT_BADREPLY);
424                 return;
425         }
426
427         get_systime(&pp->lastrec);
428         pp->polls++;
429         vme->lasttime = current_time;
430
431         /*
432          * Get VME time and convert to timestamp format. 
433          * The year must come from the system clock.
434          */
435         
436           time(&tloc);
437           tadr = gmtime(&tloc);
438           tptr->year = (unsigned short)(tadr->tm_year + 1900);
439
440         snprintf(pp->a_lastcode,
441                  sizeof(pp->a_lastcode),
442                  "%3.3d %2.2d:%2.2d:%2.2d.%.6ld %1d",
443                  tptr->day, 
444                  tptr->hr, 
445                  tptr->mn,
446                  tptr->sec, 
447                  tptr->frac, 
448                  tptr->status);
449
450         pp->lencode = (u_short) strlen(pp->a_lastcode);
451
452         pp->day =  tptr->day;
453         pp->hour =   tptr->hr;
454         pp->minute =  tptr->mn;
455         pp->second =  tptr->sec;
456         pp->nsec =   tptr->frac;        
457
458 #ifdef DEBUG
459         if (debug)
460             printf("pp: %3d %02d:%02d:%02d.%06ld %1x\n",
461                    pp->day, pp->hour, pp->minute, pp->second,
462                    pp->nsec, tptr->status);
463 #endif
464         if (tptr->status ) {       /*  Status 0 is locked to ref., 1 is not */
465                 refclock_report(peer, CEVNT_BADREPLY);
466                 return;
467         }
468
469         /*
470          * Now, compute the reference time value. Use the heavy
471          * machinery for the seconds and the millisecond field for the
472          * fraction when present. If an error in conversion to internal
473          * format is found, the program declares bad data and exits.
474          * Note that this code does not yet know how to do the years and
475          * relies on the clock-calendar chip for sanity.
476          */
477         if (!refclock_process(pp)) {
478                 refclock_report(peer, CEVNT_BADTIME);
479                 return;
480         }
481         pp->lastref = pp->lastrec;
482         refclock_receive(peer);
483         record_clock_stats(&peer->srcadr, pp->a_lastcode);
484 }
485
486 struct vmedate *
487 get_datumtime(struct vmedate *time_vme)
488 {
489         char cbuf[7];
490         struct btfp_time vts;
491         uint32_t btm[2];
492         uint8_t dmy;
493         struct stfp_time stfpm;
494         
495         if (time_vme == NULL)
496                 time_vme = emalloc(sizeof(*time_vme));
497
498         switch (tfp_type) {
499                 case 1:                         /* BSD, PCI, 2 32bit time words */
500                         if (ioctl(fd_vme, READTIME, &btm)) {
501                         msyslog(LOG_ERR, "get_bc63x error: %m");
502                                 return(NULL);
503                         }
504                         tvme_fill(time_vme, btm);
505                         break;
506
507                 case 2:                         /* Linux/Windows, PCI, 2 32bit time words */
508                         if (safeReadBinTime(stfp_handle, &btm[1], &btm[0], &dmy) == 0) {
509                         msyslog(LOG_ERR, "get_datumtime error: %m"); 
510                                 return(NULL);
511                         }
512                         tvme_fill(time_vme, btm);
513                         break;
514                         
515                 case 3: /** solaris **/
516                         memset(&stfpm,0,sizeof(stfpm));
517                         
518                         /* we need the time in decimal format */
519                         /* Here we rudely assume that we are the only user of the driver.
520                          * Other programs will have to set their own time format before reading 
521                          * the time.
522                          */
523                         if(ioctl (fd_vme, SELTIMEFORMAT, TIME_DECIMAL)){        
524                                         msyslog(LOG_ERR, "Could not set time format");
525                                         return (NULL);  
526                         }
527                         /* read the time */
528                         if (ioctl(fd_vme, READTIME, &stfpm)) {
529                                 msyslog(LOG_ERR, "ioctl error: %m");
530                                 return(NULL);
531                         }
532                         stfp_time2tvme(time_vme,  &stfpm);
533                         break;                  
534
535                 default:                        /* legacy bancomm card */
536
537                         if (ioctl(fd_vme, READTIME, &vts)) {
538                                 msyslog(LOG_ERR,
539                                         "get_datumtime error: %m");
540                                 return(NULL);
541                         }
542                         /* Get day */
543                         snprintf(cbuf, sizeof(cbuf), "%3.3x",
544                                  ((vts.btfp_time[ 0 ] & 0x000f) << 8) +
545                                   ((vts.btfp_time[ 1 ] & 0xff00) >> 8));  
546                         time_vme->day = (unsigned short)atoi(cbuf);
547
548                         /* Get hour */
549                         snprintf(cbuf, sizeof(cbuf), "%2.2x",
550                                  vts.btfp_time[ 1 ] & 0x00ff);
551                         time_vme->hr = (unsigned short)atoi(cbuf);
552
553                         /* Get minutes */
554                         snprintf(cbuf, sizeof(cbuf), "%2.2x",
555                                  (vts.btfp_time[ 2 ] & 0xff00) >> 8);
556                         time_vme->mn = (unsigned short)atoi(cbuf);
557
558                         /* Get seconds */
559                         snprintf(cbuf, sizeof(cbuf), "%2.2x",
560                                  vts.btfp_time[ 2 ] & 0x00ff);
561                         time_vme->sec = (unsigned short)atoi(cbuf);
562
563                         /* Get microseconds.  Yes, we ignore the 0.1 microsecond digit so
564                                  we can use the TVTOTSF function  later on...*/
565
566                         snprintf(cbuf, sizeof(cbuf), "%4.4x%2.2x",
567                                  vts.btfp_time[ 3 ],
568                                  vts.btfp_time[ 4 ] >> 8);
569                         time_vme->frac = (u_long) atoi(cbuf);
570
571                         /* Get status bit */
572                         time_vme->status = (vts.btfp_time[0] & 0x0010) >> 4;
573
574                         break;
575         }
576
577         if (time_vme->status) 
578                 return ((void *)NULL);
579         else
580             return (time_vme);
581 }
582 /* Assign values to time_vme struct. Mostly for readability */
583 void
584 tvme_fill(struct vmedate *time_vme, uint32_t btm[2])
585 {
586         struct tm maj;
587         time_t   dmaj;
588         uint32_t dmin;
589
590         dmaj = btm[1];                  /* syntax sugar & expansion */
591         dmin = btm[0];                  /* just syntax sugar */
592
593         gmtime_r(&dmaj, &maj);
594         time_vme->day  = maj.tm_yday+1;
595         time_vme->hr   = maj.tm_hour;
596         time_vme->mn   = maj.tm_min;
597         time_vme->sec  = maj.tm_sec;
598         time_vme->frac = (dmin & 0x000fffff) * 1000; 
599         time_vme->frac += ((dmin & 0x00f00000) >> 20) * 100;
600         time_vme->status = (dmin & 0x01000000) >> 24;
601         return;
602 }
603
604
605 /* Assign values to time_vme struct. Mostly for readability */
606 void
607 stfp_time2tvme(struct vmedate *time_vme, struct stfp_time *stfp)
608 {
609
610         time_vme->day  = stfp->tm.tm_yday+1;
611         time_vme->hr   = stfp->tm.tm_hour;
612         time_vme->mn   = stfp->tm.tm_min;
613         time_vme->sec  = stfp->tm.tm_sec;
614         time_vme->frac = stfp->usec*1000;  
615         time_vme->frac += stfp->hnsec * 100;
616         time_vme->status = stfp->status;
617         return;
618 }
619 #else
620 int refclock_bancomm_bs;
621 #endif /* REFCLOCK */