]> CyberLeo.Net >> Repos - FreeBSD/releng/9.3.git/blob - contrib/ntp/ntpd/refclock_shm.c
o Fix invalid TCP checksums with pf(4). [EN-16:02.pf]
[FreeBSD/releng/9.3.git] / contrib / ntp / ntpd / refclock_shm.c
1 /*
2  * refclock_shm - clock driver for utc via shared memory
3  * - under construction -
4  * To add new modes: Extend or union the shmTime-struct. Do not
5  * extend/shrink size, because otherwise existing implementations
6  * will specify wrong size of shared memory-segment
7  * PB 18.3.97
8  */
9
10 #ifdef HAVE_CONFIG_H
11 # include <config.h>
12 #endif
13
14 #include "ntp_types.h"
15
16 #if defined(REFCLOCK) && defined(CLOCK_SHM)
17
18 #include "ntpd.h"
19 #undef fileno
20 #include "ntp_io.h"
21 #undef fileno
22 #include "ntp_refclock.h"
23 #undef fileno
24 #include "timespecops.h"
25 #undef fileno
26 #include "ntp_stdlib.h"
27 #include "ntp_assert.h"
28
29 #undef fileno
30 #include <ctype.h>
31 #undef fileno
32
33 #ifndef SYS_WINNT
34 # include <sys/ipc.h>
35 # include <sys/shm.h>
36 # include <assert.h>
37 # include <unistd.h>
38 # include <stdio.h>
39 #endif
40
41 #ifdef HAVE_STDATOMIC_H
42 # include <stdatomic.h>
43 #endif /* HAVE_STDATOMIC_H */
44
45 /*
46  * This driver supports a reference clock attached thru shared memory
47  */
48
49 /*
50  * SHM interface definitions
51  */
52 #define PRECISION       (-1)    /* precision assumed (0.5 s) */
53 #define REFID           "SHM"   /* reference ID */
54 #define DESCRIPTION     "SHM/Shared memory interface"
55
56 #define NSAMPLES        3       /* stages of median filter */
57
58 /*
59  * Mode flags
60  */
61 #define SHM_MODE_PRIVATE 0x0001
62
63 /*
64  * Function prototypes
65  */
66 static  int     shm_start       (int unit, struct peer *peer);
67 static  void    shm_shutdown    (int unit, struct peer *peer);
68 static  void    shm_poll        (int unit, struct peer *peer);
69 static  void    shm_timer       (int unit, struct peer *peer);
70 static  void    shm_clockstats  (int unit, struct peer *peer);
71 static  void    shm_control     (int unit, const struct refclockstat * in_st,
72                                  struct refclockstat * out_st, struct peer *peer);
73
74 /*
75  * Transfer vector
76  */
77 struct  refclock refclock_shm = {
78         shm_start,              /* start up driver */
79         shm_shutdown,           /* shut down driver */
80         shm_poll,               /* transmit poll message */
81         shm_control,            /* control settings */
82         noentry,                /* not used: init */
83         noentry,                /* not used: buginfo */
84         shm_timer,              /* once per second */
85 };
86
87 struct shmTime {
88         int    mode; /* 0 - if valid is set:
89                       *       use values,
90                       *       clear valid
91                       * 1 - if valid is set:
92                       *       if count before and after read of values is equal,
93                       *         use values
94                       *       clear valid
95                       */
96         volatile int    count;
97         time_t          clockTimeStampSec;
98         int             clockTimeStampUSec;
99         time_t          receiveTimeStampSec;
100         int             receiveTimeStampUSec;
101         int             leap;
102         int             precision;
103         int             nsamples;
104         volatile int    valid;
105         unsigned        clockTimeStampNSec;     /* Unsigned ns timestamps */
106         unsigned        receiveTimeStampNSec;   /* Unsigned ns timestamps */
107         int             dummy[8];
108 };
109
110 struct shmunit {
111         struct shmTime *shm;    /* pointer to shared memory segment */
112         int forall;             /* access for all UIDs? */
113
114         /* debugging/monitoring counters - reset when printed */
115         int ticks;              /* number of attempts to read data*/
116         int good;               /* number of valid samples */
117         int notready;           /* number of peeks without data ready */
118         int bad;                /* number of invalid samples */
119         int clash;              /* number of access clashes while reading */
120
121         time_t max_delta;       /* difference limit */
122         time_t max_delay;       /* age/stale limit */
123 };
124
125
126 static struct shmTime*
127 getShmTime(
128         int unit,
129         int/*BOOL*/ forall
130         )
131 {
132         struct shmTime *p = NULL;
133
134 #ifndef SYS_WINNT
135
136         int shmid;
137
138         /* 0x4e545030 is NTP0.
139          * Big units will give non-ascii but that's OK
140          * as long as everybody does it the same way.
141          */
142         shmid=shmget(0x4e545030 + unit, sizeof (struct shmTime),
143                       IPC_CREAT | (forall ? 0666 : 0600));
144         if (shmid == -1) { /* error */
145                 msyslog(LOG_ERR, "SHM shmget (unit %d): %m", unit);
146                 return NULL;
147         }
148         p = (struct shmTime *)shmat (shmid, 0, 0);
149         if (p == (struct shmTime *)-1) { /* error */
150                 msyslog(LOG_ERR, "SHM shmat (unit %d): %m", unit);
151                 return NULL;
152         }
153
154         return p;
155 #else
156
157         static const char * nspref[2] = { "Local", "Global" };
158         char buf[20];
159         LPSECURITY_ATTRIBUTES psec = 0;
160         HANDLE shmid = 0;
161         SECURITY_DESCRIPTOR sd;
162         SECURITY_ATTRIBUTES sa;
163         unsigned int numch;
164
165         numch = snprintf(buf, sizeof(buf), "%s\\NTP%d",
166                          nspref[forall != 0], (unit & 0xFF));
167         if (numch >= sizeof(buf)) {
168                 msyslog(LOG_ERR, "SHM name too long (unit %d)", unit);
169                 return NULL;
170         }
171         if (forall) { /* world access */
172                 if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
173                         msyslog(LOG_ERR,"SHM InitializeSecurityDescriptor (unit %d): %m", unit);
174                         return NULL;
175                 }
176                 if (!SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE)) {
177                         msyslog(LOG_ERR, "SHM SetSecurityDescriptorDacl (unit %d): %m", unit);
178                         return NULL;
179                 }
180                 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
181                 sa.lpSecurityDescriptor = &sd;
182                 sa.bInheritHandle = FALSE;
183                 psec = &sa;
184         }
185         shmid = CreateFileMapping ((HANDLE)0xffffffff, psec, PAGE_READWRITE,
186                                    0, sizeof (struct shmTime), buf);
187         if (shmid == NULL) { /*error*/
188                 char buf[1000];         
189                 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM,
190                                0, GetLastError (), 0, buf, sizeof (buf), 0);
191                 msyslog(LOG_ERR, "SHM CreateFileMapping (unit %d): %s", unit, buf);
192                 return NULL;
193         }
194         p = (struct shmTime *)MapViewOfFile(shmid, FILE_MAP_WRITE, 0, 0,
195                                             sizeof (struct shmTime));
196         if (p == NULL) { /*error*/
197                 char buf[1000];         
198                 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM,
199                                0, GetLastError (), 0, buf, sizeof (buf), 0);
200                 msyslog(LOG_ERR,"SHM MapViewOfFile (unit %d): %s", unit, buf);
201                 return NULL;
202         }
203
204         return p;
205 #endif
206
207         /* NOTREACHED */
208         ENSURE(!"getShmTime(): Not reached.");
209 }
210
211
212 /*
213  * shm_start - attach to shared memory
214  */
215 static int
216 shm_start(
217         int unit,
218         struct peer *peer
219         )
220 {
221         struct refclockproc * const pp = peer->procptr;
222         struct shmunit *      const up = emalloc_zero(sizeof(*up));
223
224         pp->io.clock_recv = noentry;
225         pp->io.srcclock = peer;
226         pp->io.datalen = 0;
227         pp->io.fd = -1;
228
229         up->forall = (unit >= 2) && !(peer->ttl & SHM_MODE_PRIVATE);
230
231         up->shm = getShmTime(unit, up->forall);
232
233         /*
234          * Initialize miscellaneous peer variables
235          */
236         memcpy((char *)&pp->refid, REFID, 4);
237         if (up->shm != 0) {
238                 pp->unitptr = up;
239                 up->shm->precision = PRECISION;
240                 peer->precision = up->shm->precision;
241                 up->shm->valid = 0;
242                 up->shm->nsamples = NSAMPLES;
243                 pp->clockdesc = DESCRIPTION;
244                 /* items to be changed later in 'shm_control()': */
245                 up->max_delay = 5;
246                 up->max_delta = 4*3600;
247                 return 1;
248         } else {
249                 free(up);
250                 pp->unitptr = NULL;
251                 return 0;
252         }
253 }
254
255
256 /*
257  * shm_control - configure flag1/time2 params
258  *
259  * These are not yet available during 'shm_start', so we have to do any
260  * pre-computations we want to avoid during regular poll/timer callbacks
261  * in this callback.
262  */
263 static void
264 shm_control(
265         int                         unit,
266         const struct refclockstat * in_st,
267         struct refclockstat       * out_st,
268         struct peer               * peer
269         )
270 {
271         struct refclockproc * const pp = peer->procptr;
272         struct shmunit *      const up = pp->unitptr;
273
274         UNUSED_ARG(unit);
275         UNUSED_ARG(in_st);
276         UNUSED_ARG(out_st);
277         if (NULL == up)
278                 return;
279         if (pp->sloppyclockflag & CLK_FLAG1)
280                 up->max_delta = 0;
281         else if (pp->fudgetime2 < 1. || pp->fudgetime2 > 86400.)
282                 up->max_delta = 4*3600;
283         else
284                 up->max_delta = (time_t)floor(pp->fudgetime2 + 0.5);
285 }
286
287
288 /*
289  * shm_shutdown - shut down the clock
290  */
291 static void
292 shm_shutdown(
293         int unit,
294         struct peer *peer
295         )
296 {
297         struct refclockproc * const pp = peer->procptr;
298         struct shmunit *      const up = pp->unitptr;
299
300         UNUSED_ARG(unit);
301         if (NULL == up)
302                 return;
303 #ifndef SYS_WINNT
304
305         /* HMS: shmdt() wants char* or const void * */
306         (void)shmdt((char *)up->shm);
307
308 #else
309
310         UnmapViewOfFile(up->shm);
311
312 #endif
313         free(up);
314 }
315
316
317 /*
318  * shm_poll - called by the transmit procedure
319  */
320 static void
321 shm_poll(
322         int unit,
323         struct peer *peer
324         )
325 {
326         struct refclockproc * const pp = peer->procptr;
327         struct shmunit *      const up = pp->unitptr;
328         int major_error;
329
330         pp->polls++;
331
332         /* get dominant reason if we have no samples at all */
333         major_error = max(up->notready, up->bad);
334         major_error = max(major_error, up->clash);
335
336         /*
337          * Process median filter samples. If none received, see what
338          * happened, tell the core and keep going.
339          */
340         if (pp->coderecv != pp->codeproc) {
341                 /* have some samples, everything OK */
342                 pp->lastref = pp->lastrec;
343                 refclock_receive(peer);
344         } else if (NULL == up->shm) { /* is this possible at all? */
345                 /* we're out of business without SHM access */
346                 refclock_report(peer, CEVNT_FAULT);
347         } else if (major_error == up->clash) {
348                 /* too many collisions is like a bad signal */
349                 refclock_report(peer, CEVNT_PROP);
350         } else if (major_error == up->bad) {
351                 /* too much stale/bad/garbled data */
352                 refclock_report(peer, CEVNT_BADREPLY);
353         } else {
354                 /* in any other case assume it's just a timeout */
355                 refclock_report(peer, CEVNT_TIMEOUT);
356         }
357         /* shm_clockstats() clears the tallies, so it must be last... */
358         shm_clockstats(unit, peer);
359 }
360
361
362 enum segstat_t {
363     OK, NO_SEGMENT, NOT_READY, BAD_MODE, CLASH
364 };
365
366 struct shm_stat_t {
367     int status;
368     int mode;
369     struct timespec tvc, tvr, tvt;
370     int precision;
371     int leap;
372 };
373
374 static inline void memory_barrier(void)
375 {
376 #ifdef HAVE_ATOMIC_THREAD_FENCE
377     atomic_thread_fence(memory_order_seq_cst);
378 #endif /* HAVE_ATOMIC_THREAD_FENCE */
379 }
380
381 static enum segstat_t shm_query(volatile struct shmTime *shm_in, struct shm_stat_t *shm_stat)
382 /* try to grab a sample from the specified SHM segment */
383 {
384     struct shmTime shmcopy;
385     volatile struct shmTime *shm = shm_in;
386     volatile int cnt;
387
388     unsigned int cns_new, rns_new;
389
390     /*
391      * This is the main routine. It snatches the time from the shm
392      * board and tacks on a local timestamp.
393      */
394     if (shm == NULL) {
395         shm_stat->status = NO_SEGMENT;
396         return NO_SEGMENT;
397     }
398
399     /*@-type@*//* splint is confused about struct timespec */
400     shm_stat->tvc.tv_sec = shm_stat->tvc.tv_nsec = 0;
401     {
402         time_t now;
403
404         time(&now);
405         shm_stat->tvc.tv_sec = now;
406     }
407
408     /* relying on word access to be atomic here */
409     if (shm->valid == 0) {
410         shm_stat->status = NOT_READY;
411         return NOT_READY;
412     }
413
414     cnt = shm->count;
415
416     /*
417      * This is proof against concurrency issues if either
418      * (a) the memory_barrier() call works on this host, or
419      * (b) memset compiles to an uninterruptible single-instruction bitblt.
420      */
421     memory_barrier();
422     memcpy(&shmcopy, (void*)(uintptr_t)shm, sizeof(struct shmTime));
423     shm->valid = 0;
424     memory_barrier();
425
426     /* 
427      * Clash detection in case neither (a) nor (b) was true.
428      * Not supported in mode 0, and word access to the count field 
429      * must be atomic for this to work.
430      */
431     if (shmcopy.mode > 0 && cnt != shm->count) {
432         shm_stat->status = CLASH;
433         return shm_stat->status;
434     }
435
436     shm_stat->status = OK;
437     shm_stat->mode = shmcopy.mode;
438
439     switch (shmcopy.mode) {
440     case 0:
441         shm_stat->tvr.tv_sec    = shmcopy.receiveTimeStampSec;
442         shm_stat->tvr.tv_nsec   = shmcopy.receiveTimeStampUSec * 1000;
443         rns_new         = shmcopy.receiveTimeStampNSec;
444         shm_stat->tvt.tv_sec    = shmcopy.clockTimeStampSec;
445         shm_stat->tvt.tv_nsec   = shmcopy.clockTimeStampUSec * 1000;
446         cns_new         = shmcopy.clockTimeStampNSec;
447
448         /* Since the following comparisons are between unsigned
449         ** variables they are always well defined, and any
450         ** (signed) underflow will turn into very large unsigned
451         ** values, well above the 1000 cutoff.
452         **
453         ** Note: The usecs *must* be a *truncated*
454         ** representation of the nsecs. This code will fail for
455         ** *rounded* usecs, and the logic to deal with
456         ** wrap-arounds in the presence of rounded values is
457         ** much more convoluted.
458         */
459         if (   ((cns_new - (unsigned)shm_stat->tvt.tv_nsec) < 1000)
460                && ((rns_new - (unsigned)shm_stat->tvr.tv_nsec) < 1000)) {
461             shm_stat->tvt.tv_nsec = cns_new;
462             shm_stat->tvr.tv_nsec = rns_new;
463         }
464         /* At this point shm_stat->tvr and shm_stat->tvt contain valid ns-level
465         ** timestamps, possibly generated by extending the old
466         ** us-level timestamps
467         */
468         break;
469
470     case 1:
471
472         shm_stat->tvr.tv_sec    = shmcopy.receiveTimeStampSec;
473         shm_stat->tvr.tv_nsec   = shmcopy.receiveTimeStampUSec * 1000;
474         rns_new         = shmcopy.receiveTimeStampNSec;
475         shm_stat->tvt.tv_sec    = shmcopy.clockTimeStampSec;
476         shm_stat->tvt.tv_nsec   = shmcopy.clockTimeStampUSec * 1000;
477         cns_new         = shmcopy.clockTimeStampNSec;
478                 
479         /* See the case above for an explanation of the
480         ** following test.
481         */
482         if (   ((cns_new - (unsigned)shm_stat->tvt.tv_nsec) < 1000)
483                && ((rns_new - (unsigned)shm_stat->tvr.tv_nsec) < 1000)) {
484             shm_stat->tvt.tv_nsec = cns_new;
485             shm_stat->tvr.tv_nsec = rns_new;
486         }
487         /* At this point shm_stat->tvr and shm_stat->tvt contains valid ns-level
488         ** timestamps, possibly generated by extending the old
489         ** us-level timestamps
490         */
491         break;
492
493     default:
494         shm_stat->status = BAD_MODE;
495         break;
496     }
497     /*@-type@*/
498
499     /*
500      * leap field is not a leap offset but a leap notification code.
501      * The values are magic numbers used by NTP and set by GPSD, if at all, in
502      * the subframe code.
503      */
504     shm_stat->leap = shmcopy.leap;
505     shm_stat->precision = shmcopy.precision;
506
507     return shm_stat->status;
508 }
509
510 /*
511  * shm_timer - called once every second.
512  *
513  * This tries to grab a sample from the SHM segment, filtering bad ones
514  */
515 static void
516 shm_timer(
517         int unit,
518         struct peer *peer
519         )
520 {
521         struct refclockproc * const pp = peer->procptr;
522         struct shmunit *      const up = pp->unitptr;
523
524         volatile struct shmTime *shm;
525
526         l_fp tsrcv;
527         l_fp tsref;
528         int c;
529
530         /* for formatting 'a_lastcode': */
531         struct calendar cd;
532         time_t tt;
533         vint64 ts;
534
535         enum segstat_t status;
536         struct shm_stat_t shm_stat;
537
538         up->ticks++;
539         if ((shm = up->shm) == NULL) {
540                 /* try to map again - this may succeed if meanwhile some-
541                 body has ipcrm'ed the old (unaccessible) shared mem segment */
542                 shm = up->shm = getShmTime(unit, up->forall);
543                 if (shm == NULL) {
544                         DPRINTF(1, ("%s: no SHM segment\n",
545                                     refnumtoa(&peer->srcadr)));
546                         return;
547                 }
548         }
549
550         /* query the segment, atomically */
551         status = shm_query(shm, &shm_stat);
552
553         switch (status) {
554         case OK:
555             DPRINTF(2, ("%s: SHM type %d sample\n",
556                         refnumtoa(&peer->srcadr), shm_stat.mode));
557             break;
558         case NO_SEGMENT:
559             /* should never happen, but is harmless */
560             return;
561         case NOT_READY:
562             DPRINTF(1, ("%s: SHM not ready\n",refnumtoa(&peer->srcadr)));
563             up->notready++;
564             return;
565         case BAD_MODE:
566             DPRINTF(1, ("%s: SHM type blooper, mode=%d\n",
567                         refnumtoa(&peer->srcadr), shm->mode));
568             up->bad++;
569             msyslog (LOG_ERR, "SHM: bad mode found in shared memory: %d",
570                      shm->mode);
571             return;
572         case CLASH:
573             DPRINTF(1, ("%s: type 1 access clash\n",
574                         refnumtoa(&peer->srcadr)));
575             msyslog (LOG_NOTICE, "SHM: access clash in shared memory");
576             up->clash++;
577             return;
578         default:
579             DPRINTF(1, ("%s: internal error, unknown SHM fetch status\n",
580                         refnumtoa(&peer->srcadr)));
581             msyslog (LOG_NOTICE, "internal error, unknown SHM fetch status");
582             up->bad++;
583             return;
584         }
585
586
587         /* format the last time code in human-readable form into
588          * 'pp->a_lastcode'. Someone claimed: "NetBSD has incompatible
589          * tv_sec". I can't find a base for this claim, but we can work
590          * around that potential problem. BTW, simply casting a pointer
591          * is a receipe for disaster on some architectures.
592          */
593         tt = (time_t)shm_stat.tvt.tv_sec;
594         ts = time_to_vint64(&tt);
595         ntpcal_time_to_date(&cd, &ts);
596                 
597         /* add ntpq -c cv timecode in ISO 8601 format */
598         c = snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
599                      "%04u-%02u-%02uT%02u:%02u:%02u.%09ldZ",
600                      cd.year, cd.month, cd.monthday,
601                      cd.hour, cd.minute, cd.second,
602                      (long)shm_stat.tvt.tv_nsec);
603         pp->lencode = (c < sizeof(pp->a_lastcode)) ? c : 0;
604
605         /* check 1: age control of local time stamp */
606         tt = shm_stat.tvc.tv_sec - shm_stat.tvr.tv_sec;
607         if (tt < 0 || tt > up->max_delay) {
608                 DPRINTF(1, ("%s:SHM stale/bad receive time, delay=%llds\n",
609                             refnumtoa(&peer->srcadr), (long long)tt));
610                 up->bad++;
611                 msyslog (LOG_ERR, "SHM: stale/bad receive time, delay=%llds",
612                          (long long)tt);
613                 return;
614         }
615
616         /* check 2: delta check */
617         tt = shm_stat.tvr.tv_sec - shm_stat.tvt.tv_sec - (shm_stat.tvr.tv_nsec < shm_stat.tvt.tv_nsec);
618         if (tt < 0)
619                 tt = -tt;
620         if (up->max_delta > 0 && tt > up->max_delta) {
621                 DPRINTF(1, ("%s: SHM diff limit exceeded, delta=%llds\n",
622                             refnumtoa(&peer->srcadr), (long long)tt));
623                 up->bad++;
624                 msyslog (LOG_ERR, "SHM: difference limit exceeded, delta=%llds\n",
625                          (long long)tt);
626                 return;
627         }
628
629         /* if we really made it to this point... we're winners! */
630         DPRINTF(2, ("%s: SHM feeding data\n",
631                     refnumtoa(&peer->srcadr)));
632         tsrcv = tspec_stamp_to_lfp(shm_stat.tvr);
633         tsref = tspec_stamp_to_lfp(shm_stat.tvt);
634         pp->leap = shm_stat.leap;
635         peer->precision = shm_stat.precision;
636         refclock_process_offset(pp, tsref, tsrcv, pp->fudgetime1);
637         up->good++;
638 }
639
640 /*
641  * shm_clockstats - dump and reset counters
642  */
643 static void shm_clockstats(
644         int unit,
645         struct peer *peer
646         )
647 {
648         struct refclockproc * const pp = peer->procptr;
649         struct shmunit *      const up = pp->unitptr;
650
651         UNUSED_ARG(unit);
652         if (pp->sloppyclockflag & CLK_FLAG4) {
653                 mprintf_clock_stats(
654                         &peer->srcadr, "%3d %3d %3d %3d %3d",
655                         up->ticks, up->good, up->notready,
656                         up->bad, up->clash);
657         }
658         up->ticks = up->good = up->notready = up->bad = up->clash = 0;
659 }
660
661 #else
662 NONEMPTY_TRANSLATION_UNIT
663 #endif /* REFCLOCK */