]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
lib/rules: add usable tagging
authorVladimír Čunát <vladimir.cunat@nic.cz>
Mon, 4 Jul 2022 11:35:50 +0000 (13:35 +0200)
committerVladimír Čunát <vladimir.cunat@nic.cz>
Mon, 12 Jun 2023 08:32:28 +0000 (10:32 +0200)
daemon/lua/kres-gen-30.lua
daemon/lua/kres-gen-31.lua
daemon/lua/kres-gen-32.lua
daemon/lua/kres-gen.sh
lib/rules/api.c
lib/rules/api.h
modules/policy/policy.lua

index 1e4a288025e8a44da115f0d810951a0b03e3c9d6..dd671d15083db54016342de7fc7c2b32199adb54 100644 (file)
@@ -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;
index 810560dd2799e8e603e526d7f9eb2c79b67ce5b0..370de14310163581844adfc20b2102c21d13a83f 100644 (file)
@@ -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;
index 84156d05fd13c43409f31c395da23dc27e143c03..899c05cf04c4a5796fa347a4cd9b31fde427170f 100644 (file)
@@ -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;
index b56a8a38c21d8fd9325942d0a306926f46bc3dc3..5af527ae4ef0815f3722c0c4f29729b23e67f58c 100755 (executable)
@@ -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
 
 
index b8f27b55c7d896b277989ed194b0834d223d2fd3..e37efdd81669eb3aecce39c0a1df3fc8c7d7a35b 100644 (file)
@@ -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;
 
index e9d10277e71e21c5e7490d9a8900f7bcb5da7c59..6ccbde89cad6192a53adedb5b99e64f496f1d4bd 100644 (file)
@@ -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);
+
index 1990837aa5a44632532e40aabfd6936adf588214..cc77e487bc072a9d5986f296ed5dac83f3adef0a 100644 (file)
@@ -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