]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
modules/daf: support multiple targets in forward
authorMarek Vavrusa <marek@vavrusa.com>
Thu, 25 Aug 2016 17:39:40 +0000 (10:39 -0700)
committerMarek Vavrusa <marek@vavrusa.com>
Thu, 25 Aug 2016 17:39:40 +0000 (10:39 -0700)
RTT tracking for all targets is also supported,
but no loadbalancing is done based on that yet

daemon/lua/kres.lua
lib/layer/iterate.c
lib/nsrep.c
lib/nsrep.h
lib/resolve.c
lib/rplan.c
modules/daf/README.rst
modules/daf/daf.lua
modules/policy/policy.lua

index 396cc445aa04c9820bd94c5bfe03e8bc392193e1..9a0675a2458e7c2542e4dc07fbf021d043dfed84 100644 (file)
@@ -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,
        },
 })
index 9e3523f819aed60a5e8f99ae9b61bac64229bbb9..dfe66a71ea8e710e247abb6bd096d9dd873c5ee8 100644 (file)
@@ -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. */
index 71677277d80f8961d2cfee206cf97153cd98d7d4..8b47e171eb3482ed6f9209b946b85494d34c5218 100644 (file)
@@ -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();
 }
 
index 5ae272adf0e6ad90a24566b44793940b39809dd7..87a99eceedfdbaba6dfc5937878bbf05eee07ebf 100644 (file)
@@ -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.
index 2648e6384f345f718df583fa1119913cab1f50ff..7f8c7f896427bbda078a2d5d2ccc3bb846c5b440 100644 (file)
@@ -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();
 }
index 4a610c6c0a82be1b59021aa8bcecd22795583f96..e38a4d8c578b8f9d20ab8a5fd34e08e99b0640ca 100644 (file)
@@ -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);
index 0c89a8fa5f75ccd1635bf99a6cd0ce04e5e2f7a4..30570af7a7263ed010775a640045f4dd154aa7d8 100644 (file)
@@ -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'
index ebadda7f06e00c7809e8515d31b414481800cfbb..941e269649c5769863f86f64f128fcd1ad8b2948 100644 (file)
@@ -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())
index 6a3944d025b0addadd0e9ce2a7ad0c489e154a68..5e1bc45bd0c4ec924e3239186b2b8f3f2f268732 100644 (file)
@@ -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,