xref: /freebsd/contrib/ntp/ntpd/ntp_restrict.c (revision c0b746e5e8d9479f05b3749cbf1f73b8928719bd)
1c0b746e5SOllivier Robert /*
2c0b746e5SOllivier Robert  * ntp_restrict.c - find out what restrictions this host is running under
3c0b746e5SOllivier Robert  */
4c0b746e5SOllivier Robert #ifdef HAVE_CONFIG_H
5c0b746e5SOllivier Robert #include <config.h>
6c0b746e5SOllivier Robert #endif
7c0b746e5SOllivier Robert 
8c0b746e5SOllivier Robert #include <stdio.h>
9c0b746e5SOllivier Robert #include <sys/types.h>
10c0b746e5SOllivier Robert 
11c0b746e5SOllivier Robert #include "ntpd.h"
12c0b746e5SOllivier Robert #include "ntp_if.h"
13c0b746e5SOllivier Robert #include "ntp_stdlib.h"
14c0b746e5SOllivier Robert 
15c0b746e5SOllivier Robert /*
16c0b746e5SOllivier Robert  * This code keeps a simple address-and-mask list of hosts we want
17c0b746e5SOllivier Robert  * to place restrictions on (or remove them from).  The restrictions
18c0b746e5SOllivier Robert  * are implemented as a set of flags which tell you what the host
19c0b746e5SOllivier Robert  * can't do.  There is a subroutine entry to return the flags.  The
20c0b746e5SOllivier Robert  * list is kept sorted to reduce the average number of comparisons
21c0b746e5SOllivier Robert  * and make sure you get the set of restrictions most specific to
22c0b746e5SOllivier Robert  * the address.
23c0b746e5SOllivier Robert  *
24c0b746e5SOllivier Robert  * The algorithm is that, when looking up a host, it is first assumed
25c0b746e5SOllivier Robert  * that the default set of restrictions will apply.  It then searches
26c0b746e5SOllivier Robert  * down through the list.  Whenever it finds a match it adopts the match's
27c0b746e5SOllivier Robert  * flags instead.  When you hit the point where the sorted address is
28c0b746e5SOllivier Robert  * greater than the target, you return with the last set of flags you
29c0b746e5SOllivier Robert  * found.  Because of the ordering of the list, the most specific match
30c0b746e5SOllivier Robert  * will provide the final set of flags.
31c0b746e5SOllivier Robert  *
32c0b746e5SOllivier Robert  * This was originally intended to restrict you from sync'ing to your
33c0b746e5SOllivier Robert  * own broadcasts when you are doing that, by restricting yourself
34c0b746e5SOllivier Robert  * from your own interfaces.  It was also thought it would sometimes
35c0b746e5SOllivier Robert  * be useful to keep a misbehaving host or two from abusing your primary
36c0b746e5SOllivier Robert  * clock.  It has been expanded, however, to suit the needs of those
37c0b746e5SOllivier Robert  * with more restrictive access policies.
38c0b746e5SOllivier Robert  */
39c0b746e5SOllivier Robert 
40c0b746e5SOllivier Robert /*
41c0b746e5SOllivier Robert  * Memory allocation parameters.  We allocate INITRESLIST entries
42c0b746e5SOllivier Robert  * initially, and add INCRESLIST entries to the free list whenever
43c0b746e5SOllivier Robert  * we run out.
44c0b746e5SOllivier Robert  */
45c0b746e5SOllivier Robert #define	INITRESLIST	10
46c0b746e5SOllivier Robert #define	INCRESLIST	5
47c0b746e5SOllivier Robert 
48c0b746e5SOllivier Robert /*
49c0b746e5SOllivier Robert  * The restriction list
50c0b746e5SOllivier Robert  */
51c0b746e5SOllivier Robert struct restrictlist *restrictlist;
52c0b746e5SOllivier Robert static	int restrictcount;	/* count of entries in the restriction list */
53c0b746e5SOllivier Robert 
54c0b746e5SOllivier Robert /*
55c0b746e5SOllivier Robert  * The free list and associated counters.  Also some uninteresting
56c0b746e5SOllivier Robert  * stat counters.
57c0b746e5SOllivier Robert  */
58c0b746e5SOllivier Robert static	struct restrictlist *resfree;
59c0b746e5SOllivier Robert static	int numresfree;		/* number of structures on free list */
60c0b746e5SOllivier Robert 
61c0b746e5SOllivier Robert static	u_long res_calls;
62c0b746e5SOllivier Robert static	u_long res_found;
63c0b746e5SOllivier Robert static	u_long res_not_found;
64c0b746e5SOllivier Robert /* static	u_long res_timereset; */
65c0b746e5SOllivier Robert 
66c0b746e5SOllivier Robert /*
67c0b746e5SOllivier Robert  * Parameters of the RES_LIMITED restriction option.
68c0b746e5SOllivier Robert  * client_limit is the number of hosts allowed per source net
69c0b746e5SOllivier Robert  * client_limit_period is the number of seconds after which an entry
70c0b746e5SOllivier Robert  * is no longer considered for client limit determination
71c0b746e5SOllivier Robert  */
72c0b746e5SOllivier Robert u_long client_limit;
73c0b746e5SOllivier Robert u_long client_limit_period;
74c0b746e5SOllivier Robert /*
75c0b746e5SOllivier Robert  * count number of restriction entries referring to RES_LIMITED
76c0b746e5SOllivier Robert  * controls activation/deactivation of monitoring
77c0b746e5SOllivier Robert  * (with respect to RES_LIMITED control)
78c0b746e5SOllivier Robert  */
79c0b746e5SOllivier Robert static	u_long res_limited_refcnt;
80c0b746e5SOllivier Robert 
81c0b746e5SOllivier Robert /*
82c0b746e5SOllivier Robert  * Our initial allocation of list entries.
83c0b746e5SOllivier Robert  */
84c0b746e5SOllivier Robert static	struct restrictlist resinit[INITRESLIST];
85c0b746e5SOllivier Robert 
86c0b746e5SOllivier Robert /*
87c0b746e5SOllivier Robert  * init_restrict - initialize the restriction data structures
88c0b746e5SOllivier Robert  */
89c0b746e5SOllivier Robert void
90c0b746e5SOllivier Robert init_restrict(void)
91c0b746e5SOllivier Robert {
92c0b746e5SOllivier Robert 	register int i;
93c0b746e5SOllivier Robert 	char bp[80];
94c0b746e5SOllivier Robert 
95c0b746e5SOllivier Robert 	/*
96c0b746e5SOllivier Robert 	 * Zero the list and put all but one on the free list
97c0b746e5SOllivier Robert 	 */
98c0b746e5SOllivier Robert 	resfree = 0;
99c0b746e5SOllivier Robert 	memset((char *)resinit, 0, sizeof resinit);
100c0b746e5SOllivier Robert 
101c0b746e5SOllivier Robert 	for (i = 1; i < INITRESLIST; i++) {
102c0b746e5SOllivier Robert 		resinit[i].next = resfree;
103c0b746e5SOllivier Robert 		resfree = &resinit[i];
104c0b746e5SOllivier Robert 	}
105c0b746e5SOllivier Robert 
106c0b746e5SOllivier Robert 	numresfree = INITRESLIST-1;
107c0b746e5SOllivier Robert 
108c0b746e5SOllivier Robert 	/*
109c0b746e5SOllivier Robert 	 * Put the remaining item at the head of the
110c0b746e5SOllivier Robert 	 * list as our default entry.  Everything in here
111c0b746e5SOllivier Robert 	 * should be zero for now.
112c0b746e5SOllivier Robert 	 */
113c0b746e5SOllivier Robert 	resinit[0].addr = htonl(INADDR_ANY);
114c0b746e5SOllivier Robert 	resinit[0].mask = 0;
115c0b746e5SOllivier Robert 	restrictlist = &resinit[0];
116c0b746e5SOllivier Robert 	restrictcount = 1;
117c0b746e5SOllivier Robert 
118c0b746e5SOllivier Robert 
119c0b746e5SOllivier Robert 	/*
120c0b746e5SOllivier Robert 	 * fix up stat counters
121c0b746e5SOllivier Robert 	 */
122c0b746e5SOllivier Robert 	res_calls = 0;
123c0b746e5SOllivier Robert 	res_found = 0;
124c0b746e5SOllivier Robert 	res_not_found = 0;
125c0b746e5SOllivier Robert 	/* res_timereset = 0; */
126c0b746e5SOllivier Robert 
127c0b746e5SOllivier Robert 	/*
128c0b746e5SOllivier Robert 	 * set default values for RES_LIMIT functionality
129c0b746e5SOllivier Robert 	 */
130c0b746e5SOllivier Robert 	client_limit = 3;
131c0b746e5SOllivier Robert 	client_limit_period = 3600;
132c0b746e5SOllivier Robert 	res_limited_refcnt = 0;
133c0b746e5SOllivier Robert 
134c0b746e5SOllivier Robert 	sprintf(bp, "client_limit=%ld", client_limit);
135c0b746e5SOllivier Robert 	set_sys_var(bp, strlen(bp)+1, RO);
136c0b746e5SOllivier Robert 	sprintf(bp, "client_limit_period=%ld", client_limit_period);
137c0b746e5SOllivier Robert 	set_sys_var(bp, strlen(bp)+1, RO);
138c0b746e5SOllivier Robert }
139c0b746e5SOllivier Robert 
140c0b746e5SOllivier Robert 
141c0b746e5SOllivier Robert /*
142c0b746e5SOllivier Robert  * restrictions - return restrictions for this host
143c0b746e5SOllivier Robert  */
144c0b746e5SOllivier Robert int
145c0b746e5SOllivier Robert restrictions(
146c0b746e5SOllivier Robert 	struct sockaddr_in *srcadr
147c0b746e5SOllivier Robert 	)
148c0b746e5SOllivier Robert {
149c0b746e5SOllivier Robert 	register struct restrictlist *rl;
150c0b746e5SOllivier Robert 	register struct restrictlist *match;
151c0b746e5SOllivier Robert 	register u_int32 hostaddr;
152c0b746e5SOllivier Robert 	register int isntpport;
153c0b746e5SOllivier Robert 
154c0b746e5SOllivier Robert 	res_calls++;
155c0b746e5SOllivier Robert 	/*
156c0b746e5SOllivier Robert 	 * We need the host address in host order.  Also need to know
157c0b746e5SOllivier Robert 	 * whether this is from the ntp port or not.
158c0b746e5SOllivier Robert 	 */
159c0b746e5SOllivier Robert 	hostaddr = SRCADR(srcadr);
160c0b746e5SOllivier Robert 	isntpport = (SRCPORT(srcadr) == NTP_PORT);
161c0b746e5SOllivier Robert 
162c0b746e5SOllivier Robert 	/*
163c0b746e5SOllivier Robert 	 * Ignore any packets with a multicast source address
164c0b746e5SOllivier Robert 	 * (this should be done early in the receive process, later!)
165c0b746e5SOllivier Robert 	 */
166c0b746e5SOllivier Robert 	if (IN_CLASSD(ntohl(srcadr->sin_addr.s_addr)))
167c0b746e5SOllivier Robert 	    return (int)RES_IGNORE;
168c0b746e5SOllivier Robert 
169c0b746e5SOllivier Robert 	/*
170c0b746e5SOllivier Robert 	 * Set match to first entry, which is default entry.  Work our
171c0b746e5SOllivier Robert 	 * way down from there.
172c0b746e5SOllivier Robert 	 */
173c0b746e5SOllivier Robert 	match = restrictlist;
174c0b746e5SOllivier Robert 
175c0b746e5SOllivier Robert 	for (rl = match->next; rl != 0 && rl->addr <= hostaddr; rl = rl->next)
176c0b746e5SOllivier Robert 	    if ((hostaddr & rl->mask) == rl->addr) {
177c0b746e5SOllivier Robert 		    if ((rl->mflags & RESM_NTPONLY) && !isntpport)
178c0b746e5SOllivier Robert 			continue;
179c0b746e5SOllivier Robert 		    match = rl;
180c0b746e5SOllivier Robert 	    }
181c0b746e5SOllivier Robert 
182c0b746e5SOllivier Robert 	match->count++;
183c0b746e5SOllivier Robert 	if (match == restrictlist)
184c0b746e5SOllivier Robert 	    res_not_found++;
185c0b746e5SOllivier Robert 	else
186c0b746e5SOllivier Robert 	    res_found++;
187c0b746e5SOllivier Robert 
188c0b746e5SOllivier Robert 	/*
189c0b746e5SOllivier Robert 	 * The following implements limiting the number of clients
190c0b746e5SOllivier Robert 	 * accepted from a given network. The notion of "same network"
191c0b746e5SOllivier Robert 	 * is determined by the mask and addr fields of the restrict
192c0b746e5SOllivier Robert 	 * list entry. The monitor mechanism has to be enabled for
193c0b746e5SOllivier Robert 	 * collecting info on current clients.
194c0b746e5SOllivier Robert 	 *
195c0b746e5SOllivier Robert 	 * The policy is as follows:
196c0b746e5SOllivier Robert 	 *	- take the list of clients recorded
197c0b746e5SOllivier Robert 	 *        from the given "network" seen within the last
198c0b746e5SOllivier Robert 	 *        client_limit_period seconds
199c0b746e5SOllivier Robert 	 *      - if there are at most client_limit entries:
200c0b746e5SOllivier Robert 	 *        --> access allowed
201c0b746e5SOllivier Robert 	 *      - otherwise sort by time first seen
202c0b746e5SOllivier Robert 	 *      - current client among the first client_limit seen
203c0b746e5SOllivier Robert 	 *        hosts?
204c0b746e5SOllivier Robert 	 *        if yes: access allowed
205c0b746e5SOllivier Robert 	 *        else:   eccess denied
206c0b746e5SOllivier Robert 	 */
207c0b746e5SOllivier Robert 	if (match->flags & RES_LIMITED) {
208c0b746e5SOllivier Robert 		int lcnt;
209c0b746e5SOllivier Robert 		struct mon_data *md, *this_client;
210c0b746e5SOllivier Robert 
211c0b746e5SOllivier Robert #ifdef DEBUG
212c0b746e5SOllivier Robert 		if (debug > 2)
213c0b746e5SOllivier Robert 		    printf("limited clients check: %ld clients, period %ld seconds, net is 0x%lX\n",
214c0b746e5SOllivier Robert 			   client_limit, client_limit_period,
215c0b746e5SOllivier Robert 			   (u_long)netof(hostaddr));
216c0b746e5SOllivier Robert #endif /*DEBUG*/
217c0b746e5SOllivier Robert 		if (mon_enabled == MON_OFF) {
218c0b746e5SOllivier Robert #ifdef DEBUG
219c0b746e5SOllivier Robert 			if (debug > 4)
220c0b746e5SOllivier Robert 			    printf("no limit - monitoring is off\n");
221c0b746e5SOllivier Robert #endif
222c0b746e5SOllivier Robert 			return (int)(match->flags & ~RES_LIMITED);
223c0b746e5SOllivier Robert 		}
224c0b746e5SOllivier Robert 
225c0b746e5SOllivier Robert 		/*
226c0b746e5SOllivier Robert 		 * How nice, MRU list provides our current client as the
227c0b746e5SOllivier Robert 		 * first entry in the list.
228c0b746e5SOllivier Robert 		 * Monitoring was verified to be active above, thus we
229c0b746e5SOllivier Robert 		 * know an entry for our client must exist, or some
230c0b746e5SOllivier Robert 		 * brain dead set the memory limit for mon entries to ZERO!!!
231c0b746e5SOllivier Robert 		 */
232c0b746e5SOllivier Robert 		this_client = mon_mru_list.mru_next;
233c0b746e5SOllivier Robert 
234c0b746e5SOllivier Robert 		for (md = mon_fifo_list.fifo_next,lcnt = 0;
235c0b746e5SOllivier Robert 		     md != &mon_fifo_list;
236c0b746e5SOllivier Robert 		     md = md->fifo_next) {
237c0b746e5SOllivier Robert 			if ((current_time - md->lasttime)
238c0b746e5SOllivier Robert 			    > client_limit_period) {
239c0b746e5SOllivier Robert #ifdef DEBUG
240c0b746e5SOllivier Robert 				if (debug > 5)
241c0b746e5SOllivier Robert 				    printf("checking: %s: ignore: too old: %ld\n",
242c0b746e5SOllivier Robert 					   numtoa(md->rmtadr),
243c0b746e5SOllivier Robert 					   current_time - md->lasttime);
244c0b746e5SOllivier Robert #endif
245c0b746e5SOllivier Robert 				continue;
246c0b746e5SOllivier Robert 			}
247c0b746e5SOllivier Robert 			if (md->mode == MODE_BROADCAST ||
248c0b746e5SOllivier Robert 			    md->mode == MODE_CONTROL ||
249c0b746e5SOllivier Robert 			    md->mode == MODE_PRIVATE) {
250c0b746e5SOllivier Robert #ifdef DEBUG
251c0b746e5SOllivier Robert 				if (debug > 5)
252c0b746e5SOllivier Robert 				    printf("checking: %s: ignore mode %d\n",
253c0b746e5SOllivier Robert 					   numtoa(md->rmtadr),
254c0b746e5SOllivier Robert 					   md->mode);
255c0b746e5SOllivier Robert #endif
256c0b746e5SOllivier Robert 				continue;
257c0b746e5SOllivier Robert 			}
258c0b746e5SOllivier Robert 			if (netof(md->rmtadr) !=
259c0b746e5SOllivier Robert 			    netof(hostaddr)) {
260c0b746e5SOllivier Robert #ifdef DEBUG
261c0b746e5SOllivier Robert 				if (debug > 5)
262c0b746e5SOllivier Robert 				    printf("checking: %s: different net 0x%lX\n",
263c0b746e5SOllivier Robert 					   numtoa(md->rmtadr),
264c0b746e5SOllivier Robert 					   (u_long)netof(md->rmtadr));
265c0b746e5SOllivier Robert #endif
266c0b746e5SOllivier Robert 				continue;
267c0b746e5SOllivier Robert 			}
268c0b746e5SOllivier Robert 			lcnt++;
269c0b746e5SOllivier Robert 			if (lcnt >  (int) client_limit ||
270c0b746e5SOllivier Robert 			    md->rmtadr == hostaddr) {
271c0b746e5SOllivier Robert #ifdef DEBUG
272c0b746e5SOllivier Robert 				if (debug > 5)
273c0b746e5SOllivier Robert 				    printf("considering %s: found host\n",
274c0b746e5SOllivier Robert 					   numtoa(md->rmtadr));
275c0b746e5SOllivier Robert #endif
276c0b746e5SOllivier Robert 				break;
277c0b746e5SOllivier Robert 			}
278c0b746e5SOllivier Robert #ifdef DEBUG
279c0b746e5SOllivier Robert 			else {
280c0b746e5SOllivier Robert 				if (debug > 5)
281c0b746e5SOllivier Robert 				    printf("considering %s: same net\n",
282c0b746e5SOllivier Robert 					   numtoa(md->rmtadr));
283c0b746e5SOllivier Robert 			}
284c0b746e5SOllivier Robert #endif
285c0b746e5SOllivier Robert 
286c0b746e5SOllivier Robert 		}
287c0b746e5SOllivier Robert #ifdef DEBUG
288c0b746e5SOllivier Robert 		if (debug > 4)
289c0b746e5SOllivier Robert 		    printf("this one is rank %d in list, limit is %lu: %s\n",
290c0b746e5SOllivier Robert 			   lcnt, client_limit,
291c0b746e5SOllivier Robert 			   (lcnt <= (int) client_limit) ? "ALLOW" : "REJECT");
292c0b746e5SOllivier Robert #endif
293c0b746e5SOllivier Robert 		if (lcnt <= (int) client_limit) {
294c0b746e5SOllivier Robert 			this_client->lastdrop = 0;
295c0b746e5SOllivier Robert 			return (int)(match->flags & ~RES_LIMITED);
296c0b746e5SOllivier Robert 		} else {
297c0b746e5SOllivier Robert 			this_client->lastdrop = current_time;
298c0b746e5SOllivier Robert 		}
299c0b746e5SOllivier Robert 	}
300c0b746e5SOllivier Robert 	return (int)match->flags;
301c0b746e5SOllivier Robert }
302c0b746e5SOllivier Robert 
303c0b746e5SOllivier Robert 
304c0b746e5SOllivier Robert /*
305c0b746e5SOllivier Robert  * hack_restrict - add/subtract/manipulate entries on the restrict list
306c0b746e5SOllivier Robert  */
307c0b746e5SOllivier Robert void
308c0b746e5SOllivier Robert hack_restrict(
309c0b746e5SOllivier Robert 	int op,
310c0b746e5SOllivier Robert 	struct sockaddr_in *resaddr,
311c0b746e5SOllivier Robert 	struct sockaddr_in *resmask,
312c0b746e5SOllivier Robert 	int mflags,
313c0b746e5SOllivier Robert 	int flags
314c0b746e5SOllivier Robert 	)
315c0b746e5SOllivier Robert {
316c0b746e5SOllivier Robert 	register u_int32 addr;
317c0b746e5SOllivier Robert 	register u_int32 mask;
318c0b746e5SOllivier Robert 	register struct restrictlist *rl;
319c0b746e5SOllivier Robert 	register struct restrictlist *rlprev;
320c0b746e5SOllivier Robert 	int i;
321c0b746e5SOllivier Robert 
322c0b746e5SOllivier Robert 	/*
323c0b746e5SOllivier Robert 	 * Get address and mask in host byte order
324c0b746e5SOllivier Robert 	 */
325c0b746e5SOllivier Robert 	addr = SRCADR(resaddr);
326c0b746e5SOllivier Robert 	mask = SRCADR(resmask);
327c0b746e5SOllivier Robert 	addr &= mask;		/* make sure low bits are zero */
328c0b746e5SOllivier Robert 
329c0b746e5SOllivier Robert 	/*
330c0b746e5SOllivier Robert 	 * If this is the default address, point at first on list.  Else
331c0b746e5SOllivier Robert 	 * go searching for it.
332c0b746e5SOllivier Robert 	 */
333c0b746e5SOllivier Robert 	if (addr == htonl(INADDR_ANY)) {
334c0b746e5SOllivier Robert 		rlprev = 0;
335c0b746e5SOllivier Robert 		rl = restrictlist;
336c0b746e5SOllivier Robert 	} else {
337c0b746e5SOllivier Robert 		rlprev = restrictlist;
338c0b746e5SOllivier Robert 		rl = rlprev->next;
339c0b746e5SOllivier Robert 		while (rl != 0) {
340c0b746e5SOllivier Robert 			if (rl->addr > addr) {
341c0b746e5SOllivier Robert 				rl = 0;
342c0b746e5SOllivier Robert 				break;
343c0b746e5SOllivier Robert 			} else if (rl->addr == addr) {
344c0b746e5SOllivier Robert 				if (rl->mask == mask) {
345c0b746e5SOllivier Robert 					if ((mflags & RESM_NTPONLY)
346c0b746e5SOllivier Robert 					    == (rl->mflags & RESM_NTPONLY))
347c0b746e5SOllivier Robert 					    break;	/* exact match */
348c0b746e5SOllivier Robert 					if (!(mflags & RESM_NTPONLY)) {
349c0b746e5SOllivier Robert 						/*
350c0b746e5SOllivier Robert 						 * No flag fits before flag
351c0b746e5SOllivier Robert 						 */
352c0b746e5SOllivier Robert 						rl = 0;
353c0b746e5SOllivier Robert 						break;
354c0b746e5SOllivier Robert 					}
355c0b746e5SOllivier Robert 					/* continue on */
356c0b746e5SOllivier Robert 				} else if (rl->mask > mask) {
357c0b746e5SOllivier Robert 					rl = 0;
358c0b746e5SOllivier Robert 					break;
359c0b746e5SOllivier Robert 				}
360c0b746e5SOllivier Robert 			}
361c0b746e5SOllivier Robert 			rlprev = rl;
362c0b746e5SOllivier Robert 			rl = rl->next;
363c0b746e5SOllivier Robert 		}
364c0b746e5SOllivier Robert 	}
365c0b746e5SOllivier Robert 	/*
366c0b746e5SOllivier Robert 	 * In case the above wasn't clear :-), either rl now points
367c0b746e5SOllivier Robert 	 * at the entry this call refers to, or rl is zero and rlprev
368c0b746e5SOllivier Robert 	 * points to the entry prior to where this one should go in
369c0b746e5SOllivier Robert 	 * the sort.
370c0b746e5SOllivier Robert 	 */
371c0b746e5SOllivier Robert 
372c0b746e5SOllivier Robert 	/*
373c0b746e5SOllivier Robert 	 * Switch based on operation
374c0b746e5SOllivier Robert 	 */
375c0b746e5SOllivier Robert 	switch (op) {
376c0b746e5SOllivier Robert 	    case RESTRICT_FLAGS:
377c0b746e5SOllivier Robert 		/*
378c0b746e5SOllivier Robert 		 * Here we add bits to the flags.  If this is a new
379c0b746e5SOllivier Robert 		 * restriction add it.
380c0b746e5SOllivier Robert 		 */
381c0b746e5SOllivier Robert 		if (rl == 0) {
382c0b746e5SOllivier Robert 			if (numresfree == 0) {
383c0b746e5SOllivier Robert 				rl = (struct restrictlist *) emalloc(
384c0b746e5SOllivier Robert 					INCRESLIST*sizeof(struct restrictlist));
385c0b746e5SOllivier Robert 				memset((char *)rl, 0,
386c0b746e5SOllivier Robert 				       INCRESLIST*sizeof(struct restrictlist));
387c0b746e5SOllivier Robert 
388c0b746e5SOllivier Robert 				for (i = 0; i < INCRESLIST; i++) {
389c0b746e5SOllivier Robert 					rl->next = resfree;
390c0b746e5SOllivier Robert 					resfree = rl;
391c0b746e5SOllivier Robert 					rl++;
392c0b746e5SOllivier Robert 				}
393c0b746e5SOllivier Robert 				numresfree = INCRESLIST;
394c0b746e5SOllivier Robert 			}
395c0b746e5SOllivier Robert 
396c0b746e5SOllivier Robert 			rl = resfree;
397c0b746e5SOllivier Robert 			resfree = rl->next;
398c0b746e5SOllivier Robert 			numresfree--;
399c0b746e5SOllivier Robert 
400c0b746e5SOllivier Robert 			rl->addr = addr;
401c0b746e5SOllivier Robert 			rl->mask = mask;
402c0b746e5SOllivier Robert 			rl->mflags = (u_short)mflags;
403c0b746e5SOllivier Robert 
404c0b746e5SOllivier Robert 			rl->next = rlprev->next;
405c0b746e5SOllivier Robert 			rlprev->next = rl;
406c0b746e5SOllivier Robert 			restrictcount++;
407c0b746e5SOllivier Robert 		}
408c0b746e5SOllivier Robert 		if ((rl->flags ^ (u_short)flags) & RES_LIMITED) {
409c0b746e5SOllivier Robert 			res_limited_refcnt++;
410c0b746e5SOllivier Robert 			mon_start(MON_RES); /* ensure data gets collected */
411c0b746e5SOllivier Robert 		}
412c0b746e5SOllivier Robert 		rl->flags |= (u_short)flags;
413c0b746e5SOllivier Robert 		break;
414c0b746e5SOllivier Robert 
415c0b746e5SOllivier Robert 	    case RESTRICT_UNFLAG:
416c0b746e5SOllivier Robert 		/*
417c0b746e5SOllivier Robert 		 * Remove some bits from the flags.  If we didn't
418c0b746e5SOllivier Robert 		 * find this one, just return.
419c0b746e5SOllivier Robert 		 */
420c0b746e5SOllivier Robert 		if (rl != 0) {
421c0b746e5SOllivier Robert 			if ((rl->flags ^ (u_short)flags) & RES_LIMITED) {
422c0b746e5SOllivier Robert 				res_limited_refcnt--;
423c0b746e5SOllivier Robert 				if (res_limited_refcnt == 0)
424c0b746e5SOllivier Robert 				    mon_stop(MON_RES);
425c0b746e5SOllivier Robert 			}
426c0b746e5SOllivier Robert 			rl->flags &= (u_short)~flags;
427c0b746e5SOllivier Robert 		}
428c0b746e5SOllivier Robert 		break;
429c0b746e5SOllivier Robert 
430c0b746e5SOllivier Robert 	    case RESTRICT_REMOVE:
431c0b746e5SOllivier Robert 		/*
432c0b746e5SOllivier Robert 		 * Remove an entry from the table entirely if we found one.
433c0b746e5SOllivier Robert 		 * Don't remove the default entry and don't remove an
434c0b746e5SOllivier Robert 		 * interface entry.
435c0b746e5SOllivier Robert 		 */
436c0b746e5SOllivier Robert 		if (rl != 0
437c0b746e5SOllivier Robert 		    && rl->addr != htonl(INADDR_ANY)
438c0b746e5SOllivier Robert 		    && !(rl->mflags & RESM_INTERFACE)) {
439c0b746e5SOllivier Robert 			rlprev->next = rl->next;
440c0b746e5SOllivier Robert 			restrictcount--;
441c0b746e5SOllivier Robert 			if (rl->flags & RES_LIMITED) {
442c0b746e5SOllivier Robert 				res_limited_refcnt--;
443c0b746e5SOllivier Robert 				if (res_limited_refcnt == 0)
444c0b746e5SOllivier Robert 				    mon_stop(MON_RES);
445c0b746e5SOllivier Robert 			}
446c0b746e5SOllivier Robert 			memset((char *)rl, 0, sizeof(struct restrictlist));
447c0b746e5SOllivier Robert 
448c0b746e5SOllivier Robert 			rl->next = resfree;
449c0b746e5SOllivier Robert 			resfree = rl;
450c0b746e5SOllivier Robert 			numresfree++;
451c0b746e5SOllivier Robert 		}
452c0b746e5SOllivier Robert 		break;
453c0b746e5SOllivier Robert 
454c0b746e5SOllivier Robert 	    default:
455c0b746e5SOllivier Robert 		/* Oh, well */
456c0b746e5SOllivier Robert 		break;
457c0b746e5SOllivier Robert 	}
458c0b746e5SOllivier Robert 
459c0b746e5SOllivier Robert 	/* done! */
460c0b746e5SOllivier Robert }
461