]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- Add RPZ response IP override option, logging, and statistics
authorRalph Dolmans <ralph@nlnetlabs.nl>
Thu, 1 Aug 2019 12:31:37 +0000 (14:31 +0200)
committerRalph Dolmans <ralph@nlnetlabs.nl>
Thu, 1 Aug 2019 12:31:37 +0000 (14:31 +0200)
daemon/stats.c
daemon/worker.c
respip/respip.c
respip/respip.h
services/mesh.c
services/mesh.h
services/rpz.c
services/rpz.h

index 504b0efccc4a4ade3b0d54e5a6e9332e293bd051..3cf0d2b8dcf08f23a266b9630237ddfa6cdb7223 100644 (file)
@@ -273,6 +273,8 @@ server_stats_compile(struct worker* worker, struct ub_stats_info* s, int reset)
        s->svr.ans_rcode_nodata += (long long)worker->env.mesh->ans_nodata;
        for(i=0; i<16; i++)
                s->svr.ans_rcode[i] += (long long)worker->env.mesh->ans_rcode[i];
+       for(i=0; i<UB_STATS_RPZ_ACTION_NUM; i++)
+               s->svr.rpz_action[i] += (long long)worker->env.mesh->rpz_action[i];
        timehist_export(worker->env.mesh->histogram, s->svr.hist, 
                NUM_BUCKETS_HIST);
        /* values from outside network */
@@ -446,6 +448,8 @@ void server_stats_add(struct ub_stats_info* total, struct ub_stats_info* a)
                        total->svr.ans_rcode[i] += a->svr.ans_rcode[i];
                for(i=0; i<NUM_BUCKETS_HIST; i++)
                        total->svr.hist[i] += a->svr.hist[i];
+               for(i=0; i<UB_STATS_RPZ_ACTION_NUM; i++)
+                       total->svr.rpz_action[i] += a->svr.rpz_action[i];
        }
 
        total->mesh_num_states += a->mesh_num_states;
index 547a7610bb17a0603b78f958244af74de1c91a19..2cfc96845618c7357ce183e57fdf8320ea6df047 100644 (file)
@@ -61,6 +61,7 @@
 #include "services/authzone.h"
 #include "services/mesh.h"
 #include "services/localzone.h"
+#include "services/rpz.h"
 #include "util/data/msgparse.h"
 #include "util/data/msgencode.h"
 #include "util/data/dname.h"
