xref: /freebsd/contrib/ntp/ntpd/ntp_monitor.c (revision c0b746e5e8d9479f05b3749cbf1f73b8928719bd)
1c0b746e5SOllivier Robert /*
2c0b746e5SOllivier Robert  * ntp_monitor.c - monitor who is using the ntpd server
3c0b746e5SOllivier Robert  */
4c0b746e5SOllivier Robert #ifdef HAVE_CONFIG_H
5c0b746e5SOllivier Robert #include <config.h>
6c0b746e5SOllivier Robert #endif
7c0b746e5SOllivier Robert 
8c0b746e5SOllivier Robert #include <stdio.h>
9c0b746e5SOllivier Robert #include <sys/types.h>
10c0b746e5SOllivier Robert #include <signal.h>
11c0b746e5SOllivier Robert # ifdef HAVE_SYS_IOCTL_H
12c0b746e5SOllivier Robert #  include <sys/ioctl.h>
13c0b746e5SOllivier Robert # endif
14c0b746e5SOllivier Robert # include <sys/time.h>
15c0b746e5SOllivier Robert 
16c0b746e5SOllivier Robert #include "ntpd.h"
17c0b746e5SOllivier Robert #include "ntp_io.h"
18c0b746e5SOllivier Robert #include "ntp_if.h"
19c0b746e5SOllivier Robert #include "ntp_stdlib.h"
20c0b746e5SOllivier Robert 
21c0b746e5SOllivier Robert /*
22c0b746e5SOllivier Robert  * I'm still not sure I like what I've done here.  It certainly consumes
23c0b746e5SOllivier Robert  * memory like it is going out of style, and also may not be as low
24c0b746e5SOllivier Robert  * overhead as I'd imagined.
25c0b746e5SOllivier Robert  *
26c0b746e5SOllivier Robert  * Anyway, we record statistics based on source address, mode and version
27c0b746e5SOllivier Robert  * (for now, anyway.  Check the code).  The receive procedure calls us with
28c0b746e5SOllivier Robert  * the incoming rbufp before it does anything else.
29c0b746e5SOllivier Robert  *
30c0b746e5SOllivier Robert  * Each entry is doubly linked into two lists, a hash table and a
31c0b746e5SOllivier Robert  * most-recently-used list.  When a packet arrives it is looked up
32c0b746e5SOllivier Robert  * in the hash table.  If found, the statistics are updated and the
33c0b746e5SOllivier Robert  * entry relinked at the head of the MRU list.  If not found, a new
34c0b746e5SOllivier Robert  * entry is allocated, initialized and linked into both the hash
35c0b746e5SOllivier Robert  * table and at the head of the MRU list.
36c0b746e5SOllivier Robert  *
37c0b746e5SOllivier Robert  * Memory is usually allocated by grabbing a big chunk of new memory
38c0b746e5SOllivier Robert  * and cutting it up into littler pieces.  The exception to this when we
39c0b746e5SOllivier Robert  * hit the memory limit.  Then we free memory by grabbing entries off
40c0b746e5SOllivier Robert  * the tail for the MRU list, unlinking from the hash table, and
41c0b746e5SOllivier Robert  * reinitializing.
42c0b746e5SOllivier Robert  *
43c0b746e5SOllivier Robert  * trimmed back memory consumption ... jdg 8/94
44c0b746e5SOllivier Robert  */
45c0b746e5SOllivier Robert 
46c0b746e5SOllivier Robert /*
47c0b746e5SOllivier Robert  * Limits on the number of structures allocated.  This limit is picked
48c0b746e5SOllivier Robert  * with the illicit knowlege that we can only return somewhat less
49c0b746e5SOllivier Robert  * than 8K bytes in a mode 7 response packet, and that each structure
50c0b746e5SOllivier Robert  * will require about 20 bytes of space in the response.
51c0b746e5SOllivier Robert  *
52c0b746e5SOllivier Robert  * ... I don't believe the above is true anymore ... jdg
53c0b746e5SOllivier Robert  */
54c0b746e5SOllivier Robert #ifndef MAXMONMEM
55c0b746e5SOllivier Robert #define	MAXMONMEM	600	/* we allocate up to 600 structures */
56c0b746e5SOllivier Robert #endif
57c0b746e5SOllivier Robert #ifndef MONMEMINC
58c0b746e5SOllivier Robert #define	MONMEMINC	40	/* allocate them 40 at a time */
59c0b746e5SOllivier Robert #endif
60c0b746e5SOllivier Robert 
61c0b746e5SOllivier Robert /*
62c0b746e5SOllivier Robert  * Hashing stuff
63c0b746e5SOllivier Robert  */
64c0b746e5SOllivier Robert #define	MON_HASH_SIZE	128
65c0b746e5SOllivier Robert #define	MON_HASH_MASK	(MON_HASH_SIZE-1)
66c0b746e5SOllivier Robert #define	MON_HASH(addr)	((int)(ntohl((addr)) & MON_HASH_MASK))
67c0b746e5SOllivier Robert 
68c0b746e5SOllivier Robert /*
69c0b746e5SOllivier Robert  * Pointers to the hash table, the MRU list and the count table.  Memory
70c0b746e5SOllivier Robert  * for the hash and count tables is only allocated if monitoring is turned on.
71c0b746e5SOllivier Robert  */
72c0b746e5SOllivier Robert static  struct mon_data *mon_hash[MON_HASH_SIZE];  /* array of list ptrs */
73c0b746e5SOllivier Robert struct mon_data mon_mru_list;
74c0b746e5SOllivier Robert struct mon_data mon_fifo_list;
75c0b746e5SOllivier Robert /*
76c0b746e5SOllivier Robert  * List of free structures structures, and counters of free and total
77c0b746e5SOllivier Robert  * structures.  The free structures are linked with the hash_next field.
78c0b746e5SOllivier Robert  */
79c0b746e5SOllivier Robert static  struct mon_data *mon_free;      /* the free list or null if none */
80c0b746e5SOllivier Robert 
81c0b746e5SOllivier Robert static	int mon_total_mem;		/* total number of structures allocated */
82c0b746e5SOllivier Robert static	int mon_mem_increments;		/* number of times we've called malloc() */
83c0b746e5SOllivier Robert 
84c0b746e5SOllivier Robert /*
85c0b746e5SOllivier Robert  * Initialization state.  We may be monitoring, we may not.  If
86c0b746e5SOllivier Robert  * we aren't, we may not even have allocated any memory yet.
87c0b746e5SOllivier Robert  */
88c0b746e5SOllivier Robert int mon_enabled;
89c0b746e5SOllivier Robert static	int mon_have_memory;
90c0b746e5SOllivier Robert 
91c0b746e5SOllivier Robert static	void	mon_getmoremem	P((void));
92c0b746e5SOllivier Robert static	void	remove_from_hash P((struct mon_data *));
93c0b746e5SOllivier Robert 
94c0b746e5SOllivier Robert /*
95c0b746e5SOllivier Robert  * init_mon - initialize monitoring global data
96c0b746e5SOllivier Robert  */
97c0b746e5SOllivier Robert void
98c0b746e5SOllivier Robert init_mon(void)
99c0b746e5SOllivier Robert {
100c0b746e5SOllivier Robert 	/*
101c0b746e5SOllivier Robert 	 * Don't do much of anything here.  We don't allocate memory
102c0b746e5SOllivier Robert 	 * until someone explicitly starts us.
103c0b746e5SOllivier Robert 	 */
104c0b746e5SOllivier Robert 	mon_enabled = MON_OFF;
105c0b746e5SOllivier Robert 	mon_have_memory = 0;
106c0b746e5SOllivier Robert 
107c0b746e5SOllivier Robert 	mon_total_mem = 0;
108c0b746e5SOllivier Robert 	mon_mem_increments = 0;
109c0b746e5SOllivier Robert 	mon_free = NULL;
110c0b746e5SOllivier Robert 	memset((char *)&mon_hash[0], 0, sizeof mon_hash);
111c0b746e5SOllivier Robert 	memset((char *)&mon_mru_list, 0, sizeof mon_mru_list);
112c0b746e5SOllivier Robert 	memset((char *)&mon_fifo_list, 0, sizeof mon_fifo_list);
113c0b746e5SOllivier Robert }
114c0b746e5SOllivier Robert 
115c0b746e5SOllivier Robert 
116c0b746e5SOllivier Robert /*
117c0b746e5SOllivier Robert  * mon_start - start up the monitoring software
118c0b746e5SOllivier Robert  */
119c0b746e5SOllivier Robert void
120c0b746e5SOllivier Robert mon_start(
121c0b746e5SOllivier Robert 	int mode
122c0b746e5SOllivier Robert 	)
123c0b746e5SOllivier Robert {
124c0b746e5SOllivier Robert 
125c0b746e5SOllivier Robert 	if (mon_enabled != MON_OFF) {
126c0b746e5SOllivier Robert 		mon_enabled |= mode;
127c0b746e5SOllivier Robert 		return;
128c0b746e5SOllivier Robert 	}
129c0b746e5SOllivier Robert 	if (mode == MON_OFF)
130c0b746e5SOllivier Robert 	    return;		/* Ooops.. */
131c0b746e5SOllivier Robert 
132c0b746e5SOllivier Robert 	if (!mon_have_memory) {
133c0b746e5SOllivier Robert 		mon_total_mem = 0;
134c0b746e5SOllivier Robert 		mon_mem_increments = 0;
135c0b746e5SOllivier Robert 		mon_free = NULL;
136c0b746e5SOllivier Robert 		mon_getmoremem();
137c0b746e5SOllivier Robert 		mon_have_memory = 1;
138c0b746e5SOllivier Robert 	}
139c0b746e5SOllivier Robert 
140c0b746e5SOllivier Robert 	mon_mru_list.mru_next = &mon_mru_list;
141c0b746e5SOllivier Robert 	mon_mru_list.mru_prev = &mon_mru_list;
142c0b746e5SOllivier Robert 
143c0b746e5SOllivier Robert 	mon_fifo_list.fifo_next = &mon_fifo_list;
144c0b746e5SOllivier Robert 	mon_fifo_list.fifo_prev = &mon_fifo_list;
145c0b746e5SOllivier Robert 
146c0b746e5SOllivier Robert 	mon_enabled = mode;
147c0b746e5SOllivier Robert }
148c0b746e5SOllivier Robert 
149c0b746e5SOllivier Robert 
150c0b746e5SOllivier Robert /*
151c0b746e5SOllivier Robert  * mon_stop - stop the monitoring software
152c0b746e5SOllivier Robert  */
153c0b746e5SOllivier Robert void
154c0b746e5SOllivier Robert mon_stop(
155c0b746e5SOllivier Robert 	int mode
156c0b746e5SOllivier Robert 	)
157c0b746e5SOllivier Robert {
158c0b746e5SOllivier Robert 	register struct mon_data *md, *md_next;
159c0b746e5SOllivier Robert 	register int i;
160c0b746e5SOllivier Robert 
161c0b746e5SOllivier Robert 	if (mon_enabled == MON_OFF)
162c0b746e5SOllivier Robert 	    return;
163c0b746e5SOllivier Robert 	if ((mon_enabled & mode) == 0 || mode == MON_OFF)
164c0b746e5SOllivier Robert 	    return;
165c0b746e5SOllivier Robert 
166c0b746e5SOllivier Robert 	mon_enabled &= ~mode;
167c0b746e5SOllivier Robert 	if (mon_enabled != MON_OFF)
168c0b746e5SOllivier Robert 	    return;
169c0b746e5SOllivier Robert 
170c0b746e5SOllivier Robert 	/*
171c0b746e5SOllivier Robert 	 * Put everything back on the free list
172c0b746e5SOllivier Robert 	 */
173c0b746e5SOllivier Robert 	for (i = 0; i < MON_HASH_SIZE; i++) {
174c0b746e5SOllivier Robert 		md = mon_hash[i];               /* get next list */
175c0b746e5SOllivier Robert 		mon_hash[i] = NULL;             /* zero the list head */
176c0b746e5SOllivier Robert 		while (md != NULL) {
177c0b746e5SOllivier Robert 			md_next = md->hash_next;
178c0b746e5SOllivier Robert 			md->hash_next = mon_free;
179c0b746e5SOllivier Robert 			mon_free = md;
180c0b746e5SOllivier Robert 			md = md_next;
181c0b746e5SOllivier Robert 		}
182c0b746e5SOllivier Robert 	}
183c0b746e5SOllivier Robert 
184c0b746e5SOllivier Robert 	mon_mru_list.mru_next = &mon_mru_list;
185c0b746e5SOllivier Robert 	mon_mru_list.mru_prev = &mon_mru_list;
186c0b746e5SOllivier Robert 
187c0b746e5SOllivier Robert 	mon_fifo_list.fifo_next = &mon_fifo_list;
188c0b746e5SOllivier Robert 	mon_fifo_list.fifo_prev = &mon_fifo_list;
189c0b746e5SOllivier Robert }
190c0b746e5SOllivier Robert 
191c0b746e5SOllivier Robert 
192c0b746e5SOllivier Robert /*
193c0b746e5SOllivier Robert  * ntp_monitor - record stats about this packet
194c0b746e5SOllivier Robert  */
195c0b746e5SOllivier Robert void
196c0b746e5SOllivier Robert ntp_monitor(
197c0b746e5SOllivier Robert 	struct recvbuf *rbufp
198c0b746e5SOllivier Robert 	)
199c0b746e5SOllivier Robert {
200c0b746e5SOllivier Robert 	register struct pkt *pkt;
201c0b746e5SOllivier Robert 	register struct mon_data *md;
202c0b746e5SOllivier Robert 	register u_long netnum;
203c0b746e5SOllivier Robert 	register int hash;
204c0b746e5SOllivier Robert 	register int mode;
205c0b746e5SOllivier Robert 
206c0b746e5SOllivier Robert 	if (mon_enabled == MON_OFF)
207c0b746e5SOllivier Robert 	    return;
208c0b746e5SOllivier Robert 
209c0b746e5SOllivier Robert 	pkt = &rbufp->recv_pkt;
210c0b746e5SOllivier Robert 	netnum = NSRCADR(&rbufp->recv_srcadr);
211c0b746e5SOllivier Robert 	hash = MON_HASH(netnum);
212c0b746e5SOllivier Robert 	mode = PKT_MODE(pkt->li_vn_mode);
213c0b746e5SOllivier Robert 
214c0b746e5SOllivier Robert 	md = mon_hash[hash];
215c0b746e5SOllivier Robert 	while (md != NULL) {
216c0b746e5SOllivier Robert 		if (md->rmtadr == netnum &&
217c0b746e5SOllivier Robert 		    /* ?? md->interface == rbufp->dstadr && ?? */
218c0b746e5SOllivier Robert 		    md->mode == (u_char)mode) {
219c0b746e5SOllivier Robert 			md->lasttime = current_time;
220c0b746e5SOllivier Robert 			md->count++;
221c0b746e5SOllivier Robert 			md->version = PKT_VERSION(pkt->li_vn_mode);
222c0b746e5SOllivier Robert 			md->rmtport = NSRCPORT(&rbufp->recv_srcadr);
223c0b746e5SOllivier Robert 
224c0b746e5SOllivier Robert 			/*
225c0b746e5SOllivier Robert 			 * Shuffle him to the head of the
226c0b746e5SOllivier Robert 			 * mru list.  What a crock.
227c0b746e5SOllivier Robert 			 */
228c0b746e5SOllivier Robert 			md->mru_next->mru_prev = md->mru_prev;
229c0b746e5SOllivier Robert 			md->mru_prev->mru_next = md->mru_next;
230c0b746e5SOllivier Robert 			md->mru_next = mon_mru_list.mru_next;
231c0b746e5SOllivier Robert 			md->mru_prev = &mon_mru_list;
232c0b746e5SOllivier Robert 			mon_mru_list.mru_next->mru_prev = md;
233c0b746e5SOllivier Robert 			mon_mru_list.mru_next = md;
234c0b746e5SOllivier Robert 
235c0b746e5SOllivier Robert 			return;
236c0b746e5SOllivier Robert 		}
237c0b746e5SOllivier Robert 		md = md->hash_next;
238c0b746e5SOllivier Robert 	}
239c0b746e5SOllivier Robert 
240c0b746e5SOllivier Robert 	/*
241c0b746e5SOllivier Robert 	 * If we got here, this is the first we've heard of this
242c0b746e5SOllivier Robert 	 * guy.  Get him some memory, either from the free list
243c0b746e5SOllivier Robert 	 * or from the tail of the MRU list.
244c0b746e5SOllivier Robert 	 */
245c0b746e5SOllivier Robert 	if (mon_free == NULL && mon_total_mem >= MAXMONMEM) {
246c0b746e5SOllivier Robert 		/*
247c0b746e5SOllivier Robert 		 * Get it from MRU list
248c0b746e5SOllivier Robert 		 */
249c0b746e5SOllivier Robert 		md = mon_mru_list.mru_prev;
250c0b746e5SOllivier Robert 		md->mru_prev->mru_next = &mon_mru_list;
251c0b746e5SOllivier Robert 		mon_mru_list.mru_prev = md->mru_prev;
252c0b746e5SOllivier Robert 
253c0b746e5SOllivier Robert 		remove_from_hash(md);
254c0b746e5SOllivier Robert 
255c0b746e5SOllivier Robert 		/*
256c0b746e5SOllivier Robert 		 * Get it from FIFO list
257c0b746e5SOllivier Robert 		 */
258c0b746e5SOllivier Robert 		md->fifo_prev->fifo_next = md->fifo_next;
259c0b746e5SOllivier Robert 		md->fifo_next->fifo_prev = md->fifo_prev;
260c0b746e5SOllivier Robert 
261c0b746e5SOllivier Robert 	} else {
262c0b746e5SOllivier Robert 		if (mon_free == NULL)           /* if free list empty */
263c0b746e5SOllivier Robert 		    mon_getmoremem();       /* then get more */
264c0b746e5SOllivier Robert 		md = mon_free;
265c0b746e5SOllivier Robert 		mon_free = md->hash_next;
266c0b746e5SOllivier Robert 	}
267c0b746e5SOllivier Robert 
268c0b746e5SOllivier Robert 	/*
269c0b746e5SOllivier Robert 	 * Got one, initialize it
270c0b746e5SOllivier Robert 	 */
271c0b746e5SOllivier Robert 	md->lasttime = md->firsttime = current_time;
272c0b746e5SOllivier Robert 	md->lastdrop = 0;
273c0b746e5SOllivier Robert 	md->count = 1;
274c0b746e5SOllivier Robert 	md->rmtadr = netnum;
275c0b746e5SOllivier Robert 	md->rmtport = NSRCPORT(&rbufp->recv_srcadr);
276c0b746e5SOllivier Robert 	md->mode = (u_char) mode;
277c0b746e5SOllivier Robert 	md->version = PKT_VERSION(pkt->li_vn_mode);
278c0b746e5SOllivier Robert 	md->interface = rbufp->dstadr;
279c0b746e5SOllivier Robert 	md->cast_flags = ((rbufp->dstadr->flags & INT_MULTICAST) &&
280c0b746e5SOllivier Robert 			  rbufp->fd == md->interface->fd) ? MDF_MCAST: rbufp->fd ==
281c0b746e5SOllivier Robert 		md->interface->bfd ? MDF_BCAST : MDF_UCAST;
282c0b746e5SOllivier Robert 
283c0b746e5SOllivier Robert 	/*
284c0b746e5SOllivier Robert 	 * Drop him into front of the hash table.
285c0b746e5SOllivier Robert 	 * Also put him on top of the MRU list
286c0b746e5SOllivier Robert 	 * and at bottom of FIFO list
287c0b746e5SOllivier Robert 	 */
288c0b746e5SOllivier Robert 
289c0b746e5SOllivier Robert 	md->hash_next = mon_hash[hash];
290c0b746e5SOllivier Robert 	mon_hash[hash] = md;
291c0b746e5SOllivier Robert 
292c0b746e5SOllivier Robert 	md->mru_next = mon_mru_list.mru_next;
293c0b746e5SOllivier Robert 	md->mru_prev = &mon_mru_list;
294c0b746e5SOllivier Robert 	mon_mru_list.mru_next->mru_prev = md;
295c0b746e5SOllivier Robert 	mon_mru_list.mru_next = md;
296c0b746e5SOllivier Robert 
297c0b746e5SOllivier Robert 	md->fifo_prev = mon_fifo_list.fifo_prev;
298c0b746e5SOllivier Robert 	md->fifo_next = &mon_fifo_list;
299c0b746e5SOllivier Robert 	mon_fifo_list.fifo_prev->fifo_next = md;
300c0b746e5SOllivier Robert 	mon_fifo_list.fifo_prev = md;
301c0b746e5SOllivier Robert }
302c0b746e5SOllivier Robert 
303c0b746e5SOllivier Robert 
304c0b746e5SOllivier Robert /*
305c0b746e5SOllivier Robert  * mon_getmoremem - get more memory and put it on the free list
306c0b746e5SOllivier Robert  */
307c0b746e5SOllivier Robert static void
308c0b746e5SOllivier Robert mon_getmoremem(void)
309c0b746e5SOllivier Robert {
310c0b746e5SOllivier Robert 	register struct mon_data *md;
311c0b746e5SOllivier Robert 	register int i;
312c0b746e5SOllivier Robert 	struct mon_data *freedata;      /* 'old' free list (null) */
313c0b746e5SOllivier Robert 
314c0b746e5SOllivier Robert 	md = (struct mon_data *)emalloc(MONMEMINC * sizeof(struct mon_data));
315c0b746e5SOllivier Robert 	freedata = mon_free;
316c0b746e5SOllivier Robert 	mon_free = md;
317c0b746e5SOllivier Robert 
318c0b746e5SOllivier Robert 	for (i = 0; i < (MONMEMINC-1); i++) {
319c0b746e5SOllivier Robert 		md->hash_next = (md + 1);
320c0b746e5SOllivier Robert 		md++;
321c0b746e5SOllivier Robert 	}
322c0b746e5SOllivier Robert 
323c0b746e5SOllivier Robert 	/*
324c0b746e5SOllivier Robert 	 * md now points at the last.  Link in the rest of the chain.
325c0b746e5SOllivier Robert 	 */
326c0b746e5SOllivier Robert 	md->hash_next = freedata;
327c0b746e5SOllivier Robert 
328c0b746e5SOllivier Robert 	mon_total_mem += MONMEMINC;
329c0b746e5SOllivier Robert 	mon_mem_increments++;
330c0b746e5SOllivier Robert }
331c0b746e5SOllivier Robert 
332c0b746e5SOllivier Robert static void
333c0b746e5SOllivier Robert remove_from_hash(
334c0b746e5SOllivier Robert 	struct mon_data *md
335c0b746e5SOllivier Robert 	)
336c0b746e5SOllivier Robert {
337c0b746e5SOllivier Robert 	register int hash;
338c0b746e5SOllivier Robert 	register struct mon_data *md_prev;
339c0b746e5SOllivier Robert 
340c0b746e5SOllivier Robert 	hash = MON_HASH(md->rmtadr);
341c0b746e5SOllivier Robert 	if (mon_hash[hash] == md) {
342c0b746e5SOllivier Robert 		mon_hash[hash] = md->hash_next;
343c0b746e5SOllivier Robert 	} else {
344c0b746e5SOllivier Robert 		md_prev = mon_hash[hash];
345c0b746e5SOllivier Robert 		while (md_prev->hash_next != md) {
346c0b746e5SOllivier Robert 			md_prev = md_prev->hash_next;
347c0b746e5SOllivier Robert 			if (md_prev == NULL) {
348c0b746e5SOllivier Robert 				/* logic error */
349c0b746e5SOllivier Robert 				return;
350c0b746e5SOllivier Robert 			}
351c0b746e5SOllivier Robert 		}
352c0b746e5SOllivier Robert 		md_prev->hash_next = md->hash_next;
353c0b746e5SOllivier Robert 	}
354c0b746e5SOllivier Robert }
355