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"
13290001Sglebius#include "ntp_lists.h"
1454359Sroberto#include "ntp_stdlib.h"
15290001Sglebius#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 */
46290001Sglebius#define MASK_IPV6_ADDR(dst, src, msk)					\
47290001Sglebius	do {								\
48290001Sglebius		int idx;						\
49290001Sglebius		for (idx = 0; idx < (int)COUNTOF((dst)->s6_addr); idx++) { \
50290001Sglebius			(dst)->s6_addr[idx] = (src)->s6_addr[idx]	\
51290001Sglebius					      & (msk)->s6_addr[idx];	\
52290001Sglebius		}							\
53132451Sroberto	} while (0)
5454359Sroberto
5554359Sroberto/*
56290001Sglebius * We allocate INC_RESLIST{4|6} entries to the free list whenever empty.
57290001Sglebius * Auto-tune these to be just less than 1KB (leaving at least 16 bytes
58290001Sglebius * for allocator overhead).
5954359Sroberto */
60290001Sglebius#define	INC_RESLIST4	((1024 - 16) / V4_SIZEOF_RESTRICT_U)
61290001Sglebius#define	INC_RESLIST6	((1024 - 16) / V6_SIZEOF_RESTRICT_U)
6254359Sroberto
6354359Sroberto/*
6454359Sroberto * The restriction list
6554359Sroberto */
66290001Sglebiusrestrict_u *restrictlist4;
67290001Sglebiusrestrict_u *restrictlist6;
68290001Sglebiusstatic int restrictcount;	/* count in the restrict lists */
6954359Sroberto
7054359Sroberto/*
7154359Sroberto * The free list and associated counters.  Also some uninteresting
7254359Sroberto * stat counters.
7354359Sroberto */
74290001Sglebiusstatic restrict_u *resfree4;	/* available entries (free list) */
75290001Sglebiusstatic restrict_u *resfree6;
7654359Sroberto
77290001Sglebiusstatic u_long res_calls;
78290001Sglebiusstatic u_long res_found;
79290001Sglebiusstatic u_long res_not_found;
8054359Sroberto
8154359Sroberto/*
82290001Sglebius * Count number of restriction entries referring to RES_LIMITED, to
83290001Sglebius * control implicit activation/deactivation of the MRU monlist.
8454359Sroberto */
85290001Sglebiusstatic	u_long res_limited_refcnt;
86132451Sroberto
8754359Sroberto/*
88290001Sglebius * Our default entries.
8954359Sroberto */
90290001Sglebiusstatic	restrict_u	restrict_def4;
91290001Sglebiusstatic	restrict_u	restrict_def6;
9254359Sroberto
9354359Sroberto/*
94290001Sglebius * "restrict source ..." enabled knob and restriction bits.
9554359Sroberto */
96290001Sglebiusstatic	int		restrict_source_enabled;
97290001Sglebiusstatic	u_short		restrict_source_flags;
98290001Sglebiusstatic	u_short		restrict_source_mflags;
9954359Sroberto
10054359Sroberto/*
101290001Sglebius * private functions
102290001Sglebius */
103290001Sglebiusstatic restrict_u *	alloc_res4(void);
104290001Sglebiusstatic restrict_u *	alloc_res6(void);
105290001Sglebiusstatic void		free_res(restrict_u *, int);
106290001Sglebiusstatic void		inc_res_limited(void);
107290001Sglebiusstatic void		dec_res_limited(void);
108290001Sglebiusstatic restrict_u *	match_restrict4_addr(u_int32, u_short);
109290001Sglebiusstatic restrict_u *	match_restrict6_addr(const struct in6_addr *,
110290001Sglebius					     u_short);
111290001Sglebiusstatic restrict_u *	match_restrict_entry(const restrict_u *, int);
112290001Sglebiusstatic int		res_sorts_before4(restrict_u *, restrict_u *);
113290001Sglebiusstatic int		res_sorts_before6(restrict_u *, restrict_u *);
114290001Sglebius
115290001Sglebius
116290001Sglebius/*
11754359Sroberto * init_restrict - initialize the restriction data structures
11854359Sroberto */
11954359Srobertovoid
12054359Srobertoinit_restrict(void)
12154359Sroberto{
12254359Sroberto	/*
123290001Sglebius	 * The restriction lists begin with a default entry with address
124290001Sglebius	 * and mask 0, which will match any entry.  The lists are kept
125290001Sglebius	 * sorted by descending address followed by descending mask:
126290001Sglebius	 *
127290001Sglebius	 *   address	  mask
128290001Sglebius	 * 192.168.0.0	255.255.255.0	kod limited noquery nopeer
129290001Sglebius	 * 192.168.0.0	255.255.0.0	kod limited
130290001Sglebius	 * 0.0.0.0	0.0.0.0		kod limited noquery
131290001Sglebius	 *
132290001Sglebius	 * The first entry which matches an address is used.  With the
133290001Sglebius	 * example restrictions above, 192.168.0.0/24 matches the first
134290001Sglebius	 * entry, the rest of 192.168.0.0/16 matches the second, and
135290001Sglebius	 * everything else matches the third (default).
136290001Sglebius	 *
137290001Sglebius	 * Note this achieves the same result a little more efficiently
138290001Sglebius	 * than the documented behavior, which is to keep the lists
139290001Sglebius	 * sorted by ascending address followed by ascending mask, with
140290001Sglebius	 * the _last_ matching entry used.
141290001Sglebius	 *
142290001Sglebius	 * An additional wrinkle is we may have multiple entries with
143290001Sglebius	 * the same address and mask but differing match flags (mflags).
144290001Sglebius	 * At present there is only one, RESM_NTPONLY.  Entries with
145290001Sglebius	 * RESM_NTPONLY are sorted earlier so they take precedence over
146290001Sglebius	 * any otherwise similar entry without.  Again, this is the same
147290001Sglebius	 * behavior as but reversed implementation compared to the docs.
148290001Sglebius	 *
14954359Sroberto	 */
150290001Sglebius	LINK_SLIST(restrictlist4, &restrict_def4, link);
151290001Sglebius	LINK_SLIST(restrictlist6, &restrict_def6, link);
152290001Sglebius	restrictcount = 2;
153290001Sglebius}
154290001Sglebius
155290001Sglebius
156290001Sglebiusstatic restrict_u *
157290001Sglebiusalloc_res4(void)
158290001Sglebius{
159290001Sglebius	const size_t	cb = V4_SIZEOF_RESTRICT_U;
160290001Sglebius	const size_t	count = INC_RESLIST4;
161290001Sglebius	restrict_u *	rl;
162290001Sglebius	restrict_u *	res;
163293896Sglebius	size_t		i;
164290001Sglebius
165290001Sglebius	UNLINK_HEAD_SLIST(res, resfree4, link);
166290001Sglebius	if (res != NULL)
167290001Sglebius		return res;
168290001Sglebius
169290001Sglebius	rl = emalloc_zero(count * cb);
170290001Sglebius	/* link all but the first onto free list */
171290001Sglebius	res = (void *)((char *)rl + (count - 1) * cb);
172290001Sglebius	for (i = count - 1; i > 0; i--) {
173290001Sglebius		LINK_SLIST(resfree4, res, link);
174290001Sglebius		res = (void *)((char *)res - cb);
17554359Sroberto	}
176290001Sglebius	INSIST(rl == res);
177290001Sglebius	/* allocate the first */
178290001Sglebius	return res;
179290001Sglebius}
18054359Sroberto
18154359Sroberto
182290001Sglebiusstatic restrict_u *
183290001Sglebiusalloc_res6(void)
184290001Sglebius{
185290001Sglebius	const size_t	cb = V6_SIZEOF_RESTRICT_U;
186290001Sglebius	const size_t	count = INC_RESLIST6;
187290001Sglebius	restrict_u *	rl;
188290001Sglebius	restrict_u *	res;
189293896Sglebius	size_t		i;
19054359Sroberto
191290001Sglebius	UNLINK_HEAD_SLIST(res, resfree6, link);
192290001Sglebius	if (res != NULL)
193290001Sglebius		return res;
194290001Sglebius
195290001Sglebius	rl = emalloc_zero(count * cb);
196290001Sglebius	/* link all but the first onto free list */
197290001Sglebius	res = (void *)((char *)rl + (count - 1) * cb);
198290001Sglebius	for (i = count - 1; i > 0; i--) {
199290001Sglebius		LINK_SLIST(resfree6, res, link);
200290001Sglebius		res = (void *)((char *)res - cb);
201290001Sglebius	}
202290001Sglebius	INSIST(rl == res);
203290001Sglebius	/* allocate the first */
204290001Sglebius	return res;
20554359Sroberto}
20654359Sroberto
20754359Sroberto
208290001Sglebiusstatic void
209290001Sglebiusfree_res(
210290001Sglebius	restrict_u *	res,
211290001Sglebius	int		v6
212290001Sglebius	)
213290001Sglebius{
214290001Sglebius	restrict_u **	plisthead;
215290001Sglebius	restrict_u *	unlinked;
216290001Sglebius
217290001Sglebius	restrictcount--;
218290001Sglebius	if (RES_LIMITED & res->flags)
219290001Sglebius		dec_res_limited();
220290001Sglebius
221290001Sglebius	if (v6)
222290001Sglebius		plisthead = &restrictlist6;
223290001Sglebius	else
224290001Sglebius		plisthead = &restrictlist4;
225290001Sglebius	UNLINK_SLIST(unlinked, *plisthead, res, link, restrict_u);
226290001Sglebius	INSIST(unlinked == res);
227290001Sglebius
228290001Sglebius	if (v6) {
229290001Sglebius		zero_mem(res, V6_SIZEOF_RESTRICT_U);
230290001Sglebius		plisthead = &resfree6;
231290001Sglebius	} else {
232290001Sglebius		zero_mem(res, V4_SIZEOF_RESTRICT_U);
233290001Sglebius		plisthead = &resfree4;
234290001Sglebius	}
235290001Sglebius	LINK_SLIST(*plisthead, res, link);
236290001Sglebius}
237290001Sglebius
238290001Sglebius
239290001Sglebiusstatic void
240290001Sglebiusinc_res_limited(void)
241290001Sglebius{
242290001Sglebius	if (!res_limited_refcnt)
243290001Sglebius		mon_start(MON_RES);
244290001Sglebius	res_limited_refcnt++;
245290001Sglebius}
246290001Sglebius
247290001Sglebius
248290001Sglebiusstatic void
249290001Sglebiusdec_res_limited(void)
250290001Sglebius{
251290001Sglebius	res_limited_refcnt--;
252290001Sglebius	if (!res_limited_refcnt)
253290001Sglebius		mon_stop(MON_RES);
254290001Sglebius}
255290001Sglebius
256290001Sglebius
257290001Sglebiusstatic restrict_u *
258290001Sglebiusmatch_restrict4_addr(
259290001Sglebius	u_int32	addr,
260290001Sglebius	u_short	port
261290001Sglebius	)
262290001Sglebius{
263290001Sglebius	const int	v6 = 0;
264290001Sglebius	restrict_u *	res;
265290001Sglebius	restrict_u *	next;
266290001Sglebius
267290001Sglebius	for (res = restrictlist4; res != NULL; res = next) {
268290001Sglebius		next = res->link;
269290001Sglebius		if (res->expire &&
270290001Sglebius		    res->expire <= current_time)
271290001Sglebius			free_res(res, v6);
272290001Sglebius		if (res->u.v4.addr == (addr & res->u.v4.mask)
273290001Sglebius		    && (!(RESM_NTPONLY & res->mflags)
274290001Sglebius			|| NTP_PORT == port))
275290001Sglebius			break;
276290001Sglebius	}
277290001Sglebius	return res;
278290001Sglebius}
279290001Sglebius
280290001Sglebius
281290001Sglebiusstatic restrict_u *
282290001Sglebiusmatch_restrict6_addr(
283290001Sglebius	const struct in6_addr *	addr,
284290001Sglebius	u_short			port
285290001Sglebius	)
286290001Sglebius{
287290001Sglebius	const int	v6 = 1;
288290001Sglebius	restrict_u *	res;
289290001Sglebius	restrict_u *	next;
290290001Sglebius	struct in6_addr	masked;
291290001Sglebius
292290001Sglebius	for (res = restrictlist6; res != NULL; res = next) {
293290001Sglebius		next = res->link;
294290001Sglebius		INSIST(next != res);
295290001Sglebius		if (res->expire &&
296290001Sglebius		    res->expire <= current_time)
297290001Sglebius			free_res(res, v6);
298290001Sglebius		MASK_IPV6_ADDR(&masked, addr, &res->u.v6.mask);
299290001Sglebius		if (ADDR6_EQ(&masked, &res->u.v6.addr)
300290001Sglebius		    && (!(RESM_NTPONLY & res->mflags)
301290001Sglebius			|| NTP_PORT == (int)port))
302290001Sglebius			break;
303290001Sglebius	}
304290001Sglebius	return res;
305290001Sglebius}
306290001Sglebius
307290001Sglebius
30854359Sroberto/*
309290001Sglebius * match_restrict_entry - find an exact match on a restrict list.
310290001Sglebius *
311290001Sglebius * Exact match is addr, mask, and mflags all equal.
312290001Sglebius * In order to use more common code for IPv4 and IPv6, this routine
313290001Sglebius * requires the caller to populate a restrict_u with mflags and either
314290001Sglebius * the v4 or v6 address and mask as appropriate.  Other fields in the
315290001Sglebius * input restrict_u are ignored.
316290001Sglebius */
317290001Sglebiusstatic restrict_u *
318290001Sglebiusmatch_restrict_entry(
319290001Sglebius	const restrict_u *	pmatch,
320290001Sglebius	int			v6
321290001Sglebius	)
322290001Sglebius{
323290001Sglebius	restrict_u *res;
324290001Sglebius	restrict_u *rlist;
325290001Sglebius	size_t cb;
326290001Sglebius
327290001Sglebius	if (v6) {
328290001Sglebius		rlist = restrictlist6;
329290001Sglebius		cb = sizeof(pmatch->u.v6);
330290001Sglebius	} else {
331290001Sglebius		rlist = restrictlist4;
332290001Sglebius		cb = sizeof(pmatch->u.v4);
333290001Sglebius	}
334290001Sglebius
335290001Sglebius	for (res = rlist; res != NULL; res = res->link)
336290001Sglebius		if (res->mflags == pmatch->mflags &&
337290001Sglebius		    !memcmp(&res->u, &pmatch->u, cb))
338290001Sglebius			break;
339290001Sglebius	return res;
340290001Sglebius}
341290001Sglebius
342290001Sglebius
343290001Sglebius/*
344290001Sglebius * res_sorts_before4 - compare two restrict4 entries
345290001Sglebius *
346290001Sglebius * Returns nonzero if r1 sorts before r2.  We sort by descending
347290001Sglebius * address, then descending mask, then descending mflags, so sorting
348290001Sglebius * before means having a higher value.
349290001Sglebius */
350290001Sglebiusstatic int
351290001Sglebiusres_sorts_before4(
352290001Sglebius	restrict_u *r1,
353290001Sglebius	restrict_u *r2
354290001Sglebius	)
355290001Sglebius{
356290001Sglebius	int r1_before_r2;
357290001Sglebius
358290001Sglebius	if (r1->u.v4.addr > r2->u.v4.addr)
359290001Sglebius		r1_before_r2 = 1;
360290001Sglebius	else if (r1->u.v4.addr < r2->u.v4.addr)
361290001Sglebius		r1_before_r2 = 0;
362290001Sglebius	else if (r1->u.v4.mask > r2->u.v4.mask)
363290001Sglebius		r1_before_r2 = 1;
364290001Sglebius	else if (r1->u.v4.mask < r2->u.v4.mask)
365290001Sglebius		r1_before_r2 = 0;
366290001Sglebius	else if (r1->mflags > r2->mflags)
367290001Sglebius		r1_before_r2 = 1;
368290001Sglebius	else
369290001Sglebius		r1_before_r2 = 0;
370290001Sglebius
371290001Sglebius	return r1_before_r2;
372290001Sglebius}
373290001Sglebius
374290001Sglebius
375290001Sglebius/*
376290001Sglebius * res_sorts_before6 - compare two restrict6 entries
377290001Sglebius *
378290001Sglebius * Returns nonzero if r1 sorts before r2.  We sort by descending
379290001Sglebius * address, then descending mask, then descending mflags, so sorting
380290001Sglebius * before means having a higher value.
381290001Sglebius */
382290001Sglebiusstatic int
383290001Sglebiusres_sorts_before6(
384290001Sglebius	restrict_u *r1,
385290001Sglebius	restrict_u *r2
386290001Sglebius	)
387290001Sglebius{
388290001Sglebius	int r1_before_r2;
389290001Sglebius	int cmp;
390290001Sglebius
391290001Sglebius	cmp = ADDR6_CMP(&r1->u.v6.addr, &r2->u.v6.addr);
392290001Sglebius	if (cmp > 0)		/* r1->addr > r2->addr */
393290001Sglebius		r1_before_r2 = 1;
394290001Sglebius	else if (cmp < 0)	/* r2->addr > r1->addr */
395290001Sglebius		r1_before_r2 = 0;
396290001Sglebius	else {
397290001Sglebius		cmp = ADDR6_CMP(&r1->u.v6.mask, &r2->u.v6.mask);
398290001Sglebius		if (cmp > 0)		/* r1->mask > r2->mask*/
399290001Sglebius			r1_before_r2 = 1;
400290001Sglebius		else if (cmp < 0)	/* r2->mask > r1->mask */
401290001Sglebius			r1_before_r2 = 0;
402290001Sglebius		else if (r1->mflags > r2->mflags)
403290001Sglebius			r1_before_r2 = 1;
404290001Sglebius		else
405290001Sglebius			r1_before_r2 = 0;
406290001Sglebius	}
407290001Sglebius
408290001Sglebius	return r1_before_r2;
409290001Sglebius}
410290001Sglebius
411290001Sglebius
412290001Sglebius/*
41354359Sroberto * restrictions - return restrictions for this host
41454359Sroberto */
415290001Sglebiusu_short
41654359Srobertorestrictions(
417290001Sglebius	sockaddr_u *srcadr
41854359Sroberto	)
41954359Sroberto{
420290001Sglebius	restrict_u *match;
421290001Sglebius	struct in6_addr *pin6;
422290001Sglebius	u_short flags;
42354359Sroberto
42454359Sroberto	res_calls++;
425290001Sglebius	flags = 0;
426290001Sglebius	/* IPv4 source address */
427290001Sglebius	if (IS_IPV4(srcadr)) {
428132451Sroberto		/*
429132451Sroberto		 * Ignore any packets with a multicast source address
430132451Sroberto		 * (this should be done early in the receive process,
431290001Sglebius		 * not later!)
432132451Sroberto		 */
433132451Sroberto		if (IN_CLASSD(SRCADR(srcadr)))
434132451Sroberto			return (int)RES_IGNORE;
43554359Sroberto
436290001Sglebius		match = match_restrict4_addr(SRCADR(srcadr),
437290001Sglebius					     SRCPORT(srcadr));
438290001Sglebius
439290001Sglebius		INSIST(match != NULL);
440290001Sglebius
441290001Sglebius		match->count++;
442132451Sroberto		/*
443290001Sglebius		 * res_not_found counts only use of the final default
444290001Sglebius		 * entry, not any "restrict default ntpport ...", which
445290001Sglebius		 * would be just before the final default.
446132451Sroberto		 */
447290001Sglebius		if (&restrict_def4 == match)
448132451Sroberto			res_not_found++;
449132451Sroberto		else
450132451Sroberto			res_found++;
451132451Sroberto		flags = match->flags;
452132451Sroberto	}
45354359Sroberto
454132451Sroberto	/* IPv6 source address */
455290001Sglebius	if (IS_IPV6(srcadr)) {
456290001Sglebius		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,
461290001Sglebius		 * not later!)
462132451Sroberto		 */
463290001Sglebius		if (IN6_IS_ADDR_MULTICAST(pin6))
464132451Sroberto			return (int)RES_IGNORE;
46554359Sroberto
466290001Sglebius		match = match_restrict6_addr(pin6, SRCPORT(srcadr));
467290001Sglebius		INSIST(match != NULL);
468290001Sglebius		match->count++;
469290001Sglebius		if (&restrict_def6 == match)
470132451Sroberto			res_not_found++;
471132451Sroberto		else
472132451Sroberto			res_found++;
473290001Sglebius		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(
484290001Sglebius	int		op,
485290001Sglebius	sockaddr_u *	resaddr,
486290001Sglebius	sockaddr_u *	resmask,
487290001Sglebius	u_short		mflags,
488290001Sglebius	u_short		flags,
489290001Sglebius	u_long		expire
49054359Sroberto	)
49154359Sroberto{
492290001Sglebius	int		v6;
493290001Sglebius	restrict_u	match;
494290001Sglebius	restrict_u *	res;
495290001Sglebius	restrict_u **	plisthead;
49654359Sroberto
497290001Sglebius	DPRINTF(1, ("restrict: op %d addr %s mask %s mflags %08x flags %08x\n",
498290001Sglebius		    op, stoa(resaddr), stoa(resmask), mflags, flags));
499290001Sglebius
500290001Sglebius	if (NULL == resaddr) {
501290001Sglebius		REQUIRE(NULL == resmask);
502290001Sglebius		REQUIRE(RESTRICT_FLAGS == op);
503290001Sglebius		restrict_source_flags = flags;
504290001Sglebius		restrict_source_mflags = mflags;
505290001Sglebius		restrict_source_enabled = 1;
506290001Sglebius		return;
507290001Sglebius	}
508290001Sglebius
509290001Sglebius	ZERO(match);
510290001Sglebius
511290001Sglebius#if 0
512290001Sglebius	/* silence VC9 potentially uninit warnings */
513290001Sglebius	// HMS: let's use a compiler-specific "enable" for this.
514290001Sglebius	res = NULL;
515290001Sglebius	v6 = 0;
516290001Sglebius#endif
517290001Sglebius
518290001Sglebius	if (IS_IPV4(resaddr)) {
519290001Sglebius		v6 = 0;
520132451Sroberto		/*
521290001Sglebius		 * Get address and mask in host byte order for easy
522290001Sglebius		 * comparison as u_int32
523132451Sroberto		 */
524290001Sglebius		match.u.v4.addr = SRCADR(resaddr);
525290001Sglebius		match.u.v4.mask = SRCADR(resmask);
526290001Sglebius		match.u.v4.addr &= match.u.v4.mask;
52754359Sroberto
528290001Sglebius	} else if (IS_IPV6(resaddr)) {
529290001Sglebius		v6 = 1;
530132451Sroberto		/*
531290001Sglebius		 * Get address and mask in network byte order for easy
532290001Sglebius		 * comparison as byte sequences (e.g. memcmp())
533132451Sroberto		 */
534290001Sglebius		match.u.v6.mask = SOCK_ADDR6(resmask);
535290001Sglebius		MASK_IPV6_ADDR(&match.u.v6.addr, PSOCK_ADDR6(resaddr),
536290001Sglebius			       &match.u.v6.mask);
537132451Sroberto
538290001Sglebius	} else	/* not IPv4 nor IPv6 */
539290001Sglebius		REQUIRE(0);
540132451Sroberto
541290001Sglebius	match.flags = flags;
542290001Sglebius	match.mflags = mflags;
543290001Sglebius	match.expire = expire;
544290001Sglebius	res = match_restrict_entry(&match, v6);
545132451Sroberto
546290001Sglebius	switch (op) {
547290001Sglebius
548290001Sglebius	case RESTRICT_FLAGS:
549290001Sglebius		/*
550290001Sglebius		 * Here we add bits to the flags. If this is a
551290001Sglebius		 * new restriction add it.
552290001Sglebius		 */
553290001Sglebius		if (NULL == res) {
554290001Sglebius			if (v6) {
555290001Sglebius				res = alloc_res6();
556290001Sglebius				memcpy(res, &match,
557290001Sglebius				       V6_SIZEOF_RESTRICT_U);
558290001Sglebius				plisthead = &restrictlist6;
559290001Sglebius			} else {
560290001Sglebius				res = alloc_res4();
561290001Sglebius				memcpy(res, &match,
562290001Sglebius				       V4_SIZEOF_RESTRICT_U);
563290001Sglebius				plisthead = &restrictlist4;
56454359Sroberto			}
565290001Sglebius			LINK_SORT_SLIST(
566290001Sglebius				*plisthead, res,
567290001Sglebius				(v6)
568290001Sglebius				  ? res_sorts_before6(res, L_S_S_CUR())
569290001Sglebius				  : res_sorts_before4(res, L_S_S_CUR()),
570290001Sglebius				link, restrict_u);
571290001Sglebius			restrictcount++;
572290001Sglebius			if (RES_LIMITED & flags)
573290001Sglebius				inc_res_limited();
574290001Sglebius		} else {
575290001Sglebius			if ((RES_LIMITED & flags) &&
576290001Sglebius			    !(RES_LIMITED & res->flags))
577290001Sglebius				inc_res_limited();
578290001Sglebius			res->flags |= flags;
57954359Sroberto		}
580290001Sglebius		break;
581132451Sroberto
582290001Sglebius	case RESTRICT_UNFLAG:
583290001Sglebius		/*
584290001Sglebius		 * Remove some bits from the flags. If we didn't
585290001Sglebius		 * find this one, just return.
586290001Sglebius		 */
587290001Sglebius		if (res != NULL) {
588290001Sglebius			if ((RES_LIMITED & res->flags)
589290001Sglebius			    && (RES_LIMITED & flags))
590290001Sglebius				dec_res_limited();
591290001Sglebius			res->flags &= ~flags;
592290001Sglebius		}
593290001Sglebius		break;
59454359Sroberto
595290001Sglebius	case RESTRICT_REMOVE:
596290001Sglebius	case RESTRICT_REMOVEIF:
597290001Sglebius		/*
598290001Sglebius		 * Remove an entry from the table entirely if we
599290001Sglebius		 * found one. Don't remove the default entry and
600290001Sglebius		 * don't remove an interface entry.
601290001Sglebius		 */
602290001Sglebius		if (res != NULL
603290001Sglebius		    && (RESTRICT_REMOVEIF == op
604290001Sglebius			|| !(RESM_INTERFACE & res->mflags))
605290001Sglebius		    && res != &restrict_def4
606290001Sglebius		    && res != &restrict_def6)
607290001Sglebius			free_res(res, v6);
608290001Sglebius		break;
60954359Sroberto
610290001Sglebius	default:	/* unknown op */
611290001Sglebius		INSIST(0);
612290001Sglebius		break;
613290001Sglebius	}
61454359Sroberto
615290001Sglebius}
61654359Sroberto
617132451Sroberto
618290001Sglebius/*
619290001Sglebius * restrict_source - maintains dynamic "restrict source ..." entries as
620290001Sglebius *		     peers come and go.
621290001Sglebius */
622290001Sglebiusvoid
623290001Sglebiusrestrict_source(
624290001Sglebius	sockaddr_u *	addr,
625290001Sglebius	int		farewell,	/* 0 to add, 1 to remove */
626290001Sglebius	u_long		expire		/* 0 is infinite, valid until */
627290001Sglebius	)
628290001Sglebius{
629290001Sglebius	sockaddr_u	onesmask;
630290001Sglebius	restrict_u *	res;
631290001Sglebius	int		found_specific;
632132451Sroberto
633290001Sglebius	if (!restrict_source_enabled || SOCK_UNSPEC(addr) ||
634290001Sglebius	    IS_MCAST(addr) || ISREFCLOCKADR(addr))
635290001Sglebius		return;
636132451Sroberto
637290001Sglebius	REQUIRE(AF_INET == AF(addr) || AF_INET6 == AF(addr));
638132451Sroberto
639290001Sglebius	SET_HOSTMASK(&onesmask, AF(addr));
640290001Sglebius	if (farewell) {
641290001Sglebius		hack_restrict(RESTRICT_REMOVE, addr, &onesmask,
642290001Sglebius			      0, 0, 0);
643290001Sglebius		DPRINTF(1, ("restrict_source: %s removed", stoa(addr)));
644290001Sglebius		return;
645290001Sglebius	}
64654359Sroberto
647290001Sglebius	/*
648290001Sglebius	 * If there is a specific entry for this address, hands
649290001Sglebius	 * off, as it is condidered more specific than "restrict
650290001Sglebius	 * server ...".
651290001Sglebius	 * However, if the specific entry found is a fleeting one
652290001Sglebius	 * added by pool_xmit() before soliciting, replace it
653290001Sglebius	 * immediately regardless of the expire value to make way
654290001Sglebius	 * for the more persistent entry.
655290001Sglebius	 */
656290001Sglebius	if (IS_IPV4(addr)) {
657290001Sglebius		res = match_restrict4_addr(SRCADR(addr), SRCPORT(addr));
658290001Sglebius		INSIST(res != NULL);
659290001Sglebius		found_specific = (SRCADR(&onesmask) == res->u.v4.mask);
660290001Sglebius	} else {
661290001Sglebius		res = match_restrict6_addr(&SOCK_ADDR6(addr),
662290001Sglebius					   SRCPORT(addr));
663290001Sglebius		INSIST(res != NULL);
664290001Sglebius		found_specific = ADDR6_EQ(&res->u.v6.mask,
665290001Sglebius					  &SOCK_ADDR6(&onesmask));
666290001Sglebius	}
667290001Sglebius	if (!expire && found_specific && res->expire) {
668290001Sglebius		found_specific = 0;
669290001Sglebius		free_res(res, IS_IPV6(addr));
670290001Sglebius	}
671290001Sglebius	if (found_specific)
672290001Sglebius		return;
673132451Sroberto
674290001Sglebius	hack_restrict(RESTRICT_FLAGS, addr, &onesmask,
675290001Sglebius		      restrict_source_mflags, restrict_source_flags,
676290001Sglebius		      expire);
677290001Sglebius	DPRINTF(1, ("restrict_source: %s host restriction added\n",
678290001Sglebius		    stoa(addr)));
67954359Sroberto}
680