From e20bef69ccc4a85ef62359ee539c9db2dbe09127 Mon Sep 17 00:00:00 2001
From: Ondrej Zajicek <santiago@crfreenet.org>
Date: Sat, 7 Jan 2023 20:18:44 +0100
Subject: [PATCH 1/3] Filter: Change linearization of branches in switch
 instruction

Most branching instructions (FI_CONDITION, FI_AND, FI_OR) linearize its
branches in a recursive way, while FI_SWITCH branches are linearized
from parser even before the switch instruction is allocated.

Change linearization of FI_SWITCH branches to make it similar to other
branching instructions. This also fixes an issue with constant
switch evaluation, where linearized branch is mistaken for
non-linearized during switch construction.

Thanks to Jiten Kumar Pathy for the bugreport.
---
 filter/config.Y |  7 +++----
 filter/data.h   |  1 +
 filter/f-inst.c | 25 ++++++++++++++++++++++---
 filter/tree.c   | 20 ++++++++++++++++++++
 4 files changed, 46 insertions(+), 7 deletions(-)

diff --git a/filter/config.Y b/filter/config.Y
index 68ee1a847..1d9d9aa97 100644
--- a/filter/config.Y
+++ b/filter/config.Y
@@ -676,16 +676,15 @@ switch_body: /* EMPTY */ { $$ = NULL; }
  | switch_body switch_items ':' cmds_scoped  {
      /* Fill data fields */
      struct f_tree *t;
-     struct f_line *line = f_linearize($4, 0);
      for (t = $2; t; t = t->left)
-       t->data = line;
+       t->data = $4;
      $$ = f_merge_items($1, $2);
    }
  | switch_body ELSECOL cmds_scoped {
      struct f_tree *t = f_new_tree();
      t->from.type = t->to.type = T_VOID;
      t->right = t;
-     t->data = f_linearize($3, 0);
+     t->data = $3;
      $$ = f_merge_items($1, t);
  }
  ;
@@ -972,7 +971,7 @@ cmd:
    }
  | function_call ';' { $$ = f_new_inst(FI_DROP_RESULT, $1); }
  | CASE term '{' switch_body '}' {
-      $$ = f_new_inst(FI_SWITCH, $2, build_tree($4));
+      $$ = f_new_inst(FI_SWITCH, $2, $4);
    }
 
  | dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($1); }
diff --git a/filter/data.h b/filter/data.h
index 5edeaedb7..700609e9d 100644
--- a/filter/data.h
+++ b/filter/data.h
@@ -198,6 +198,7 @@ struct f_trie_walk_state
 struct f_tree *f_new_tree(void);
 struct f_tree *build_tree(struct f_tree *);
 const struct f_tree *find_tree(const struct f_tree *t, const struct f_val *val);
+const struct f_tree *find_tree_linear(const struct f_tree *t, const struct f_val *val);
 int same_tree(const struct f_tree *t0, const struct f_tree *t2);
 int tree_node_count(const struct f_tree *t);
 void tree_format(const struct f_tree *t, buffer *buf);
diff --git a/filter/f-inst.c b/filter/f-inst.c
index 9a3a22abc..2d2a30e4f 100644
--- a/filter/f-inst.c
+++ b/filter/f-inst.c
@@ -1285,14 +1285,33 @@
 
     FID_MEMBER(struct f_tree *, tree, [[!same_tree(f1->tree, f2->tree)]], "tree %p", item->tree);
 
+    FID_LINEARIZE_BODY()
+    /* Linearize all branches in switch */
+    struct f_inst *last_inst = NULL;
+    struct f_line *last_line = NULL;
+    for (struct f_tree *t = whati->tree; t; t = t->left)
+    {
+      if (t->data != last_inst)
+      {
+	last_inst = t->data;
+	last_line = f_linearize(t->data, 0);
+      }
+
+      t->data = last_line;
+    }
+
+    /* Balance the tree */
+    item->tree = build_tree(whati->tree);
+
     FID_ITERATE_BODY()
