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