154359Sroberto/*
2132451Sroberto * ntp_restrict.c - determine host restrictions
354359Sroberto */
454359Sroberto#ifdef HAVE_CONFIG_H
554359Sroberto#include <config.h>
654359Sroberto#endif
754359Sroberto
854359Sroberto#include <stdio.h>
954359Sroberto#include <sys/types.h>
1054359Sroberto
1154359Sroberto#include "ntpd.h"
1254359Sroberto#include "ntp_if.h"
13290000Sglebius#include "ntp_lists.h"
1454359Sroberto#include "ntp_stdlib.h"
15290000Sglebius#include "ntp_assert.h"
1654359Sroberto
1754359Sroberto/*
1854359Sroberto * This code keeps a simple address-and-mask list of hosts we want
19132451Sroberto * to place restrictions on (or remove them from). The restrictions
2054359Sroberto * are implemented as a set of flags which tell you what the host
21132451Sroberto * can't do. There is a subroutine entry to return the flags. The
2254359Sroberto * list is kept sorted to reduce the average number of comparisons
2354359Sroberto * and make sure you get the set of restrictions most specific to
2454359Sroberto * the address.
2554359Sroberto *
2654359Sroberto * The algorithm is that, when looking up a host, it is first assumed
27132451Sroberto * that the default set of restrictions will apply. It then searches
28132451Sroberto * down through the list. Whenever it finds a match it adopts the
29132451Sroberto * match's flags instead. When you hit the point where the sorted
30132451Sroberto * address is greater than the target, you return with the last set of
31132451Sroberto * flags you found. Because of the ordering of the list, the most
32132451Sroberto * specific match will provide the final set of flags.
3354359Sroberto *
3454359Sroberto * This was originally intended to restrict you from sync'ing to your
35132451Sroberto * own broadcasts when you are doing that, by restricting yourself from
36132451Sroberto * your own interfaces. It was also thought it would sometimes be useful
37132451Sroberto * to keep a misbehaving host or two from abusing your primary clock. It
38132451Sroberto * has been expanded, however, to suit the needs of those with more
39132451Sroberto * restrictive access policies.
4054359Sroberto */
41132451Sroberto/*
42132451Sroberto * We will use two lists, one for IPv4 addresses and one for IPv6
43132451Sroberto * addresses. This is not protocol-independant but for now I can't
44132451Sroberto * find a way to respect this. We'll check this later... JFB 07/2001
45132451Sroberto */
46290000Sglebius#define MASK_IPV6_ADDR(dst, src, msk)					\
47290000Sglebius	do {								\
48290000Sglebius		int idx;						\
49290000Sglebius		for (idx = 0; idx < (int)COUNTOF((dst)->s6_addr); idx++) { \
50290000Sglebius			(dst)->s6_addr[idx] = (src)->s6_addr[idx]	\
51290000Sglebius					      & (msk)->s6_addr[idx];	\
52290000Sglebius		}							\
53132451Sroberto	} while (0)
5454359Sroberto
5554359Sroberto/*
56290000Sglebius * We allocate INC_RESLIST{4|6} entries to the free list whenever empty.
57290000Sglebius * Auto-tune these to be just less than 1KB (leaving at least 16 bytes
58290000Sglebius * for allocator overhead).
5954359Sroberto */
60290000Sglebius#define	INC_RESLIST4	((1024 - 16) / V4_SIZEOF_RESTRICT_U)
61290000Sglebius#define	INC_RESLIST6	((1024 - 16) / V6_SIZEOF_RESTRICT_U)
6254359Sroberto
6354359Sroberto/*
6454359Sroberto * The restriction list
6554359Sroberto */
66290000Sglebiusrestrict_u *restrictlist4;
67290000Sglebiusrestrict_u *restrictlist6;
68290000Sglebiusstatic int restrictcount;	/* count in the restrict lists */
6954359Sroberto
7054359Sroberto/*
7154359Sroberto * The free list and associated counters.  Also some uninteresting
7254359Sroberto * stat counters.
7354359Sroberto */
74290000Sglebiusstatic restrict_u *resfree4;	/* available entries (free list) */
75290000Sglebiusstatic restrict_u *resfree6;
7654359Sroberto
77290000Sglebiusstatic u_long res_calls;
78290000Sglebiusstatic u_long res_found;
79290000Sglebiusstatic u_long res_not_found;
8054359Sroberto
8154359Sroberto/*
82290000Sglebius * Count number of restriction entries referring to RES_LIMITED, to
83290000Sglebius * control implicit activation/deactivation of the MRU monlist.
8454359Sroberto */
85290000Sglebiusstatic	u_long res_limited_refcnt;
86132451Sroberto
8754359Sroberto/*
88290000Sglebius * Our default entries.
8954359Sroberto */
90290000Sglebiusstatic	restrict_u	restrict_def4;
91290000Sglebiusstatic	restrict_u	restrict_def6;
9254359Sroberto
9354359Sroberto/*
94290000Sglebius * "restrict source ..." enabled knob and restriction bits.
9554359Sroberto */
96290000Sglebiusstatic	int		restrict_source_enabled;
97290000Sglebiusstatic	u_short		restrict_source_flags;
98290000Sglebiusstatic	u_short		restrict_source_mflags;
9954359Sroberto
10054359Sroberto/*
101290000Sglebius * private functions
102290000Sglebius */
103290000Sglebiusstatic restrict_u *	alloc_res4(void);
104290000Sglebiusstatic restrict_u *	alloc_res6(void);
105290000Sglebiusstatic void		free_res(restrict_u *, int);
106290000Sglebiusstatic void		inc_res_limited(void);
107290000Sglebiusstatic void		dec_res_limited(void);
108290000Sglebiusstatic restrict_u *	match_restrict4_addr(u_int32, u_short);
109290000Sglebiusstatic restrict_u *	match_restrict6_addr(const struct in6_addr *,
110290000Sglebius					     u_short);
111290000Sglebiusstatic restrict_u *	match_restrict_entry(const restrict_u *, int);
112290000Sglebiusstatic int		res_sorts_before4(restrict_u *, restrict_u *);
113290000Sglebiusstatic int		res_sorts_before6(restrict_u *, restrict_u *);
114290000Sglebius
115290000Sglebius
116290000Sglebius/*
11754359Sroberto * init_restrict - initialize the restriction data structures
11854359Sroberto */
11954359Srobertovoid
12054359Srobertoinit_restrict(void)
12154359Sroberto{
12254359Sroberto	/*
123290000Sglebius	 * The restriction lists begin with a default entry with address
124290000Sglebius	 * and mask 0, which will match any entry.  The lists are kept
125290000Sglebius	 * sorted by descending address followed by descending mask:
126290000Sglebius	 *
127290000Sglebius	 *   address	  mask
128290000Sglebius	 * 192.168.0.0	255.255.255.0	kod limited noquery nopeer
129290000Sglebius	 * 192.168.0.0	255.255.0.0	kod limited
130290000Sglebius	 * 0.0.0.0	0.0.0.0		kod limited noquery
131290000Sglebius	 *
132290000Sglebius	 * The first entry which matches an address is used.  With the
133290000Sglebius	 * example restrictions above, 192.168.0.0/24 matches the first
134290000Sglebius	 * entry, the rest of 192.168.0.0/16 matches the second, and
135290000Sglebius	 * everything else matches the third (default).
136290000Sglebius	 *
137290000Sglebius	 * Note this achieves the same result a little more efficiently
138290000Sglebius	 * than the documented behavior, which is to keep the lists
139290000Sglebius	 * sorted by ascending address followed by ascending mask, with
140290000Sglebius	 * the _last_ matching entry used.
141290000Sglebius	 *
142290000Sglebius	 * An additional wrinkle is we may have multiple entries with
143290000Sglebius	 * the same address and mask but differing match flags (mflags).
144290000Sglebius	 * At present there is only one, RESM_NTPONLY.  Entries with
145290000Sglebius	 * RESM_NTPONLY are sorted earlier so they take precedence over
146290000Sglebius	 * any otherwise similar entry without.  Again, this is the same
147290000Sglebius	 * behavior as but reversed implementation compared to the docs.
148290000Sglebius	 *
14954359Sroberto	 */
150290000Sglebius	LINK_SLIST(restrictlist4, &restrict_def4, link);
151290000Sglebius	LINK_SLIST(restrictlist6, &restrict_def6, link);
152290000Sglebius	restrictcount = 2;
153290000Sglebius}
154290000Sglebius
155290000Sglebius
156290000Sglebiusstatic restrict_u *
157290000Sglebiusalloc_res4(void)
158290000Sglebius{
159290000Sglebius	const size_t	cb = V4_SIZEOF_RESTRICT_U;
160290000Sglebius	const size_t	count = INC_RESLIST4;
161290000Sglebius	restrict_u *	rl;
162290000Sglebius	restrict_u *	res;
163293894Sglebius	size_t		i;
164290000Sglebius
165290000Sglebius	UNLINK_HEAD_SLIST(res, resfree4, link);
166290000Sglebius	if (res != NULL)
167290000Sglebius		return res;
168290000Sglebius
169290000Sglebius	rl = emalloc_zero(count * cb);
170290000Sglebius	/* link all but the first onto free list */
171290000Sglebius	res = (void *)((char *)rl + (count - 1) * cb);
172290000Sglebius	for (i = count - 1; i > 0; i--) {
173290000Sglebius		LINK_SLIST(resfree4, res, link);
174290000Sglebius		res = (void *)((char *)res - cb);
17554359Sroberto	}
176290000Sglebius	INSIST(rl == res);
177290000Sglebius	/* allocate the first */
178290000Sglebius	return res;
179290000Sglebius}
18054359Sroberto
18154359Sroberto
182290000Sglebiusstatic restrict_u *
183290000Sglebiusalloc_res6(void)
184290000Sglebius{
185290000Sglebius	const size_t	cb = V6_SIZEOF_RESTRICT_U;
186290000Sglebius	const size_t	count = INC_RESLIST6;
187290000Sglebius	restrict_u *	rl;
188290000Sglebius	restrict_u *	res;
189293894Sglebius	size_t		i;
19054359Sroberto
191290000Sglebius	UNLINK_HEAD_SLIST(res, resfree6, link);
192290000Sglebius	if (res != NULL)
193290000Sglebius		return res;
194290000Sglebius
195290000Sglebius	rl = emalloc_zero(count * cb);
196290000Sglebius	/* link all but the first onto free list */
197290000Sglebius	res = (void *)((char *)rl + (count - 1) * cb);
198290000Sglebius	for (i = count - 1; i > 0; i--) {
199290000Sglebius		LINK_SLIST(resfree6, res, link);
200290000Sglebius		res = (void *)((char *)res - cb);
201290000Sglebius	}
202290000Sglebius	INSIST(rl == res);
203290000Sglebius	/* allocate the first */
204290000Sglebius	return res;
20554359Sroberto}
20654359Sroberto
20754359Sroberto
208290000Sglebiusstatic void
209290000Sglebiusfree_res(
210290000Sglebius	restrict_u *	res,
211290000Sglebius	int		v6
212290000Sglebius	)
213290000Sglebius{
214290000Sglebius	restrict_u **	plisthead;
215290000Sglebius	restrict_u *	unlinked;
216290000Sglebius
217290000Sglebius	restrictcount--;
218290000Sglebius	if (RES_LIMITED & res->flags)
219290000Sglebius		dec_res_limited();
220290000Sglebius
221290000Sglebius	if (v6)
222290000Sglebius		plisthead = &restrictlist6;
223290000Sglebius	else
224290000Sglebius		plisthead = &restrictlist4;
225290000Sglebius	UNLINK_SLIST(unlinked, *plisthead, res, link, restrict_u);
226290000Sglebius	INSIST(unlinked == res);
227290000Sglebius
228290000Sglebius	if (v6) {
229290000Sglebius		zero_mem(res, V6_SIZEOF_RESTRICT_U);
230290000Sglebius		plisthead = &resfree6;
231290000Sglebius	} else {
232290000Sglebius		zero_mem(res, V4_SIZEOF_RESTRICT_U);
233290000Sglebius		plisthead = &resfree4;
234290000Sglebius	}
235290000Sglebius	LINK_SLIST(*plisthead, res, link);
236290000Sglebius}
237290000Sglebius
238290000Sglebius
239290000Sglebiusstatic void
240290000Sglebiusinc_res_limited(void)
241290000Sglebius{
242290000Sglebius	if (!res_limited_refcnt)
243290000Sglebius		mon_start(MON_RES);
244290000Sglebius	res_limited_refcnt++;
245290000Sglebius}
246290000Sglebius
247290000Sglebius
248290000Sglebiusstatic void
249290000Sglebiusdec_res_limited(void)
250290000Sglebius{
251290000Sglebius	res_limited_refcnt--;
252290000Sglebius	if (!res_limited_refcnt)
253290000Sglebius		mon_stop(MON_RES);
254290000Sglebius}
255290000Sglebius
256290000Sglebius
257290000Sglebiusstatic restrict_u *
258290000Sglebiusmatch_restrict4_addr(
259290000Sglebius	u_int32	addr,
260290000Sglebius	u_short	port
261290000Sglebius	)
262290000Sglebius{
263290000Sglebius	const int	v6 = 0;
264290000Sglebius	restrict_u *	res;
265290000Sglebius	restrict_u *	next;
266290000Sglebius
267290000Sglebius	for (res = restrictlist4; res != NULL; res = next) {
268290000Sglebius		next = res->link;
269290000Sglebius		if (res->expire &&
270290000Sglebius		    res->expire <= current_time)
271290000Sglebius			free_res(res, v6);
272290000Sglebius		if (res->u.v4.addr == (addr & res->u.v4.mask)
273290000Sglebius		    && (!(RESM_NTPONLY & res->mflags)
274290000Sglebius			|| NTP_PORT == port))
275290000Sglebius			break;
276290000Sglebius	}
277290000Sglebius	return res;
278290000Sglebius}
279290000Sglebius
280290000Sglebius
281290000Sglebiusstatic restrict_u *
282290000Sglebiusmatch_restrict6_addr(
283290000Sglebius	const struct in6_addr *	addr,
284290000Sglebius	u_short			port
285290000Sglebius	)
286290000Sglebius{
287290000Sglebius	const int	v6 = 1;
288290000Sglebius	restrict_u *	res;
289290000Sglebius	restrict_u *	next;
290290000Sglebius	struct in6_addr	masked;
291290000Sglebius
292290000Sglebius	for (res = restrictlist6; res != NULL; res = next) {
293290000Sglebius		next = res->link;
294290000Sglebius		INSIST(next != res);
295290000Sglebius		if (res->expire &&
296290000Sglebius		    res->expire <= current_time)
297290000Sglebius			free_res(res, v6);
298290000Sglebius		MASK_IPV6_ADDR(&masked, addr, &res->u.v6.mask);
299290000Sglebius		if (ADDR6_EQ(&masked, &res->u.v6.addr)
300290000Sglebius		    && (!(RESM_NTPONLY & res->mflags)
301290000Sglebius			|| NTP_PORT == (int)port))
302290000Sglebius			break;
303290000Sglebius	}
304290000Sglebius	return res;
305290000Sglebius}
306290000Sglebius
307290000Sglebius
30854359Sroberto/*
309290000Sglebius * match_restrict_entry - find an exact match on a restrict list.
310290000Sglebius *
311290000Sglebius * Exact match is addr, mask, and mflags all equal.
312290000Sglebius * In order to use more common code for IPv4 and IPv6, this routine
313290000Sglebius * requires the caller to populate a restrict_u with mflags and either
314290000Sglebius * the v4 or v6 address and mask as appropriate.  Other fields in the
315290000Sglebius * input restrict_u are ignored.
316290000Sglebius */
317290000Sglebiusstatic restrict_u *
318290000Sglebiusmatch_restrict_entry(
319290000Sglebius	const restrict_u *	pmatch,
320290000Sglebius	int			v6
321290000Sglebius	)
322290000Sglebius{
323290000Sglebius	restrict_u *res;
324290000Sglebius	restrict_u *rlist;
325290000Sglebius	size_t cb;
326290000Sglebius
327290000Sglebius	if (v6) {
328290000Sglebius		rlist = restrictlist6;
329290000Sglebius		cb = sizeof(pmatch->u.v6);
330290000Sglebius	} else {
331290000Sglebius		rlist = restrictlist4;
332290000Sglebius		cb = sizeof(pmatch->u.v4);
333290000Sglebius	}
334290000Sglebius
335290000Sglebius	for (res = rlist; res != NULL; res = res->link)
336290000Sglebius		if (res->mflags == pmatch->mflags &&
337290000Sglebius		    !memcmp(&res->u, &pmatch->u, cb))
338290000Sglebius			break;
339290000Sglebius	return res;
340290000Sglebius}
341290000Sglebius
342290000Sglebius
343290000Sglebius/*
344290000Sglebius * res_sorts_before4 - compare two restrict4 entries
345290000Sglebius *
346290000Sglebius * Returns nonzero if r1 sorts before r2.  We sort by descending
347290000Sglebius * address, then descending mask, then descending mflags, so sorting
348290000Sglebius * before means having a higher value.
349290000Sglebius */
350290000Sglebiusstatic int
351290000Sglebiusres_sorts_before4(
352290000Sglebius	restrict_u *r1,
353290000Sglebius	restrict_u *r2
354290000Sglebius	)
355290000Sglebius{
356290000Sglebius	int r1_before_r2;
357290000Sglebius
358290000Sglebius	if (r1->u.v4.addr > r2->u.v4.addr)
359290000Sglebius		r1_before_r2 = 1;
360290000Sglebius	else if (r1->u.v4.addr < r2->u.v4.addr)
361290000Sglebius		r1_before_r2 = 0;
362290000Sglebius	else if (r1->u.v4.mask > r2->u.v4.mask)
363290000Sglebius		r1_before_r2 = 1;
364290000Sglebius	else if (r1->u.v4.mask < r2->u.v4.mask)
365290000Sglebius		r1_before_r2 = 0;
366290000Sglebius	else if (r1->mflags > r2->mflags)
367290000Sglebius		r1_before_r2 = 1;
368290000Sglebius	else
369290000Sglebius		r1_before_r2 = 0;
370290000Sglebius
371290000Sglebius	return r1_before_r2;
372290000Sglebius}
373290000Sglebius
374290000Sglebius
375290000Sglebius/*
376290000Sglebius * res_sorts_before6 - compare two restrict6 entries
377290000Sglebius *
378290000Sglebius * Returns nonzero if r1 sorts before r2.  We sort by descending
379290000Sglebius * address, then descending mask, then descending mflags, so sorting
380290000Sglebius * before means having a higher value.
381290000Sglebius */
382290000Sglebiusstatic int
383290000Sglebiusres_sorts_before6(
384290000Sglebius	restrict_u *r1,
385290000Sglebius	restrict_u *r2
386290000Sglebius	)
387290000Sglebius{
388290000Sglebius	int r1_before_r2;
389290000Sglebius	int cmp;
390290000Sglebius
391290000Sglebius	cmp = ADDR6_CMP(&r1->u.v6.addr, &r2->u.v6.addr);
392290000Sglebius	if (cmp > 0)		/* r1->addr > r2->addr */
393290000Sglebius		r1_before_r2 = 1;
394290000Sglebius	else if (cmp < 0)	/* r2->addr > r1->addr */
395290000Sglebius		r1_before_r2 = 0;
396290000Sglebius	else {
397290000Sglebius		cmp = ADDR6_CMP(&r1->u.v6.mask, &r2->u.v6.mask);
398290000Sglebius		if (cmp > 0)		/* r1->mask > r2->mask*/
399290000Sglebius			r1_before_r2 = 1;
400290000Sglebius		else if (cmp < 0)	/* r2->mask > r1->mask */
401290000Sglebius			r1_before_r2 = 0;
402290000Sglebius		else if (r1->mflags > r2->mflags)
403290000Sglebius			r1_before_r2 = 1;
404290000Sglebius		else
405290000Sglebius			r1_before_r2 = 0;
406290000Sglebius	}
407290000Sglebius
408290000Sglebius	return r1_before_r2;
409290000Sglebius}
410290000Sglebius
411290000Sglebius
412290000Sglebius/*
41354359Sroberto * restrictions - return restrictions for this host
41454359Sroberto */
415290000Sglebiusu_short
41654359Srobertorestrictions(
417290000Sglebius	sockaddr_u *srcadr
41854359Sroberto	)
41954359Sroberto{
420290000Sglebius	restrict_u *match;
421290000Sglebius	struct in6_addr *pin6;
422290000Sglebius	u_short flags;
42354359Sroberto
42454359Sroberto	res_calls++;
425290000Sglebius	flags = 0;
426290000Sglebius	/* IPv4 source address */
427290000Sglebius	if (IS_IPV4(srcadr)) {
428132451Sroberto		/*
429132451Sroberto		 * Ignore any packets with a multicast source address
430132451Sroberto		 * (this should be done early in the receive process,
431290000Sglebius		 * not later!)
432132451Sroberto		 */
433132451Sroberto		if (IN_CLASSD(SRCADR(srcadr)))
434132451Sroberto			return (int)RES_IGNORE;
43554359Sroberto
436290000Sglebius		match = match_restrict4_addr(SRCADR(srcadr),
437290000Sglebius					     SRCPORT(srcadr));
438290000Sglebius
439290000Sglebius		INSIST(match != NULL);
440290000Sglebius
441290000Sglebius		match->count++;
442132451Sroberto		/*
443290000Sglebius		 * res_not_found counts only use of the final default
444290000Sglebius		 * entry, not any "restrict default ntpport ...", which
445290000Sglebius		 * would be just before the final default.
446132451Sroberto		 */
447290000Sglebius		if (&restrict_def4 == match)
448132451Sroberto			res_not_found++;
449132451Sroberto		else
450132451Sroberto			res_found++;
451132451Sroberto		flags = match->flags;
452132451Sroberto	}
45354359Sroberto
454132451Sroberto	/* IPv6 source address */
455290000Sglebius	if (IS_IPV6(srcadr)) {
456290000Sglebius		pin6 = PSOCK_ADDR6(srcadr);
45754359Sroberto
458132451Sroberto		/*
459132451Sroberto		 * Ignore any packets with a multicast source address
460132451Sroberto		 * (this should be done early in the receive process,
461290000Sglebius		 * not later!)
462132451Sroberto		 */
463290000Sglebius		if (IN6_IS_ADDR_MULTICAST(pin6))
464132451Sroberto			return (int)RES_IGNORE;
46554359Sroberto
466290000Sglebius		match = match_restrict6_addr(pin6, SRCPORT(srcadr));
467290000Sglebius		INSIST(match != NULL);
468290000Sglebius		match->count++;
469290000Sglebius		if (&restrict_def6 == match)
470132451Sroberto			res_not_found++;
471132451Sroberto		else
472132451Sroberto			res_found++;
473290000Sglebius		flags = match->flags;
47454359Sroberto	}
475132451Sroberto	return (flags);
47654359Sroberto}
47754359Sroberto
47854359Sroberto
47954359Sroberto/*
48054359Sroberto * hack_restrict - add/subtract/manipulate entries on the restrict list
48154359Sroberto */
48254359Srobertovoid
48354359Srobertohack_restrict(
484290000Sglebius	int		op,
485290000Sglebius	sockaddr_u *	resaddr,
486290000Sglebius	sockaddr_u *	resmask,
487290000Sglebius	u_short		mflags,
488290000Sglebius	u_short		flags,
489290000Sglebius	u_long		expire
49054359Sroberto	)
49154359Sroberto{
492290000Sglebius	int		v6;
493290000Sglebius	restrict_u	match;
494290000Sglebius	restrict_u *	res;
495290000Sglebius	restrict_u **	plisthead;
49654359Sroberto
497290000Sglebius	DPRINTF(1, ("restrict: op %d addr %s mask %s mflags %08x flags %08x\n",
498290000Sglebius		    op, stoa(resaddr), stoa(resmask), mflags, flags));
499290000Sglebius
500290000Sglebius	if (NULL == resaddr) {
501290000Sglebius		REQUIRE(NULL == resmask);
502290000Sglebius		REQUIRE(RESTRICT_FLAGS == op);
503290000Sglebius		restrict_source_flags = flags;
504290000Sglebius		restrict_source_mflags = mflags;
505290000Sglebius		restrict_source_enabled = 1;
506290000Sglebius		return;
507290000Sglebius	}
508290000Sglebius
509290000Sglebius	ZERO(match);
510290000Sglebius
511290000Sglebius#if 0
512290000Sglebius	/* silence VC9 potentially uninit warnings */
513290000Sglebius	// HMS: let's use a compiler-specific "enable" for this.
514290000Sglebius	res = NULL;
515290000Sglebius	v6 = 0;
516290000Sglebius#endif
517290000Sglebius
518290000Sglebius	if (IS_IPV4(resaddr)) {
519290000Sglebius		v6 = 0;
520132451Sroberto		/*
521290000Sglebius		 * Get address and mask in host byte order for easy
522290000Sglebius		 * comparison as u_int32
523132451Sroberto		 */
524290000Sglebius		match.u.v4.addr = SRCADR(resaddr);
525290000Sglebius		match.u.v4.mask = SRCADR(resmask);
526290000Sglebius		match.u.v4.addr &= match.u.v4.mask;
52754359Sroberto
528290000Sglebius	} else if (IS_IPV6(resaddr)) {
529290000Sglebius		v6 = 1;
530132451Sroberto		/*
531290000Sglebius		 * Get address and mask in network byte order for easy
532290000Sglebius		 * comparison as byte sequences (e.g. memcmp())
533132451Sroberto		 */
534290000Sglebius		match.u.v6.mask = SOCK_ADDR6(resmask);
535290000Sglebius		MASK_IPV6_ADDR(&match.u.v6.addr, PSOCK_ADDR6(resaddr),
536290000Sglebius			       &match.u.v6.mask);
537132451Sroberto
538290000Sglebius	} else	/* not IPv4 nor IPv6 */
539290000Sglebius		REQUIRE(0);
540132451Sroberto
541290000Sglebius	match.flags = flags;
542290000Sglebius	match.mflags = mflags;
543290000Sglebius	match.expire = expire;
544290000Sglebius	res = match_restrict_entry(&match, v6);
545132451Sroberto
546290000Sglebius	switch (op) {
547290000Sglebius
548290000Sglebius	case RESTRICT_FLAGS:
549290000Sglebius		/*
550290000Sglebius		 * Here we add bits to the flags. If this is a
551290000Sglebius		 * new restriction add it.
552290000Sglebius		 */
553290000Sglebius		if (NULL == res) {
554290000Sglebius			if (v6) {
555290000Sglebius				res = alloc_res6();
556290000Sglebius				memcpy(res, &match,
557290000Sglebius				       V6_SIZEOF_RESTRICT_U);
558290000Sglebius				plisthead = &restrictlist6;
559290000Sglebius			} else {
560290000Sglebius				res = alloc_res4();
561290000Sglebius				memcpy(res, &match,
562290000Sglebius				       V4_SIZEOF_RESTRICT_U);
563290000Sglebius				plisthead = &restrictlist4;
56454359Sroberto			}
565290000Sglebius			LINK_SORT_SLIST(
566290000Sglebius				*plisthead, res,
567290000Sglebius				(v6)
568290000Sglebius				  ? res_sorts_before6(res, L_S_S_CUR())
569290000Sglebius				  : res_sorts_before4(res, L_S_S_CUR()),
570290000Sglebius				link, restrict_u);
571290000Sglebius			restrictcount++;
572290000Sglebius			if (RES_LIMITED & flags)
573290000Sglebius				inc_res_limited();
574290000Sglebius		} else {
575290000Sglebius			if ((RES_LIMITED & flags) &&
576290000Sglebius			    !(RES_LIMITED & res->flags))
577290000Sglebius				inc_res_limited();
578290000Sglebius			res->flags |= flags;
57954359Sroberto		}
580290000Sglebius		break;
581132451Sroberto
582290000Sglebius	case RESTRICT_UNFLAG:
583290000Sglebius		/*
584290000Sglebius		 * Remove some bits from the flags. If we didn't
585290000Sglebius		 * find this one, just return.
586290000Sglebius		 */
587290000Sglebius		if (res != NULL) {
588290000Sglebius			if ((RES_LIMITED & res->flags)
589290000Sglebius			    && (RES_LIMITED & flags))
590290000Sglebius				dec_res_limited();
591290000Sglebius			res->flags &= ~flags;
592290000Sglebius		}
593290000Sglebius		break;
59454359Sroberto
595290000Sglebius	case RESTRICT_REMOVE:
596290000Sglebius	case RESTRICT_REMOVEIF:
597290000Sglebius		/*
598290000Sglebius		 * Remove an entry from the table entirely if we
599290000Sglebius		 * found one. Don't remove the default entry and
600290000Sglebius		 * don't remove an interface entry.
601290000Sglebius		 */
602290000Sglebius		if (res != NULL
603290000Sglebius		    && (RESTRICT_REMOVEIF == op
604290000Sglebius			|| !(RESM_INTERFACE & res->mflags))
605290000Sglebius		    && res != &restrict_def4
606290000Sglebius		    && res != &restrict_def6)
607290000Sglebius			free_res(res, v6);
608290000Sglebius		break;
60954359Sroberto
610290000Sglebius	default:	/* unknown op */
611290000Sglebius		INSIST(0);
612290000Sglebius		break;
613290000Sglebius	}
61454359Sroberto
615290000Sglebius}
61654359Sroberto
617132451Sroberto
618290000Sglebius/*
619290000Sglebius * restrict_source - maintains dynamic "restrict source ..." entries as
620290000Sglebius *		     peers come and go.
621290000Sglebius */
622290000Sglebiusvoid
623290000Sglebiusrestrict_source(
624290000Sglebius	sockaddr_u *	addr,
625290000Sglebius	int		farewell,	/* 0 to add, 1 to remove */
626290000Sglebius	u_long		expire		/* 0 is infinite, valid until */
627290000Sglebius	)
628290000Sglebius{
629290000Sglebius	sockaddr_u	onesmask;
630290000Sglebius	restrict_u *	res;
631290000Sglebius	int		found_specific;
632132451Sroberto
633290000Sglebius	if (!restrict_source_enabled || SOCK_UNSPEC(addr) ||
634290000Sglebius	    IS_MCAST(addr) || ISREFCLOCKADR(addr))
635290000Sglebius		return;
636132451Sroberto
637290000Sglebius	REQUIRE(AF_INET == AF(addr) || AF_INET6 == AF(addr));
638132451Sroberto
639290000Sglebius	SET_HOSTMASK(&onesmask, AF(addr));
640290000Sglebius	if (farewell) {
641290000Sglebius		hack_restrict(RESTRICT_REMOVE, addr, &onesmask,
642290000Sglebius			      0, 0, 0);
643290000Sglebius		DPRINTF(1, ("restrict_source: %s removed", stoa(addr)));
644290000Sglebius		return;
645290000Sglebius	}
64654359Sroberto
647290000Sglebius	/*
648290000Sglebius	 * If there is a specific entry for this address, hands
649290000Sglebius	 * off, as it is condidered more specific than "restrict
650290000Sglebius	 * server ...".
651290000Sglebius	 * However, if the specific entry found is a fleeting one
652290000Sglebius	 * added by pool_xmit() before soliciting, replace it
653290000Sglebius	 * immediately regardless of the expire value to make way
654290000Sglebius	 * for the more persistent entry.
655290000Sglebius	 */
656290000Sglebius	if (IS_IPV4(addr)) {
657290000Sglebius		res = match_restrict4_addr(SRCADR(addr), SRCPORT(addr));
658290000Sglebius		INSIST(res != NULL);
659290000Sglebius		found_specific = (SRCADR(&onesmask) == res->u.v4.mask);
660290000Sglebius	} else {
661290000Sglebius		res = match_restrict6_addr(&SOCK_ADDR6(addr),
662290000Sglebius					   SRCPORT(addr));
663290000Sglebius		INSIST(res != NULL);
664290000Sglebius		found_specific = ADDR6_EQ(&res->u.v6.mask,
665290000Sglebius					  &SOCK_ADDR6(&onesmask));
666290000Sglebius	}
667290000Sglebius	if (!expire && found_specific && res->expire) {
668290000Sglebius		found_specific = 0;
669290000Sglebius		free_res(res, IS_IPV6(addr));
670290000Sglebius	}
671290000Sglebius	if (found_specific)
672290000Sglebius		return;
673132451Sroberto
674290000Sglebius	hack_restrict(RESTRICT_FLAGS, addr, &onesmask,
675290000Sglebius		      restrict_source_mflags, restrict_source_flags,
676290000Sglebius		      expire);
677290000Sglebius	DPRINTF(1, ("restrict_source: %s host restriction added\n",
678290000Sglebius		    stoa(addr)));
67954359Sroberto}
680