-      tree_walk(whati->tree, f_add_tree_lines, fit);
+    tree_walk(whati->tree, f_add_tree_lines, fit);
 
     FID_INTERPRET_BODY()
-    const struct f_tree *t = find_tree(tree, &v1);
+    /* In parse-time use find_tree_linear(), in runtime use find_tree() */
+    const struct f_tree *t = FID_HIC(,find_tree,find_tree_linear)(tree, &v1);
     if (!t) {
       v1.type = T_VOID;
-      t = find_tree(tree, &v1);
+      t = FID_HIC(,find_tree,find_tree_linear)(tree, &v1);
       if (!t) {
 	debug( "No else statement?\n");
 	FID_HIC(,break,return NULL);
diff --git a/filter/tree.c b/filter/tree.c
index 97bf7dae5..25cf93e49 100644
--- a/filter/tree.c
+++ b/filter/tree.c
@@ -38,6 +38,26 @@ find_tree(const struct f_tree *t, const struct f_val *val)
     return find_tree(t->left, val);
 }
 
+/**
+ * find_tree_linear
+ * @t: tree to search in
+ * @val: value to find
+ *
+ * Search for given value in the degenerated linear tree, which is generated by
+ * parser before build_tree() is applied. The tree is not sorted and all nodes
+ * are linked by left ptr.
+ */
+const struct f_tree *
+find_tree_linear(const struct f_tree *t, const struct f_val *val)
+{
+  for (; t; t = t->left)
+    if ((val_compare(&(t->from), val) != 1) &&
+	(val_compare(&(t->to), val) != -1))
+      return t;
+
+  return NULL;
+}
+
 static struct f_tree *
 build_tree_rec(struct f_tree **buf, int l, int h)
 {

From 64a2b7aaa303be0b407508747bfc96c1c656f1e2 Mon Sep 17 00:00:00 2001
From: Mike Crute <mike@crute.us>
Date: Thu, 12 Jan 2023 17:40:53 +0100
Subject: [PATCH 2/3] Log message before aborting

Log message before aborting due to watchdog timeout. We have to use
async-safe write to debug log, as it is done in signal handler.

Minor changes from committer.
---
 lib/birdlib.h     |  1 +
 sysdep/unix/io.c  |  2 ++
 sysdep/unix/log.c | 22 ++++++++++++++++++++++
 3 files changed, 25 insertions(+)

diff --git a/lib/birdlib.h b/lib/birdlib.h
index 81d4908ac..e03bd0b2f 100644
--- a/lib/birdlib.h
+++ b/lib/birdlib.h
@@ -160,6 +160,7 @@ void bug(const char *msg, ...) NORET;
 #define L_BUG "\011"			/* BIRD bugs */
 
 void debug(const char *msg, ...);	/* Printf to debug output */
+void debug_safe(const char *msg);	/* Printf to debug output, async-safe */
 
 /* Debugging */
 
diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c
index 810e782d9..e131ca41f 100644
--- a/sysdep/unix/io.c
+++ b/sysdep/unix/io.c
@@ -2128,6 +2128,8 @@ watchdog_sigalrm(int sig UNUSED)
   config->latency_limit = 0xffffffff;
   io_update_time();
 
+  debug_safe("Watchdog timer timed out\n");
+
   /* We want core dump */
   abort();
 }
diff --git a/sysdep/unix/log.c b/sysdep/unix/log.c
index 4e9df069e..53122aee3 100644
--- a/sysdep/unix/log.c
+++ b/sysdep/unix/log.c
@@ -31,6 +31,7 @@
 #include "lib/lists.h"
 #include "sysdep/unix/unix.h"
 
+static int dbg_fd = -1;
 static FILE *dbgf;
 static list *current_log_list;
 static char *current_syslog_name; /* NULL -> syslog closed */
@@ -324,6 +325,21 @@ debug(const char *msg, ...)
   va_end(args);
 }
 
