diff options
author | Vladimír Čunát <vladimir.cunat@nic.cz> | 2022-07-04 13:35:50 +0200 |
---|---|---|
committer | Vladimír Čunát <vladimir.cunat@nic.cz> | 2023-06-12 10:32:28 +0200 |
commit | 2f51749d2b5614c8ee4e90c1ca0bb5d69c18e5d5 (patch) | |
tree | 71917d6646b904656befcc88f4ad69b507792832 | |
parent | lib/rules: add basic view capability (diff) | |
download | knot-resolver-2f51749d2b5614c8ee4e90c1ca0bb5d69c18e5d5.tar.xz knot-resolver-2f51749d2b5614c8ee4e90c1ca0bb5d69c18e5d5.zip |
lib/rules: add usable tagging
-rw-r--r-- | daemon/lua/kres-gen-30.lua | 2 | ||||
-rw-r--r-- | daemon/lua/kres-gen-31.lua | 2 | ||||
-rw-r--r-- | daemon/lua/kres-gen-32.lua | 2 | ||||
-rwxr-xr-x | daemon/lua/kres-gen.sh | 2 | ||||
-rw-r--r-- | lib/rules/api.c | 90 | ||||
-rw-r--r-- | lib/rules/api.h | 12 | ||||
-rw-r--r-- | modules/policy/policy.lua | 23 |
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 = ∅ + 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 |