xref: /freebsd/contrib/ntp/ntpd/ntp_monitor.c (revision 9c2daa00c2315f101948c7144d62af5d5fb515cf)
1c0b746e5SOllivier Robert /*
29c2daa00SOllivier Robert  * ntp_monitor - monitor ntpd statistics
3c0b746e5SOllivier Robert  */
4c0b746e5SOllivier Robert #ifdef HAVE_CONFIG_H
5c0b746e5SOllivier Robert # include <config.h>
6c0b746e5SOllivier Robert #endif
7c0b746e5SOllivier Robert 
8c0b746e5SOllivier Robert #include "ntpd.h"
9c0b746e5SOllivier Robert #include "ntp_io.h"
10c0b746e5SOllivier Robert #include "ntp_if.h"
11c0b746e5SOllivier Robert #include "ntp_stdlib.h"
12c0b746e5SOllivier Robert 
13224ba2bdSOllivier Robert #include <stdio.h>
14224ba2bdSOllivier Robert #include <signal.h>
15224ba2bdSOllivier Robert #ifdef HAVE_SYS_IOCTL_H
16224ba2bdSOllivier Robert # include <sys/ioctl.h>
17224ba2bdSOllivier Robert #endif
18224ba2bdSOllivier Robert 
19c0b746e5SOllivier Robert /*
20c0b746e5SOllivier Robert  * I'm still not sure I like what I've done here. It certainly consumes
21c0b746e5SOllivier Robert  * memory like it is going out of style, and also may not be as low
22c0b746e5SOllivier Robert  * overhead as I'd imagined.
23c0b746e5SOllivier Robert  *
249c2daa00SOllivier Robert  * Anyway, we record statistics based on source address, mode and
259c2daa00SOllivier Robert  * version (for now, anyway. Check the code).  The receive procedure
269c2daa00SOllivier Robert  * calls us with the incoming rbufp before it does anything else.
27c0b746e5SOllivier Robert  *
28c0b746e5SOllivier Robert  * Each entry is doubly linked into two lists, a hash table and a
299c2daa00SOllivier Robert  * most-recently-used list. When a packet arrives it is looked up in
309c2daa00SOllivier Robert  * the hash table.  If found, the statistics are updated and the entry
319c2daa00SOllivier Robert  * relinked at the head of the MRU list. If not found, a new entry is
329c2daa00SOllivier Robert  * allocated, initialized and linked into both the hash table and at the
339c2daa00SOllivier Robert  * head of the MRU list.
34c0b746e5SOllivier Robert  *
359c2daa00SOllivier Robert  * Memory is usually allocated by grabbing a big chunk of new memory and
369c2daa00SOllivier Robert  * cutting it up into littler pieces. The exception to this when we hit
379c2daa00SOllivier Robert  * the memory limit. Then we free memory by grabbing entries off the
389c2daa00SOllivier Robert  * tail for the MRU list, unlinking from the hash table, and
39c0b746e5SOllivier Robert  * reinitializing.
40c0b746e5SOllivier Robert  *
41c0b746e5SOllivier Robert  * trimmed back memory consumption ... jdg 8/94
42c0b746e5SOllivier Robert  */
43c0b746e5SOllivier Robert /*
44c0b746e5SOllivier Robert  * Limits on the number of structures allocated.  This limit is picked
45c0b746e5SOllivier Robert  * with the illicit knowlege that we can only return somewhat less
46c0b746e5SOllivier Robert  * than 8K bytes in a mode 7 response packet, and that each structure
47c0b746e5SOllivier Robert  * will require about 20 bytes of space in the response.
48c0b746e5SOllivier Robert  *
49c0b746e5SOllivier Robert  * ... I don't believe the above is true anymore ... jdg
50c0b746e5SOllivier Robert  */
51c0b746e5SOllivier Robert #ifndef MAXMONMEM
52c0b746e5SOllivier Robert #define	MAXMONMEM	600	/* we allocate up to 600 structures */
53c0b746e5SOllivier Robert #endif
54c0b746e5SOllivier Robert #ifndef MONMEMINC
55c0b746e5SOllivier Robert #define	MONMEMINC	40	/* allocate them 40 at a time */
56c0b746e5SOllivier Robert #endif
57c0b746e5SOllivier Robert 
58c0b746e5SOllivier Robert /*
59c0b746e5SOllivier Robert  * Hashing stuff
60c0b746e5SOllivier Robert  */
61c0b746e5SOllivier Robert #define	MON_HASH_SIZE	128
62c0b746e5SOllivier Robert #define	MON_HASH_MASK	(MON_HASH_SIZE-1)
639c2daa00SOllivier Robert #define	MON_HASH(addr)	sock_hash(addr)
64c0b746e5SOllivier Robert 
65c0b746e5SOllivier Robert /*
66c0b746e5SOllivier Robert  * Pointers to the hash table, the MRU list and the count table.  Memory
679c2daa00SOllivier Robert  * for the hash and count tables is only allocated if monitoring is
689c2daa00SOllivier Robert  * turned on.
69c0b746e5SOllivier Robert  */
709c2daa00SOllivier Robert static	struct mon_data *mon_hash[MON_HASH_SIZE];  /* list ptrs */
71c0b746e5SOllivier Robert struct	mon_data mon_mru_list;
729c2daa00SOllivier Robert 
73c0b746e5SOllivier Robert /*
74c0b746e5SOllivier Robert  * List of free structures structures, and counters of free and total
75c0b746e5SOllivier Robert  * structures.  The free structures are linked with the hash_next field.
76c0b746e5SOllivier Robert  */
779c2daa00SOllivier Robert static  struct mon_data *mon_free;      /* free list or null if none */
789c2daa00SOllivier Robert static	int mon_total_mem;		/* total structures allocated */
799c2daa00SOllivier Robert static	int mon_mem_increments;		/* times called malloc() */
80c0b746e5SOllivier Robert 
81c0b746e5SOllivier Robert /*
82c0b746e5SOllivier Robert  * Initialization state.  We may be monitoring, we may not.  If
83c0b746e5SOllivier Robert  * we aren't, we may not even have allocated any memory yet.
84c0b746e5SOllivier Robert  */
859c2daa00SOllivier Robert int	mon_enabled;			/* enable switch */
869c2daa00SOllivier Robert u_long	mon_age = 3000;			/* preemption limit */
87c0b746e5SOllivier Robert static	int mon_have_memory;
88c0b746e5SOllivier Robert static	void	mon_getmoremem	P((void));
89c0b746e5SOllivier Robert static	void	remove_from_hash P((struct mon_data *));
90c0b746e5SOllivier Robert 
91c0b746e5SOllivier Robert /*
92c0b746e5SOllivier Robert  * init_mon - initialize monitoring global data
93c0b746e5SOllivier Robert  */
94c0b746e5SOllivier Robert void
95c0b746e5SOllivier Robert init_mon(void)
96c0b746e5SOllivier Robert {
97c0b746e5SOllivier Robert 	/*
98c0b746e5SOllivier Robert 	 * Don't do much of anything here.  We don't allocate memory
99c0b746e5SOllivier Robert 	 * until someone explicitly starts us.
100c0b746e5SOllivier Robert 	 */
101c0b746e5SOllivier Robert 	mon_enabled = MON_OFF;
102c0b746e5SOllivier Robert 	mon_have_memory = 0;
103c0b746e5SOllivier Robert 
104c0b746e5SOllivier Robert 	mon_total_mem = 0;
105c0b746e5SOllivier Robert 	mon_mem_increments = 0;
106c0b746e5SOllivier Robert 	mon_free = NULL;
1079c2daa00SOllivier Robert 	memset(&mon_hash[0], 0, sizeof mon_hash);
1089c2daa00SOllivier Robert 	memset(&mon_mru_list, 0, sizeof mon_mru_list);
109c0b746e5SOllivier Robert }
110c0b746e5SOllivier Robert 
111c0b746e5SOllivier Robert 
112c0b746e5SOllivier Robert /*
113c0b746e5SOllivier Robert  * mon_start - start up the monitoring software
114c0b746e5SOllivier Robert  */
115c0b746e5SOllivier Robert void
116c0b746e5SOllivier Robert mon_start(
117c0b746e5SOllivier Robert 	int mode
118c0b746e5SOllivier Robert 	)
119c0b746e5SOllivier Robert {
120c0b746e5SOllivier Robert 
121c0b746e5SOllivier Robert 	if (mon_enabled != MON_OFF) {
122c0b746e5SOllivier Robert 		mon_enabled |= mode;
123c0b746e5SOllivier Robert 		return;
124c0b746e5SOllivier Robert 	}
125c0b746e5SOllivier Robert 	if (mode == MON_OFF)
1269c2daa00SOllivier Robert 	    return;
127c0b746e5SOllivier Robert 
128c0b746e5SOllivier Robert 	if (!mon_have_memory) {
129c0b746e5SOllivier Robert 		mon_total_mem = 0;
130c0b746e5SOllivier Robert 		mon_mem_increments = 0;
131c0b746e5SOllivier Robert 		mon_free = NULL;
132c0b746e5SOllivier Robert 		mon_getmoremem();
133c0b746e5SOllivier Robert 		mon_have_memory = 1;
134c0b746e5SOllivier Robert 	}
135c0b746e5SOllivier Robert 
136c0b746e5SOllivier Robert 	mon_mru_list.mru_next = &mon_mru_list;
137c0b746e5SOllivier Robert 	mon_mru_list.mru_prev = &mon_mru_list;
138c0b746e5SOllivier Robert 	mon_enabled = mode;
139c0b746e5SOllivier Robert }
140c0b746e5SOllivier Robert 
141c0b746e5SOllivier Robert 
142c0b746e5SOllivier Robert /*
143c0b746e5SOllivier Robert  * mon_stop - stop the monitoring software
144c0b746e5SOllivier Robert  */
145c0b746e5SOllivier Robert void
146c0b746e5SOllivier Robert mon_stop(
147c0b746e5SOllivier Robert 	int mode
148c0b746e5SOllivier Robert 	)
149c0b746e5SOllivier Robert {
150c0b746e5SOllivier Robert 	register struct mon_data *md, *md_next;
151c0b746e5SOllivier Robert 	register int i;
152c0b746e5SOllivier Robert 
153c0b746e5SOllivier Robert 	if (mon_enabled == MON_OFF)
154c0b746e5SOllivier Robert 	    return;
155c0b746e5SOllivier Robert 	if ((mon_enabled & mode) == 0 || mode == MON_OFF)
156c0b746e5SOllivier Robert 	    return;
157c0b746e5SOllivier Robert 
158c0b746e5SOllivier Robert 	mon_enabled &= ~mode;
159c0b746e5SOllivier Robert 	if (mon_enabled != MON_OFF)
160c0b746e5SOllivier Robert 	    return;
161c0b746e5SOllivier Robert 
162c0b746e5SOllivier Robert 	/*
163c0b746e5SOllivier Robert 	 * Put everything back on the free list
164c0b746e5SOllivier Robert 	 */
165c0b746e5SOllivier Robert 	for (i = 0; i < MON_HASH_SIZE; i++) {
166c0b746e5SOllivier Robert 		md = mon_hash[i];               /* get next list */
167c0b746e5SOllivier Robert 		mon_hash[i] = NULL;             /* zero the list head */
168c0b746e5SOllivier Robert 		while (md != NULL) {
169c0b746e5SOllivier Robert 			md_next = md->hash_next;
170c0b746e5SOllivier Robert 			md->hash_next = mon_free;
171c0b746e5SOllivier Robert 			mon_free = md;
172c0b746e5SOllivier Robert 			md = md_next;
173c0b746e5SOllivier Robert 		}
174c0b746e5SOllivier Robert 	}
175c0b746e5SOllivier Robert 
176c0b746e5SOllivier Robert 	mon_mru_list.mru_next = &mon_mru_list;
177c0b746e5SOllivier Robert 	mon_mru_list.mru_prev = &mon_mru_list;
178c0b746e5SOllivier Robert }
179c0b746e5SOllivier Robert 
180c0b746e5SOllivier Robert 
181c0b746e5SOllivier Robert /*
182c0b746e5SOllivier Robert  * ntp_monitor - record stats about this packet
183c0b746e5SOllivier Robert  */
184c0b746e5SOllivier Robert void
185c0b746e5SOllivier Robert ntp_monitor(
186c0b746e5SOllivier Robert 	struct recvbuf *rbufp
187c0b746e5SOllivier Robert 	)
188c0b746e5SOllivier Robert {
189c0b746e5SOllivier Robert 	register struct pkt *pkt;
190c0b746e5SOllivier Robert 	register struct mon_data *md;
1919c2daa00SOllivier Robert         struct sockaddr_storage addr;
192c0b746e5SOllivier Robert 	register int hash;
193c0b746e5SOllivier Robert 	register int mode;
194c0b746e5SOllivier Robert 
195c0b746e5SOllivier Robert 	if (mon_enabled == MON_OFF)
196c0b746e5SOllivier Robert 		return;
197c0b746e5SOllivier Robert 
198c0b746e5SOllivier Robert 	pkt = &rbufp->recv_pkt;
1999c2daa00SOllivier Robert 	memset(&addr, 0, sizeof(addr));
2009c2daa00SOllivier Robert 	memcpy(&addr, &(rbufp->recv_srcadr), sizeof(addr));
2019c2daa00SOllivier Robert 	hash = MON_HASH(&addr);
202c0b746e5SOllivier Robert 	mode = PKT_MODE(pkt->li_vn_mode);
203c0b746e5SOllivier Robert 	md = mon_hash[hash];
204c0b746e5SOllivier Robert 	while (md != NULL) {
205c0b746e5SOllivier Robert 
206c0b746e5SOllivier Robert 		/*
2079c2daa00SOllivier Robert 		 * Match address only to conserve MRU size.
2089c2daa00SOllivier Robert 		 */
2099c2daa00SOllivier Robert 		if (SOCKCMP(&md->rmtadr, &addr)) {
2109c2daa00SOllivier Robert 			md->drop_count = current_time - md->lasttime;
2119c2daa00SOllivier Robert 			md->lasttime = current_time;
2129c2daa00SOllivier Robert 			md->count++;
2139c2daa00SOllivier Robert 			md->rmtport = NSRCPORT(&rbufp->recv_srcadr);
2149c2daa00SOllivier Robert 			md->mode = (u_char) mode;
2159c2daa00SOllivier Robert 			md->version = PKT_VERSION(pkt->li_vn_mode);
2169c2daa00SOllivier Robert 
2179c2daa00SOllivier Robert 			/*
2189c2daa00SOllivier Robert 			 * Shuffle to the head of the MRU list.
219c0b746e5SOllivier Robert 			 */
220c0b746e5SOllivier Robert 			md->mru_next->mru_prev = md->mru_prev;
221c0b746e5SOllivier Robert 			md->mru_prev->mru_next = md->mru_next;
222c0b746e5SOllivier Robert 			md->mru_next = mon_mru_list.mru_next;
223c0b746e5SOllivier Robert 			md->mru_prev = &mon_mru_list;
224c0b746e5SOllivier Robert 			mon_mru_list.mru_next->mru_prev = md;
225c0b746e5SOllivier Robert 			mon_mru_list.mru_next = md;
226c0b746e5SOllivier Robert 			return;
227c0b746e5SOllivier Robert 		}
228c0b746e5SOllivier Robert 		md = md->hash_next;
229c0b746e5SOllivier Robert 	}
230c0b746e5SOllivier Robert 
231c0b746e5SOllivier Robert 	/*
232c0b746e5SOllivier Robert 	 * If we got here, this is the first we've heard of this
233c0b746e5SOllivier Robert 	 * guy.  Get him some memory, either from the free list
234c0b746e5SOllivier Robert 	 * or from the tail of the MRU list.
235c0b746e5SOllivier Robert 	 */
236c0b746e5SOllivier Robert 	if (mon_free == NULL && mon_total_mem >= MAXMONMEM) {
2379c2daa00SOllivier Robert 
238c0b746e5SOllivier Robert 		/*
2399c2daa00SOllivier Robert 		 * Preempt from the MRU list if old enough.
240c0b746e5SOllivier Robert 		 */
241c0b746e5SOllivier Robert 		md = mon_mru_list.mru_prev;
2429c2daa00SOllivier Robert 		if (((u_long)RANDOM & 0xffffffff) / FRAC >
2439c2daa00SOllivier Robert 		    (double)(current_time - md->lasttime) / mon_age)
2449c2daa00SOllivier Robert 			return;
2459c2daa00SOllivier Robert 
246c0b746e5SOllivier Robert 		md->mru_prev->mru_next = &mon_mru_list;
247c0b746e5SOllivier Robert 		mon_mru_list.mru_prev = md->mru_prev;
248c0b746e5SOllivier Robert 		remove_from_hash(md);
249c0b746e5SOllivier Robert 	} else {
2509c2daa00SOllivier Robert 		if (mon_free == NULL)
2519c2daa00SOllivier Robert 			mon_getmoremem();
252c0b746e5SOllivier Robert 		md = mon_free;
253c0b746e5SOllivier Robert 		mon_free = md->hash_next;
254c0b746e5SOllivier Robert 	}
255c0b746e5SOllivier Robert 
256c0b746e5SOllivier Robert 	/*
257c0b746e5SOllivier Robert 	 * Got one, initialize it
258c0b746e5SOllivier Robert 	 */
2599c2daa00SOllivier Robert 	md->avg_interval = 0;
2609c2daa00SOllivier Robert 	md->lasttime = current_time;
261c0b746e5SOllivier Robert 	md->count = 1;
2629c2daa00SOllivier Robert 	md->drop_count = 0;
2639c2daa00SOllivier Robert 	memset(&md->rmtadr, 0, sizeof(md->rmtadr));
2649c2daa00SOllivier Robert 	memcpy(&md->rmtadr, &addr, sizeof(addr));
265c0b746e5SOllivier Robert 	md->rmtport = NSRCPORT(&rbufp->recv_srcadr);
266c0b746e5SOllivier Robert 	md->mode = (u_char) mode;
267c0b746e5SOllivier Robert 	md->version = PKT_VERSION(pkt->li_vn_mode);
268c0b746e5SOllivier Robert 	md->interface = rbufp->dstadr;
2699c2daa00SOllivier Robert 	md->cast_flags = (u_char)(((rbufp->dstadr->flags & INT_MULTICAST) &&
270c0b746e5SOllivier Robert 	    rbufp->fd == md->interface->fd) ? MDF_MCAST: rbufp->fd ==
2719c2daa00SOllivier Robert 		md->interface->bfd ? MDF_BCAST : MDF_UCAST);
272c0b746e5SOllivier Robert 
273c0b746e5SOllivier Robert 	/*
2749c2daa00SOllivier Robert 	 * Drop him into front of the hash table. Also put him on top of
2759c2daa00SOllivier Robert 	 * the MRU list.
276c0b746e5SOllivier Robert 	 */
277c0b746e5SOllivier Robert 	md->hash_next = mon_hash[hash];
278c0b746e5SOllivier Robert 	mon_hash[hash] = md;
279c0b746e5SOllivier Robert 	md->mru_next = mon_mru_list.mru_next;
280c0b746e5SOllivier Robert 	md->mru_prev = &mon_mru_list;
281c0b746e5SOllivier Robert 	mon_mru_list.mru_next->mru_prev = md;
282c0b746e5SOllivier Robert 	mon_mru_list.mru_next = md;
283c0b746e5SOllivier Robert }
284c0b746e5SOllivier Robert 
285c0b746e5SOllivier Robert 
286c0b746e5SOllivier Robert /*
287c0b746e5SOllivier Robert  * mon_getmoremem - get more memory and put it on the free list
288c0b746e5SOllivier Robert  */
289c0b746e5SOllivier Robert static void
290c0b746e5SOllivier Robert mon_getmoremem(void)
291c0b746e5SOllivier Robert {
292c0b746e5SOllivier Robert 	register struct mon_data *md;
293c0b746e5SOllivier Robert 	register int i;
294c0b746e5SOllivier Robert 	struct mon_data *freedata;      /* 'old' free list (null) */
295c0b746e5SOllivier Robert 
2969c2daa00SOllivier Robert 	md = (struct mon_data *)emalloc(MONMEMINC *
2979c2daa00SOllivier Robert 	    sizeof(struct mon_data));
298c0b746e5SOllivier Robert 	freedata = mon_free;
299c0b746e5SOllivier Robert 	mon_free = md;
300c0b746e5SOllivier Robert 	for (i = 0; i < (MONMEMINC-1); i++) {
301c0b746e5SOllivier Robert 		md->hash_next = (md + 1);
302c0b746e5SOllivier Robert 		md++;
303c0b746e5SOllivier Robert 	}
304c0b746e5SOllivier Robert 
305c0b746e5SOllivier Robert 	/*
306c0b746e5SOllivier Robert 	 * md now points at the last.  Link in the rest of the chain.
307c0b746e5SOllivier Robert 	 */
308c0b746e5SOllivier Robert 	md->hash_next = freedata;
309c0b746e5SOllivier Robert 	mon_total_mem += MONMEMINC;
310c0b746e5SOllivier Robert 	mon_mem_increments++;
311c0b746e5SOllivier Robert }
312c0b746e5SOllivier Robert 
313c0b746e5SOllivier Robert static void
314c0b746e5SOllivier Robert remove_from_hash(
315c0b746e5SOllivier Robert 	struct mon_data *md
316c0b746e5SOllivier Robert 	)
317c0b746e5SOllivier Robert {
318c0b746e5SOllivier Robert 	register int hash;
319c0b746e5SOllivier Robert 	register struct mon_data *md_prev;
320c0b746e5SOllivier Robert 
3219c2daa00SOllivier Robert 	hash = MON_HASH(&md->rmtadr);
322c0b746e5SOllivier Robert 	if (mon_hash[hash] == md) {
323c0b746e5SOllivier Robert 		mon_hash[hash] = md->hash_next;
324c0b746e5SOllivier Robert 	} else {
325c0b746e5SOllivier Robert 		md_prev = mon_hash[hash];
326c0b746e5SOllivier Robert 		while (md_prev->hash_next != md) {
327c0b746e5SOllivier Robert 			md_prev = md_prev->hash_next;
328c0b746e5SOllivier Robert 			if (md_prev == NULL) {
329c0b746e5SOllivier Robert 				/* logic error */
330c0b746e5SOllivier Robert 				return;
331c0b746e5SOllivier Robert 			}
332c0b746e5SOllivier Robert 		}
333c0b746e5SOllivier Robert 		md_prev->hash_next = md->hash_next;
334c0b746e5SOllivier Robert 	}
335c0b746e5SOllivier Robert }
336