4 * ntp_monitor - monitor ntpd statistics
13 #include "ntp_stdlib.h"
14 #include <ntp_random.h>
18 #ifdef HAVE_SYS_IOCTL_H
19 # include <sys/ioctl.h>
23 * Record statistics based on source address, mode and version. The
24 * receive procedure calls us with the incoming rbufp before it does
25 * anything else. While at it, implement rate controls for inbound
28 * Each entry is doubly linked into two lists, a hash table and a most-
29 * recently-used (MRU) list. When a packet arrives it is looked up in
30 * the hash table. If found, the statistics are updated and the entry
31 * relinked at the head of the MRU list. If not found, a new entry is
32 * allocated, initialized and linked into both the hash table and at the
33 * head of the MRU list.
35 * Memory is usually allocated by grabbing a big chunk of new memory and
36 * cutting it up into littler pieces. The exception to this when we hit
37 * the memory limit. Then we free memory by grabbing entries off the
38 * tail for the MRU list, unlinking from the hash table, and
42 * Limits on the number of structures allocated. This limit is picked
43 * with the illicit knowlege that we can only return somewhat less than
44 * 8K bytes in a mode 7 response packet, and that each structure will
45 * require about 20 bytes of space in the response.
47 * ... I don't believe the above is true anymore ... jdg
50 #define MAXMONMEM 600 /* we allocate up to 600 structures */
53 #define MONMEMINC 40 /* allocate them 40 at a time */
59 #define MON_HASH_SIZE NTP_HASH_SIZE
60 #define MON_HASH_MASK NTP_HASH_MASK
61 #define MON_HASH(addr) NTP_HASH_ADDR(addr)
64 * Pointers to the hash table, the MRU list and the count table. Memory
65 * for the hash and count tables is only allocated if monitoring is
68 static struct mon_data
*mon_hash
[MON_HASH_SIZE
]; /* list ptrs */
69 struct mon_data mon_mru_list
;
72 * List of free structures structures, and counters of free and total
73 * structures. The free structures are linked with the hash_next field.
75 static struct mon_data
*mon_free
; /* free list or null if none */
76 static int mon_total_mem
; /* total structures allocated */
77 static int mon_mem_increments
; /* times called malloc() */
80 * Parameters of the RES_LIMITED restriction option. We define headway
81 * as the idle time between packets. A packet is discarded if the
82 * headway is less than the minimum, as well as if the average headway
83 * is less than eight times the increment.
85 int ntp_minpkt
= NTP_MINPKT
; /* minimum (log 2 s) */
86 int ntp_minpoll
= NTP_MINPOLL
; /* increment (log 2 s) */
89 * Initialization state. We may be monitoring, we may not. If
90 * we aren't, we may not even have allocated any memory yet.
92 int mon_enabled
; /* enable switch */
93 int mon_age
= 3000; /* preemption limit */
94 static int mon_have_memory
;
95 static void mon_getmoremem (void);
96 static void remove_from_hash (struct mon_data
*);
99 * init_mon - initialize monitoring global data
105 * Don't do much of anything here. We don't allocate memory
106 * until someone explicitly starts us.
108 mon_enabled
= MON_OFF
;
111 mon_mem_increments
= 0;
113 memset(&mon_hash
[0], 0, sizeof mon_hash
);
114 memset(&mon_mru_list
, 0, sizeof mon_mru_list
);
119 * mon_start - start up the monitoring software
127 if (mon_enabled
!= MON_OFF
) {
134 if (!mon_have_memory
) {
136 mon_mem_increments
= 0;
142 mon_mru_list
.mru_next
= &mon_mru_list
;
143 mon_mru_list
.mru_prev
= &mon_mru_list
;
149 * mon_stop - stop the monitoring software
156 register struct mon_data
*md
, *md_next
;
159 if (mon_enabled
== MON_OFF
)
161 if ((mon_enabled
& mode
) == 0 || mode
== MON_OFF
)
164 mon_enabled
&= ~mode
;
165 if (mon_enabled
!= MON_OFF
)
169 * Put everything back on the free list
171 for (i
= 0; i
< MON_HASH_SIZE
; i
++) {
172 md
= mon_hash
[i
]; /* get next list */
173 mon_hash
[i
] = NULL
; /* zero the list head */
175 md_next
= md
->hash_next
;
176 md
->hash_next
= mon_free
;
181 mon_mru_list
.mru_next
= &mon_mru_list
;
182 mon_mru_list
.mru_prev
= &mon_mru_list
;
186 ntp_monclearinterface(struct interface
*interface
)
190 for (md
= mon_mru_list
.mru_next
; md
!= &mon_mru_list
;
192 if (md
->interface
== interface
) {
193 /* dequeue from mru list and put to free list */
194 md
->mru_prev
->mru_next
= md
->mru_next
;
195 md
->mru_next
->mru_prev
= md
->mru_prev
;
196 remove_from_hash(md
);
197 md
->hash_next
= mon_free
;
205 * ntp_monitor - record stats about this packet
211 struct recvbuf
*rbufp
,
215 register struct pkt
*pkt
;
216 register struct mon_data
*md
;
222 if (mon_enabled
== MON_OFF
)
225 pkt
= &rbufp
->recv_pkt
;
226 memset(&addr
, 0, sizeof(addr
));
227 memcpy(&addr
, &(rbufp
->recv_srcadr
), sizeof(addr
));
228 hash
= MON_HASH(&addr
);
229 mode
= PKT_MODE(pkt
->li_vn_mode
);
232 int head
; /* headway increment */
233 int leak
; /* new headway */
234 int limit
; /* average threshold */
237 * Match address only to conserve MRU size.
239 if (SOCK_EQ(&md
->rmtadr
, &addr
)) {
240 interval
= current_time
- md
->lasttime
;
241 md
->lasttime
= current_time
;
244 md
->rmtport
= NSRCPORT(&rbufp
->recv_srcadr
);
245 md
->mode
= (u_char
) mode
;
246 md
->version
= PKT_VERSION(pkt
->li_vn_mode
);
249 * Shuffle to the head of the MRU list.
251 md
->mru_next
->mru_prev
= md
->mru_prev
;
252 md
->mru_prev
->mru_next
= md
->mru_next
;
253 md
->mru_next
= mon_mru_list
.mru_next
;
254 md
->mru_prev
= &mon_mru_list
;
255 mon_mru_list
.mru_next
->mru_prev
= md
;
256 mon_mru_list
.mru_next
= md
;
259 * At this point the most recent arrival is
260 * first in the MRU list. Decrease the counter
261 * by the headway, but not less than zero.
263 md
->leak
-= interval
;
266 head
= 1 << ntp_minpoll
;
267 leak
= md
->leak
+ head
;
268 limit
= NTP_SHIFT
* head
;
271 printf("restrict: interval %d headway %d limit %d\n",
272 interval
, leak
, limit
);
276 * If the minimum and average thresholds are not
277 * exceeded, douse the RES_LIMITED and RES_KOD
278 * bits and increase the counter by the headway
279 * increment. Note that we give a 1-s grace for
280 * the minimum threshold and a 2-s grace for the
281 * headway increment. If one or both thresholds
282 * are exceeded and the old counter is less than
283 * the average threshold, set the counter to the
284 * average threshold plus the inrcrment and
285 * leave the RES_KOD bit lit. Othewise, leave
286 * the counter alone and douse the RES_KOD bit.
287 * This rate-limits the KoDs to no less than the
290 if (interval
+ 1 >= (1 << ntp_minpkt
) &&
293 md
->flags
&= ~(RES_LIMITED
| RES_KOD
);
294 } else if (md
->leak
< limit
) {
295 md
->leak
= limit
+ head
;
297 md
->flags
&= ~RES_KOD
;
305 * If we got here, this is the first we've heard of this
306 * guy. Get him some memory, either from the free list
307 * or from the tail of the MRU list.
309 if (mon_free
== NULL
&& mon_total_mem
>= MAXMONMEM
) {
312 * Preempt from the MRU list if old enough.
314 md
= mon_mru_list
.mru_prev
;
315 if (md
->count
== 1 || ntp_random() / (2. * FRAC
) >
316 (double)(current_time
- md
->lasttime
) / mon_age
)
317 return (flags
& ~RES_LIMITED
);
319 md
->mru_prev
->mru_next
= &mon_mru_list
;
320 mon_mru_list
.mru_prev
= md
->mru_prev
;
321 remove_from_hash(md
);
323 if (mon_free
== NULL
)
326 mon_free
= md
->hash_next
;
330 * Got one, initialize it
332 md
->lasttime
= md
->firsttime
= current_time
;
334 md
->flags
= flags
& ~RES_LIMITED
;
336 memset(&md
->rmtadr
, 0, sizeof(md
->rmtadr
));
337 memcpy(&md
->rmtadr
, &addr
, sizeof(addr
));
338 md
->rmtport
= NSRCPORT(&rbufp
->recv_srcadr
);
339 md
->mode
= (u_char
) mode
;
340 md
->version
= PKT_VERSION(pkt
->li_vn_mode
);
341 md
->interface
= rbufp
->dstadr
;
342 md
->cast_flags
= (u_char
)(((rbufp
->dstadr
->flags
&
343 INT_MCASTOPEN
) && rbufp
->fd
== md
->interface
->fd
) ?
344 MDF_MCAST
: rbufp
->fd
== md
->interface
->bfd
? MDF_BCAST
:
348 * Drop him into front of the hash table. Also put him on top of
351 md
->hash_next
= mon_hash
[hash
];
353 md
->mru_next
= mon_mru_list
.mru_next
;
354 md
->mru_prev
= &mon_mru_list
;
355 mon_mru_list
.mru_next
->mru_prev
= md
;
356 mon_mru_list
.mru_next
= md
;
362 * mon_getmoremem - get more memory and put it on the free list
367 register struct mon_data
*md
;
369 struct mon_data
*freedata
; /* 'old' free list (null) */
371 md
= (struct mon_data
*)emalloc(MONMEMINC
*
372 sizeof(struct mon_data
));
375 for (i
= 0; i
< (MONMEMINC
-1); i
++) {
376 md
->hash_next
= (md
+ 1);
381 * md now points at the last. Link in the rest of the chain.
383 md
->hash_next
= freedata
;
384 mon_total_mem
+= MONMEMINC
;
385 mon_mem_increments
++;
394 register struct mon_data
*md_prev
;
396 hash
= MON_HASH(&md
->rmtadr
);
397 if (mon_hash
[hash
] == md
) {
398 mon_hash
[hash
] = md
->hash_next
;
400 md_prev
= mon_hash
[hash
];
401 while (md_prev
->hash_next
!= md
) {
402 md_prev
= md_prev
->hash_next
;
403 if (md_prev
== NULL
) {
408 md_prev
->hash_next
= md
->hash_next
;