mirror of
https://github.com/NLnetLabs/unbound.git
synced 2025-12-20 23:00:56 -05:00
- Add RPZ response IP override option, logging, and statistics
This commit is contained in:
parent
9ce7045413
commit
1c5d081853
8 changed files with 153 additions and 22 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Reference in a new issue