]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- Update ratelimit code for recent serviced_query changes and more
authorGeorge Thessalonikefs <george@nlnetlabs.nl>
Sat, 29 Jan 2022 22:49:38 +0000 (23:49 +0100)
committerGeorge Thessalonikefs <george@nlnetlabs.nl>
Sat, 29 Jan 2022 22:49:38 +0000 (23:49 +0100)
  accurate ratelimit calculation.

17 files changed:
daemon/worker.c
dnstap/unbound-dnstap-socket.c
doc/unbound.conf.5.in
iterator/iterator.c
iterator/iterator.h
libunbound/libworker.c
libunbound/worker.h
pythonmod/interface.i
services/cache/infra.c
services/cache/infra.h
services/outside_network.c
services/outside_network.h
smallapp/worker_cb.c
testcode/fake_event.c
util/fptr_wlist.c
util/fptr_wlist.h
util/module.h

index 5d2483cd2cd94060eab85a66c8c76049bf260174..a6b161f7a3f1d442829c8606f69bf47735aaa88d 100644 (file)
@@ -1967,9 +1967,10 @@ worker_delete(struct worker* worker)
 
 struct outbound_entry*
 worker_send_query(struct query_info* qinfo, uint16_t flags, int dnssec,
-       int want_dnssec, int nocaps, struct sockaddr_storage* addr,
-       socklen_t addrlen, uint8_t* zone, size_t zonelen, int tcp_upstream,
-       int ssl_upstream, char* tls_auth_name, struct module_qstate* q)
+       int want_dnssec, int nocaps, int check_ratelimit,
+       struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
+       size_t zonelen, int tcp_upstream, int ssl_upstream, char* tls_auth_name,
+       struct module_qstate* q, int* was_ratelimited)
 {
        struct worker* worker = q->env->worker;
        struct outbound_entry* e = (struct outbound_entry*)regional_alloc(
@@ -1978,9 +1979,10 @@ worker_send_query(struct query_info* qinfo, uint16_t flags, int dnssec,
                return NULL;
        e->qstate = q;
        e->qsent = outnet_serviced_query(worker->back, qinfo, flags, dnssec,
-               want_dnssec, nocaps, tcp_upstream,
+               want_dnssec, nocaps, check_ratelimit, tcp_upstream,
                ssl_upstream, tls_auth_name, addr, addrlen, zone, zonelen, q,
-               worker_handle_service_reply, e, worker->back->udp_buff, q->env);
+               worker_handle_service_reply, e, worker->back->udp_buff, q->env,
+               was_ratelimited);
        if(!e->qsent) {
                return NULL;
        }
@@ -2024,10 +2026,11 @@ struct outbound_entry* libworker_send_query(
        struct query_info* ATTR_UNUSED(qinfo),
        uint16_t ATTR_UNUSED(flags), int ATTR_UNUSED(dnssec),
        int ATTR_UNUSED(want_dnssec), int ATTR_UNUSED(nocaps),
+       int ATTR_UNUSED(check_ratelimit),
        struct sockaddr_storage* ATTR_UNUSED(addr), socklen_t ATTR_UNUSED(addrlen),
        uint8_t* ATTR_UNUSED(zone), size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(tcp_upstream),
        int ATTR_UNUSED(ssl_upstream), char* ATTR_UNUSED(tls_auth_name),
-       struct module_qstate* ATTR_UNUSED(q))
+       struct module_qstate* ATTR_UNUSED(q), int* ATTR_UNUSED(was_ratelimited))
 {
        log_assert(0);
        return 0;
index 990b8a866af1212350e9b1a71a2489dbed68999e..63292fbca36edbfa9a74efd7813c8f3067c0205a 100644 (file)
@@ -1413,11 +1413,12 @@ void worker_sighandler(int ATTR_UNUSED(sig), void* ATTR_UNUSED(arg))
 struct outbound_entry* worker_send_query(
        struct query_info* ATTR_UNUSED(qinfo), uint16_t ATTR_UNUSED(flags),
        int ATTR_UNUSED(dnssec), int ATTR_UNUSED(want_dnssec),
-       int ATTR_UNUSED(nocaps), struct sockaddr_storage* ATTR_UNUSED(addr),
+       int ATTR_UNUSED(nocaps), int ATTR_UNUSED(check_ratelimit),
+       struct sockaddr_storage* ATTR_UNUSED(addr),
        socklen_t ATTR_UNUSED(addrlen), uint8_t* ATTR_UNUSED(zone),
        size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(tcp_upstream),
        int ATTR_UNUSED(ssl_upstream), char* ATTR_UNUSED(tls_auth_name),
-       struct module_qstate* ATTR_UNUSED(q))
+       struct module_qstate* ATTR_UNUSED(q), int* ATTR_UNUSED(was_ratelimited))
 {
        log_assert(0);
        return 0;
@@ -1446,11 +1447,12 @@ worker_alloc_cleanup(void* ATTR_UNUSED(arg))
 struct outbound_entry* libworker_send_query(
        struct query_info* ATTR_UNUSED(qinfo), uint16_t ATTR_UNUSED(flags),
        int ATTR_UNUSED(dnssec), int ATTR_UNUSED(want_dnssec),
-       int ATTR_UNUSED(nocaps), struct sockaddr_storage* ATTR_UNUSED(addr),
+       int ATTR_UNUSED(nocaps), int ATTR_UNUSED(check_ratelimit),
+       struct sockaddr_storage* ATTR_UNUSED(addr),
        socklen_t ATTR_UNUSED(addrlen), uint8_t* ATTR_UNUSED(zone),
        size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(tcp_upstream),
        int ATTR_UNUSED(ssl_upstream), char* ATTR_UNUSED(tls_auth_name),
-       struct module_qstate* ATTR_UNUSED(q))
+       struct module_qstate* ATTR_UNUSED(q), int* ATTR_UNUSED(was_ratelimited))
 {
        log_assert(0);
        return 0;
index c0878dad1defd23e0c92480f269ecdb4c5b11bfd..a4d7ca3bcb75e39cccd4f575a72d1a575ae8cd34 100644 (file)
@@ -1648,7 +1648,8 @@ ratelimited by this setting.  The zone of the query is determined by examining
 the nameservers for it, the zone name is used to keep track of the rate.
 For example, 1000 may be a suitable value to stop the server from being
 overloaded with random names, and keeps Unbound from sending traffic to the
-nameservers for those zones.
+nameservers for those zones.  Configured forwarders are excluded from
+ratelimiting.
 .TP 5
 .B ratelimit\-size: \fI<memory size>
 Give the size of the data structure in which the current ongoing rates are
index 69e7e53dd074fc93d2c301f6d42045b66d8e0bb1..81120c6b66d101c0d182747862205db3bc49c2d9 100644 (file)
@@ -1533,36 +1533,6 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
                if(!iq->ratelimit_ok && qstate->prefetch_leeway)
                        iq->ratelimit_ok = 1; /* allow prefetches, this keeps
                        otherwise valid data in the cache */
-               if(!iq->ratelimit_ok && infra_ratelimit_exceeded(
-                       qstate->env->infra_cache, iq->dp->name,
-                       iq->dp->namelen, *qstate->env->now)) {
-                       /* and increment the rate, so that the rate for time
-                        * now will also exceed the rate, keeping cache fresh */
-                       (void)infra_ratelimit_inc(qstate->env->infra_cache,
-                               iq->dp->name, iq->dp->namelen,
-                               *qstate->env->now, &qstate->qinfo,
-                               qstate->reply);
-                       /* see if we are passed through with slip factor */
-                       if(qstate->env->cfg->ratelimit_factor != 0 &&
-                               ub_random_max(qstate->env->rnd,
-                                   qstate->env->cfg->ratelimit_factor) == 1) {
-                               iq->ratelimit_ok = 1;
-                               log_nametypeclass(VERB_ALGO, "ratelimit allowed through for "
-                                       "delegation point", iq->dp->name,
-                                       LDNS_RR_TYPE_NS, LDNS_RR_CLASS_IN);
-                       } else {
-                               lock_basic_lock(&ie->queries_ratelimit_lock);
-                               ie->num_queries_ratelimited++;
-                               lock_basic_unlock(&ie->queries_ratelimit_lock);
-                               log_nametypeclass(VERB_ALGO, "ratelimit exceeded with "
-                                       "delegation point", iq->dp->name,
-                                       LDNS_RR_TYPE_NS, LDNS_RR_CLASS_IN);
-                               qstate->was_ratelimited = 1;
-                               errinf(qstate, "query was ratelimited");
-                               errinf_dname(qstate, "for zone", iq->dp->name);
-                               return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
-                       }
-               }
 
                /* see if this dp not useless.
                 * It is useless if:
@@ -2211,9 +2181,11 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
        int auth_fallback = 0;
        uint8_t* qout_orig = NULL;
        size_t qout_orig_len = 0;
+       int sq_check_ratelimit = 1;
+       int sq_was_ratelimited = 0;
 
-       /* NOTE: a request will encounter this state for each target it 
-        * needs to send a query to. That is, at least one per referral, 
+       /* NOTE: a request will encounter this state for each target it
+        * needs to send a query to. That is, at least one per referral,
         * more if some targets timeout or return throwaway answers. */
 
        log_query_info(VERB_QUERY, "processQueryTargets:", &qstate->qinfo);
@@ -2646,22 +2618,9 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
                return 0;
        }
 
-       /* if not forwarding, check ratelimits per delegationpoint name */
-       if(!(iq->chase_flags & BIT_RD) && !iq->ratelimit_ok) {
-               if(!infra_ratelimit_inc(qstate->env->infra_cache, iq->dp->name,
-                       iq->dp->namelen, *qstate->env->now, &qstate->qinfo,
-                       qstate->reply)) {
-                       lock_basic_lock(&ie->queries_ratelimit_lock);
-                       ie->num_queries_ratelimited++;
-                       lock_basic_unlock(&ie->queries_ratelimit_lock);
-                       verbose(VERB_ALGO, "query exceeded ratelimits");
-                       qstate->was_ratelimited = 1;
-                       errinf_dname(qstate, "exceeded ratelimit for zone",
-                               iq->dp->name);
-                       return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
-               }
-       }
-
+       /* Do not check ratelimit for forwarding queries or if we already got a
+        * pass. */
+       sq_check_ratelimit = (!(iq->chase_flags & BIT_RD) && !iq->ratelimit_ok);
        /* We have a valid target. */
        if(verbosity >= VERB_QUERY) {
                log_query_info(VERB_QUERY, "sending query:", &iq->qinfo_out);
@@ -2673,25 +2632,32 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
        }
        fptr_ok(fptr_whitelist_modenv_send_query(qstate->env->send_query));
        outq = (*qstate->env->send_query)(&iq->qinfo_out,
-               iq->chase_flags | (iq->chase_to_rd?BIT_RD:0), 
+               iq->chase_flags | (iq->chase_to_rd?BIT_RD:0),
                /* unset CD if to forwarder(RD set) and not dnssec retry
                 * (blacklist nonempty) and no trust-anchors are configured
                 * above the qname or on the first attempt when dnssec is on */
                EDNS_DO| ((iq->chase_to_rd||(iq->chase_flags&BIT_RD)!=0)&&
                !qstate->blacklist&&(!iter_qname_indicates_dnssec(qstate->env,
-               &iq->qinfo_out)||target->attempts==1)?0:BIT_CD), 
+               &iq->qinfo_out)||target->attempts==1)?0:BIT_CD),
                iq->dnssec_expected, iq->caps_fallback || is_caps_whitelisted(
-               ie, iq), &target->addr, target->addrlen,
+               ie, iq), sq_check_ratelimit, &target->addr, target->addrlen,
                iq->dp->name, iq->dp->namelen,
                (iq->dp->tcp_upstream || qstate->env->cfg->tcp_upstream),
                (iq->dp->ssl_upstream || qstate->env->cfg->ssl_upstream),
