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