#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"
#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)
#define INC_RESLIST4 ((1024 - 16) / V4_SIZEOF_RESTRICT_U)
#define INC_RESLIST6 ((1024 - 16) / V6_SIZEOF_RESTRICT_U)
restrict_u *restrictlist4;
restrict_u *restrictlist6;
static int restrictcount;
static restrict_u *resfree4;
static restrict_u *resfree6;
static u_long res_calls;
static u_long res_found;
static u_long res_not_found;
static u_long res_limited_refcnt;
static restrict_u restrict_def4;
static restrict_u restrict_def6;
static int restrict_source_enabled;
static u_short restrict_source_flags;
static u_short restrict_source_mflags;
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 *);
void
init_restrict(void)
{
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);
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);
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);
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);
return res;
}
static void
free_res(
restrict_u * res,
int v6
)
{
restrict_u ** plisthead;
restrict_u * unlinked;
restrictcount--;
if (RES_LIMITED & res->flags)
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) {
next = res->link;
if (res->expire &&
res->expire <= current_time)
free_res(res, v6);
if (res->u.v4.addr == (addr & res->u.v4.mask)
&& (!(RESM_NTPONLY & res->mflags)
|| NTP_PORT == port))
break;
}
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;
}
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;
}
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;
}
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_before_r2 = 1;
else if (cmp < 0)
r1_before_r2 = 0;
else {
cmp = ADDR6_CMP(&r1->u.v6.mask, &r2->u.v6.mask);
if (cmp > 0)
r1_before_r2 = 1;
else if (cmp < 0)
r1_before_r2 = 0;
else if (r1->mflags > r2->mflags)
r1_before_r2 = 1;
else
r1_before_r2 = 0;
}
return r1_before_r2;
}
u_short
restrictions(
sockaddr_u *srcadr
)
{
restrict_u *match;
struct in6_addr *pin6;
u_short flags;
res_calls++;
flags = 0;
if (IS_IPV4(srcadr)) {
if (IN_CLASSD(SRCADR(srcadr)))
return (int)RES_IGNORE;
match = match_restrict4_addr(SRCADR(srcadr),
SRCPORT(srcadr));
INSIST(match != NULL);
match->count++;
if (&restrict_def4 == match)
res_not_found++;
else
res_found++;
flags = match->flags;
}
if (IS_IPV6(srcadr)) {
pin6 = PSOCK_ADDR6(srcadr);
if (IN6_IS_ADDR_MULTICAST(pin6))
return (int)RES_IGNORE;
match = match_restrict6_addr(pin6, SRCPORT(srcadr));
INSIST(match != NULL);
match->count++;
if (&restrict_def6 == match)
res_not_found++;
else
res_found++;
flags = match->flags;
}
return (flags);
}
void
hack_restrict(
int op,
sockaddr_u * resaddr,
sockaddr_u * resmask,
u_short mflags,
u_short flags,
u_long expire
)
{
int v6;
restrict_u match;
restrict_u * res;
restrict_u ** plisthead;
DPRINTF(1, ("restrict: op %d addr %s mask %s mflags %08x flags %08x\n",
op, stoa(resaddr), stoa(resmask), mflags, flags));
if (NULL == resaddr) {
REQUIRE(NULL == resmask);
REQUIRE(RESTRICT_FLAGS == op);
restrict_source_flags = flags;
restrict_source_mflags = mflags;
restrict_source_enabled = 1;
return;
}
ZERO(match);
#if 0
res = NULL;
v6 = 0;
#endif
if (IS_IPV4(resaddr)) {
v6 = 0;
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;
match.u.v6.mask = SOCK_ADDR6(resmask);
MASK_IPV6_ADDR(&match.u.v6.addr, PSOCK_ADDR6(resaddr),
&match.u.v6.mask);
} else {
REQUIRE(0);
}
match.flags = flags;
match.mflags = mflags;
match.expire = expire;
res = match_restrict_entry(&match, v6);
switch (op) {
case RESTRICT_FLAGS:
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 & flags)
inc_res_limited();
} else {
if ((RES_LIMITED & flags) &&
!(RES_LIMITED & res->flags))
inc_res_limited();
res->flags |= flags;
}
break;
case RESTRICT_UNFLAG:
if (res != NULL) {
if ((RES_LIMITED & res->flags)
&& (RES_LIMITED & flags))
dec_res_limited();
res->flags &= ~flags;
}
break;
case RESTRICT_REMOVE:
case RESTRICT_REMOVEIF:
if (res != NULL
&& (RESTRICT_REMOVEIF == op
|| !(RESM_INTERFACE & res->mflags))
&& res != &restrict_def4
&& res != &restrict_def6)
free_res(res, v6);
break;
default:
INSIST(0);
break;
}
}
void
restrict_source(
sockaddr_u * addr,
int farewell,
u_long expire
)
{
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,
0, 0, 0);
DPRINTF(1, ("restrict_source: %s removed", stoa(addr)));
return;
}
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_mflags, restrict_source_flags,
expire);
DPRINTF(1, ("restrict_source: %s host restriction added\n",
stoa(addr)));
}