/*
* This code keeps a simple address-and-mask list of addressses we want
* to place restrictions on (or remove them from). The restrictions are
* implemented as a set of flags which tell you what matching addresses
* can't do. The list is sorted retrieve the restrictions most specific
* to the address.
*
* This was originally intended to restrict you from sync'ing to your
* own broadcasts when you are doing that, by restricting yourself from
* your own interfaces. It was also thought it would sometimes be useful
* to keep a misbehaving host or two from abusing your primary clock. It
* has been expanded, however, to suit the needs of those with more
* restrictive access policies.
*/
#define MASK_IPV6_ADDR(dst, src, msk) \
do { \
int x; \
\
for (x = 0; x < (int)COUNTOF((dst)->s6_addr); x++) { \
(dst)->s6_addr[x] = (src)->s6_addr[x] \
& (msk)->s6_addr[x]; \
} \
} while (FALSE)
/*
* We allocate INC_RESLIST{4|6} entries to the free list whenever empty.
* Auto-tune these to be just less than 1KB (leaving at least 32 bytes
* for allocator overhead).
*/
#define INC_RESLIST4 ((1024 - 32) / sizeof(struct restrict_4))
#define INC_RESLIST6 ((1024 - 32) / sizeof(struct restrict_6))
/*
* The restriction list
*/
struct restrict_4 *restrictlist4;
struct restrict_6 *restrictlist6;
static size_t restrictcount; /* count in the restrict lists */
/*
* The free list and associated counters. Also some uninteresting
* stat counters.
*/
static struct restrict_4 *resfree4; /* available entries (free list) */
static struct restrict_6 *resfree6;
/*
* Count number of restriction entries referring to RES_LIMITED, to
* control implicit activation/deactivation of the MRU monlist.
*/
static u_long res_limited_refcnt;
/*
* Our default entries.
*
* We can make this cleaner with c99 support: see init_restrict().
*/
static struct restrict_4 restrict_def4;
static struct restrict_6 restrict_def6;
/*
* "restrict source ..." enabled knob and restriction bits.
*/
static int restrict_source_enabled;
static u_int32 restrict_source_rflags;
static u_short restrict_source_mflags;
static short restrict_source_ippeerlimit;
/* Spit out the IPv4 list */
printf("dump_restricts: restrictlist4: %p\n", restrictlist4);
for (res4 = restrictlist4; res4 != NULL; res4 = res4->link) {
dump_restrict4(res4);
}
/* Spit out the IPv6 list */
printf("dump_restricts: restrictlist6: %p\n", restrictlist6);
for (res6 = restrictlist6; res6 != NULL; res6 = res6->link) {
dump_restrict6(res6);
}
}
#endif /* DEBUG - dump_restrict() / dump_restricts() */
/*
* init_restrict - initialize the restriction data structures
*/
void
init_restrict(void)
{
/*
* The restriction lists end with a default entry with address
* and mask 0, which will match any entry. The lists are kept
* sorted by descending address followed by descending mask:
*
* address mask
* 192.168.0.0 255.255.255.0 kod limited noquery nopeer
* 192.168.0.0 255.255.0.0 kod limited
* 0.0.0.0 0.0.0.0 kod limited noquery
*
* The first entry which matches an address is used. With the
* example restrictions above, 192.168.0.0/24 matches the first
* entry, the rest of 192.168.0.0/16 matches the second, and
* everything else matches the third (default).
*
* Note this achieves the same result a little more efficiently
* than the documented behavior, which is to keep the lists
* sorted by ascending address followed by ascending mask, with
* the _last_ matching entry used.
*
* An additional wrinkle is we may have multiple entries with
* the same address and mask but differing match flags (mflags).
* We want to never talk to ourself, so RES_IGNORE entries for
* each local address are added by ntp_io.c with a host mask and
* both RESM_INTERFACE and RESM_NTPONLY set. We sort those
* entries before entries without those flags to achieve this.
* The remaining match flag is RESM_SOURCE, used to dynamically
* set restrictions for each peer based on the prototype set by
* "restrict source" in the configuration. We want those entries
* to be considered only when there is not a static host
* restriction for the address in the configuration, to allow
* operators to blacklist pool and manycast servers at runtime as
* desired using ntpq runtime configuration. Such static entries
* have no RESM_ bits set, so the sort order for mflags is first
* RESM_INTERFACE, then entries without RESM_SOURCE, finally the
* remaining.
*/
restrict_def4.ri.ippeerlimit = -1; /* Cleaner if we have C99 */
restrict_def6.ri.ippeerlimit = -1; /* Cleaner if we have C99 */
UNLINK_HEAD_SLIST(res, resfree4, link);
if (res != NULL) {
return res;
}
rl = eallocarray(count, cb);
/* link all but the first onto free list */
res = (void *)((char *)rl + (count - 1) * cb);
for (i = count - 1; i > 0; i--) {
LINK_SLIST(resfree4, res, link);
res = (void *)((char *)res - cb);
}
DEBUG_INSIST(rl == res);
/* allocate the first */
return res;
}
UNLINK_HEAD_SLIST(res, resfree6, link);
if (res != NULL) {
return res;
}
rl = eallocarray(count, cb);
/* link all but the first onto free list */
res = (void *)((char *)rl + (count - 1) * cb);
for (i = count - 1; i > 0; i--) {
LINK_SLIST(resfree6, res, link);
res = (void *)((char *)res - cb);
}
DEBUG_INSIST(rl == res);
/* allocate the first */
return res;
}
for (res = restrictlist6; res != NULL; res = next) {
next = res->link;
if (res->ri.expire && res->ri.expire <= current_time) {
free_res6(res);
}
MASK_IPV6_ADDR(&masked, addr, &res->v6.mask);
if (ADDR6_EQ(&masked, &res->v6.addr)
&& ( !(RESM_NTPONLY & res->ri.mflags)
|| NTP_PORT == (int)port)) {
break;
}
}
return res;
}
/*
* match_restrict_entry - find an exact match on a restrict list.
*
* Exact match is addr, mask, and mflags all equal.
* In order to use more common code for IPv4 and IPv6, this routine
* requires the caller to populate a restrict_[46] with mflags and either
* the v4 or v6 address and mask as appropriate. Other fields in the
* input restrict_u are ignored.
*/
static struct restrict_4 *
match_restrict4_entry(
const struct restrict_4 * pmatch)
{
struct restrict_4 *res;
for (res = restrictlist4; res != NULL; res = res->link) {
if (res->ri.mflags == pmatch->ri.mflags &&
!memcmp(&res->v4, &pmatch->v4, sizeof(res->v4))) {
break;
}
}
return res;
}
/*
* res_sorts_before4 - compare IPv4 restriction entries
*
* Returns nonzero if r1 sorts before r2. We sort by descending
* address, then descending mask, then an intricate mflags sort
* order explained in a block comment near the top of this file.
*/
static int/*BOOL*/
res_sorts_before4(
struct restrict_4 *r1,
struct restrict_4 *r2
)
{
int r1_before_r2;
/*
* res_sorts_before6 - compare IPv6 restriction entries
*
* Returns nonzero if r1 sorts before r2. We sort by descending
* address, then descending mask, then an intricate mflags sort
* order explained in a block comment near the top of this file.
*/
static int/*BOOL*/
res_sorts_before6(
struct restrict_6* r1,
struct restrict_6* r2
)
{
int r1_before_r2;
int cmp;
/*
* restrictions - return restrictions for this host in *r4a
*/
void
restrictions(
sockaddr_u *srcadr,
r4addr *r4a
)
{
struct in6_addr *pin6;
DEBUG_REQUIRE(NULL != r4a);
res_calls++;
if (IS_IPV4(srcadr)) {
struct restrict_4 *match;
/*
* Ignore any packets with a multicast source address
* (this should be done early in the receive process,
* not later!)
*/
if (IN_CLASSD(SRCADR(srcadr))) {
goto multicast;
}
match = match_restrict4_addr(SRCADR(srcadr),
SRCPORT(srcadr));
DEBUG_INSIST(match != NULL);
match->ri.count++;
/*
* res_not_found counts only use of the final default
* entry, not any "restrict default ntpport ...", which
* would be just before the final default.
*/
if (&restrict_def4 == match)
res_not_found++;
else
res_found++;
r4a->rflags = match->ri.rflags;
r4a->ippeerlimit = match->ri.ippeerlimit;
} else {
struct restrict_6 *match;
DEBUG_REQUIRE(IS_IPV6(srcadr));
pin6 = PSOCK_ADDR6(srcadr);
/*
* Ignore any packets with a multicast source address
* (this should be done early in the receive process,
* not later!)
*/
if (IN6_IS_ADDR_MULTICAST(pin6)) {
goto multicast;
}
match = match_restrict6_addr(pin6, SRCPORT(srcadr));
DEBUG_INSIST(match != NULL);
match->ri.count++;
if (&restrict_def6 == match)
res_not_found++;
else
res_found++;
r4a->rflags = match->ri.rflags;
r4a->ippeerlimit = match->ri.ippeerlimit;
}
if (IS_IPV4(resaddr)) {
DEBUG_INVARIANT(IS_IPV4(resmask));
/*
* Get address and mask in host byte order for easy
* comparison as u_int32
*/
ZERO(match4);
match4.v4.addr = SRCADR(resaddr);
match4.v4.mask = SRCADR(resmask);
match4.v4.addr &= match4.v4.mask;
match4.ri.mflags = mflags;
res4 = match_restrict4_entry(&match4);
ri = res4 ? &res4->ri : NULL;
} else {
DEBUG_INVARIANT(IS_IPV6(resaddr));
DEBUG_INVARIANT(IS_IPV6(resmask));
/*
* Get address and mask in network byte order for easy
* comparison as byte sequences (e.g. memcmp())
*/
ZERO(match6);
match6.v6.mask = SOCK_ADDR6(resmask);
MASK_IPV6_ADDR(&match6.v6.addr, PSOCK_ADDR6(resaddr),
&match6.v6.mask);
match6.ri.mflags = mflags;
res6 = match_restrict6_entry(&match6);
ri = res6 ? &res6->ri : NULL;
}
switch (op) {
case RESTRICT_FLAGS:
/*
* Here we add bits to the rflags. If we already have
* this restriction modify it.
*/
if (NULL != ri) {
if ( (RES_LIMITED & rflags)
&& !(RES_LIMITED & ri->rflags)) {
case RESTRICT_UNFLAG:
/*
* Remove some bits from the rflags. If we didn't
* find this one, just return.
*/
if (NULL == ri) {
DPRINTF(1, ("No match for %s %s removing rflags %s\n",
stoa(resaddr), stoa(resmask),
rflags_str(rflags)));
return FALSE;
}
if ( (RES_LIMITED & ri->rflags)
&& (RES_LIMITED & rflags)) {
dec_res_limited();
}
ri->rflags &= ~rflags;
return TRUE;
case RESTRICT_REMOVE:
case RESTRICT_REMOVEIF:
/*
* Remove an entry from the table entirely if we
* found one. Don't remove the default entry and
* don't remove an interface entry unless asked.
*/
if ( ri != NULL
&& ( RESTRICT_REMOVEIF == op
|| !(RESM_INTERFACE & ri->mflags))) {
if (res4 && res4 != &restrict_def4) {
free_res4(res4);
return TRUE;
}
if (res6 && res6 != &restrict_def6) {
free_res6(res6);
return TRUE;
}
}
DPRINTF(1, ("No match removing %s %s restriction\n",
stoa(resaddr), stoa(resmask)));
return FALSE;
}
/* notreached */
return FALSE;
}
/*
* restrict_source - maintains dynamic "restrict source ..." entries as
* peers come and go.
*/
void
restrict_source(
sockaddr_u * addr,
int farewell, /* TRUE to remove */
u_int32 lifetime /* seconds, 0 forever */
)
{
sockaddr_u onesmask;
int/*BOOL*/ success;