@@ -574,7 +575,8 @@ apply_respip_action(struct worker* worker, const struct query_info* qinfo,
        struct comm_reply* repinfo, struct ub_packed_rrset_key** alias_rrset,
        struct reply_info** encode_repp, struct auth_zones* az)
 {
-       struct respip_action_info actinfo = {respip_none, NULL};
+       struct respip_action_info actinfo = {0};
+       actinfo.action = respip_none;
 
        if(qinfo->qtype != LDNS_RR_TYPE_A &&
                qinfo->qtype != LDNS_RR_TYPE_AAAA &&
@@ -595,9 +597,18 @@ apply_respip_action(struct worker* worker, const struct query_info* qinfo,
        /* If address info is returned, it means the action should be an
         * 'inform' variant and the information should be logged. */
        if(actinfo.addrinfo) {
-               respip_inform_print(actinfo.addrinfo, qinfo->qname,
+               respip_inform_print(&actinfo, qinfo->qname,
                        qinfo->qtype, qinfo->qclass, qinfo->local_alias,
                        repinfo);
+
+               if(worker->stats.extended && actinfo.rpz_used) {
+                       /* TODO: does not work for disabled (override) actions */
+                       if(actinfo.rpz_cname_override)
+                               worker->stats.rpz_action[RPZ_CNAME_OVERRIDE_ACTION]++;
+                       else
+                               worker->stats.rpz_action[
+                                       respip_action_to_rpz_action(actinfo.action)]++;
+               }
        }
 
        return 1;
index 176abea829eaa9abaa947cabb1362668972b18a9..a529ccf624b45b3fc77d4a376f73d275942efa6d 100644 (file)
@@ -656,6 +656,7 @@ make_new_reply_info(const struct reply_info* rep, struct regional* region,
  * the "no data" action in case of error.
  * @param raddr: address span that requires an action
  * @param action: action to apply
+ * @param data: RRset to use for override
  * @param qtype: original query type
  * @param rep: original reply message
  * @param rrset_id: the rrset ID in 'rep' to which the action should apply
@@ -671,13 +672,14 @@ make_new_reply_info(const struct reply_info* rep, struct regional* region,
  */
 static int
 respip_data_answer(const struct resp_addr* raddr, enum respip_action action,
+       struct ub_packed_rrset_key* data,
        uint16_t qtype, const struct reply_info* rep,
        size_t rrset_id, struct reply_info** new_repp, int tag,
        struct config_strlist** tag_datas, size_t tag_datas_size,
        char* const* tagname, int num_tags,
        struct ub_packed_rrset_key** redirect_rrsetp, struct regional* region)
 {
-       struct ub_packed_rrset_key* rp = raddr->data;
+       struct ub_packed_rrset_key* rp = data;
        struct reply_info* new_rep;
        *redirect_rrsetp = NULL;
 
@@ -715,7 +717,7 @@ respip_data_answer(const struct resp_addr* raddr, enum respip_action action,
         * to replace the rrset's dname.  Note that, unlike local data, we
         * rename the dname for other actions than redirect.  This is because
         * response-ip-data isn't associated to any specific name. */
-       if(rp == raddr->data) {
+       if(rp == data) {
                rp = copy_rrset(rp, region);
                if(!rp)
                        return -1;
@@ -807,16 +809,22 @@ populate_action_info(struct respip_action_info* actinfo,
        enum respip_action action, const struct resp_addr* raddr,
        const struct ub_packed_rrset_key* ATTR_UNUSED(rrset),
        int ATTR_UNUSED(tag), const struct respip_set* ATTR_UNUSED(ipset),
-       int ATTR_UNUSED(action_only), struct regional* region)
+       int ATTR_UNUSED(action_only), struct regional* region, int rpz_used,
+       int rpz_log, char* log_name, int rpz_cname_override)
 {
        if(action == respip_none || !raddr)
                return 1;
        actinfo->action = action;
+       actinfo->rpz_used = 1;
+       actinfo->rpz_log = rpz_log;
+       actinfo->log_name = log_name;
+       actinfo->rpz_cname_override = rpz_cname_override;
 
        /* for inform variants, make a copy of the matched address block for
         * later logging.  We make a copy to proactively avoid disruption if
         *  and when we allow a dynamic update to the respip tree. */
-       if(action == respip_inform || action == respip_inform_deny) {
+       if(action == respip_inform || action == respip_inform_deny ||
+               rpz_log) {
                struct respip_addr_info* a =
                        regional_alloc_zero(region, sizeof(*a));
                if(!a) {
@@ -832,6 +840,30 @@ populate_action_info(struct respip_action_info* actinfo,
        return 1;
 }
 
+static int
+respip_use_rpz(struct resp_addr* raddr, struct rpz* r,
+       enum respip_action* action,
+       struct ub_packed_rrset_key** data, int* rpz_log, char** log_name,
+       int* rpz_cname_override, struct regional* region)
+{
+       if(r->action_override == RPZ_DISABLED_ACTION) {
+               return 0;
+       }
+       else if(r->action_override == RPZ_NO_OVERRIDE_ACTION)
+               *action = raddr->action;
+       else
+               *action = rpz_action_to_respip_action(r->action_override);
+       if(r->action_override == RPZ_CNAME_OVERRIDE_ACTION &&
+               r->cname_override) {
+               *data = r->cname_override;
+               *rpz_cname_override = 1;
+       }
+       *rpz_log = r->log;
+       if(r->log_name)
+               *log_name = regional_strdup(region, r->log_name);
+       return 1;
+}
+
 int
 respip_rewrite_reply(const struct query_info* qinfo,
        const struct respip_client_info* cinfo, const struct reply_info* rep,
@@ -854,6 +886,11 @@ respip_rewrite_reply(const struct query_info* qinfo,
        int ret = 1;
        struct ub_packed_rrset_key* redirect_rrset = NULL;
        struct rpz* r;
+       struct ub_packed_rrset_key* data = NULL;
+       int rpz_used = 0;
+       int rpz_log = 0;
+       int rpz_cname_override = 0;
+       char* log_name = NULL;
 
        if(!cinfo)
                goto done;
@@ -903,7 +940,15 @@ respip_rewrite_reply(const struct query_info* qinfo,
                        r->taglistlen, ctaglist, ctaglen)) {
                        if((raddr = respip_addr_lookup(rep,
                                r->respip_set, &rrset_id))) {
-                               action = raddr->action;
+                       }
+                       if(raddr) {
+                               if(!respip_use_rpz(raddr, r, &action, &data,
+                                       &rpz_log, &log_name, &rpz_cname_override,
+                                       region)) {
+                                       lock_rw_unlock(&raddr->lock);
+                                       raddr = NULL;
+                               }
+                               rpz_used = 1;
                        }
                }       
        }
@@ -918,9 +963,10 @@ respip_rewrite_reply(const struct query_info* qinfo,
                        && action != respip_always_nxdomain
                        && action != respip_always_nodata
                        && (result = respip_data_answer(raddr, action,
-                       qinfo->qtype, rep, rrset_id, new_repp, tag, tag_datas,
-                       tag_datas_size, ipset->tagname, ipset->num_tags,
-                       &redirect_rrset, region)) < 0) {
+                       (data) ? data : raddr->data, qinfo->qtype, rep,
+                       rrset_id, new_repp, tag, tag_datas, tag_datas_size,
+                       ipset->tagname, ipset->num_tags, &redirect_rrset,
+                       region)) < 0) {
                        ret = 0;
                        goto done;
                }
@@ -951,7 +997,8 @@ respip_rewrite_reply(const struct query_info* qinfo,
                        *alias_rrset = redirect_rrset;
                /* on success, populate respip result structure */
                ret = populate_action_info(actinfo, action, raddr,
-                       redirect_rrset, tag, ipset, search_only, region);
+                       redirect_rrset, tag, ipset, search_only, region,
+                               rpz_used, rpz_log, log_name, rpz_cname_override);
        }
        if(raddr)
                lock_rw_unlock(&raddr->lock);
@@ -1009,9 +1056,10 @@ respip_operate(struct module_qstate* qstate, enum module_ev event, int id,
                        qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA ||
                        qstate->qinfo.qtype == LDNS_RR_TYPE_ANY) &&
                        qstate->return_msg && qstate->return_msg->rep) {
-                       struct respip_action_info actinfo = {respip_none, NULL};
                        struct reply_info* new_rep = qstate->return_msg->rep;
                        struct ub_packed_rrset_key* alias_rrset = NULL;
+                       struct respip_action_info actinfo = {0};
+                       actinfo.action = respip_none;
 
                        if(!respip_rewrite_reply(&qstate->qinfo,
                                qstate->client_info, qstate->return_msg->rep,
@@ -1070,7 +1118,8 @@ respip_merge_cname(struct reply_info* base_rep,
        struct ub_packed_rrset_key* alias_rrset = NULL; /* ditto */
        uint16_t tgt_rcode;
        size_t i, j;
-       struct respip_action_info actinfo = {respip_none, NULL};
+       struct respip_action_info actinfo = {0};
+       actinfo.action = respip_none;
 
        /* If the query for the CNAME target would result in an unusual rcode,
         * we generally translate it as a failure for the base query
@@ -1201,12 +1250,15 @@ respip_set_is_empty(const struct respip_set* set)
 }
 
 void
-respip_inform_print(struct respip_addr_info* respip_addr, uint8_t* qname,
+respip_inform_print(struct respip_action_info* respip_actinfo, uint8_t* qname,
        uint16_t qtype, uint16_t qclass, struct local_rrset* local_alias,
        struct comm_reply* repinfo)
 {
        char srcip[128], respip[128], txt[512];
        unsigned port;
+       struct respip_addr_info* respip_addr = respip_actinfo->addrinfo;
+       size_t txtlen = 0;
+       char* actionstr = NULL;
 
        if(local_alias)
                qname = local_alias->rrset->rk.dname;
@@ -1216,7 +1268,25 @@ respip_inform_print(struct respip_addr_info* respip_addr, uint8_t* qname,
        addr_to_str(&repinfo->addr, repinfo->addrlen, srcip, sizeof(srcip));
        addr_to_str(&respip_addr->addr, respip_addr->addrlen,
                respip, sizeof(respip));
-       snprintf(txt, sizeof(txt), "%s/%d inform %s@%u", respip,
-               respip_addr->net, srcip, port);
+       if(respip_actinfo->rpz_log) {
+               txtlen += snprintf(txt+txtlen, sizeof(txt)-txtlen, "%s",
+                       "RPZ applied ");
+               if(respip_actinfo->rpz_cname_override)
+                       actionstr = strdup(
+                               rpz_action_to_string(RPZ_CNAME_OVERRIDE_ACTION));
+               else
+                       actionstr = strdup(rpz_action_to_string(
+                               respip_action_to_rpz_action(
+                                       respip_actinfo->action)));
+       }
+       if(respip_actinfo->log_name) {
+               txtlen += snprintf(txt+txtlen, sizeof(txt)-txtlen,
+                       "[%s] ", respip_actinfo->log_name);
+       }
+       txtlen += snprintf(txt+txtlen, sizeof(txt)-txtlen,
+               "%s/%d %s %s@%u", respip, respip_addr->net,
+               (actionstr) ? actionstr : "inform", srcip, port);
+       if(actionstr)
+               free(actionstr);
        log_nametypeclass(0, txt, qname, qtype, qclass);
 }
index d2d39872ce1eb27ad234c25d8ac1a5f7d94ee580..4fd3a08d9383b7580320ee9ae6b24b1091849b92 100644 (file)
@@ -49,6 +49,7 @@ struct resp_addr {
 /**
  * Forward declaration for the structure that represents a tree of view data.
  */
+
 struct views;
 
 struct respip_addr_info;
@@ -78,6 +79,10 @@ struct respip_client_info {
  */
 struct respip_action_info {
        enum respip_action action;
+       int rpz_used;
+       int rpz_log;
+       char* log_name;
+       int rpz_cname_override;
        struct respip_addr_info* addrinfo; /* set only for inform variants */
 };
 
@@ -234,7 +239,7 @@ int respip_set_is_empty(const struct respip_set* set);
 /**
  * print log information for a query subject to an inform or inform-deny
  * response-ip action.
- * @param respip_addr: response-ip information that causes the action
+ * @param respip_actinfo: response-ip information that causes the action
  * @param qname: query name in the context, will be ignored if local_alias is
  *   non-NULL.
  * @param qtype: query type, in host byte order.
@@ -244,9 +249,9 @@ int respip_set_is_empty(const struct respip_set* set);
  *  query name.
  * @param repinfo: reply info containing the client's source address and port.
  */
-void respip_inform_print(struct respip_addr_info* respip_addr, uint8_t* qname,
-       uint16_t qtype, uint16_t qclass, struct local_rrset* local_alias,
-       struct comm_reply* repinfo);
+void respip_inform_print(struct respip_action_info* respip_actinfo,
+       uint8_t* qname, uint16_t qtype, uint16_t qclass,
+       struct local_rrset* local_alias, struct comm_reply* repinfo);
 
 /**
  * Find resp_addr in tree, create and add to tree if it does not exist.
index bee0f76a4ad6fc6c81b90986ae70e83761ea78d6..65f26390eb8ce51d2186e10283c43cb59ac9bfd7 100644 (file)
@@ -1180,10 +1180,19 @@ void mesh_query_done(struct mesh_state* mstate)
                 *  information should be logged for each client. */
                if(mstate->s.respip_action_info &&
                        mstate->s.respip_action_info->addrinfo) {
-                       respip_inform_print(mstate->s.respip_action_info->addrinfo,
+                       respip_inform_print(mstate->s.respip_action_info,
                                r->qname, mstate->s.qinfo.qtype,
                                mstate->s.qinfo.qclass, r->local_alias,
                                &r->query_reply);
+                       if(mstate->s.env->cfg->stat_extended &&
+                               mstate->s.respip_action_info->rpz_used) {
+                               /* TODO: does not work for disabled (override) actions */
+                               if(mstate->s.respip_action_info->rpz_cname_override)
+                                       mstate->s.env->mesh->rpz_action[RPZ_CNAME_OVERRIDE_ACTION]++;
+                               else
+                                       mstate->s.env->mesh->rpz_action[respip_action_to_rpz_action(
+                                               mstate->s.respip_action_info->action)]++;
+                       }
                }
 
                /* if this query is determined to be dropped during the
@@ -1580,6 +1589,7 @@ mesh_stats_clear(struct mesh_area* mesh)
        mesh->ans_secure = 0;
        mesh->ans_bogus = 0;
        memset(&mesh->ans_rcode[0], 0, sizeof(size_t)*16);
+       memset(&mesh->rpz_action[0], 0, sizeof(size_t)*10);
        mesh->ans_nodata = 0;
 }
 
index a2622844bbf0d4e3d64d08bb06b2cd35c7c2bd9e..c28176465a0ebe3548ec6c0b0a36cf93ba2da40a 100644 (file)
@@ -51,6 +51,7 @@
 #include "util/data/msgparse.h"
 #include "util/module.h"
 #include "services/modstack.h"
+#include "services/rpz.h"
 struct sldns_buffer;
 struct mesh_state;
 struct mesh_reply;
@@ -124,6 +125,8 @@ struct mesh_area {
        size_t ans_rcode[16];
        /** (extended stats) rcode nodata in replies */
        size_t ans_nodata;
+       /** (extended stats) type of applied RPZ action */
+       size_t rpz_action[10];
 
        /** backup of query if other operations recurse and need the
         * network buffers */
index ecf32f4301b3826df588412801126cfc1f21d260..543ab148b9487a8ca87fbe0d70034c516ff3d8b9 100644 (file)
@@ -209,7 +209,7 @@ rpz_action_to_localzone_type(enum rpz_action a)
        }
 }
 
-static enum respip_action
+enum respip_action
 rpz_action_to_respip_action(enum rpz_action a)
 {
        switch(a) {
@@ -242,6 +242,21 @@ localzone_type_to_rpz_action(enum localzone_type lzt)
        }
 }
 
+enum rpz_action
+respip_action_to_rpz_action(enum respip_action a)
+{
+       switch(a) {
+       case respip_always_nxdomain:    return RPZ_NXDOMAIN_ACTION;
+       case respip_always_nodata:      return RPZ_NODATA_ACTION;
+       case respip_deny:               return RPZ_DROP_ACTION;
+       case respip_always_transparent: return RPZ_PASSTHRU_ACTION;
+       case respip_redirect:           return RPZ_LOCAL_DATA_ACTION;
+       case respip_invalid:
+       default:
+               return RPZ_INVALID_ACTION;
+       }
+}
+
 /**
  * Get RPZ trigger for dname
  * @param dname: dname containing RPZ trigger
@@ -861,6 +876,8 @@ rpz_apply_qname_trigger(struct auth_zones* az, struct module_env* env,
                                        log_rpz_apply(z->name,
                                                r->action_override,
                                                qinfo, repinfo, r->log_name);
+                               /* TODO only register stats when stats_extended?
+                                * */
                                stats->rpz_action[r->action_override]++;
                                lock_rw_unlock(&z->lock);
                                z = NULL;
index a5b9e67ab02fc2fa68f27007c7b2876b32a7f500..042773f339ed1c07de4be37a06665027b2a2d32d 100644 (file)
@@ -178,10 +178,21 @@ struct rpz* rpz_create(struct config_auth* p);
  */
 const char* rpz_action_to_string(enum rpz_action a);
 
+enum rpz_action
+respip_action_to_rpz_action(enum respip_action a);
+
 /**
  * Prepare RPZ after procesing feed content.
  * @param r: RPZ to use
  */
 void rpz_finish_config(struct rpz* r);
 
+/**
+ * Classify respip action for RPZ action
+ * @param a: RPZ action
+ * @return: the respip action
+ */
+enum respip_action
+rpz_action_to_respip_action(enum rpz_action a);
+
 #endif /* SERVICES_RPZ_H */