From: Marek Vavrusa Date: Thu, 25 Aug 2016 17:39:40 +0000 (-0700) Subject: modules/daf: support multiple targets in forward X-Git-Tag: v1.2.0-rc1~109 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=54f66ed60aeced79f4e93fab08cbc8afcd27e2be;p=thirdparty%2Fknot-resolver.git modules/daf: support multiple targets in forward RTT tracking for all targets is also supported, but no loadbalancing is done based on that yet --- diff --git a/daemon/lua/kres.lua b/daemon/lua/kres.lua index 396cc445a..9a0675a24 100644 --- a/daemon/lua/kres.lua +++ b/daemon/lua/kres.lua @@ -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, }, }) diff --git a/lib/layer/iterate.c b/lib/layer/iterate.c index 9e3523f81..dfe66a71e 100644 --- a/lib/layer/iterate.c +++ b/lib/layer/iterate.c @@ -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. */ diff --git a/lib/nsrep.c b/lib/nsrep.c index 71677277d..8b47e171e 100644 --- a/lib/nsrep.c +++ b/lib/nsrep.c @@ -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(); } diff --git a/lib/nsrep.h b/lib/nsrep.h index 5ae272adf..87a99ecee 100644 --- a/lib/nsrep.h +++ b/lib/nsrep.h @@ -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. diff --git a/lib/resolve.c b/lib/resolve.c index 2648e6384..7f8c7f896 100644 --- a/lib/resolve.c +++ b/lib/resolve.c @@ -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(); } diff --git a/lib/rplan.c b/lib/rplan.c index 4a610c6c0..e38a4d8c5 100644 --- a/lib/rplan.c +++ b/lib/rplan.c @@ -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); diff --git a/modules/daf/README.rst b/modules/daf/README.rst index 0c89a8fa5..30570af7a 100644 --- a/modules/daf/README.rst +++ b/modules/daf/README.rst @@ -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' diff --git a/modules/daf/daf.lua b/modules/daf/daf.lua index ebadda7f0..941e26964 100644 --- a/modules/daf/daf.lua +++ b/modules/daf/daf.lua @@ -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()) diff --git a/modules/policy/policy.lua b/modules/policy/policy.lua index 6a3944d02..5e1bc45bd 100644 --- a/modules/policy/policy.lua +++ b/modules/policy/policy.lua @@ -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,