+/**
+ * debug_safe - async-safe write to debug output
+ * @msg: a string message
+ *
+ * This function prints the message @msg to the debugging output in a
+ * way that is async safe and can be used in signal handlers. No newline
+ * character is appended.
+ */
+void
+debug_safe(const char *msg)
+{
+  if (dbg_fd >= 0)
+    write(dbg_fd, msg, strlen(msg));
+}
+
 static list *
 default_log_list(int initial, const char **syslog_name)
 {
@@ -422,8 +438,10 @@ done:
 void
 log_init_debug(char *f)
 {
+  dbg_fd = -1;
   if (dbgf && dbgf != stderr)
     fclose(dbgf);
+
   if (!f)
     dbgf = NULL;
   else if (!*f)
@@ -434,6 +452,10 @@ log_init_debug(char *f)
     fprintf(stderr, "bird: Unable to open debug file %s: %s\n", f, strerror(errno));
     exit(1);
   }
+
   if (dbgf)
+  {
     setvbuf(dbgf, NULL, _IONBF, 0);
+    dbg_fd = fileno(dbgf);
+  }
 }

From 7fb23041a52d01754c53ba963e2282e524813364 Mon Sep 17 00:00:00 2001
From: Ondrej Zajicek <santiago@crfreenet.org>
Date: Fri, 13 Jan 2023 13:17:46 +0100
Subject: [PATCH 3/3] BSD: Add support for kernel route metric

Add support for kernel route metric/priority, exported as krt_metric
attribute, like in Linux. This should also fix issues with overwriting
or removing system routes.
---
 sysdep/bsd/krt-sock.Y | 10 +++++++++-
 sysdep/bsd/krt-sock.c | 39 +++++++++++++++++++++++++++++++++++++--
 sysdep/bsd/krt-sys.h  |  2 ++
 3 files changed, 48 insertions(+), 3 deletions(-)

diff --git a/sysdep/bsd/krt-sock.Y b/sysdep/bsd/krt-sock.Y
index 8581bd43b..a03d6df5f 100644
--- a/sysdep/bsd/krt-sock.Y
+++ b/sysdep/bsd/krt-sock.Y
@@ -10,7 +10,7 @@ CF_HDR
 
 CF_DECLS
 
-CF_KEYWORDS(KERNEL, TABLE)
+CF_KEYWORDS(KERNEL, TABLE, METRIC)
 
 CF_GRAMMAR
 
@@ -25,6 +25,14 @@ kern_sys_item:
 
 	THIS_KRT->sys.table_id = $3;
    }
+ | METRIC expr {
+     if ($2 && !krt_max_metric)
+       cf_error("Kernel route metric not supported");
+     if ($2 > krt_max_metric)
+       cf_error("Kernel table id must be in range 0-%u", krt_max_metric);
+
+     THIS_KRT->sys.metric = $2;
+   }
  ;
 
 CF_CODE
diff --git a/sysdep/bsd/krt-sock.c b/sysdep/bsd/krt-sock.c
index 47f5cf595..540c246f3 100644
--- a/sysdep/bsd/krt-sock.c
+++ b/sysdep/bsd/krt-sock.c
@@ -47,6 +47,11 @@ const int rt_default_ecmp = 0;
  * table_id is specified explicitly as sysctl scan argument, while in FreeBSD it
  * is handled implicitly by changing default table using setfib() syscall.
  *
+ * OpenBSD allows to use route metric. The behavior is controlled by these macro
+ * KRT_USE_METRIC, which enables use of rtm_priority in route send/recevive.
+ * There is also KRT_DEFAULT_METRIC and KRT_MAX_METRIC for default and maximum
+ * metric values.
+ *
  * KRT_SHARED_SOCKET	- use shared kernel socked instead of one for each krt_proto
  * KRT_USE_SETFIB_SCAN	- use setfib() for sysctl() route scan
  * KRT_USE_SETFIB_SOCK	- use SO_SETFIB socket option for kernel sockets
