Commit 54f66ed6 authored by Marek Vavrusa's avatar Marek Vavrusa

modules/daf: support multiple targets in forward

RTT tracking for all targets is also supported,
but no loadbalancing is done based on that yet
parent 9397638d
......@@ -290,7 +290,7 @@ struct kr_query *kr_rplan_push(struct kr_rplan *rplan, struct kr_query *parent,
struct kr_query *kr_rplan_resolved(struct kr_rplan *rplan);
struct kr_query *kr_rplan_next(struct kr_query *qry);
/* Nameservers */
int kr_nsrep_set(struct kr_query *qry, uint8_t *addr, size_t addr_len, int port);
int kr_nsrep_set(struct kr_query *qry, size_t index, uint8_t *addr, size_t addr_len, int port);
/* Query */
/* Utils */
unsigned kr_rand_uint(unsigned max);
......@@ -430,9 +430,15 @@ ffi.metatype( kr_query_t, {
final = function(qry)
return qry:resolved() and (qry.parent == nil)
end,
nslist = function(qry, ns, port)
if ns ~= nil then C.kr_nsrep_set(qry, ffi.cast(ub_t, ns), #ns, port) end
-- @todo: Return list of NS entries, not possible ATM because the NSLIST is union and missing typedef
nslist = function(qry, list)
assert(#list <= 4, 'maximum of 4 addresses can be evaluated for each query')
for i, ns in ipairs(list) do
C.kr_nsrep_set(qry, i - 1, ffi.cast(ub_t, ns[1]), #ns[1], ns[2] or 53)
end
-- If less than maximum NSs, insert guard to terminate the list
if #list < 4 then
C.kr_nsrep_set(qry, #list, nil, 0, 0)
end
end,
},
})
......
......@@ -628,8 +628,8 @@ static int resolve(knot_layer_t *ctx, knot_pkt_t *pkt)
break; /* OK */
case KNOT_RCODE_REFUSED:
case KNOT_RCODE_SERVFAIL: {
DEBUG_MSG("<= rcode: %s\n", rcode ? rcode->name : "??");
if (query->flags & QUERY_STUB) { break; } /* Pass through in stub mode */
DEBUG_MSG("<= rcode: %s\n", rcode ? rcode->name : "??");
query->fails += 1;
if (query->fails >= KR_QUERY_NSRETRY_LIMIT) {
query->fails = 0; /* Reset per-query counter. */
......
......@@ -169,16 +169,29 @@ static int eval_nsrep(const char *k, void *v, void *baton)
return kr_ok();
}
int kr_nsrep_set(struct kr_query *qry, uint8_t *addr, size_t addr_len, int port)
int kr_nsrep_set(struct kr_query *qry, size_t index, uint8_t *addr, size_t addr_len, int port)
{
if (!qry || !addr) {
if (!qry) {
return kr_error(EINVAL);
}
if (index >= KR_NSREP_MAXADDR) {
return kr_error(ENOSPC);
}
qry->ns.name = (const uint8_t *)"";
qry->ns.score = KR_NS_UNKNOWN;
qry->ns.reputation = 0;
update_nsrep(&qry->ns, 0, addr, addr_len, port);
update_nsrep(&qry->ns, 1, NULL, 0, 0);
/* Reset score on first entry */
if (index == 0) {
qry->ns.score = KR_NS_UNKNOWN;
qry->ns.reputation = 0;
}
/* Retrieve RTT from cache */
if (addr && addr_len > 0) {
struct kr_context *ctx = qry->ns.ctx;
unsigned *score = ctx ? lru_get(ctx->cache_rtt, (const char *)addr, addr_len) : NULL;
if (score) {
qry->ns.score = MIN(qry->ns.score, *score);
}
}
update_nsrep(&qry->ns, index, addr, addr_len, port);
return kr_ok();
}
......
......@@ -94,13 +94,14 @@ struct kr_nsrep
/**
* Set given NS address.
* @param qry updated query
* @param index index of the updated target
* @param addr address bytes (struct in_addr or struct in6_addr)
* @param addr_len address bytes length (type will be derived from this)
* @param port address port (if <= 0, 53 will be used)
* @return 0 or an error code
*/
KR_EXPORT
int kr_nsrep_set(struct kr_query *qry, uint8_t *addr, size_t addr_len, int port);
int kr_nsrep_set(struct kr_query *qry, size_t index, uint8_t *addr, size_t addr_len, int port);
/**
* Elect best nameserver/address pair from the nsset.
......
......@@ -976,13 +976,14 @@ int kr_resolve_checkout(struct kr_request *request, struct sockaddr *src,
if (addr->sa_family == AF_UNSPEC) {
break;
}
if (!kr_inaddr_equal(dst, addr)) {
continue;
}
inet_ntop(addr->sa_family, kr_nsrep_inaddr(qry->ns.addr[i]), ns_str, sizeof(ns_str));
DEBUG_MSG(qry, "%s%s: '%s' score: %u zone cut: '%s' m12n: '%s' type: '%s' proto: '%s'\n",
i == 0 ? "=>" : " ",
kr_inaddr_equal(dst, addr) ? "*querying" : " optional",
ns_str, qry->ns.score, zonecut_str, qname_str, type_str, (qry->flags & QUERY_TCP)?"tcp":"udp");
}
}
DEBUG_MSG(qry, "=> querying: '%s' score: %u zone cut: '%s' m12n: '%s' type: '%s' proto: '%s'\n",
ns_str, qry->ns.score, zonecut_str, qname_str, type_str, (qry->flags & QUERY_TCP) ? "tcp" : "udp");
break;
}}
return kr_ok();
}
......
......@@ -128,6 +128,7 @@ static struct kr_query *kr_rplan_push_query(struct kr_rplan *rplan,
/* Class and type must be set outside this function. */
qry->flags = rplan->request->options;
qry->parent = parent;
qry->ns.ctx = rplan->request->ctx;
qry->ns.addr[0].ip.sa_family = AF_UNSPEC;
gettimeofday(&qry->timestamp, NULL);
kr_zonecut_init(&qry->zone_cut, (const uint8_t *)"", rplan->pool);
......
......@@ -35,11 +35,13 @@ Firewall rules are declarative and consist of filters and actions. Filters have
daf.add 'src = 127.0.0.0/8 rewrite example.com A 127.0.0.2'
-- Mirror queries matching given name to DNS logger
daf.add 'qname ~ %w+.example.com MIRROR 127.0.0.2'
daf.add 'qname ~ example-%d.com MIRROR 127.0.0.3@5353'
daf.add 'qname ~ %w+.example.com mirror 127.0.0.2'
daf.add 'qname ~ example-%d.com mirror 127.0.0.3@5353'
-- Forward queries from subnet
daf.add 'src = 127.0.0.1/8 forward 127.0.0.1@5353'
-- Forward to multiple targets
daf.add 'src = 127.0.0.1/8 forward 127.0.0.1@5353,127.0.0.2@5353'
-- Truncate queries based on destination IPs
daf.add 'dst = 192.0.2.51 truncate'
......
......@@ -6,7 +6,12 @@ if not policy then modules.load('policy') end
local actions = {
pass = 1, deny = 2, drop = 3, tc = 4, truncate = 4,
forward = function (g)
return policy.FORWARD(g())
local addrs = {}
local tok = g()
for addr in string.gmatch(tok, '[^,]+') do
table.insert(addrs, addr)
end
return policy.FORWARD(addrs)
end,
mirror = function (g)
return policy.MIRROR(g())
......
......@@ -35,10 +35,19 @@ if not has_ffi then
socket_client = function () return error("missing ffi library, required for this policy") end
end
local function parse_target(target)
local addr, port = target:match '([^@]*)@?(.*)'
port = port and tonumber(port) or 53
addr = kres.str2ip(addr)
if addr == nil then
error("target '"..target..'" is not a valid IP address')
end
return addr, port
end
-- Mirror request elsewhere, and continue solving
local function mirror(target)
local addr, port = target:match '([^@]*)@?(.*)'
if not port or #port == 0 then port = 53 end
local addr, port = parse_target(target)
local sink, err = socket_client(addr, port)
if not sink then panic('MIRROR target %s is not a valid: %s', target, err) end
return function(state, req)
......@@ -54,16 +63,21 @@ end
-- Forward request, and solve as stub query
local function forward(target)
local addr, port = target:match '([^@]*)@?(.*)'
port = port and tonumber(port) or 53
addr = kres.str2ip(addr)
if addr == nil then error("FORWARD target '"..target..'" is not a valid IP address') end
local list = {}
if type(target) == 'table' then
for _, v in pairs(target) do
table.insert(list, {parse_target(v)})
assert(#list <= 4, 'at most 4 FORWARD targets are supported')
end
else
table.insert(list, {parse_target(target)})
end
return function(state, req)
req = kres.request_t(req)
local qry = req:current()
-- Switch mode to stub resolver, do not track origin zone cut since it's not real authority NS
qry.flags = bit.band(bit.bor(qry.flags, kres.query.STUB), bit.bnot(kres.query.ALWAYS_CUT))
qry:nslist(addr, port)
qry:nslist(list)
return state
end
end
......@@ -77,7 +91,6 @@ local function reroute(tbl, names)
table.insert(prefixes, names and ren.name(from, to) or ren.prefix(from, to))
end
-- Return rule closure
tbl = nil
return ren.rule(prefixes)
end
......@@ -139,7 +152,6 @@ end
local function rpz_parse(action, path)
local rules = {}
local ffi = require('ffi')
local action_map = {
-- RPZ Policy Actions
['\0'] = action,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment