1 /* $NetBSD: ntp_monitor.c,v 1.3 2006/06/11 19:34:11 kardel Exp $ */
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 * I'm still not sure I like what I've done here. It certainly consumes
24 * memory like it is going out of style, and also may not be as low
25 * overhead as I'd imagined.
27 * Anyway, we record statistics based on source address, mode and
28 * version (for now, anyway. Check the code). The receive procedure
29 * calls us with the incoming rbufp before it does anything else.
31 * Each entry is doubly linked into two lists, a hash table and a
32 * most-recently-used list. When a packet arrives it is looked up in
33 * the hash table. If found, the statistics are updated and the entry
34 * relinked at the head of the MRU list. If not found, a new entry is
35 * allocated, initialized and linked into both the hash table and at the
36 * head of the MRU list.
38 * Memory is usually allocated by grabbing a big chunk of new memory and
39 * cutting it up into littler pieces. The exception to this when we hit
40 * the memory limit. Then we free memory by grabbing entries off the
41 * tail for the MRU list, unlinking from the hash table, and
44 * trimmed back memory consumption ... jdg 8/94
47 * Limits on the number of structures allocated. This limit is picked
48 * with the illicit knowlege that we can only return somewhat less
49 * than 8K bytes in a mode 7 response packet, and that each structure
50 * will require about 20 bytes of space in the response.
52 * ... I don't believe the above is true anymore ... jdg
55 #define MAXMONMEM 600 /* we allocate up to 600 structures */
58 #define MONMEMINC 40 /* allocate them 40 at a time */
64 #define MON_HASH_SIZE 128
65 #define MON_HASH_MASK (MON_HASH_SIZE-1)
66 #define MON_HASH(addr) sock_hash(addr)
69 * Pointers to the hash table, the MRU list and the count table. Memory
70 * for the hash and count tables is only allocated if monitoring is
73 static struct mon_data
*mon_hash
[MON_HASH_SIZE
]; /* list ptrs */
74 struct mon_data mon_mru_list
;
77 * List of free structures structures, and counters of free and total
78 * structures. The free structures are linked with the hash_next field.
80 static struct mon_data
*mon_free
; /* free list or null if none */
81 static int mon_total_mem
; /* total structures allocated */
82 static int mon_mem_increments
; /* times called malloc() */
85 * Initialization state. We may be monitoring, we may not. If
86 * we aren't, we may not even have allocated any memory yet.
88 int mon_enabled
; /* enable switch */
89 u_long mon_age
= 3000; /* preemption limit */
90 static int mon_have_memory
;
91 static void mon_getmoremem
P((void));
92 static void remove_from_hash
P((struct mon_data
*));
95 * init_mon - initialize monitoring global data
101 * Don't do much of anything here. We don't allocate memory
102 * until someone explicitly starts us.
104 mon_enabled
= MON_OFF
;
108 mon_mem_increments
= 0;
110 memset(&mon_hash
[0], 0, sizeof mon_hash
);
111 memset(&mon_mru_list
, 0, sizeof mon_mru_list
);
116 * mon_start - start up the monitoring software
124 if (mon_enabled
!= MON_OFF
) {
131 if (!mon_have_memory
) {
133 mon_mem_increments
= 0;
139 mon_mru_list
.mru_next
= &mon_mru_list
;
140 mon_mru_list
.mru_prev
= &mon_mru_list
;
146 * mon_stop - stop the monitoring software
153 register struct mon_data
*md
, *md_next
;
156 if (mon_enabled
== MON_OFF
)
158 if ((mon_enabled
& mode
) == 0 || mode
== MON_OFF
)
161 mon_enabled
&= ~mode
;
162 if (mon_enabled
!= MON_OFF
)
166 * Put everything back on the free list
168 for (i
= 0; i
< MON_HASH_SIZE
; i
++) {
169 md
= mon_hash
[i
]; /* get next list */
170 mon_hash
[i
] = NULL
; /* zero the list head */
172 md_next
= md
->hash_next
;
173 md
->hash_next
= mon_free
;
179 mon_mru_list
.mru_next
= &mon_mru_list
;
180 mon_mru_list
.mru_prev
= &mon_mru_list
;
184 ntp_monclearinterface(struct interface
*interface
)
188 for (md
= mon_mru_list
.mru_next
; md
!= &mon_mru_list
;
190 if (md
->interface
== interface
)
192 /* dequeue from mru list and put to free list */
193 md
->mru_prev
->mru_next
= md
->mru_next
;
194 md
->mru_next
->mru_prev
= md
->mru_prev
;
195 remove_from_hash(md
);
196 md
->hash_next
= mon_free
;
203 * ntp_monitor - record stats about this packet
205 * Returns 1 if the packet is at the head of the list, 0 otherwise.
209 struct recvbuf
*rbufp
212 register struct pkt
*pkt
;
213 register struct mon_data
*md
;
214 struct sockaddr_storage addr
;
218 if (mon_enabled
== MON_OFF
)
221 pkt
= &rbufp
->recv_pkt
;
222 memset(&addr
, 0, sizeof(addr
));
223 memcpy(&addr
, &(rbufp
->recv_srcadr
), sizeof(addr
));
224 hash
= MON_HASH(&addr
);
225 mode
= PKT_MODE(pkt
->li_vn_mode
);
230 * Match address only to conserve MRU size.
232 if (SOCKCMP(&md
->rmtadr
, &addr
)) {
233 md
->drop_count
= current_time
- md
->lasttime
;
234 md
->lasttime
= current_time
;
236 md
->rmtport
= NSRCPORT(&rbufp
->recv_srcadr
);
237 md
->mode
= (u_char
) mode
;
238 md
->version
= PKT_VERSION(pkt
->li_vn_mode
);
241 * Shuffle to the head of the MRU list.
243 md
->mru_next
->mru_prev
= md
->mru_prev
;
244 md
->mru_prev
->mru_next
= md
->mru_next
;
245 md
->mru_next
= mon_mru_list
.mru_next
;
246 md
->mru_prev
= &mon_mru_list
;
247 mon_mru_list
.mru_next
->mru_prev
= md
;
248 mon_mru_list
.mru_next
= md
;
255 * If we got here, this is the first we've heard of this
256 * guy. Get him some memory, either from the free list
257 * or from the tail of the MRU list.
259 if (mon_free
== NULL
&& mon_total_mem
>= MAXMONMEM
) {
262 * Preempt from the MRU list if old enough.
264 md
= mon_mru_list
.mru_prev
;
265 /* We get 31 bits from ntp_random() */
266 if (((u_long
)ntp_random()) / FRAC
>
267 (double)(current_time
- md
->lasttime
) / mon_age
)
270 md
->mru_prev
->mru_next
= &mon_mru_list
;
271 mon_mru_list
.mru_prev
= md
->mru_prev
;
272 remove_from_hash(md
);
274 if (mon_free
== NULL
)
277 mon_free
= md
->hash_next
;
281 * Got one, initialize it
283 md
->avg_interval
= 0;
284 md
->lasttime
= current_time
;
287 memset(&md
->rmtadr
, 0, sizeof(md
->rmtadr
));
288 memcpy(&md
->rmtadr
, &addr
, sizeof(addr
));
289 md
->rmtport
= NSRCPORT(&rbufp
->recv_srcadr
);
290 md
->mode
= (u_char
) mode
;
291 md
->version
= PKT_VERSION(pkt
->li_vn_mode
);
292 md
->interface
= rbufp
->dstadr
;
293 md
->cast_flags
= (u_char
)(((rbufp
->dstadr
->flags
& INT_MCASTOPEN
) &&
294 rbufp
->fd
== md
->interface
->fd
) ? MDF_MCAST
: rbufp
->fd
==
295 md
->interface
->bfd
? MDF_BCAST
: MDF_UCAST
);
298 * Drop him into front of the hash table. Also put him on top of
301 md
->hash_next
= mon_hash
[hash
];
303 md
->mru_next
= mon_mru_list
.mru_next
;
304 md
->mru_prev
= &mon_mru_list
;
305 mon_mru_list
.mru_next
->mru_prev
= md
;
306 mon_mru_list
.mru_next
= md
;
312 * mon_getmoremem - get more memory and put it on the free list
317 register struct mon_data
*md
;
319 struct mon_data
*freedata
; /* 'old' free list (null) */
321 md
= (struct mon_data
*)emalloc(MONMEMINC
*
322 sizeof(struct mon_data
));
325 for (i
= 0; i
< (MONMEMINC
-1); i
++) {
326 md
->hash_next
= (md
+ 1);
331 * md now points at the last. Link in the rest of the chain.
333 md
->hash_next
= freedata
;
334 mon_total_mem
+= MONMEMINC
;
335 mon_mem_increments
++;
344 register struct mon_data
*md_prev
;
346 hash
= MON_HASH(&md
->rmtadr
);
347 if (mon_hash
[hash
] == md
) {
348 mon_hash
[hash
] = md
->hash_next
;
350 md_prev
= mon_hash
[hash
];
351 while (md_prev
->hash_next
!= md
) {
352 md_prev
= md_prev
->hash_next
;
353 if (md_prev
== NULL
) {
358 md_prev
->hash_next
= md
->hash_next
;