]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
KRT: Using obligatory export table when non-trivial filters are applied.
authorMaria Matejka <mq@ucw.cz>
Tue, 25 Feb 2020 11:07:50 +0000 (12:07 +0100)
committerMaria Matejka <mq@ucw.cz>
Thu, 30 Apr 2020 14:14:26 +0000 (16:14 +0200)
When the kernel filter is anything more sophisticated than FILTER_ACCEPT
or FILTER_REJECT, we should ensure that the old route being sent to the
kernel protocol is really what was sent there before. This fixes the old
misbehavior when an unfiltered old route was used.

filter/filter.h
nest/protocol.h
nest/route.h
nest/rt-table.c
sysdep/unix/krt.Y
sysdep/unix/krt.c

index 9d997efbf9b9c755256d11b2a024dbe4dc39527d..890b869d2d2d172851cb2a1845ac0c051e377137 100644 (file)
@@ -70,6 +70,8 @@ void filters_dump_all(void);
 #define FILTER_REJECT ((struct filter *) 1)
 #define FILTER_UNDEF  ((struct filter *) 2)    /* Used in BGP */
 
+#define TRIVIAL_FILTER(f)   (((f) == FILTER_REJECT) || ((f) == FILTER_ACCEPT))
+
 #define FF_SILENT 2                    /* Silent filter execution */
 
 /* Custom route attributes */
index 7806b9c0ebe5d17b0f6cd797144c4a70a75d5f18..df31d95bca5f7a586005087d8f77a9a81c04a46a 100644 (file)
@@ -466,7 +466,7 @@ struct channel_class {
 #endif
 };
 
-extern struct channel_class channel_bgp;
+extern struct channel_class channel_bgp, channel_krt;
 
 struct channel_config {
   node n;
index 43ad858f942d9ca002add04c3e7ab5abda76eaf7..688d26ff8fc60707a98b4f0860ca102048b7077b 100644 (file)
@@ -329,7 +329,7 @@ void rt_feed_channel_abort(struct channel *c);
 int rt_reload_channel(struct channel *c);
 void rt_reload_channel_abort(struct channel *c);
 void rt_prune_sync(rtable *t, int all);
-int rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, int refeed);
+int rte_update_out(struct channel *c, const net_addr *n, struct rte_src *src, rte *new, rte **old_exported, int refeed);
 struct rtable_config *rt_new_table(struct symbol *s, uint addr_type);
 
 
index b527a6b73374f6353649bb28c475e9125f16488b..f8a7990ca23aa7e490cabe2a2bd6dbbdffeb3fa9 100644 (file)
@@ -330,6 +330,27 @@ rte_cow_rta(rte *r, linpool *lp)
   return r;
 }
 
+/**
+ * rte_free - delete a &rte
+ * @e: &rte to be deleted
+ *
+ * rte_free() deletes the given &rte from the routing table it's linked to.
+ */
+void
+rte_free(rte *e)
+{
+  if (rta_is_cached(e->attrs))
+    rta_free(e->attrs);
+  sl_free(rte_slab, e);
+}
+
+static inline void
+rte_free_quick(rte *e)
+{
+  rta_free(e->attrs);
+  sl_free(rte_slab, e);
+}
+
 static int                             /* Actually better or at least as good as */
 rte_better(rte *new, rte *old)
 {
@@ -483,9 +504,17 @@ do_rt_notify(struct channel *c, net *net, rte *new, rte *old, int refeed)
     }
   }
 
+  struct rte_src *src = old ? old->attrs->src : new->attrs->src;
+
   /* Apply export table */
-  if (c->out_table && !rte_update_out(c, net->n.addr, new, old, refeed))
-    return;
+  struct rte *old_exported = NULL;
+  if (c->out_table)
+  {
+    if (!rte_update_out(c, net->n.addr, src, new, &old_exported, refeed))
+      return;
+  }
+  else if (c->out_filter == FILTER_ACCEPT)
+    old_exported = old;
 
   if (new)
     stats->exp_updates_accepted++;
@@ -515,6 +544,9 @@ do_rt_notify(struct channel *c, net *net, rte *new, rte *old, int refeed)
   }
 
   p->rt_notify(p, c, net, new, old);
+
+  if (c->out_table && old_exported)
+    rte_free_quick(old_exported);
 }
 
 static void
@@ -865,27 +897,6 @@ rte_validate(rte *e)
   return 1;
 }
 
-/**
- * rte_free - delete a &rte
- * @e: &rte to be deleted
- *
- * rte_free() deletes the given &rte from the routing table it's linked to.
- */
-void
-rte_free(rte *e)
-{
-  if (rta_is_cached(e->attrs))
-    rta_free(e->attrs);
-  sl_free(rte_slab, e);
-}
-
-static inline void
-rte_free_quick(rte *e)
-{
-  rta_free(e->attrs);
-  sl_free(rte_slab, e);
-}
-
 static int
 rte_same(rte *x, rte *y)
 {
@@ -2366,17 +2377,15 @@ rt_prune_sync(rtable *t, int all)
  */
 
 int
-rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, int refeed)
+rte_update_out(struct channel *c, const net_addr *n, struct rte_src *src, rte *new, rte **old_exported, int refeed)
 {
   struct rtable *tab = c->out_table;
-  struct rte_src *src;
   rte *old, **pos;
   net *net;
 
   if (new)
   {
     net = net_get(tab, n);
-    src = new->attrs->src;
 
     if (!rta_is_cached(new->attrs))
       new->attrs = rta_lookup(new->attrs);
@@ -2384,7 +2393,6 @@ rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, int re
   else
   {
     net = net_find(tab, n);
-    src = old0->attrs->src;
 
     if (!net)
       goto drop_withdraw;
@@ -2410,7 +2418,7 @@ rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, int re
 
       /* Remove the old rte */
       *pos = old->next;
-      rte_free_quick(old);
+      *old_exported = old;
       tab->rt_count--;
 
       break;
index 95b54d65dae1454bc09fcbbdd7d4bfff3677cd43..623cbd92dc702a4fac0a43c6022141fc89962b7b 100644 (file)
@@ -33,6 +33,7 @@ CF_KEYWORDS(KERNEL, PERSIST, SCAN, TIME, LEARN, DEVICE, ROUTES, GRACEFUL, RESTAR
 CF_KEYWORDS(INTERFACE, PREFERRED)
 
 %type <i> kern_mp_limit
+%type <cc> kern_channel_start kern_proto_channel
 
 CF_GRAMMAR
 
@@ -53,9 +54,17 @@ kern_mp_limit:
  | LIMIT expr  { $$ = $2; if (($2 <= 0) || ($2 > 255)) cf_error("Merge paths limit must be in range 1-255"); }
  ;
 
+kern_channel_start: net_type
+{
+  $$ = this_channel = channel_config_get(&channel_krt, net_label[$1], $1, this_proto);
+  this_proto->net_type = $1;
+};
+
+kern_proto_channel: kern_channel_start channel_opt_list channel_end;
+
 kern_item:
    proto_item
- | proto_channel { this_proto->net_type = $1->net_type; }
+ | kern_proto_channel
  | PERSIST bool { THIS_KRT->persist = $2; }
  | SCAN TIME expr {
       /* Scan time of 0 means scan on startup only */
index 8fdc67f48097381503dda204a4b1cbecb83085ff..7e89a5900004b16ccbc4f12b2ea18e75864cc9ac 100644 (file)
@@ -1029,6 +1029,11 @@ krt_start(struct proto *P)
   default: log(L_ERR "KRT: Tried to start with strange net type: %d", p->p.net_type); return PS_START; break;
   }
 
+  /* If it is needed, setup out table automagically */
+  if (!TRIVIAL_FILTER(p->p.main_channel->out_filter))
+    channel_setup_out_table(p->p.main_channel);
+
+
   bmap_init(&p->sync_map, p->p.pool, 1024);
   bmap_init(&p->seen_map, p->p.pool, 1024);
   add_tail(&krt_proto_list, &p->krt_node);
@@ -1075,6 +1080,15 @@ krt_shutdown(struct proto *P)
   return PS_DOWN;
 }
 
+static int
+krt_channel_reconfigure(struct channel *C, struct channel_config *CC, int *import_changed UNUSED, int *export_changed)
+{
+  if (!*export_changed)
+    return 1;
+
+  return (TRIVIAL_FILTER(C->out_filter) == TRIVIAL_FILTER(CC->out_filter));
+}
+
 static int
 krt_reconfigure(struct proto *p, struct proto_config *CF)
 {
@@ -1147,6 +1161,12 @@ krt_get_attr(const eattr *a, byte *buf, int buflen)
 #define MAYBE_MPLS     0
 #endif
 
+struct channel_class channel_krt = {
+  .channel_size = sizeof(struct channel),
+  .config_size = sizeof(struct channel_config),
+  .reconfigure = krt_channel_reconfigure,
+};
+
 struct protocol proto_unix_kernel = {
   .name =              "Kernel",
   .template =          "kernel%d",