diff options
Diffstat (limited to 'ntpd/ntp_restrict.c')
-rw-r--r-- | ntpd/ntp_restrict.c | 586 |
1 files changed, 586 insertions, 0 deletions
diff --git a/ntpd/ntp_restrict.c b/ntpd/ntp_restrict.c new file mode 100644 index 0000000..ede4225 --- /dev/null +++ b/ntpd/ntp_restrict.c @@ -0,0 +1,586 @@ +/* + * 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_stdlib.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 SET_IPV6_ADDR_MASK(dst, src, msk) \ + do { \ + int idx; \ + for (idx = 0; idx < 16; idx++) { \ + (dst)->s6_addr[idx] = \ + (u_char) ((src)->s6_addr[idx] & (msk)->s6_addr[idx]); \ + } \ + } while (0) + +/* + * Memory allocation parameters. We allocate INITRESLIST entries + * initially, and add INCRESLIST entries to the free list whenever + * we run out. + */ +#define INITRESLIST 10 +#define INCRESLIST 5 + +#define RES_AVG 8. /* interpacket averaging factor */ + +/* + * The restriction list + */ +struct restrictlist *restrictlist; +struct restrictlist6 *restrictlist6; +static int restrictcount; /* count of entries in the res list */ +static int restrictcount6; /* count of entries in the res list 2*/ + +/* + * The free list and associated counters. Also some uninteresting + * stat counters. + */ +static struct restrictlist *resfree; +static struct restrictlist6 *resfree6; +static int numresfree; /* number of structures on free list */ +static int numresfree6; /* number of structures on free list 2 */ + +static u_long res_calls; +static u_long res_found; +static u_long res_not_found; + +/* + * Parameters of the RES_LIMITED restriction option. + */ +u_long res_avg_interval = 5; /* min average interpacket interval */ +u_long res_min_interval = 1; /* min interpacket interval */ + +/* + * Count number of restriction entries referring to RES_LIMITED controls + * activation/deactivation of monitoring (with respect to RES_LIMITED + * control) + */ +static u_long res_limited_refcnt; +static u_long res_limited_refcnt6; + +/* + * Our initial allocation of lists entries. + */ +static struct restrictlist resinit[INITRESLIST]; +static struct restrictlist6 resinit6[INITRESLIST]; + +/* + * init_restrict - initialize the restriction data structures + */ +void +init_restrict(void) +{ + register int i; + + /* + * Zero the list and put all but one on the free list + */ + resfree = 0; + memset((char *)resinit, 0, sizeof resinit); + resfree6 = 0; + memset((char *)resinit6, 0, sizeof resinit6); + for (i = 1; i < INITRESLIST; i++) { + resinit[i].next = resfree; + resinit6[i].next = resfree6; + resfree = &resinit[i]; + resfree6 = &resinit6[i]; + } + numresfree = INITRESLIST-1; + numresfree6 = INITRESLIST-1; + + /* + * Put the remaining item at the head of the list as our default + * entry. Everything in here should be zero for now. + */ + resinit[0].addr = htonl(INADDR_ANY); + resinit[0].mask = 0; + memset(&resinit6[0].addr6, 0, sizeof(struct in6_addr)); + memset(&resinit6[0].mask6, 0, sizeof(struct in6_addr)); + restrictlist = &resinit[0]; + restrictlist6 = &resinit6[0]; + restrictcount = 1; + restrictcount = 2; + + /* + * fix up stat counters + */ + res_calls = 0; + res_found = 0; + res_not_found = 0; + + /* + * set default values for RES_LIMIT functionality + */ + res_limited_refcnt = 0; + res_limited_refcnt6 = 0; +} + + +/* + * restrictions - return restrictions for this host + */ +int +restrictions( + struct sockaddr_storage *srcadr + ) +{ + struct restrictlist *rl; + struct restrictlist *match = NULL; + struct restrictlist6 *rl6; + struct restrictlist6 *match6 = NULL; + struct in6_addr hostaddr6; + struct in6_addr hostservaddr6; + u_int32 hostaddr; + int flags = 0; + int isntpport; + + res_calls++; + if (srcadr->ss_family == AF_INET) { + /* + * We need the host address in host order. Also need to + * know whether this is from the ntp port or not. + */ + hostaddr = SRCADR(srcadr); + isntpport = (SRCPORT(srcadr) == NTP_PORT); + + /* + * Ignore any packets with a multicast source address + * (this should be done early in the receive process, + * later!) + */ + if (IN_CLASSD(SRCADR(srcadr))) + return (int)RES_IGNORE; + + /* + * Set match to first entry, which is default entry. + * Work our way down from there. + */ + match = restrictlist; + for (rl = match->next; rl != 0 && rl->addr <= hostaddr; + rl = rl->next) + if ((hostaddr & rl->mask) == rl->addr) { + if ((rl->mflags & RESM_NTPONLY) && + !isntpport) + continue; + match = rl; + } + match->count++; + if (match == restrictlist) + res_not_found++; + else + res_found++; + flags = match->flags; + } + + /* IPv6 source address */ + if (srcadr->ss_family == AF_INET6) { + /* + * Need to know whether this is from the ntp port or + * not. + */ + hostaddr6 = GET_INADDR6(*srcadr); + isntpport = (ntohs(( + (struct sockaddr_in6 *)srcadr)->sin6_port) == + NTP_PORT); + + /* + * Ignore any packets with a multicast source address + * (this should be done early in the receive process, + * later!) + */ + if (IN6_IS_ADDR_MULTICAST(&hostaddr6)) + return (int)RES_IGNORE; + + /* + * Set match to first entry, which is default entry. + * Work our way down from there. + */ + match6 = restrictlist6; + for (rl6 = match6->next; rl6 != 0 && + (memcmp(&(rl6->addr6), &hostaddr6, + sizeof(hostaddr6)) <= 0); rl6 = rl6->next) { + SET_IPV6_ADDR_MASK(&hostservaddr6, &hostaddr6, + &rl6->mask6); + if (memcmp(&hostservaddr6, &(rl6->addr6), + sizeof(hostservaddr6)) == 0) { + if ((rl6->mflags & RESM_NTPONLY) && + !isntpport) + continue; + match6 = rl6; + } + } + match6->count++; + if (match6 == restrictlist6) + res_not_found++; + else + res_found++; + flags = match6->flags; + } + + /* + * The following implements a generalized call gap facility. + * Douse the RES_LIMITED bit only if the interval since the last + * packet is greater than res_min_interval and the average is + * greater thatn res_avg_interval. + */ + if (mon_enabled == MON_OFF) { + flags &= ~RES_LIMITED; + } else { + struct mon_data *md; + + /* + * At this poin the most recent arrival is first in the + * MRU list. Let the first 10 packets in for free until + * the average stabilizes. + */ + md = mon_mru_list.mru_next; + if (md->avg_interval == 0) + md->avg_interval = md->drop_count; + else + md->avg_interval += (md->drop_count - + md->avg_interval) / RES_AVG; + if (md->count < 10 || (md->drop_count > + res_min_interval && md->avg_interval > + res_avg_interval)) + flags &= ~RES_LIMITED; + md->drop_count = flags; + } + return (flags); +} + + +/* + * hack_restrict - add/subtract/manipulate entries on the restrict list + */ +void +hack_restrict( + int op, + struct sockaddr_storage *resaddr, + struct sockaddr_storage *resmask, + int mflags, + int flags + ) +{ + register u_int32 addr = 0; + register u_int32 mask = 0; + struct in6_addr addr6; + struct in6_addr mask6; + register struct restrictlist *rl = NULL; + register struct restrictlist *rlprev = NULL; + register struct restrictlist6 *rl6 = NULL; + register struct restrictlist6 *rlprev6 = NULL; + int i, addr_cmp, mask_cmp; + memset(&addr6, 0, sizeof(struct in6_addr)); + memset(&mask6, 0, sizeof(struct in6_addr)); + + if (resaddr->ss_family == AF_INET) { + /* + * Get address and mask in host byte order + */ + addr = SRCADR(resaddr); + mask = SRCADR(resmask); + addr &= mask; /* make sure low bits zero */ + + /* + * If this is the default address, point at first on + * list. Else go searching for it. + */ + if (addr == 0) { + rlprev = 0; + rl = restrictlist; + } else { + rlprev = restrictlist; + rl = rlprev->next; + while (rl != 0) { + if (rl->addr > addr) { + rl = 0; + break; + } else if (rl->addr == addr) { + if (rl->mask == mask) { + if ((mflags & + RESM_NTPONLY) == + (rl->mflags & + RESM_NTPONLY)) + break; + + if (!(mflags & + RESM_NTPONLY)) { + rl = 0; + break; + } + } else if (rl->mask > mask) { + rl = 0; + break; + } + } + rlprev = rl; + rl = rl->next; + } + } + } + + if (resaddr->ss_family == AF_INET6) { + mask6 = GET_INADDR6(*resmask); + SET_IPV6_ADDR_MASK(&addr6, + &GET_INADDR6(*resaddr), &mask6); + if (IN6_IS_ADDR_UNSPECIFIED(&addr6)) { + rlprev6 = 0; + rl6 = restrictlist6; + } else { + rlprev6 = restrictlist6; + rl6 = rlprev6->next; + while (rl6 != 0) { + addr_cmp = memcmp(&rl6->addr6, &addr6, + sizeof(addr6)); + if (addr_cmp > 0) { + rl6 = 0; + break; + } else if (addr_cmp == 0) { + mask_cmp = memcmp(&rl6->mask6, + &mask6, sizeof(mask6)); + if (mask_cmp == 0) { + if ((mflags & + RESM_NTPONLY) == + (rl6->mflags & + RESM_NTPONLY)) + break; + + if (!(mflags & + RESM_NTPONLY)) { + rl6 = 0; + break; + } + } else if (mask_cmp > 0) { + rl6 = 0; + break; + } + } + rlprev6 = rl6; + rl6 = rl6->next; + } + } + } + + /* + * In case the above wasn't clear :-), either rl now points + * at the entry this call refers to, or rl is zero and rlprev + * points to the entry prior to where this one should go in + * the sort. + */ + + /* + * Switch based on operation + */ + if (resaddr->ss_family == AF_INET) { + switch (op) { + case RESTRICT_FLAGS: + /* + * Here we add bits to the flags. If this is a + * new restriction add it. + */ + if (rl == 0) { + if (numresfree == 0) { + rl = (struct restrictlist *) + emalloc(INCRESLIST * + sizeof(struct + restrictlist)); + memset((char *)rl, 0, + INCRESLIST * sizeof(struct + restrictlist)); + for (i = 0; i < INCRESLIST; i++) { + rl->next = resfree; + resfree = rl; + rl++; + } + numresfree = INCRESLIST; + } + + rl = resfree; + resfree = rl->next; + numresfree--; + + rl->addr = addr; + rl->mask = mask; + rl->mflags = (u_short)mflags; + + rl->next = rlprev->next; + rlprev->next = rl; + restrictcount++; + } + if ((rl->flags ^ (u_short)flags) & + RES_LIMITED) { + res_limited_refcnt++; + mon_start(MON_RES); + } + rl->flags |= (u_short)flags; + break; + + case RESTRICT_UNFLAG: + /* + * Remove some bits from the flags. If we didn't + * find this one, just return. + */ + if (rl != 0) { + if ((rl->flags ^ (u_short)flags) & + RES_LIMITED) { + res_limited_refcnt--; + if (res_limited_refcnt == 0) + mon_stop(MON_RES); + } + rl->flags &= (u_short)~flags; + } + break; + + case RESTRICT_REMOVE: + /* + * 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 (rl != 0 + && rl->addr != htonl(INADDR_ANY) + && !(rl->mflags & RESM_INTERFACE)) { + rlprev->next = rl->next; + restrictcount--; + if (rl->flags & RES_LIMITED) { + res_limited_refcnt--; + if (res_limited_refcnt == 0) + mon_stop(MON_RES); + } + memset((char *)rl, 0, + sizeof(struct restrictlist)); + + rl->next = resfree; + resfree = rl; + numresfree++; + } + break; + + default: + break; + } + } else if (resaddr->ss_family == AF_INET6) { + switch (op) { + case RESTRICT_FLAGS: + /* + * Here we add bits to the flags. If this is a + * new restriction add it. + */ + if (rl6 == 0) { + if (numresfree6 == 0) { + rl6 = (struct + restrictlist6 *)emalloc( + INCRESLIST * sizeof(struct + restrictlist6)); + memset((char *)rl6, 0, + INCRESLIST * sizeof(struct + restrictlist6)); + + for (i = 0; i < INCRESLIST; + i++) { + rl6->next = resfree6; + resfree6 = rl6; + rl6++; + } + numresfree6 = INCRESLIST; + } + rl6 = resfree6; + resfree6 = rl6->next; + numresfree6--; + rl6->addr6 = addr6; + rl6->mask6 = mask6; + rl6->mflags = (u_short)mflags; + rl6->next = rlprev6->next; + rlprev6->next = rl6; + restrictcount6++; + } + if ((rl6->flags ^ (u_short)flags) & + RES_LIMITED) { + res_limited_refcnt6++; + mon_start(MON_RES); + } + rl6->flags |= (u_short)flags; + break; + + case RESTRICT_UNFLAG: + /* + * Remove some bits from the flags. If we didn't + * find this one, just return. + */ + if (rl6 != 0) { + if ((rl6->flags ^ (u_short)flags) & + RES_LIMITED) { + res_limited_refcnt6--; + if (res_limited_refcnt6 == 0) + mon_stop(MON_RES); + } + rl6->flags &= (u_short)~flags; + } + break; + + case RESTRICT_REMOVE: + /* + * 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 (rl6 != 0 && + !IN6_IS_ADDR_UNSPECIFIED(&rl6->addr6) + && !(rl6->mflags & RESM_INTERFACE)) { + rlprev6->next = rl6->next; + restrictcount6--; + if (rl6->flags & RES_LIMITED) { + res_limited_refcnt6--; + if (res_limited_refcnt6 == 0) + mon_stop(MON_RES); + } + memset((char *)rl6, 0, + sizeof(struct restrictlist6)); + rl6->next = resfree6; + resfree6 = rl6; + numresfree6++; + } + break; + + default: + break; + } + } +} |