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