154359Sroberto/* 2132451Sroberto * ntp_restrict.c - determine host restrictions 354359Sroberto */ 454359Sroberto#ifdef HAVE_CONFIG_H 554359Sroberto#include <config.h> 654359Sroberto#endif 754359Sroberto 854359Sroberto#include <stdio.h> 954359Sroberto#include <sys/types.h> 1054359Sroberto 1154359Sroberto#include "ntpd.h" 1254359Sroberto#include "ntp_if.h" 13290000Sglebius#include "ntp_lists.h" 1454359Sroberto#include "ntp_stdlib.h" 15290000Sglebius#include "ntp_assert.h" 1654359Sroberto 1754359Sroberto/* 1854359Sroberto * This code keeps a simple address-and-mask list of hosts we want 19132451Sroberto * to place restrictions on (or remove them from). The restrictions 2054359Sroberto * are implemented as a set of flags which tell you what the host 21132451Sroberto * can't do. There is a subroutine entry to return the flags. The 2254359Sroberto * list is kept sorted to reduce the average number of comparisons 2354359Sroberto * and make sure you get the set of restrictions most specific to 2454359Sroberto * the address. 2554359Sroberto * 2654359Sroberto * The algorithm is that, when looking up a host, it is first assumed 27132451Sroberto * that the default set of restrictions will apply. It then searches 28132451Sroberto * down through the list. Whenever it finds a match it adopts the 29132451Sroberto * match's flags instead. When you hit the point where the sorted 30132451Sroberto * address is greater than the target, you return with the last set of 31132451Sroberto * flags you found. Because of the ordering of the list, the most 32132451Sroberto * specific match will provide the final set of flags. 3354359Sroberto * 3454359Sroberto * This was originally intended to restrict you from sync'ing to your 35132451Sroberto * own broadcasts when you are doing that, by restricting yourself from 36132451Sroberto * your own interfaces. It was also thought it would sometimes be useful 37132451Sroberto * to keep a misbehaving host or two from abusing your primary clock. It 38132451Sroberto * has been expanded, however, to suit the needs of those with more 39132451Sroberto * restrictive access policies. 4054359Sroberto */ 41132451Sroberto/* 42132451Sroberto * We will use two lists, one for IPv4 addresses and one for IPv6 43132451Sroberto * addresses. This is not protocol-independant but for now I can't 44132451Sroberto * find a way to respect this. We'll check this later... JFB 07/2001 45132451Sroberto */ 46290000Sglebius#define MASK_IPV6_ADDR(dst, src, msk) \ 47290000Sglebius do { \ 48290000Sglebius int idx; \ 49290000Sglebius for (idx = 0; idx < (int)COUNTOF((dst)->s6_addr); idx++) { \ 50290000Sglebius (dst)->s6_addr[idx] = (src)->s6_addr[idx] \ 51290000Sglebius & (msk)->s6_addr[idx]; \ 52290000Sglebius } \ 53132451Sroberto } while (0) 5454359Sroberto 5554359Sroberto/* 56290000Sglebius * We allocate INC_RESLIST{4|6} entries to the free list whenever empty. 57290000Sglebius * Auto-tune these to be just less than 1KB (leaving at least 16 bytes 58290000Sglebius * for allocator overhead). 5954359Sroberto */ 60290000Sglebius#define INC_RESLIST4 ((1024 - 16) / V4_SIZEOF_RESTRICT_U) 61290000Sglebius#define INC_RESLIST6 ((1024 - 16) / V6_SIZEOF_RESTRICT_U) 6254359Sroberto 6354359Sroberto/* 6454359Sroberto * The restriction list 6554359Sroberto */ 66290000Sglebiusrestrict_u *restrictlist4; 67290000Sglebiusrestrict_u *restrictlist6; 68290000Sglebiusstatic int restrictcount; /* count in the restrict lists */ 6954359Sroberto 7054359Sroberto/* 7154359Sroberto * The free list and associated counters. Also some uninteresting 7254359Sroberto * stat counters. 7354359Sroberto */ 74290000Sglebiusstatic restrict_u *resfree4; /* available entries (free list) */ 75290000Sglebiusstatic restrict_u *resfree6; 7654359Sroberto 77290000Sglebiusstatic u_long res_calls; 78290000Sglebiusstatic u_long res_found; 79290000Sglebiusstatic u_long res_not_found; 8054359Sroberto 8154359Sroberto/* 82290000Sglebius * Count number of restriction entries referring to RES_LIMITED, to 83290000Sglebius * control implicit activation/deactivation of the MRU monlist. 8454359Sroberto */ 85290000Sglebiusstatic u_long res_limited_refcnt; 86132451Sroberto 8754359Sroberto/* 88290000Sglebius * Our default entries. 8954359Sroberto */ 90290000Sglebiusstatic restrict_u restrict_def4; 91290000Sglebiusstatic restrict_u restrict_def6; 9254359Sroberto 9354359Sroberto/* 94290000Sglebius * "restrict source ..." enabled knob and restriction bits. 9554359Sroberto */ 96290000Sglebiusstatic int restrict_source_enabled; 97290000Sglebiusstatic u_short restrict_source_flags; 98290000Sglebiusstatic u_short restrict_source_mflags; 9954359Sroberto 10054359Sroberto/* 101290000Sglebius * private functions 102290000Sglebius */ 103290000Sglebiusstatic restrict_u * alloc_res4(void); 104290000Sglebiusstatic restrict_u * alloc_res6(void); 105290000Sglebiusstatic void free_res(restrict_u *, int); 106290000Sglebiusstatic void inc_res_limited(void); 107290000Sglebiusstatic void dec_res_limited(void); 108290000Sglebiusstatic restrict_u * match_restrict4_addr(u_int32, u_short); 109290000Sglebiusstatic restrict_u * match_restrict6_addr(const struct in6_addr *, 110290000Sglebius u_short); 111290000Sglebiusstatic restrict_u * match_restrict_entry(const restrict_u *, int); 112290000Sglebiusstatic int res_sorts_before4(restrict_u *, restrict_u *); 113290000Sglebiusstatic int res_sorts_before6(restrict_u *, restrict_u *); 114290000Sglebius 115290000Sglebius 116290000Sglebius/* 11754359Sroberto * init_restrict - initialize the restriction data structures 11854359Sroberto */ 11954359Srobertovoid 12054359Srobertoinit_restrict(void) 12154359Sroberto{ 12254359Sroberto /* 123290000Sglebius * The restriction lists begin with a default entry with address 124290000Sglebius * and mask 0, which will match any entry. The lists are kept 125290000Sglebius * sorted by descending address followed by descending mask: 126290000Sglebius * 127290000Sglebius * address mask 128290000Sglebius * 192.168.0.0 255.255.255.0 kod limited noquery nopeer 129290000Sglebius * 192.168.0.0 255.255.0.0 kod limited 130290000Sglebius * 0.0.0.0 0.0.0.0 kod limited noquery 131290000Sglebius * 132290000Sglebius * The first entry which matches an address is used. With the 133290000Sglebius * example restrictions above, 192.168.0.0/24 matches the first 134290000Sglebius * entry, the rest of 192.168.0.0/16 matches the second, and 135290000Sglebius * everything else matches the third (default). 136290000Sglebius * 137290000Sglebius * Note this achieves the same result a little more efficiently 138290000Sglebius * than the documented behavior, which is to keep the lists 139290000Sglebius * sorted by ascending address followed by ascending mask, with 140290000Sglebius * the _last_ matching entry used. 141290000Sglebius * 142290000Sglebius * An additional wrinkle is we may have multiple entries with 143290000Sglebius * the same address and mask but differing match flags (mflags). 144290000Sglebius * At present there is only one, RESM_NTPONLY. Entries with 145290000Sglebius * RESM_NTPONLY are sorted earlier so they take precedence over 146290000Sglebius * any otherwise similar entry without. Again, this is the same 147290000Sglebius * behavior as but reversed implementation compared to the docs. 148290000Sglebius * 14954359Sroberto */ 150290000Sglebius LINK_SLIST(restrictlist4, &restrict_def4, link); 151290000Sglebius LINK_SLIST(restrictlist6, &restrict_def6, link); 152290000Sglebius restrictcount = 2; 153290000Sglebius} 154290000Sglebius 155290000Sglebius 156290000Sglebiusstatic restrict_u * 157290000Sglebiusalloc_res4(void) 158290000Sglebius{ 159290000Sglebius const size_t cb = V4_SIZEOF_RESTRICT_U; 160290000Sglebius const size_t count = INC_RESLIST4; 161290000Sglebius restrict_u * rl; 162290000Sglebius restrict_u * res; 163293894Sglebius size_t i; 164290000Sglebius 165290000Sglebius UNLINK_HEAD_SLIST(res, resfree4, link); 166290000Sglebius if (res != NULL) 167290000Sglebius return res; 168290000Sglebius 169290000Sglebius rl = emalloc_zero(count * cb); 170290000Sglebius /* link all but the first onto free list */ 171290000Sglebius res = (void *)((char *)rl + (count - 1) * cb); 172290000Sglebius for (i = count - 1; i > 0; i--) { 173290000Sglebius LINK_SLIST(resfree4, res, link); 174290000Sglebius res = (void *)((char *)res - cb); 17554359Sroberto } 176290000Sglebius INSIST(rl == res); 177290000Sglebius /* allocate the first */ 178290000Sglebius return res; 179290000Sglebius} 18054359Sroberto 18154359Sroberto 182290000Sglebiusstatic restrict_u * 183290000Sglebiusalloc_res6(void) 184290000Sglebius{ 185290000Sglebius const size_t cb = V6_SIZEOF_RESTRICT_U; 186290000Sglebius const size_t count = INC_RESLIST6; 187290000Sglebius restrict_u * rl; 188290000Sglebius restrict_u * res; 189293894Sglebius size_t i; 19054359Sroberto 191290000Sglebius UNLINK_HEAD_SLIST(res, resfree6, link); 192290000Sglebius if (res != NULL) 193290000Sglebius return res; 194290000Sglebius 195290000Sglebius rl = emalloc_zero(count * cb); 196290000Sglebius /* link all but the first onto free list */ 197290000Sglebius res = (void *)((char *)rl + (count - 1) * cb); 198290000Sglebius for (i = count - 1; i > 0; i--) { 199290000Sglebius LINK_SLIST(resfree6, res, link); 200290000Sglebius res = (void *)((char *)res - cb); 201290000Sglebius } 202290000Sglebius INSIST(rl == res); 203290000Sglebius /* allocate the first */ 204290000Sglebius return res; 20554359Sroberto} 20654359Sroberto 20754359Sroberto 208290000Sglebiusstatic void 209290000Sglebiusfree_res( 210290000Sglebius restrict_u * res, 211290000Sglebius int v6 212290000Sglebius ) 213290000Sglebius{ 214290000Sglebius restrict_u ** plisthead; 215290000Sglebius restrict_u * unlinked; 216290000Sglebius 217290000Sglebius restrictcount--; 218290000Sglebius if (RES_LIMITED & res->flags) 219290000Sglebius dec_res_limited(); 220290000Sglebius 221290000Sglebius if (v6) 222290000Sglebius plisthead = &restrictlist6; 223290000Sglebius else 224290000Sglebius plisthead = &restrictlist4; 225290000Sglebius UNLINK_SLIST(unlinked, *plisthead, res, link, restrict_u); 226290000Sglebius INSIST(unlinked == res); 227290000Sglebius 228290000Sglebius if (v6) { 229290000Sglebius zero_mem(res, V6_SIZEOF_RESTRICT_U); 230290000Sglebius plisthead = &resfree6; 231290000Sglebius } else { 232290000Sglebius zero_mem(res, V4_SIZEOF_RESTRICT_U); 233290000Sglebius plisthead = &resfree4; 234290000Sglebius } 235290000Sglebius LINK_SLIST(*plisthead, res, link); 236290000Sglebius} 237290000Sglebius 238290000Sglebius 239290000Sglebiusstatic void 240290000Sglebiusinc_res_limited(void) 241290000Sglebius{ 242290000Sglebius if (!res_limited_refcnt) 243290000Sglebius mon_start(MON_RES); 244290000Sglebius res_limited_refcnt++; 245290000Sglebius} 246290000Sglebius 247290000Sglebius 248290000Sglebiusstatic void 249290000Sglebiusdec_res_limited(void) 250290000Sglebius{ 251290000Sglebius res_limited_refcnt--; 252290000Sglebius if (!res_limited_refcnt) 253290000Sglebius mon_stop(MON_RES); 254290000Sglebius} 255290000Sglebius 256290000Sglebius 257290000Sglebiusstatic restrict_u * 258290000Sglebiusmatch_restrict4_addr( 259290000Sglebius u_int32 addr, 260290000Sglebius u_short port 261290000Sglebius ) 262290000Sglebius{ 263290000Sglebius const int v6 = 0; 264290000Sglebius restrict_u * res; 265290000Sglebius restrict_u * next; 266290000Sglebius 267290000Sglebius for (res = restrictlist4; res != NULL; res = next) { 268290000Sglebius next = res->link; 269290000Sglebius if (res->expire && 270290000Sglebius res->expire <= current_time) 271290000Sglebius free_res(res, v6); 272290000Sglebius if (res->u.v4.addr == (addr & res->u.v4.mask) 273290000Sglebius && (!(RESM_NTPONLY & res->mflags) 274290000Sglebius || NTP_PORT == port)) 275290000Sglebius break; 276290000Sglebius } 277290000Sglebius return res; 278290000Sglebius} 279290000Sglebius 280290000Sglebius 281290000Sglebiusstatic restrict_u * 282290000Sglebiusmatch_restrict6_addr( 283290000Sglebius const struct in6_addr * addr, 284290000Sglebius u_short port 285290000Sglebius ) 286290000Sglebius{ 287290000Sglebius const int v6 = 1; 288290000Sglebius restrict_u * res; 289290000Sglebius restrict_u * next; 290290000Sglebius struct in6_addr masked; 291290000Sglebius 292290000Sglebius for (res = restrictlist6; res != NULL; res = next) { 293290000Sglebius next = res->link; 294290000Sglebius INSIST(next != res); 295290000Sglebius if (res->expire && 296290000Sglebius res->expire <= current_time) 297290000Sglebius free_res(res, v6); 298290000Sglebius MASK_IPV6_ADDR(&masked, addr, &res->u.v6.mask); 299290000Sglebius if (ADDR6_EQ(&masked, &res->u.v6.addr) 300290000Sglebius && (!(RESM_NTPONLY & res->mflags) 301290000Sglebius || NTP_PORT == (int)port)) 302290000Sglebius break; 303290000Sglebius } 304290000Sglebius return res; 305290000Sglebius} 306290000Sglebius 307290000Sglebius 30854359Sroberto/* 309290000Sglebius * match_restrict_entry - find an exact match on a restrict list. 310290000Sglebius * 311290000Sglebius * Exact match is addr, mask, and mflags all equal. 312290000Sglebius * In order to use more common code for IPv4 and IPv6, this routine 313290000Sglebius * requires the caller to populate a restrict_u with mflags and either 314290000Sglebius * the v4 or v6 address and mask as appropriate. Other fields in the 315290000Sglebius * input restrict_u are ignored. 316290000Sglebius */ 317290000Sglebiusstatic restrict_u * 318290000Sglebiusmatch_restrict_entry( 319290000Sglebius const restrict_u * pmatch, 320290000Sglebius int v6 321290000Sglebius ) 322290000Sglebius{ 323290000Sglebius restrict_u *res; 324290000Sglebius restrict_u *rlist; 325290000Sglebius size_t cb; 326290000Sglebius 327290000Sglebius if (v6) { 328290000Sglebius rlist = restrictlist6; 329290000Sglebius cb = sizeof(pmatch->u.v6); 330290000Sglebius } else { 331290000Sglebius rlist = restrictlist4; 332290000Sglebius cb = sizeof(pmatch->u.v4); 333290000Sglebius } 334290000Sglebius 335290000Sglebius for (res = rlist; res != NULL; res = res->link) 336290000Sglebius if (res->mflags == pmatch->mflags && 337290000Sglebius !memcmp(&res->u, &pmatch->u, cb)) 338290000Sglebius break; 339290000Sglebius return res; 340290000Sglebius} 341290000Sglebius 342290000Sglebius 343290000Sglebius/* 344290000Sglebius * res_sorts_before4 - compare two restrict4 entries 345290000Sglebius * 346290000Sglebius * Returns nonzero if r1 sorts before r2. We sort by descending 347290000Sglebius * address, then descending mask, then descending mflags, so sorting 348290000Sglebius * before means having a higher value. 349290000Sglebius */ 350290000Sglebiusstatic int 351290000Sglebiusres_sorts_before4( 352290000Sglebius restrict_u *r1, 353290000Sglebius restrict_u *r2 354290000Sglebius ) 355290000Sglebius{ 356290000Sglebius int r1_before_r2; 357290000Sglebius 358290000Sglebius if (r1->u.v4.addr > r2->u.v4.addr) 359290000Sglebius r1_before_r2 = 1; 360290000Sglebius else if (r1->u.v4.addr < r2->u.v4.addr) 361290000Sglebius r1_before_r2 = 0; 362290000Sglebius else if (r1->u.v4.mask > r2->u.v4.mask) 363290000Sglebius r1_before_r2 = 1; 364290000Sglebius else if (r1->u.v4.mask < r2->u.v4.mask) 365290000Sglebius r1_before_r2 = 0; 366290000Sglebius else if (r1->mflags > r2->mflags) 367290000Sglebius r1_before_r2 = 1; 368290000Sglebius else 369290000Sglebius r1_before_r2 = 0; 370290000Sglebius 371290000Sglebius return r1_before_r2; 372290000Sglebius} 373290000Sglebius 374290000Sglebius 375290000Sglebius/* 376290000Sglebius * res_sorts_before6 - compare two restrict6 entries 377290000Sglebius * 378290000Sglebius * Returns nonzero if r1 sorts before r2. We sort by descending 379290000Sglebius * address, then descending mask, then descending mflags, so sorting 380290000Sglebius * before means having a higher value. 381290000Sglebius */ 382290000Sglebiusstatic int 383290000Sglebiusres_sorts_before6( 384290000Sglebius restrict_u *r1, 385290000Sglebius restrict_u *r2 386290000Sglebius ) 387290000Sglebius{ 388290000Sglebius int r1_before_r2; 389290000Sglebius int cmp; 390290000Sglebius 391290000Sglebius cmp = ADDR6_CMP(&r1->u.v6.addr, &r2->u.v6.addr); 392290000Sglebius if (cmp > 0) /* r1->addr > r2->addr */ 393290000Sglebius r1_before_r2 = 1; 394290000Sglebius else if (cmp < 0) /* r2->addr > r1->addr */ 395290000Sglebius r1_before_r2 = 0; 396290000Sglebius else { 397290000Sglebius cmp = ADDR6_CMP(&r1->u.v6.mask, &r2->u.v6.mask); 398290000Sglebius if (cmp > 0) /* r1->mask > r2->mask*/ 399290000Sglebius r1_before_r2 = 1; 400290000Sglebius else if (cmp < 0) /* r2->mask > r1->mask */ 401290000Sglebius r1_before_r2 = 0; 402290000Sglebius else if (r1->mflags > r2->mflags) 403290000Sglebius r1_before_r2 = 1; 404290000Sglebius else 405290000Sglebius r1_before_r2 = 0; 406290000Sglebius } 407290000Sglebius 408290000Sglebius return r1_before_r2; 409290000Sglebius} 410290000Sglebius 411290000Sglebius 412290000Sglebius/* 41354359Sroberto * restrictions - return restrictions for this host 41454359Sroberto */ 415290000Sglebiusu_short 41654359Srobertorestrictions( 417290000Sglebius sockaddr_u *srcadr 41854359Sroberto ) 41954359Sroberto{ 420290000Sglebius restrict_u *match; 421290000Sglebius struct in6_addr *pin6; 422290000Sglebius u_short flags; 42354359Sroberto 42454359Sroberto res_calls++; 425290000Sglebius flags = 0; 426290000Sglebius /* IPv4 source address */ 427290000Sglebius if (IS_IPV4(srcadr)) { 428132451Sroberto /* 429132451Sroberto * Ignore any packets with a multicast source address 430132451Sroberto * (this should be done early in the receive process, 431290000Sglebius * not later!) 432132451Sroberto */ 433132451Sroberto if (IN_CLASSD(SRCADR(srcadr))) 434132451Sroberto return (int)RES_IGNORE; 43554359Sroberto 436290000Sglebius match = match_restrict4_addr(SRCADR(srcadr), 437290000Sglebius SRCPORT(srcadr)); 438290000Sglebius 439290000Sglebius INSIST(match != NULL); 440290000Sglebius 441290000Sglebius match->count++; 442132451Sroberto /* 443290000Sglebius * res_not_found counts only use of the final default 444290000Sglebius * entry, not any "restrict default ntpport ...", which 445290000Sglebius * would be just before the final default. 446132451Sroberto */ 447290000Sglebius if (&restrict_def4 == match) 448132451Sroberto res_not_found++; 449132451Sroberto else 450132451Sroberto res_found++; 451132451Sroberto flags = match->flags; 452132451Sroberto } 45354359Sroberto 454132451Sroberto /* IPv6 source address */ 455290000Sglebius if (IS_IPV6(srcadr)) { 456290000Sglebius pin6 = PSOCK_ADDR6(srcadr); 45754359Sroberto 458132451Sroberto /* 459132451Sroberto * Ignore any packets with a multicast source address 460132451Sroberto * (this should be done early in the receive process, 461290000Sglebius * not later!) 462132451Sroberto */ 463290000Sglebius if (IN6_IS_ADDR_MULTICAST(pin6)) 464132451Sroberto return (int)RES_IGNORE; 46554359Sroberto 466290000Sglebius match = match_restrict6_addr(pin6, SRCPORT(srcadr)); 467290000Sglebius INSIST(match != NULL); 468290000Sglebius match->count++; 469290000Sglebius if (&restrict_def6 == match) 470132451Sroberto res_not_found++; 471132451Sroberto else 472132451Sroberto res_found++; 473290000Sglebius flags = match->flags; 47454359Sroberto } 475132451Sroberto return (flags); 47654359Sroberto} 47754359Sroberto 47854359Sroberto 47954359Sroberto/* 48054359Sroberto * hack_restrict - add/subtract/manipulate entries on the restrict list 48154359Sroberto */ 48254359Srobertovoid 48354359Srobertohack_restrict( 484290000Sglebius int op, 485290000Sglebius sockaddr_u * resaddr, 486290000Sglebius sockaddr_u * resmask, 487290000Sglebius u_short mflags, 488290000Sglebius u_short flags, 489290000Sglebius u_long expire 49054359Sroberto ) 49154359Sroberto{ 492290000Sglebius int v6; 493290000Sglebius restrict_u match; 494290000Sglebius restrict_u * res; 495290000Sglebius restrict_u ** plisthead; 49654359Sroberto 497290000Sglebius DPRINTF(1, ("restrict: op %d addr %s mask %s mflags %08x flags %08x\n", 498290000Sglebius op, stoa(resaddr), stoa(resmask), mflags, flags)); 499290000Sglebius 500290000Sglebius if (NULL == resaddr) { 501290000Sglebius REQUIRE(NULL == resmask); 502290000Sglebius REQUIRE(RESTRICT_FLAGS == op); 503290000Sglebius restrict_source_flags = flags; 504290000Sglebius restrict_source_mflags = mflags; 505290000Sglebius restrict_source_enabled = 1; 506290000Sglebius return; 507290000Sglebius } 508290000Sglebius 509290000Sglebius ZERO(match); 510290000Sglebius 511290000Sglebius#if 0 512290000Sglebius /* silence VC9 potentially uninit warnings */ 513290000Sglebius // HMS: let's use a compiler-specific "enable" for this. 514290000Sglebius res = NULL; 515290000Sglebius v6 = 0; 516290000Sglebius#endif 517290000Sglebius 518290000Sglebius if (IS_IPV4(resaddr)) { 519290000Sglebius v6 = 0; 520132451Sroberto /* 521290000Sglebius * Get address and mask in host byte order for easy 522290000Sglebius * comparison as u_int32 523132451Sroberto */ 524290000Sglebius match.u.v4.addr = SRCADR(resaddr); 525290000Sglebius match.u.v4.mask = SRCADR(resmask); 526290000Sglebius match.u.v4.addr &= match.u.v4.mask; 52754359Sroberto 528290000Sglebius } else if (IS_IPV6(resaddr)) { 529290000Sglebius v6 = 1; 530132451Sroberto /* 531290000Sglebius * Get address and mask in network byte order for easy 532290000Sglebius * comparison as byte sequences (e.g. memcmp()) 533132451Sroberto */ 534290000Sglebius match.u.v6.mask = SOCK_ADDR6(resmask); 535290000Sglebius MASK_IPV6_ADDR(&match.u.v6.addr, PSOCK_ADDR6(resaddr), 536290000Sglebius &match.u.v6.mask); 537132451Sroberto 538290000Sglebius } else /* not IPv4 nor IPv6 */ 539290000Sglebius REQUIRE(0); 540132451Sroberto 541290000Sglebius match.flags = flags; 542290000Sglebius match.mflags = mflags; 543290000Sglebius match.expire = expire; 544290000Sglebius res = match_restrict_entry(&match, v6); 545132451Sroberto 546290000Sglebius switch (op) { 547290000Sglebius 548290000Sglebius case RESTRICT_FLAGS: 549290000Sglebius /* 550290000Sglebius * Here we add bits to the flags. If this is a 551290000Sglebius * new restriction add it. 552290000Sglebius */ 553290000Sglebius if (NULL == res) { 554290000Sglebius if (v6) { 555290000Sglebius res = alloc_res6(); 556290000Sglebius memcpy(res, &match, 557290000Sglebius V6_SIZEOF_RESTRICT_U); 558290000Sglebius plisthead = &restrictlist6; 559290000Sglebius } else { 560290000Sglebius res = alloc_res4(); 561290000Sglebius memcpy(res, &match, 562290000Sglebius V4_SIZEOF_RESTRICT_U); 563290000Sglebius plisthead = &restrictlist4; 56454359Sroberto } 565290000Sglebius LINK_SORT_SLIST( 566290000Sglebius *plisthead, res, 567290000Sglebius (v6) 568290000Sglebius ? res_sorts_before6(res, L_S_S_CUR()) 569290000Sglebius : res_sorts_before4(res, L_S_S_CUR()), 570290000Sglebius link, restrict_u); 571290000Sglebius restrictcount++; 572290000Sglebius if (RES_LIMITED & flags) 573290000Sglebius inc_res_limited(); 574290000Sglebius } else { 575290000Sglebius if ((RES_LIMITED & flags) && 576290000Sglebius !(RES_LIMITED & res->flags)) 577290000Sglebius inc_res_limited(); 578290000Sglebius res->flags |= flags; 57954359Sroberto } 580290000Sglebius break; 581132451Sroberto 582290000Sglebius case RESTRICT_UNFLAG: 583290000Sglebius /* 584290000Sglebius * Remove some bits from the flags. If we didn't 585290000Sglebius * find this one, just return. 586290000Sglebius */ 587290000Sglebius if (res != NULL) { 588290000Sglebius if ((RES_LIMITED & res->flags) 589290000Sglebius && (RES_LIMITED & flags)) 590290000Sglebius dec_res_limited(); 591290000Sglebius res->flags &= ~flags; 592290000Sglebius } 593290000Sglebius break; 59454359Sroberto 595290000Sglebius case RESTRICT_REMOVE: 596290000Sglebius case RESTRICT_REMOVEIF: 597290000Sglebius /* 598290000Sglebius * Remove an entry from the table entirely if we 599290000Sglebius * found one. Don't remove the default entry and 600290000Sglebius * don't remove an interface entry. 601290000Sglebius */ 602290000Sglebius if (res != NULL 603290000Sglebius && (RESTRICT_REMOVEIF == op 604290000Sglebius || !(RESM_INTERFACE & res->mflags)) 605290000Sglebius && res != &restrict_def4 606290000Sglebius && res != &restrict_def6) 607290000Sglebius free_res(res, v6); 608290000Sglebius break; 60954359Sroberto 610290000Sglebius default: /* unknown op */ 611290000Sglebius INSIST(0); 612290000Sglebius break; 613290000Sglebius } 61454359Sroberto 615290000Sglebius} 61654359Sroberto 617132451Sroberto 618290000Sglebius/* 619290000Sglebius * restrict_source - maintains dynamic "restrict source ..." entries as 620290000Sglebius * peers come and go. 621290000Sglebius */ 622290000Sglebiusvoid 623290000Sglebiusrestrict_source( 624290000Sglebius sockaddr_u * addr, 625290000Sglebius int farewell, /* 0 to add, 1 to remove */ 626290000Sglebius u_long expire /* 0 is infinite, valid until */ 627290000Sglebius ) 628290000Sglebius{ 629290000Sglebius sockaddr_u onesmask; 630290000Sglebius restrict_u * res; 631290000Sglebius int found_specific; 632132451Sroberto 633290000Sglebius if (!restrict_source_enabled || SOCK_UNSPEC(addr) || 634290000Sglebius IS_MCAST(addr) || ISREFCLOCKADR(addr)) 635290000Sglebius return; 636132451Sroberto 637290000Sglebius REQUIRE(AF_INET == AF(addr) || AF_INET6 == AF(addr)); 638132451Sroberto 639290000Sglebius SET_HOSTMASK(&onesmask, AF(addr)); 640290000Sglebius if (farewell) { 641290000Sglebius hack_restrict(RESTRICT_REMOVE, addr, &onesmask, 642290000Sglebius 0, 0, 0); 643290000Sglebius DPRINTF(1, ("restrict_source: %s removed", stoa(addr))); 644290000Sglebius return; 645290000Sglebius } 64654359Sroberto 647290000Sglebius /* 648290000Sglebius * If there is a specific entry for this address, hands 649290000Sglebius * off, as it is condidered more specific than "restrict 650290000Sglebius * server ...". 651290000Sglebius * However, if the specific entry found is a fleeting one 652290000Sglebius * added by pool_xmit() before soliciting, replace it 653290000Sglebius * immediately regardless of the expire value to make way 654290000Sglebius * for the more persistent entry. 655290000Sglebius */ 656290000Sglebius if (IS_IPV4(addr)) { 657290000Sglebius res = match_restrict4_addr(SRCADR(addr), SRCPORT(addr)); 658290000Sglebius INSIST(res != NULL); 659290000Sglebius found_specific = (SRCADR(&onesmask) == res->u.v4.mask); 660290000Sglebius } else { 661290000Sglebius res = match_restrict6_addr(&SOCK_ADDR6(addr), 662290000Sglebius SRCPORT(addr)); 663290000Sglebius INSIST(res != NULL); 664290000Sglebius found_specific = ADDR6_EQ(&res->u.v6.mask, 665290000Sglebius &SOCK_ADDR6(&onesmask)); 666290000Sglebius } 667290000Sglebius if (!expire && found_specific && res->expire) { 668290000Sglebius found_specific = 0; 669290000Sglebius free_res(res, IS_IPV6(addr)); 670290000Sglebius } 671290000Sglebius if (found_specific) 672290000Sglebius return; 673132451Sroberto 674290000Sglebius hack_restrict(RESTRICT_FLAGS, addr, &onesmask, 675290000Sglebius restrict_source_mflags, restrict_source_flags, 676290000Sglebius expire); 677290000Sglebius DPRINTF(1, ("restrict_source: %s host restriction added\n", 678290000Sglebius stoa(addr))); 67954359Sroberto} 680