/** Examine the query and return hash and source of netblock. */ static void examine_query(query_type* query, uint32_t* hash, uint64_t* source, uint16_t* flags, uint32_t* lm) { /* compile a binary string representing the query */ uint16_t c, c2; /* size with 16 bytes to spare */ uint8_t buf[MAXDOMAINLEN + sizeof(*source) + sizeof(c) + 16]; const uint8_t* dname = NULL; size_t dname_len; uint32_t r = 0x267fcd16; *source = rrl_get_source(query, &c2); c = rrl_classify(query, &dname, &dname_len); if(query->zone && query->zone->opts && (query->zone->opts->rrl_whitelist & c)) *lm = rrl_whitelist_ratelimit; if(*lm == 0) return; c |= c2; *flags = c; memmove(buf, source, sizeof(*source)); memmove(buf+sizeof(*source), &c, sizeof(c)); DEBUG(DEBUG_QUERY, 1, (LOG_INFO, "rrl_examine type %s name %s", rrltype2str(c), dname?wiredname2str(dname):"NULL")); /* and hash it */ if(dname && dname_len <= MAXDOMAINLEN) { memmove(buf+sizeof(*source)+sizeof(c), dname, dname_len); *hash = hashlittle(buf, sizeof(*source)+sizeof(c)+dname_len, r); } else *hash = hashlittle(buf, sizeof(*source)+sizeof(c), r); }
/** log a message about ratelimits */ static void rrl_msg(query_type* query, const char* str) { uint16_t c, c2, wl = 0; const uint8_t* d = NULL; size_t d_len; uint64_t s; if(verbosity < 2) return; s = rrl_get_source(query, &c2); c = rrl_classify(query, &d, &d_len) | c2; if(query->zone && query->zone->opts && (query->zone->opts->rrl_whitelist & c)) wl = 1; log_msg(LOG_INFO, "ratelimit %s %s type %s%s target %s", str, d?wiredname2str(d):"", rrltype2str(c), wl?"(whitelisted)":"", rrlsource2str(s, c2)); }
rrl_item_t* rrl_hash(rrl_table_t *t, const struct sockaddr_storage *a, rrl_req_t *p, const zone_t *zone, uint32_t stamp, int *lock) { char buf[RRL_CLSBLK_MAXLEN]; int len = rrl_classify(buf, sizeof(buf), a, p, zone, t->seed); if (len < 0) { return NULL; } uint32_t id = hash(buf, len) % t->size; /* Lock for lookup. */ pthread_mutex_lock(&t->ll); /* Find an exact match in <id, id + HOP_LEN). */ uint16_t *qname = (uint16_t*)(buf + sizeof(uint8_t) + sizeof(uint64_t)); rrl_item_t match = { 0, *((uint64_t*)(buf + 1)), t->rate, /* hop, netblk, ntok */ buf[0], RRL_BF_NULL, /* cls, flags */ hash((char*)(qname + 1), *qname), stamp /* qname, time*/ }; unsigned d = find_match(t, id, &match); if (d > HOP_LEN) { /* not an exact match, find free element [f] */ d = find_free(t, id, stamp); } /* Reduce distance to fit <id, id + HOP_LEN) */ unsigned f = (id + d) % t->size; while (d >= HOP_LEN) { d = reduce_dist(t, id, d, &f); } /* Assign granular lock and unlock lookup. */ *lock = f % t->lk_count; rrl_lock(t, *lock); pthread_mutex_unlock(&t->ll); /* found free elm 'k' which is in <id, id + HOP_LEN) */ t->arr[id].hop |= (1 << d); rrl_item_t* b = t->arr + f; assert(f == (id+d) % t->size); dbg_rrl("%s: classified pkt as %4x '%u+%u' bucket=%p \n", __func__, f, id, d, b); /* Inspect bucket state. */ unsigned hop = b->hop; if (b->cls == CLS_NULL) { memcpy(b, &match, sizeof(rrl_item_t)); b->hop = hop; } /* Check for collisions. */ if (!bucket_match(b, &match)) { dbg_rrl("%s: collision in bucket '%4x'\n", __func__, id); if (!(b->flags & RRL_BF_SSTART)) { memcpy(b, &match, sizeof(rrl_item_t)); b->hop = hop; b->ntok = t->rate + t->rate / RRL_SSTART; b->flags |= RRL_BF_SSTART; dbg_rrl("%s: bucket '%4x' slow-start\n", __func__, id); } } return b; }