PageRenderTime 62ms CodeModel.GetById 14ms app.highlight 41ms RepoModel.GetById 1ms app.codeStats 1ms

/contrib/ntp/ntpd/ntp_monitor.c

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