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