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