xref: /freebsd/contrib/ntp/ntpd/ntp_monitor.c (revision daf1cffce2e07931f27c6c6998652e90df6ba87e)
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  */
72 static  struct mon_data *mon_hash[MON_HASH_SIZE];  /* array of list ptrs */
73 struct mon_data mon_mru_list;
74 struct 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  */
79 static  struct mon_data *mon_free;      /* the free list or null if none */
80 
81 static	int mon_total_mem;		/* total number of structures allocated */
82 static	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  */
88 int mon_enabled;
89 static	int mon_have_memory;
90 
91 static	void	mon_getmoremem	P((void));
92 static	void	remove_from_hash P((struct mon_data *));
93 
94 /*
95  * init_mon - initialize monitoring global data
96  */
97 void
98 init_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  */
119 void
120 mon_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  */
153 void
154 mon_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  */
195 void
196 ntp_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  */
307 static void
308 mon_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 
332 static void
333 remove_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