- Add RPZ response IP override option, logging, and statistics

This commit is contained in:
Ralph Dolmans 2019-08-01 14:31:37 +02:00
parent 9ce7045413
commit 1c5d081853
8 changed files with 153 additions and 22 deletions

View 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;

View 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;

View 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);
}

View 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.

View 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;
}

View 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 */

View 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;

View 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 */