diff options
Diffstat (limited to 'sebhbsd/freebsd/contrib/ntp/ntpd/ntp_restrict.c')
-rw-r--r-- | sebhbsd/freebsd/contrib/ntp/ntpd/ntp_restrict.c | 804 |
1 files changed, 804 insertions, 0 deletions
diff --git a/sebhbsd/freebsd/contrib/ntp/ntpd/ntp_restrict.c b/sebhbsd/freebsd/contrib/ntp/ntpd/ntp_restrict.c new file mode 100644 index 0000000..077d4e3 --- /dev/null +++ b/sebhbsd/freebsd/contrib/ntp/ntpd/ntp_restrict.c @@ -0,0 +1,804 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * ntp_restrict.c - determine host restrictions + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <sys/types.h> + +#include "ntpd.h" +#include "ntp_if.h" +#include "ntp_lists.h" +#include "ntp_stdlib.h" +#include "ntp_assert.h" + +/* + * This code keeps a simple address-and-mask list of hosts we want + * to place restrictions on (or remove them from). The restrictions + * are implemented as a set of flags which tell you what the host + * can't do. There is a subroutine entry to return the flags. The + * list is kept sorted to reduce the average number of comparisons + * and make sure you get the set of restrictions most specific to + * the address. + * + * The algorithm is that, when looking up a host, it is first assumed + * that the default set of restrictions will apply. It then searches + * down through the list. Whenever it finds a match it adopts the + * match's flags instead. When you hit the point where the sorted + * address is greater than the target, you return with the last set of + * flags you found. Because of the ordering of the list, the most + * specific match will provide the final set of flags. + * + * 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. + */ +/* + * We will use two lists, one for IPv4 addresses and one for IPv6 + * addresses. This is not protocol-independant but for now I can't + * find a way to respect this. We'll check this later... JFB 07/2001 + */ +#define MASK_IPV6_ADDR(dst, src, msk) \ + do { \ + int idx; \ + for (idx = 0; idx < (int)COUNTOF((dst)->s6_addr); idx++) { \ + (dst)->s6_addr[idx] = (src)->s6_addr[idx] \ + & (msk)->s6_addr[idx]; \ + } \ + } while (0) + +/* + * 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 16 bytes + * for allocator overhead). + */ +#define INC_RESLIST4 ((1024 - 16) / V4_SIZEOF_RESTRICT_U) +#define INC_RESLIST6 ((1024 - 16) / V6_SIZEOF_RESTRICT_U) + +/* + * The restriction list + */ +restrict_u *restrictlist4; +restrict_u *restrictlist6; +static int restrictcount; /* count in the restrict lists */ + +/* + * The free list and associated counters. Also some uninteresting + * stat counters. + */ +static restrict_u *resfree4; /* available entries (free list) */ +static restrict_u *resfree6; + +static u_long res_calls; +static u_long res_found; +static u_long res_not_found; + +/* + * 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 restrict_u restrict_def4; +static restrict_u restrict_def6; + +/* + * "restrict source ..." enabled knob and restriction bits. + */ +static int restrict_source_enabled; +static u_short restrict_source_rflags; +static u_short restrict_source_mflags; +static short restrict_source_ippeerlimit; + +/* + * private functions + */ +static restrict_u * alloc_res4(void); +static restrict_u * alloc_res6(void); +static void free_res(restrict_u *, int); +static void inc_res_limited(void); +static void dec_res_limited(void); +static restrict_u * match_restrict4_addr(u_int32, u_short); +static restrict_u * match_restrict6_addr(const struct in6_addr *, + u_short); +static restrict_u * match_restrict_entry(const restrict_u *, int); +static int res_sorts_before4(restrict_u *, restrict_u *); +static int res_sorts_before6(restrict_u *, restrict_u *); +static char * roptoa(restrict_op op); + + +void dump_restricts(void); + +/* + * dump_restrict - spit out a restrict_u + */ +static void +dump_restrict( + restrict_u * res, + int is_ipv6 + ) +{ + char as[INET6_ADDRSTRLEN]; + char ms[INET6_ADDRSTRLEN]; + + if (is_ipv6) { + inet_ntop(AF_INET6, &res->u.v6.addr, as, sizeof as); + inet_ntop(AF_INET6, &res->u.v6.mask, ms, sizeof ms); + } else { + struct in_addr sia = { htonl(res->u.v4.addr) }; + struct in_addr sim = { htonl(res->u.v4.mask) }; + + inet_ntop(AF_INET, &sia, as, sizeof as); + inet_ntop(AF_INET, &sim, ms, sizeof ms); + } + mprintf("restrict node at %p: %s/%s count %d, rflags %05x, mflags %05x, ippeerlimit %d, expire %lu, next %p\n", + res, as, ms, res->count, res->rflags, res->mflags, + res->ippeerlimit, res->expire, res->link); + return; +} + + +/* + * dump_restricts - spit out the 'restrict' lines + */ +void +dump_restricts(void) +{ + int defaultv4_done = 0; + int defaultv6_done = 0; + restrict_u * res; + restrict_u * next; + + mprintf("dump_restrict: restrict_def4: %p\n", &restrict_def4); + /* Spit out 'restrict {,-4,-6} default ...' lines, if needed */ + for (res = &restrict_def4; res != NULL; res = next) { + dump_restrict(res, 0); + next = res->link; + } + + mprintf("dump_restrict: restrict_def6: %p\n", &restrict_def6); + for (res = &restrict_def6; res != NULL; res = next) { + dump_restrict(res, 1); + next = res->link; + } + + /* Spit out the IPv4 list */ + mprintf("dump_restrict: restrictlist4: %p\n", &restrictlist4); + for (res = restrictlist4; res != NULL; res = next) { + dump_restrict(res, 0); + next = res->link; + } + + /* Spit out the IPv6 list */ + mprintf("dump_restrict: restrictlist6: %p\n", &restrictlist6); + for (res = restrictlist6; res != NULL; res = next) { + dump_restrict(res, 1); + next = res->link; + } + + return; +} + +/* + * init_restrict - initialize the restriction data structures + */ +void +init_restrict(void) +{ + /* + * The restriction lists begin 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). + * At present there is only one, RESM_NTPONLY. Entries with + * RESM_NTPONLY are sorted earlier so they take precedence over + * any otherwise similar entry without. Again, this is the same + * behavior as but reversed implementation compared to the docs. + * + */ + + restrict_def4.ippeerlimit = -1; /* Cleaner if we have C99 */ + restrict_def6.ippeerlimit = -1; /* Cleaner if we have C99 */ + + LINK_SLIST(restrictlist4, &restrict_def4, link); + LINK_SLIST(restrictlist6, &restrict_def6, link); + restrictcount = 2; +} + + +static restrict_u * +alloc_res4(void) +{ + const size_t cb = V4_SIZEOF_RESTRICT_U; + const size_t count = INC_RESLIST4; + restrict_u * rl; + restrict_u * res; + size_t i; + + 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); + } + INSIST(rl == res); + /* allocate the first */ + return res; +} + + +static restrict_u * +alloc_res6(void) +{ + const size_t cb = V6_SIZEOF_RESTRICT_U; + const size_t count = INC_RESLIST6; + restrict_u * rl; + restrict_u * res; + size_t i; + + 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); + } + INSIST(rl == res); + /* allocate the first */ + return res; +} + + +static void +free_res( + restrict_u * res, + int v6 + ) +{ + restrict_u ** plisthead; + restrict_u * unlinked; + + restrictcount--; + if (RES_LIMITED & res->rflags) + dec_res_limited(); + + if (v6) + plisthead = &restrictlist6; + else + plisthead = &restrictlist4; + UNLINK_SLIST(unlinked, *plisthead, res, link, restrict_u); + INSIST(unlinked == res); + + if (v6) { + zero_mem(res, V6_SIZEOF_RESTRICT_U); + plisthead = &resfree6; + } else { + zero_mem(res, V4_SIZEOF_RESTRICT_U); + plisthead = &resfree4; + } + LINK_SLIST(*plisthead, res, link); +} + + +static void +inc_res_limited(void) +{ + if (!res_limited_refcnt) + mon_start(MON_RES); + res_limited_refcnt++; +} + + +static void +dec_res_limited(void) +{ + res_limited_refcnt--; + if (!res_limited_refcnt) + mon_stop(MON_RES); +} + + +static restrict_u * +match_restrict4_addr( + u_int32 addr, + u_short port + ) +{ + const int v6 = 0; + restrict_u * res; + restrict_u * next; + + for (res = restrictlist4; res != NULL; res = next) { + struct in_addr sia = { htonl(res->u.v4.addr) }; + + next = res->link; + DPRINTF(2, ("match_restrict4_addr: Checking %s, port %d ... ", + inet_ntoa(sia), port)); + if ( res->expire + && res->expire <= current_time) + free_res(res, v6); /* zeroes the contents */ + if ( res->u.v4.addr == (addr & res->u.v4.mask) + && ( !(RESM_NTPONLY & res->mflags) + || NTP_PORT == port)) { + DPRINTF(2, ("MATCH: ippeerlimit %d\n", res->ippeerlimit)); + break; + } + DPRINTF(2, ("doesn't match: ippeerlimit %d\n", res->ippeerlimit)); + } + return res; +} + + +static restrict_u * +match_restrict6_addr( + const struct in6_addr * addr, + u_short port + ) +{ + const int v6 = 1; + restrict_u * res; + restrict_u * next; + struct in6_addr masked; + + for (res = restrictlist6; res != NULL; res = next) { + next = res->link; + INSIST(next != res); + if (res->expire && + res->expire <= current_time) + free_res(res, v6); + MASK_IPV6_ADDR(&masked, addr, &res->u.v6.mask); + if (ADDR6_EQ(&masked, &res->u.v6.addr) + && (!(RESM_NTPONLY & res->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_u with mflags and either + * the v4 or v6 address and mask as appropriate. Other fields in the + * input restrict_u are ignored. + */ +static restrict_u * +match_restrict_entry( + const restrict_u * pmatch, + int v6 + ) +{ + restrict_u *res; + restrict_u *rlist; + size_t cb; + + if (v6) { + rlist = restrictlist6; + cb = sizeof(pmatch->u.v6); + } else { + rlist = restrictlist4; + cb = sizeof(pmatch->u.v4); + } + + for (res = rlist; res != NULL; res = res->link) + if (res->mflags == pmatch->mflags && + !memcmp(&res->u, &pmatch->u, cb)) + break; + return res; +} + + +/* + * res_sorts_before4 - compare two restrict4 entries + * + * Returns nonzero if r1 sorts before r2. We sort by descending + * address, then descending mask, then descending mflags, so sorting + * before means having a higher value. + */ +static int +res_sorts_before4( + restrict_u *r1, + restrict_u *r2 + ) +{ + int r1_before_r2; + + if (r1->u.v4.addr > r2->u.v4.addr) + r1_before_r2 = 1; + else if (r1->u.v4.addr < r2->u.v4.addr) + r1_before_r2 = 0; + else if (r1->u.v4.mask > r2->u.v4.mask) + r1_before_r2 = 1; + else if (r1->u.v4.mask < r2->u.v4.mask) + r1_before_r2 = 0; + else if (r1->mflags > r2->mflags) + r1_before_r2 = 1; + else + r1_before_r2 = 0; + + return r1_before_r2; +} + + +/* + * res_sorts_before6 - compare two restrict6 entries + * + * Returns nonzero if r1 sorts before r2. We sort by descending + * address, then descending mask, then descending mflags, so sorting + * before means having a higher value. + */ +static int +res_sorts_before6( + restrict_u *r1, + restrict_u *r2 + ) +{ + int r1_before_r2; + int cmp; + + cmp = ADDR6_CMP(&r1->u.v6.addr, &r2->u.v6.addr); + if (cmp > 0) /* r1->addr > r2->addr */ + r1_before_r2 = 1; + else if (cmp < 0) /* r2->addr > r1->addr */ + r1_before_r2 = 0; + else { + cmp = ADDR6_CMP(&r1->u.v6.mask, &r2->u.v6.mask); + if (cmp > 0) /* r1->mask > r2->mask*/ + r1_before_r2 = 1; + else if (cmp < 0) /* r2->mask > r1->mask */ + r1_before_r2 = 0; + else if (r1->mflags > r2->mflags) + r1_before_r2 = 1; + else + r1_before_r2 = 0; + } + + return r1_before_r2; +} + + +/* + * restrictions - return restrictions for this host in *r4a + */ +void +restrictions( + sockaddr_u *srcadr, + r4addr *r4a + ) +{ + restrict_u *match; + struct in6_addr *pin6; + + REQUIRE(NULL != r4a); + + res_calls++; + r4a->rflags = RES_IGNORE; + r4a->ippeerlimit = 0; + + DPRINTF(1, ("restrictions: looking up %s\n", stoa(srcadr))); + + /* IPv4 source address */ + if (IS_IPV4(srcadr)) { + /* + * Ignore any packets with a multicast source address + * (this should be done early in the receive process, + * not later!) + */ + if (IN_CLASSD(SRCADR(srcadr))) { + DPRINTF(1, ("restrictions: srcadr %s is multicast\n", stoa(srcadr))); + r4a->ippeerlimit = 2; /* XXX: we should use a better value */ + return; + } + + match = match_restrict4_addr(SRCADR(srcadr), + SRCPORT(srcadr)); + + INSIST(match != NULL); + + match->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->rflags; + r4a->ippeerlimit = match->ippeerlimit; + } + + /* IPv6 source address */ + if (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)) + return; + + match = match_restrict6_addr(pin6, SRCPORT(srcadr)); + INSIST(match != NULL); + match->count++; + if (&restrict_def6 == match) + res_not_found++; + else + res_found++; + r4a->rflags = match->rflags; + r4a->ippeerlimit = match->ippeerlimit; + } + return; +} + + +/* + * roptoa - convert a restrict_op to a string + */ +char * +roptoa(restrict_op op) { + static char sb[30]; + + switch(op) { + case RESTRICT_FLAGS: return "RESTRICT_FLAGS"; + case RESTRICT_UNFLAG: return "RESTRICT_UNFLAGS"; + case RESTRICT_REMOVE: return "RESTRICT_REMOVE"; + case RESTRICT_REMOVEIF: return "RESTRICT_REMOVEIF"; + default: + snprintf(sb, sizeof sb, "**RESTRICT_#%d**", op); + return sb; + } +} + + +/* + * hack_restrict - add/subtract/manipulate entries on the restrict list + */ +void +hack_restrict( + restrict_op op, + sockaddr_u * resaddr, + sockaddr_u * resmask, + short ippeerlimit, + u_short mflags, + u_short rflags, + u_long expire + ) +{ + int v6; + restrict_u match; + restrict_u * res; + restrict_u ** plisthead; + + DPRINTF(1, ("hack_restrict: op %s addr %s mask %s ippeerlimit %d mflags %08x rflags %08x\n", + roptoa(op), stoa(resaddr), stoa(resmask), ippeerlimit, mflags, rflags)); + + if (NULL == resaddr) { + REQUIRE(NULL == resmask); + REQUIRE(RESTRICT_FLAGS == op); + restrict_source_rflags = rflags; + restrict_source_mflags = mflags; + restrict_source_ippeerlimit = ippeerlimit; + restrict_source_enabled = 1; + return; + } + + ZERO(match); + +#if 0 + /* silence VC9 potentially uninit warnings */ + // HMS: let's use a compiler-specific "enable" for this. + res = NULL; + v6 = 0; +#endif + + if (IS_IPV4(resaddr)) { + v6 = 0; + /* + * Get address and mask in host byte order for easy + * comparison as u_int32 + */ + match.u.v4.addr = SRCADR(resaddr); + match.u.v4.mask = SRCADR(resmask); + match.u.v4.addr &= match.u.v4.mask; + + } else if (IS_IPV6(resaddr)) { + v6 = 1; + /* + * Get address and mask in network byte order for easy + * comparison as byte sequences (e.g. memcmp()) + */ + match.u.v6.mask = SOCK_ADDR6(resmask); + MASK_IPV6_ADDR(&match.u.v6.addr, PSOCK_ADDR6(resaddr), + &match.u.v6.mask); + + } else /* not IPv4 nor IPv6 */ + REQUIRE(0); + + match.rflags = rflags; + match.mflags = mflags; + match.ippeerlimit = ippeerlimit; + match.expire = expire; + res = match_restrict_entry(&match, v6); + + switch (op) { + + case RESTRICT_FLAGS: + /* + * Here we add bits to the rflags. If this is a + * new restriction add it. + */ + if (NULL == res) { + if (v6) { + res = alloc_res6(); + memcpy(res, &match, + V6_SIZEOF_RESTRICT_U); + plisthead = &restrictlist6; + } else { + res = alloc_res4(); + memcpy(res, &match, + V4_SIZEOF_RESTRICT_U); + plisthead = &restrictlist4; + } + LINK_SORT_SLIST( + *plisthead, res, + (v6) + ? res_sorts_before6(res, L_S_S_CUR()) + : res_sorts_before4(res, L_S_S_CUR()), + link, restrict_u); + restrictcount++; + if (RES_LIMITED & rflags) + inc_res_limited(); + } else { + if ( (RES_LIMITED & rflags) + && !(RES_LIMITED & res->rflags)) + inc_res_limited(); + res->rflags |= rflags; + } + + res->ippeerlimit = match.ippeerlimit; + + break; + + case RESTRICT_UNFLAG: + /* + * Remove some bits from the rflags. If we didn't + * find this one, just return. + */ + if (res != NULL) { + if ( (RES_LIMITED & res->rflags) + && (RES_LIMITED & rflags)) + dec_res_limited(); + res->rflags &= ~rflags; + } + break; + + 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. + */ + if (res != NULL + && (RESTRICT_REMOVEIF == op + || !(RESM_INTERFACE & res->mflags)) + && res != &restrict_def4 + && res != &restrict_def6) + free_res(res, v6); + break; + + default: /* unknown op */ + INSIST(0); + break; + } + +} + + +/* + * restrict_source - maintains dynamic "restrict source ..." entries as + * peers come and go. + */ +void +restrict_source( + sockaddr_u * addr, + int farewell, /* 0 to add, 1 to remove */ + u_long expire /* 0 is infinite, valid until */ + ) +{ + sockaddr_u onesmask; + restrict_u * res; + int found_specific; + + if (!restrict_source_enabled || SOCK_UNSPEC(addr) || + IS_MCAST(addr) || ISREFCLOCKADR(addr)) + return; + + REQUIRE(AF_INET == AF(addr) || AF_INET6 == AF(addr)); + + SET_HOSTMASK(&onesmask, AF(addr)); + if (farewell) { + hack_restrict(RESTRICT_REMOVE, addr, &onesmask, + -2, 0, 0, 0); + DPRINTF(1, ("restrict_source: %s removed", stoa(addr))); + return; + } + + /* + * If there is a specific entry for this address, hands + * off, as it is condidered more specific than "restrict + * server ...". + * However, if the specific entry found is a fleeting one + * added by pool_xmit() before soliciting, replace it + * immediately regardless of the expire value to make way + * for the more persistent entry. + */ + if (IS_IPV4(addr)) { + res = match_restrict4_addr(SRCADR(addr), SRCPORT(addr)); + INSIST(res != NULL); + found_specific = (SRCADR(&onesmask) == res->u.v4.mask); + } else { + res = match_restrict6_addr(&SOCK_ADDR6(addr), + SRCPORT(addr)); + INSIST(res != NULL); + found_specific = ADDR6_EQ(&res->u.v6.mask, + &SOCK_ADDR6(&onesmask)); + } + if (!expire && found_specific && res->expire) { + found_specific = 0; + free_res(res, IS_IPV6(addr)); + } + if (found_specific) + return; + + hack_restrict(RESTRICT_FLAGS, addr, &onesmask, + restrict_source_ippeerlimit, restrict_source_mflags, + restrict_source_rflags, expire); + DPRINTF(1, ("restrict_source: %s host restriction added\n", + stoa(addr))); +} |