@@ -63,6 +68,9 @@ const int rt_default_ecmp = 0;
 
 #ifdef __OpenBSD__
 #define KRT_MAX_TABLES (RT_TABLEID_MAX+1)
+#define KRT_USE_METRIC
+#define KRT_MAX_METRIC 255
+#define KRT_DEFAULT_METRIC 56
 #define KRT_SHARED_SOCKET
 #define KRT_USE_SYSCTL_7
 #endif
@@ -71,6 +79,14 @@ const int rt_default_ecmp = 0;
 #define KRT_MAX_TABLES 1
 #endif
 
+#ifndef KRT_MAX_METRIC
+#define KRT_MAX_METRIC 0
+#endif
+
+#ifndef KRT_DEFAULT_METRIC
+#define KRT_DEFAULT_METRIC 0
+#endif
+
 
 /* Dynamic max number of tables */
 
@@ -143,6 +159,10 @@ static struct krt_proto *krt_table_map[KRT_MAX_TABLES][2];
 #endif
 
 
+/* Make it available to parser code */
+const uint krt_max_metric = KRT_MAX_METRIC;
+
+
 /* Route socket message processing */
 
 int
@@ -231,6 +251,10 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e)
   msg.rtm.rtm_tableid = KRT_CF->sys.table_id;
 #endif
 
+#ifdef KRT_USE_METRIC
+  msg.rtm.rtm_priority = KRT_CF->sys.metric;
+#endif
+
 #ifdef RTF_REJECT
   if(a->dest == RTD_UNREACHABLE)
     msg.rtm.rtm_flags |= RTF_REJECT;
@@ -586,7 +610,7 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan)
   e = rte_get_temp(&a, p->p.main_source);
   e->net = net;
 
-  ea_list *ea = alloca(sizeof(ea_list) + 1 * sizeof(eattr));
+  ea_list *ea = alloca(sizeof(ea_list) + 2 * sizeof(eattr));
   *ea = (ea_list) { .count = 1, .next = e->attrs->eattrs };
   e->attrs->eattrs = ea;
 
@@ -596,6 +620,15 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan)
     .u.data = src2,
   };
 
+#ifdef KRT_USE_METRIC
+  ea->count++;
+  ea->attrs[1] = (eattr) {
+    .id = EA_KRT_METRIC,
+    .type = EAF_TYPE_INT,
+    .u.data = msg->rtm.rtm_priority,
+  };
+#endif
+
   if (scan)
     krt_got_route(p, e, src);
   else
@@ -1155,7 +1188,7 @@ krt_sys_shutdown(struct krt_proto *p)
 int
 krt_sys_reconfigure(struct krt_proto *p UNUSED, struct krt_config *n, struct krt_config *o)
 {
-  return n->sys.table_id == o->sys.table_id;
+  return (n->sys.table_id == o->sys.table_id) && (n->sys.metric == o->sys.metric);
 }
 
 void
@@ -1168,11 +1201,13 @@ krt_sys_preconfig(struct config *c UNUSED)
 void krt_sys_init_config(struct krt_config *c)
 {
   c->sys.table_id = 0; /* Default table */
+  c->sys.metric = KRT_DEFAULT_METRIC;
 }
 
 void krt_sys_copy_config(struct krt_config *d, struct krt_config *s)
 {
   d->sys.table_id = s->sys.table_id;
+  d->sys.metric = s->sys.metric;
 }
 
 
diff --git a/sysdep/bsd/krt-sys.h b/sysdep/bsd/krt-sys.h
index 575018843..198373c09 100644
--- a/sysdep/bsd/krt-sys.h
+++ b/sysdep/bsd/krt-sys.h
@@ -32,9 +32,11 @@ static inline void kif_sys_copy_config(struct kif_config *d UNUSED, struct kif_c
 /* Kernel routes */
 
 extern uint krt_max_tables;
+extern const uint krt_max_metric;
 
 struct krt_params {
   int table_id;				/* Kernel table ID we sync with */
+  u32 metric;				/* Kernel metric used for all routes */
 };
 
 struct krt_state {