-               target->tls_auth_name, qstate);
+               target->tls_auth_name, qstate, &sq_was_ratelimited);
        if(!outq) {
+               if(sq_was_ratelimited) {
+                       lock_basic_lock(&ie->queries_ratelimit_lock);
+                       ie->num_queries_ratelimited++;
+                       lock_basic_unlock(&ie->queries_ratelimit_lock);
+                       verbose(VERB_ALGO, "query exceeded ratelimits");
+                       qstate->was_ratelimited = 1;
+                       errinf_dname(qstate, "exceeded ratelimit for zone",
+                               iq->dp->name);
+                       return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+               }
                log_addr(VERB_QUERY, "error sending query to auth server",
                        &target->addr, target->addrlen);
-               if(!(iq->chase_flags & BIT_RD) && !iq->ratelimit_ok)
-                   infra_ratelimit_dec(qstate->env->infra_cache, iq->dp->name,
-                       iq->dp->namelen, *qstate->env->now);
                if(qstate->env->cfg->qname_minimisation)
                        iq->minimisation_state = SKIP_MINIMISE_STATE;
                return next_state(iq, QUERYTARGETS_STATE);
@@ -2935,14 +2901,6 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
                 * delegation point, and back to the QUERYTARGETS_STATE. */
                verbose(VERB_DETAIL, "query response was REFERRAL");
 
-               if(!(iq->chase_flags & BIT_RD) && !iq->ratelimit_ok) {
-                       /* we have a referral, no ratelimit, we can send
-                        * our queries to the given name */
-                       infra_ratelimit_dec(qstate->env->infra_cache,
-                               iq->dp->name, iq->dp->namelen,
-                               *qstate->env->now);
-               }
-
                /* if hardened, only store referral if we asked for it */
                if(!qstate->no_cache_store &&
                (!qstate->env->cfg->harden_referral_path ||
index f09506f2e0b09010bd8d009907a5b8ec8f02ba02..8b840528d9d979592177fbc37171d7afb9064eb7 100644 (file)
@@ -80,7 +80,7 @@ struct rbtree_type;
 /**
  * number of labels from QNAME that are always send individually when using
  * QNAME minimisation, even when the number of labels of the QNAME is bigger
- * tham MAX_MINIMISE_COUNT */
+ * than MAX_MINIMISE_COUNT */
 #define MINIMISE_ONE_LAB       4
 #define MINIMISE_MULTIPLE_LABS (MAX_MINIMISE_COUNT - MINIMISE_ONE_LAB)
 /** at what query-sent-count to stop target fetch policy */
index 7f753435d068292be7b5bb02c371ff15d8dff2a7..ab28dd54f94258cdea8b886012b360ab3a428173 100644 (file)
@@ -882,9 +882,10 @@ void libworker_alloc_cleanup(void* arg)
 
 struct outbound_entry* libworker_send_query(struct query_info* qinfo,
        uint16_t flags, int dnssec, int want_dnssec, int nocaps,
+       int check_ratelimit,
        struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
        size_t zonelen, int tcp_upstream, int ssl_upstream, char* tls_auth_name,
-       struct module_qstate* q)
+       struct module_qstate* q, int* was_ratelimited)
 {
        struct libworker* w = (struct libworker*)q->env->worker;
        struct outbound_entry* e = (struct outbound_entry*)regional_alloc(
@@ -893,9 +894,10 @@ struct outbound_entry* libworker_send_query(struct query_info* qinfo,
                return NULL;
        e->qstate = q;
        e->qsent = outnet_serviced_query(w->back, qinfo, flags, dnssec,
-               want_dnssec, nocaps, tcp_upstream, ssl_upstream,
+               want_dnssec, nocaps, check_ratelimit, tcp_upstream, ssl_upstream,
                tls_auth_name, addr, addrlen, zone, zonelen, q,
-               libworker_handle_service_reply, e, w->back->udp_buff, q->env);
+               libworker_handle_service_reply, e, w->back->udp_buff, q->env,
+               was_ratelimited);
        if(!e->qsent) {
                return NULL;
        }
@@ -976,10 +978,11 @@ void worker_sighandler(int ATTR_UNUSED(sig), void* ATTR_UNUSED(arg))
 struct outbound_entry* worker_send_query(struct query_info* ATTR_UNUSED(qinfo),
        uint16_t ATTR_UNUSED(flags), int ATTR_UNUSED(dnssec),
        int ATTR_UNUSED(want_dnssec), int ATTR_UNUSED(nocaps),
+       int ATTR_UNUSED(check_ratelimit),
        struct sockaddr_storage* ATTR_UNUSED(addr), socklen_t ATTR_UNUSED(addrlen),
        uint8_t* ATTR_UNUSED(zone), size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(tcp_upstream),
        int ATTR_UNUSED(ssl_upstream), char* ATTR_UNUSED(tls_auth_name),
-       struct module_qstate* ATTR_UNUSED(q))
+       struct module_qstate* ATTR_UNUSED(q), int* ATTR_UNUSED(was_ratelimited))
 {
        log_assert(0);
        return 0;
index 974b66a30e849aea512e8e5152587a2b64145f36..0fa5bfa99430528c50393bdd5a67ba01e1b0614c 100644 (file)
@@ -58,6 +58,7 @@ struct query_info;
  * @param dnssec: if set, EDNS record will have DO bit set.
  * @param want_dnssec: signatures needed.
  * @param nocaps: ignore capsforid(if in config), do not perturb qname.
+ * @param check_ratelimit: if set, will check ratelimit before sending out.
  * @param addr: where to.
  * @param addrlen: length of addr.
  * @param zone: delegation point name.
@@ -67,14 +68,17 @@ struct query_info;
  * @param tls_auth_name: if ssl_upstream, use this name with TLS
  *     authentication.
  * @param q: which query state to reactivate upon return.
+ * @param was_ratelimited: it will signal back if the query failed to pass the
+ *     ratelimit check.
  * @return: false on failure (memory or socket related). no query was
  *      sent.
  */
 struct outbound_entry* libworker_send_query(struct query_info* qinfo,
        uint16_t flags, int dnssec, int want_dnssec, int nocaps,
+       int check_ratelimit,
        struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
        size_t zonelen, int tcp_upstream, int ssl_upstream, char* tls_auth_name,
-       struct module_qstate* q);
+       struct module_qstate* q, int* was_ratelimited);
 
 /** process incoming serviced query replies from the network */
 int libworker_handle_service_reply(struct comm_point* c, void* arg, int error,
@@ -110,6 +114,7 @@ void worker_sighandler(int sig, void* arg);
  * @param dnssec: if set, EDNS record will have DO bit set.
  * @param want_dnssec: signatures needed.
  * @param nocaps: ignore capsforid(if in config), do not perturb qname.
+ * @param check_ratelimit: if set, will check ratelimit before sending out.
  * @param addr: where to.
  * @param addrlen: length of addr.
  * @param zone: wireformat dname of the zone.
@@ -119,14 +124,17 @@ void worker_sighandler(int sig, void* arg);
  * @param tls_auth_name: if ssl_upstream, use this name with TLS
  *     authentication.
  * @param q: which query state to reactivate upon return.
+ * @param was_ratelimited: it will signal back if the query failed to pass the
+ *     ratelimit check.
  * @return: false on failure (memory or socket related). no query was
  *      sent.
  */
 struct outbound_entry* worker_send_query(struct query_info* qinfo,
        uint16_t flags, int dnssec, int want_dnssec, int nocaps,
+       int check_ratelimit,
        struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
        size_t zonelen, int tcp_upstream, int ssl_upstream, char* tls_auth_name,
-       struct module_qstate* q);
+       struct module_qstate* q, int* was_ratelimited);
 
 /** 
  * process control messages from the main thread. Frees the control 
index 03483abdf82425cbea641ca76943d629343245b6..1ca8686a76f36946c54b8f1bb7304c055bd4685b 100644 (file)
@@ -712,9 +712,10 @@ struct module_env {
     /* --- services --- */
     struct outbound_entry* (*send_query)(struct query_info* qinfo,
         uint16_t flags, int dnssec, int want_dnssec, int nocaps,
+        int check_ratelimit,
         struct sockaddr_storage* addr, socklen_t addrlen,
         uint8_t* zone, size_t zonelen, int tcp_upstream, int ssl_upstream,
-        char* tls_auth_name, struct module_qstate* q);
+        char* tls_auth_name, struct module_qstate* q, int* was_ratelimited);
     void (*detach_subs)(struct module_qstate* qstate);
     int (*attach_sub)(struct module_qstate* qstate,
         struct query_info* qinfo, uint16_t qflags, int prime,
index 518e69622f830d01c3afcbac21f063aad049aa4d..01e1a7cdd481cafa5f0b6a5ad87956418f6140bb 100644 (file)
@@ -898,8 +898,9 @@ static void infra_ip_create_ratedata(struct infra_cache* infra,
        slabhash_insert(infra->client_ip_rates, h, &k->entry, d, NULL);
 }
 
-/** find the second and return its rate counter, if none, remove oldest */
-static int* infra_rate_find_second(void* data, time_t t)
+/** Find the second and return its rate counter. If none and should_add, remove
+ *  oldest to accommodate. Else return none. */
+static int* infra_rate_find_second_or_none(void* data, time_t t, int should_add)
 {
        struct rate_data* d = (struct rate_data*)data;
        int i, oldest;
@@ -907,6 +908,7 @@ static int* infra_rate_find_second(void* data, time_t t)
                if(d->timestamp[i] == t)
                        return &(d->qps[i]);
        }
+       if(!should_add) return NULL;
        /* remove oldest timestamp, and insert it at t with 0 qps */
        oldest = 0;
        for(i=0; i<RATE_WINDOW; i++) {
@@ -918,14 +920,27 @@ static int* infra_rate_find_second(void* data, time_t t)
        return &(d->qps[oldest]);
 }
 
+/** find the second and return its rate counter, if none, remove oldest to
+ *  accommodate */
+static int* infra_rate_give_second(void* data, time_t t)
+{
+    return infra_rate_find_second_or_none(data, t, 1);
+}
+
+/** find the second and return its rate counter only if it exists. Caller
+ *  should check for NULL return value */
+static int* infra_rate_get_second(void* data, time_t t)
+{
+    return infra_rate_find_second_or_none(data, t, 0);
+}
+
 int infra_rate_max(void* data, time_t now)
 {
        struct rate_data* d = (struct rate_data*)data;
        int i, max = 0;
        for(i=0; i<RATE_WINDOW; i++) {
-               if(now-d->timestamp[i] <= RATE_WINDOW) {
-                       if(d->qps[i] > max)
-                               max = d->qps[i];
+               if(now == d->timestamp[i]) {
+                       return d->qps[i];
                }
        }
        return max;
@@ -950,12 +965,12 @@ int infra_ratelimit_inc(struct infra_cache* infra, uint8_t* name,
        entry = infra_find_ratedata(infra, name, namelen, 1);
        if(entry) {
                int premax = infra_rate_max(entry->data, timenow);
-               int* cur = infra_rate_find_second(entry->data, timenow);
+               int* cur = infra_rate_give_second(entry->data, timenow);
                (*cur)++;
                max = infra_rate_max(entry->data, timenow);
                lock_rw_unlock(&entry->lock);
 
-               if(premax < lim && max >= lim) {
+               if(premax <= lim && max > lim) {
                        char buf[257], qnm[257], ts[12], cs[12], ip[128];
                        dname_str(name, buf);
                        dname_str(qinfo->qname, qnm);
@@ -970,12 +985,12 @@ int infra_ratelimit_inc(struct infra_cache* infra, uint8_t* name,
                                verbose(VERB_OPS, "ratelimit exceeded %s %d query %s %s %s", buf, lim, qnm, cs, ts);
                        }
                }
-               return (max < lim);
+               return (max <= lim);
        }
 
        /* create */
        infra_create_ratedata(infra, name, namelen, timenow);
-       return (1 < lim);
+       return (1 <= lim);
 }
 
 void infra_ratelimit_dec(struct infra_cache* infra, uint8_t* name,
@@ -987,7 +1002,12 @@ void infra_ratelimit_dec(struct infra_cache* infra, uint8_t* name,
                return; /* not enabled */
        entry = infra_find_ratedata(infra, name, namelen, 1);
        if(!entry) return; /* not cached */
-       cur = infra_rate_find_second(entry->data, timenow);
+       cur = infra_rate_get_second(entry->data, timenow);
+       if(cur == NULL) {
+               /* our timenow is not available anymore; nothing to decrease */
+               lock_rw_unlock(&entry->lock);
+               return;
+       }
        if((*cur) > 0)
                (*cur)--;
        lock_rw_unlock(&entry->lock);
@@ -1027,7 +1047,7 @@ infra_get_mem(struct infra_cache* infra)
 }
 
 int infra_ip_ratelimit_inc(struct infra_cache* infra,
-  struct comm_reply* repinfo, time_t timenow, struct sldns_buffer* buffer)
+       struct comm_reply* repinfo, time_t timenow, struct sldns_buffer* buffer)
 {
        int max;
        struct lruhash_entry* entry;
@@ -1040,7 +1060,7 @@ int infra_ip_ratelimit_inc(struct infra_cache* infra,
        entry = infra_find_ip_ratedata(infra, repinfo, 1);
        if(entry) {
                int premax = infra_rate_max(entry->data, timenow);
-               int* cur = infra_rate_find_second(entry->data, timenow);
+               int* cur = infra_rate_give_second(entry->data, timenow);
                (*cur)++;
                max = infra_rate_max(entry->data, timenow);
                lock_rw_unlock(&entry->lock);
index 14f97c4c64d3ed37f32a51aedbafe96ae011e5d8..a10e70f7d3790254db741e4c06d0894350acb7a5 100644 (file)
@@ -403,7 +403,7 @@ void infra_ratelimit_dec(struct infra_cache* infra, uint8_t* name,
 int infra_ratelimit_exceeded(struct infra_cache* infra, uint8_t* name,
        size_t namelen, time_t timenow);
 
-/** find the maximum rate stored, not too old. 0 if no information. */
+/** find the maximum rate stored. 0 if no information. */
 int infra_rate_max(void* data, time_t now);
 
 /** find the ratelimit in qps for a domain. 0 if no limit for domain. */
index 651f221fd88f386ea9d2b7e4ca3f98abbd2e7f7e..d0a53fe4ba6a9ddb3422b9dbd407de553f219328 100644 (file)
@@ -3333,11 +3333,11 @@ serviced_udp_callback(struct comm_point* c, void* arg, int error,
 struct serviced_query* 
 outnet_serviced_query(struct outside_network* outnet,
        struct query_info* qinfo, uint16_t flags, int dnssec, int want_dnssec,
-       int nocaps, int tcp_upstream, int ssl_upstream, char* tls_auth_name,
-       struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
-       size_t zonelen, struct module_qstate* qstate,
+       int nocaps, int check_ratelimit, int tcp_upstream, int ssl_upstream,
+       char* tls_auth_name, struct sockaddr_storage* addr, socklen_t addrlen,
+       uint8_t* zone, size_t zonelen, struct module_qstate* qstate,
        comm_point_callback_type* callback, void* callback_arg,
-       sldns_buffer* buff, struct module_env* env)
+       sldns_buffer* buff, struct module_env* env, int* was_ratelimited)
 {
        struct serviced_query* sq;
        struct service_callback* cb;
@@ -3345,6 +3345,7 @@ outnet_serviced_query(struct outside_network* outnet,
        struct regional* region;
        struct edns_option* backed_up_opt_list = qstate->edns_opts_back_out;
        struct edns_option* per_upstream_opt_list = NULL;
+       time_t timenow = 0;
 
        /* If we have an already populated EDNS option list make a copy since
         * we may now add upstream specific EDNS options. */
@@ -3386,6 +3387,26 @@ outnet_serviced_query(struct outside_network* outnet,
        sq = lookup_serviced(outnet, buff, dnssec, addr, addrlen,
                per_upstream_opt_list);
        if(!sq) {
+               /* Check ratelimit only for new serviced_query */
+               if(check_ratelimit) {
+                       timenow = *env->now;
+                       if(!infra_ratelimit_inc(env->infra_cache, zone,
+                               zonelen, timenow, &qstate->qinfo,
+                               qstate->reply)) {
+                               /* Can we pass through with slip factor? */
+                               if(env->cfg->ratelimit_factor == 0 ||
+                                       ub_random_max(env->rnd,
+                                       env->cfg->ratelimit_factor) != 1) {
+                                       *was_ratelimited = 1;
+                                       alloc_reg_release(env->alloc, region);
+                                       return NULL;
+                               }
+                               log_nametypeclass(VERB_ALGO,
+                                       "ratelimit allowed through for "
+                                       "delegation point", zone,
+                                       LDNS_RR_TYPE_NS, LDNS_RR_CLASS_IN);
+                       }
+               }
                /* make new serviced query entry */
                sq = serviced_create(outnet, buff, dnssec, want_dnssec, nocaps,
                        tcp_upstream, ssl_upstream, tls_auth_name, addr,
@@ -3395,11 +3416,19 @@ outnet_serviced_query(struct outside_network* outnet,
                        ? env->cfg->pad_queries_block_size : 0 ),
                        env->alloc, region);
                if(!sq) {
+                       if(check_ratelimit) {
+                               infra_ratelimit_dec(env->infra_cache,
+                                       zone, zonelen, timenow);
+                       }
                        alloc_reg_release(env->alloc, region);
                        return NULL;
                }
                if(!(cb = (struct service_callback*)regional_alloc(
                        sq->region, sizeof(*cb)))) {
+                       if(check_ratelimit) {
+                               infra_ratelimit_dec(env->infra_cache,
+                                       zone, zonelen, timenow);
+                       }
                        (void)rbtree_delete(outnet->serviced, sq);
                        serviced_node_del(&sq->node, NULL);
                        return NULL;
index 8f31d43cc010b326df2dfff6acece773350cb567..4c5b96f8342411d76d06445564abd5062869cda5 100644 (file)
@@ -632,6 +632,7 @@ void pending_delete(struct outside_network* outnet, struct pending* p);
  * @param want_dnssec: signatures are needed, without EDNS the answer is
  *     likely to be useless.
  * @param nocaps: ignore use_caps_for_id and use unperturbed qname.
+ * @param check_ratelimit: if set, will check ratelimit before sending out.
  * @param tcp_upstream: use TCP for upstream queries.
  * @param ssl_upstream: use SSL for upstream queries.
  * @param tls_auth_name: when ssl_upstream is true, use this name to check
@@ -648,16 +649,18 @@ void pending_delete(struct outside_network* outnet, struct pending* p);
  * @param callback_arg: user argument to callback function.
  * @param buff: scratch buffer to create query contents in. Empty on exit.
  * @param env: the module environment.
+ * @param was_ratelimited: it will signal back if the query failed to pass the
+ *     ratelimit check.
  * @return 0 on error, or pointer to serviced query that is used to answer
  *     this serviced query may be shared with other callbacks as well.
  */
 struct serviced_query* outnet_serviced_query(struct outside_network* outnet,
        struct query_info* qinfo, uint16_t flags, int dnssec, int want_dnssec,
-       int nocaps, int tcp_upstream, int ssl_upstream, char* tls_auth_name,
-       struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
-       size_t zonelen, struct module_qstate* qstate,
+       int nocaps, int check_ratelimit, int tcp_upstream, int ssl_upstream,
+       char* tls_auth_name, struct sockaddr_storage* addr, socklen_t addrlen,
+       uint8_t* zone, size_t zonelen, struct module_qstate* qstate,
        comm_point_callback_type* callback, void* callback_arg,
-       struct sldns_buffer* buff, struct module_env* env);
+       struct sldns_buffer* buff, struct module_env* env, int* was_ratelimited);
 
 /**
  * Remove service query callback.
index 78e773938c880a52063bfa9dc10cce6c3ef5df29..c68981735cb76e947e053d2b2481b64ad510b122 100644 (file)
@@ -97,10 +97,12 @@ void worker_sighandler(int ATTR_UNUSED(sig), void* ATTR_UNUSED(arg))
 struct outbound_entry* worker_send_query(
        struct query_info* ATTR_UNUSED(qinfo), uint16_t ATTR_UNUSED(flags),
        int ATTR_UNUSED(dnssec), int ATTR_UNUSED(want_dnssec),
-       int ATTR_UNUSED(nocaps), struct sockaddr_storage* ATTR_UNUSED(addr),
+       int ATTR_UNUSED(nocaps), int ATTR_UNUSED(check_ratelimit),
+       struct sockaddr_storage* ATTR_UNUSED(addr),
        socklen_t ATTR_UNUSED(addrlen), uint8_t* ATTR_UNUSED(zone),
        size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(tcp_upstream), int ATTR_UNUSED(ssl_upstream),
-       char* ATTR_UNUSED(tls_auth_name), struct module_qstate* ATTR_UNUSED(q))
+       char* ATTR_UNUSED(tls_auth_name), struct module_qstate* ATTR_UNUSED(q),
+       int* ATTR_UNUSED(was_ratelimited))
 {
        log_assert(0);
        return 0;
@@ -129,10 +131,12 @@ worker_alloc_cleanup(void* ATTR_UNUSED(arg))
 struct outbound_entry* libworker_send_query(
        struct query_info* ATTR_UNUSED(qinfo), uint16_t ATTR_UNUSED(flags),
        int ATTR_UNUSED(dnssec), int ATTR_UNUSED(want_dnssec),
-       int ATTR_UNUSED(nocaps), struct sockaddr_storage* ATTR_UNUSED(addr),
+       int ATTR_UNUSED(nocaps), int ATTR_UNUSED(check_ratelimit),
+       struct sockaddr_storage* ATTR_UNUSED(addr),
        socklen_t ATTR_UNUSED(addrlen), uint8_t* ATTR_UNUSED(zone),
        size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(tcp_upstream), int ATTR_UNUSED(ssl_upstream),
-       char* ATTR_UNUSED(tls_auth_name), struct module_qstate* ATTR_UNUSED(q))
+       char* ATTR_UNUSED(tls_auth_name), struct module_qstate* ATTR_UNUSED(q),
+       int* ATTR_UNUSED(was_ratelimited))
 {
        log_assert(0);
        return 0;
index 741c035f8b129014e8a47e70c77f74745855083c..be06a4721c211548b585ff824f7f982cfe526386 100644 (file)
@@ -1187,12 +1187,13 @@ pending_tcp_query(struct serviced_query* sq, sldns_buffer* packet,
 struct serviced_query* outnet_serviced_query(struct outside_network* outnet,
        struct query_info* qinfo, uint16_t flags, int dnssec,
        int ATTR_UNUSED(want_dnssec), int ATTR_UNUSED(nocaps),
+       int ATTR_UNUSED(check_ratelimit),
        int ATTR_UNUSED(tcp_upstream), int ATTR_UNUSED(ssl_upstream),
        char* ATTR_UNUSED(tls_auth_name), struct sockaddr_storage* addr,
        socklen_t addrlen, uint8_t* zone, size_t zonelen,
        struct module_qstate* qstate, comm_point_callback_type* callback,
        void* callback_arg, sldns_buffer* ATTR_UNUSED(buff),
-       struct module_env* env)
+       struct module_env* env, int* ATTR_UNUSED(was_ratelimited))
 {
        struct replay_runtime* runtime = (struct replay_runtime*)outnet->base;
        struct fake_pending* pend = (struct fake_pending*)calloc(1,
index 83b32a0042438a8ee637cfba94882a01fc647106..05a22d402ee1d3db70d03b39fa8a1dcdede52f4f 100644 (file)
@@ -335,9 +335,10 @@ fptr_whitelist_hash_markdelfunc(lruhash_markdelfunc_type fptr)
 int 
 fptr_whitelist_modenv_send_query(struct outbound_entry* (*fptr)(
        struct query_info* qinfo, uint16_t flags, int dnssec, int want_dnssec,
-       int nocaps, struct sockaddr_storage* addr, socklen_t addrlen,
-       uint8_t* zone, size_t zonelen, int tcp_upstream, int ssl_upstream, char* tls_auth_name,
-       struct module_qstate* q))
+       int nocaps, int check_ratelimit, struct sockaddr_storage* addr,
+       socklen_t addrlen, uint8_t* zone, size_t zonelen, int tcp_upstream,
+       int ssl_upstream, char* tls_auth_name, struct module_qstate* q,
+       int* was_ratelimited))
 {
        if(fptr == &worker_send_query) return 1;
        else if(fptr == &libworker_send_query) return 1;
index a5470992550f8809ec59e61ea1f99ff074528227..a0d9867554b50077f7b4862b18a39df1b4e379d1 100644 (file)
@@ -211,9 +211,10 @@ int fptr_whitelist_hash_markdelfunc(lruhash_markdelfunc_type fptr);
  */
 int fptr_whitelist_modenv_send_query(struct outbound_entry* (*fptr)(
        struct query_info* qinfo, uint16_t flags, int dnssec, int want_dnssec,
-       int nocaps, struct sockaddr_storage* addr, socklen_t addrlen,
-       uint8_t* zone, size_t zonelen, int tcp_upstream, int ssl_upstream, char* tls_auth_name,
-       struct module_qstate* q));
+       int nocaps, int check_ratelimit, struct sockaddr_storage* addr,
+       socklen_t addrlen, uint8_t* zone, size_t zonelen, int tcp_upstream,
+       int ssl_upstream, char* tls_auth_name, struct module_qstate* q,
+       int* was_ratelimited));
 
 /**
  * Check function pointer whitelist for module_env detach_subs callback values.
index c6b7e573ce00c355f6666a0a5575c39d4857f418..7a548003397a9b4ed99a7a8fd8be628d0ed5cd10 100644 (file)
@@ -350,6 +350,7 @@ struct module_env {
         *      EDNS, the answer is likely to be useless for this domain.
         * @param nocaps: do not use caps_for_id, use the qname as given.
         *      (ignored if caps_for_id is disabled).
+        * @param check_ratelimit: if set, will check ratelimit before sending out.
         * @param addr: where to.
         * @param addrlen: length of addr.
         * @param zone: delegation point name.
@@ -359,6 +360,8 @@ struct module_env {
         * @param tls_auth_name: if ssl_upstream, use this name with TLS
         *      authentication.
         * @param q: which query state to reactivate upon return.
+        * @param was_ratelimited: it will signal back if the query failed to pass the
+        *      ratelimit check.
         * @return: false on failure (memory or socket related). no query was
         *      sent. Or returns an outbound entry with qsent and qstate set.
         *      This outbound_entry will be used on later module invocations
@@ -366,9 +369,10 @@ struct module_env {
         */
        struct outbound_entry* (*send_query)(struct query_info* qinfo,
                uint16_t flags, int dnssec, int want_dnssec, int nocaps,
+               int check_ratelimit,
                struct sockaddr_storage* addr, socklen_t addrlen,
                uint8_t* zone, size_t zonelen, int tcp_upstream, int ssl_upstream,
-               char* tls_auth_name, struct module_qstate* q);
+               char* tls_auth_name, struct module_qstate* q, int* was_ratelimited);
 
        /**
         * Detach-subqueries.