diff options
Diffstat (limited to 'contrib/ntp/ntpd/ntp_restrict.c')
-rw-r--r-- | contrib/ntp/ntpd/ntp_restrict.c | 460 |
1 files changed, 460 insertions, 0 deletions
diff --git a/contrib/ntp/ntpd/ntp_restrict.c b/contrib/ntp/ntpd/ntp_restrict.c new file mode 100644 index 0000000..0e5b9dc --- /dev/null +++ b/contrib/ntp/ntpd/ntp_restrict.c @@ -0,0 +1,460 @@ +/* + * ntp_restrict.c - find out what restrictions this host is running under + */ +#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. + */ + +/* + * 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 + +/* + * The restriction list + */ +struct restrictlist *restrictlist; +static int restrictcount; /* count of entries in the restriction list */ + +/* + * The free list and associated counters. Also some uninteresting + * stat counters. + */ +static struct restrictlist *resfree; +static int numresfree; /* number of structures on free list */ + +static u_long res_calls; +static u_long res_found; +static u_long res_not_found; +/* static u_long res_timereset; */ + +/* + * Parameters of the RES_LIMITED restriction option. + * client_limit is the number of hosts allowed per source net + * client_limit_period is the number of seconds after which an entry + * is no longer considered for client limit determination + */ +u_long client_limit; +u_long client_limit_period; +/* + * 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; + +/* + * Our initial allocation of list entries. + */ +static struct restrictlist resinit[INITRESLIST]; + +/* + * init_restrict - initialize the restriction data structures + */ +void +init_restrict(void) +{ + register int i; + char bp[80]; + + /* + * Zero the list and put all but one on the free list + */ + resfree = 0; + memset((char *)resinit, 0, sizeof resinit); + + for (i = 1; i < INITRESLIST; i++) { + resinit[i].next = resfree; + resfree = &resinit[i]; + } + + numresfree = 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; + restrictlist = &resinit[0]; + restrictcount = 1; + + + /* + * fix up stat counters + */ + res_calls = 0; + res_found = 0; + res_not_found = 0; + /* res_timereset = 0; */ + + /* + * set default values for RES_LIMIT functionality + */ + client_limit = 3; + client_limit_period = 3600; + res_limited_refcnt = 0; + + sprintf(bp, "client_limit=%ld", client_limit); + set_sys_var(bp, strlen(bp)+1, RO); + sprintf(bp, "client_limit_period=%ld", client_limit_period); + set_sys_var(bp, strlen(bp)+1, RO); +} + + +/* + * restrictions - return restrictions for this host + */ +int +restrictions( + struct sockaddr_in *srcadr + ) +{ + register struct restrictlist *rl; + register struct restrictlist *match; + register u_int32 hostaddr; + register int isntpport; + + res_calls++; + /* + * 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(ntohl(srcadr->sin_addr.s_addr))) + 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++; + + /* + * The following implements limiting the number of clients + * accepted from a given network. The notion of "same network" + * is determined by the mask and addr fields of the restrict + * list entry. The monitor mechanism has to be enabled for + * collecting info on current clients. + * + * The policy is as follows: + * - take the list of clients recorded + * from the given "network" seen within the last + * client_limit_period seconds + * - if there are at most client_limit entries: + * --> access allowed + * - otherwise sort by time first seen + * - current client among the first client_limit seen + * hosts? + * if yes: access allowed + * else: eccess denied + */ + if (match->flags & RES_LIMITED) { + int lcnt; + struct mon_data *md, *this_client; + +#ifdef DEBUG + if (debug > 2) + printf("limited clients check: %ld clients, period %ld seconds, net is 0x%lX\n", + client_limit, client_limit_period, + (u_long)netof(hostaddr)); +#endif /*DEBUG*/ + if (mon_enabled == MON_OFF) { +#ifdef DEBUG + if (debug > 4) + printf("no limit - monitoring is off\n"); +#endif + return (int)(match->flags & ~RES_LIMITED); + } + + /* + * How nice, MRU list provides our current client as the + * first entry in the list. + * Monitoring was verified to be active above, thus we + * know an entry for our client must exist, or some + * brain dead set the memory limit for mon entries to ZERO!!! + */ + this_client = mon_mru_list.mru_next; + + for (md = mon_fifo_list.fifo_next,lcnt = 0; + md != &mon_fifo_list; + md = md->fifo_next) { + if ((current_time - md->lasttime) + > client_limit_period) { +#ifdef DEBUG + if (debug > 5) + printf("checking: %s: ignore: too old: %ld\n", + numtoa(md->rmtadr), + current_time - md->lasttime); +#endif + continue; + } + if (md->mode == MODE_BROADCAST || + md->mode == MODE_CONTROL || + md->mode == MODE_PRIVATE) { +#ifdef DEBUG + if (debug > 5) + printf("checking: %s: ignore mode %d\n", + numtoa(md->rmtadr), + md->mode); +#endif + continue; + } + if (netof(md->rmtadr) != + netof(hostaddr)) { +#ifdef DEBUG + if (debug > 5) + printf("checking: %s: different net 0x%lX\n", + numtoa(md->rmtadr), + (u_long)netof(md->rmtadr)); +#endif + continue; + } + lcnt++; + if (lcnt > (int) client_limit || + md->rmtadr == hostaddr) { +#ifdef DEBUG + if (debug > 5) + printf("considering %s: found host\n", + numtoa(md->rmtadr)); +#endif + break; + } +#ifdef DEBUG + else { + if (debug > 5) + printf("considering %s: same net\n", + numtoa(md->rmtadr)); + } +#endif + + } +#ifdef DEBUG + if (debug > 4) + printf("this one is rank %d in list, limit is %lu: %s\n", + lcnt, client_limit, + (lcnt <= (int) client_limit) ? "ALLOW" : "REJECT"); +#endif + if (lcnt <= (int) client_limit) { + this_client->lastdrop = 0; + return (int)(match->flags & ~RES_LIMITED); + } else { + this_client->lastdrop = current_time; + } + } + return (int)match->flags; +} + + +/* + * hack_restrict - add/subtract/manipulate entries on the restrict list + */ +void +hack_restrict( + int op, + struct sockaddr_in *resaddr, + struct sockaddr_in *resmask, + int mflags, + int flags + ) +{ + register u_int32 addr; + register u_int32 mask; + register struct restrictlist *rl; + register struct restrictlist *rlprev; + int i; + + /* + * Get address and mask in host byte order + */ + addr = SRCADR(resaddr); + mask = SRCADR(resmask); + addr &= mask; /* make sure low bits are zero */ + + /* + * If this is the default address, point at first on list. Else + * go searching for it. + */ + if (addr == htonl(INADDR_ANY)) { + 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; /* exact match */ + if (!(mflags & RESM_NTPONLY)) { + /* + * No flag fits before flag + */ + rl = 0; + break; + } + /* continue on */ + } else if (rl->mask > mask) { + rl = 0; + break; + } + } + rlprev = rl; + rl = rl->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 + */ + 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); /* ensure data gets collected */ + } + 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: + /* Oh, well */ + break; + } + + /* done! */ +} |