summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVladimír Čunát <vladimir.cunat@nic.cz>2022-07-04 13:35:50 +0200
committerVladimír Čunát <vladimir.cunat@nic.cz>2023-06-12 10:32:28 +0200
commit2f51749d2b5614c8ee4e90c1ca0bb5d69c18e5d5 (patch)
tree71917d6646b904656befcc88f4ad69b507792832
parentlib/rules: add basic view capability (diff)
downloadknot-resolver-2f51749d2b5614c8ee4e90c1ca0bb5d69c18e5d5.tar.xz
knot-resolver-2f51749d2b5614c8ee4e90c1ca0bb5d69c18e5d5.zip
lib/rules: add usable tagging
-rw-r--r--daemon/lua/kres-gen-30.lua2
-rw-r--r--daemon/lua/kres-gen-31.lua2
-rw-r--r--daemon/lua/kres-gen-32.lua2
-rwxr-xr-xdaemon/lua/kres-gen.sh2
-rw-r--r--lib/rules/api.c90
-rw-r--r--lib/rules/api.h12
-rw-r--r--modules/policy/policy.lua23
7 files changed, 132 insertions, 1 deletions
diff --git a/daemon/lua/kres-gen-30.lua b/daemon/lua/kres-gen-30.lua
index 1e4a2880..dd671d15 100644
--- a/daemon/lua/kres-gen-30.lua
+++ b/daemon/lua/kres-gen-30.lua
@@ -467,6 +467,8 @@ int kr_cache_commit(struct kr_cache *);
uint32_t packet_ttl(const knot_pkt_t *);
int kr_view_insert_action(const char *, const char *);
int kr_view_select_action(const struct kr_request *, knot_db_val_t *);
+int kr_rule_tag_add(const char *, kr_rule_tags_t *);
+int kr_rule_local_data_emptyzone(const knot_dname_t *, kr_rule_tags_t);
typedef struct {
int sock_type;
_Bool tls;
diff --git a/daemon/lua/kres-gen-31.lua b/daemon/lua/kres-gen-31.lua
index 810560dd..370de143 100644
--- a/daemon/lua/kres-gen-31.lua
+++ b/daemon/lua/kres-gen-31.lua
@@ -467,6 +467,8 @@ int kr_cache_commit(struct kr_cache *);
uint32_t packet_ttl(const knot_pkt_t *);
int kr_view_insert_action(const char *, const char *);
int kr_view_select_action(const struct kr_request *, knot_db_val_t *);
+int kr_rule_tag_add(const char *, kr_rule_tags_t *);
+int kr_rule_local_data_emptyzone(const knot_dname_t *, kr_rule_tags_t);
typedef struct {
int sock_type;
_Bool tls;
diff --git a/daemon/lua/kres-gen-32.lua b/daemon/lua/kres-gen-32.lua
index 84156d05..899c05cf 100644
--- a/daemon/lua/kres-gen-32.lua
+++ b/daemon/lua/kres-gen-32.lua
@@ -478,6 +478,8 @@ int kr_cache_commit(struct kr_cache *);
uint32_t packet_ttl(const knot_pkt_t *);
int kr_view_insert_action(const char *, const char *);
int kr_view_select_action(const struct kr_request *, knot_db_val_t *);
+int kr_rule_tag_add(const char *, kr_rule_tags_t *);
+int kr_rule_local_data_emptyzone(const knot_dname_t *, kr_rule_tags_t);
typedef struct {
int sock_type;
_Bool tls;
diff --git a/daemon/lua/kres-gen.sh b/daemon/lua/kres-gen.sh
index b56a8a38..5af527ae 100755
--- a/daemon/lua/kres-gen.sh
+++ b/daemon/lua/kres-gen.sh
@@ -287,6 +287,8 @@ ${CDEFS} ${LIBKRES} functions <<-EOF
# New policy
kr_view_insert_action
kr_view_select_action
+ kr_rule_tag_add
+ kr_rule_local_data_emptyzone
EOF
diff --git a/lib/rules/api.c b/lib/rules/api.c
index b8f27b55..e37efdd8 100644
--- a/lib/rules/api.c
+++ b/lib/rules/api.c
@@ -27,6 +27,8 @@ struct kr_rules *the_rules = NULL;
/* DB key-space summary
- "\0" starts special keys like "\0rulesets" or "\0stamp"
+ - "\0tagBits" -> kr_rule_tags_t denoting the set of tags that have a name in DB
+ - "\0tag_" + tag name -> one byte with the tag's number
- some future additions?
- otherwise it's rulesets - each has a prefix, e.g. RULESET_DEFAULT,
its length is bounded by KEY_RULESET_MAXLEN - 1; after that prefix:
@@ -64,6 +66,91 @@ static int answer_zla_empty(struct kr_query *qry, knot_pkt_t *pkt,
static int answer_zla_redirect(struct kr_query *qry, knot_pkt_t *pkt, const char *ruleset_name,
knot_db_val_t zla_lf, knot_db_val_t val);
+// LATER: doing tag_names_default() and kr_rule_tag_add() inside a RW transaction would be better.
+static int tag_names_default(void)
+{
+ uint8_t key_tb_str[] = "\0tagBits";
+ knot_db_val_t key = { .data = key_tb_str, .len = sizeof(key_tb_str) };
+ knot_db_val_t val;
+ // Check what's in there.
+ int ret = ruledb_op(read, &key, &val, 1);
+ if (ret == 0 && !kr_fails_assert(val.data && val.len == sizeof(kr_rule_tags_t)))
+ return kr_ok(); // it's probably OK
+ if (ret != kr_error(ENOENT))
+ return kr_error(ret);
+ kr_rule_tags_t empty = 0;
+ val.data = &empty;
+ val.len = sizeof(empty);
+ return ruledb_op(write, &key, &val, 1);
+}
+
+int kr_rule_tag_add(const char *tag, kr_rule_tags_t *tagset)
+{
+ // Construct the DB key.
+ const uint8_t key_prefix[] = "\0tag_";
+ knot_db_val_t key;
+ knot_db_val_t val;
+ const size_t tag_len = strlen(tag);
+ key.len = sizeof(key_prefix) + tag_len;
+ uint8_t key_buf[key.len];
+ key.data = key_buf;
+ memcpy(key_buf, key_prefix, sizeof(key_prefix));
+ memcpy(key_buf + sizeof(key_prefix), tag, tag_len);
+
+ int ret = ruledb_op(read, &key, &val, 1);
+ if (ret == 0) { // tag exists already
+ uint8_t *tindex_p = val.data;
+ static_assert(KR_RULE_TAGS_CAP < (1 << 8 * sizeof(*tindex_p)),
+ "bad combination of constants");
+ if (kr_fails_assert(val.data && val.len == 1
+ && *tindex_p < KR_RULE_TAGS_CAP)) {
+ kr_log_error(RULES, "ERROR: invalid length: %d\n", (int)val.len);
+ return kr_error(EILSEQ);
+ }
+ *tagset |= (1 << *tindex_p);
+ return kr_ok();
+ } else if (ret != kr_error(ENOENT)) {
+ return ret;
+ }
+
+ // We need to add it as a new tag. First find the bitmap of named tags.
+ uint8_t key_tb_str[] = "\0tagBits";
+ knot_db_val_t key_tb = { .data = key_tb_str, .len = sizeof(key_tb_str) };
+ ret = ruledb_op(read, &key_tb, &val, 1);
+ if (ret != 0)
+ return kr_error(ret);
+ if (kr_fails_assert(val.data && val.len == sizeof(kr_rule_tags_t))) {
+ kr_log_error(RULES, "ERROR: invalid length: %d\n", (int)val.len);
+ return kr_error(EILSEQ);
+ }
+ kr_rule_tags_t bmp;
+ memcpy(&bmp, val.data, sizeof(bmp));
+ // Find a free index.
+ static_assert(sizeof(long long) >= sizeof(bmp), "bad combination of constants");
+ int ix = ffsll(~bmp) - 1;
+ if (ix < 0 || ix >= 8 * sizeof(bmp))
+ return kr_error(E2BIG);
+ const kr_rule_tags_t tag_new = 1 << ix;
+ kr_require((tag_new & bmp) == 0);
+
+ // Update the mappings
+ bmp |= tag_new;
+ val.data = &bmp;
+ val.len = sizeof(bmp);
+ ret = ruledb_op(write, &key_tb, &val, 1);
+ if (ret != 0)
+ return kr_error(ret);
+ uint8_t ix_8t = ix;
+ val.data = &ix_8t;
+ val.len = sizeof(ix_8t);
+ ret = ruledb_op(write, &key, &val, 1); // key remained correct
+ if (ret != 0)
+ return kr_error(ret);
+ *tagset |= tag_new;
+ return kr_ok();
+}
+
+
//TODO later, maybe. ATM it would be cumbersome to avoid void* arithmetics.
#pragma GCC diagnostic ignored "-Wpointer-arith"
@@ -95,6 +182,9 @@ int kr_rules_init(void)
if (ret != 0) goto failure;
kr_require(the_rules->db);
+ ret = tag_names_default();
+ if (ret != 0) goto failure;
+
ret = rules_defaults_insert();
if (ret != 0) goto failure;
diff --git a/lib/rules/api.h b/lib/rules/api.h
index e9d10277..6ccbde89 100644
--- a/lib/rules/api.h
+++ b/lib/rules/api.h
@@ -11,6 +11,8 @@ struct knot_pkt;
typedef uint64_t kr_rule_tags_t;
#define KR_RULE_TAGS_ALL ((kr_rule_tags_t)0)
+/// Tags "capacity", i.e. numbered from 0 to _CAP - 1.
+#define KR_RULE_TAGS_CAP (sizeof(kr_rule_tags_t) * 8)
KR_EXPORT
int kr_rules_init(void);
@@ -79,8 +81,16 @@ int kr_rule_local_data_redirect(const knot_dname_t *apex, kr_rule_tags_t tags);
* The concept of chain actions isn't respected; the most prioritized rule wins.
* If exactly the same subnet is specified repeatedly, that rule gets overwritten silently.
* TODO: improve? (return code, warning, ...)
- * TODO: a well-usable action that assigns a tag-set
+ * TODO: some way to do multiple actions? Will be useful e.g. with option-setting actions.
+ * On implementation side this would probably be multi-value LMDB, cf. local_data rules.
*/
KR_EXPORT
int kr_view_insert_action(const char *subnet, const char *action);
+/** Add a tag by name into a tag-set variable.
+ *
+ * It also ensures allocation of tag names in the DB, etc.
+ */
+KR_EXPORT
+int kr_rule_tag_add(const char *tag, kr_rule_tags_t *tagset);
+
diff --git a/modules/policy/policy.lua b/modules/policy/policy.lua
index 1990837a..cc77e487 100644
--- a/modules/policy/policy.lua
+++ b/modules/policy/policy.lua
@@ -834,6 +834,29 @@ end
policy.rules = {}
policy.postrules = {}
+-- This certainly isn't perfect, but it allows lua config like:
+-- kr_view_insert_action('127.0.0.0/24', policy.TAGS_ASSIGN({'t01', 't02'}))
+local kr_rule_tags_t = ffi.typeof('kr_rule_tags_t[1]')
+function policy.get_tagset(names)
+ local result = ffi.new(kr_rule_tags_t, 0)
+ for _, name in pairs(names) do
+ if ffi.C.kr_rule_tag_add(name, result) ~= 0 then
+ error('converting tagset failed')
+ end
+ end
+ return result[0] -- it's atomic value fortunately
+end
+function policy.tags_assign_bitmap(bitmap)
+ return function (_, req)
+ req.rule_tags = bitmap
+ end
+end
+function policy.TAGS_ASSIGN(names)
+ local bitmap = policy.get_tagset(names)
+ return 'policy.tags_assign_bitmap(' .. tostring(bitmap) .. ')'
+end
+
+
local view_action_buf = ffi.new('knot_db_val_t[1]')
-- Top-down policy list walk until we hit a match