Implement a way to announce flowspec rules without hitting Adj-RIB-In
authorclaudio <claudio@openbsd.org>
Wed, 19 Apr 2023 13:23:33 +0000 (13:23 +0000)
committerclaudio <claudio@openbsd.org>
Wed, 19 Apr 2023 13:23:33 +0000 (13:23 +0000)
and Loc-RIB. Flowspec objects are collected in a single flowrib RIB
and then directly distributed into the various Adj-RIB-Outs.
For this to work add a bypass in the filter logic (flowspec AFI/SAFI
are currently accepted without any rule). The filter language lacks
a way to allow prefixes based on AFI/SAFI which is the minimum needed.
OK tb@

usr.sbin/bgpd/rde.c
usr.sbin/bgpd/rde.h
usr.sbin/bgpd/rde_filter.c
usr.sbin/bgpd/rde_peer.c
usr.sbin/bgpd/rde_rib.c

index 343126e..f71218a 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: rde.c,v 1.602 2023/04/19 07:12:22 claudio Exp $ */
+/*     $OpenBSD: rde.c,v 1.603 2023/04/19 13:23:33 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -859,7 +859,13 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf)
                        asp->flags = F_ATTR_ORIGIN | F_ATTR_ASPATH |
                            F_ATTR_LOCALPREF | F_PREFIX_ANNOUNCED;
 
-                       flowspec_add(curflow, &state, &parent_set);
+                       if (flowspec_valid(curflow->data, curflow->len,
+                           curflow->aid == AID_FLOWSPECv6) == -1)
+                               log_warnx("invalid flowspec update received "
+                                   "from parent");
+                       else
+                               flowspec_add(curflow, &state, &parent_set);
+
                        rde_filterstate_clean(&state);
                        filterset_free(&parent_set);
                        free(curflow);
@@ -887,7 +893,12 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf)
                                log_warnx("rde_dispatch: wrong flowspec len");
                                break;
                        }
-                       flowspec_delete(curflow);
+                       if (flowspec_valid(curflow->data, curflow->len,
+                           curflow->aid == AID_FLOWSPECv6) == -1)
+                               log_warnx("invalid flowspec withdraw received "
+                                   "from parent");
+                       else
+                               flowspec_delete(curflow);
                        free(curflow);
                        curflow = NULL;
                        break;
@@ -4570,11 +4581,32 @@ void
 flowspec_add(struct flowspec *f, struct filterstate *state,
     struct filter_set_head *attrset)
 {
+       struct pt_entry *pte;
+       uint32_t path_id_tx;
+
+       rde_apply_set(attrset, peerself, peerself, state, f->aid);
+       rde_filterstate_set_vstate(state, ROA_NOTFOUND, ASPA_NEVER_KNOWN);
+       path_id_tx = peerself->path_id_tx; /* XXX should use pathid_assign() */
+
+       pte = pt_get_flow(f);
+       if (pte == NULL)
+               pte = pt_add_flow(f);
+
+       if (prefix_flowspec_update(peerself, state, pte, path_id_tx) == 1)
+               peerself->stats.prefix_cnt++;
 }
 
 void
 flowspec_delete(struct flowspec *f)
 {
+       struct pt_entry *pte;
+
+       pte = pt_get_flow(f);
+       if (pte == NULL)
+               return;
+
+       if (prefix_flowspec_withdraw(peerself, pte) == 1)
+               peerself->stats.prefix_cnt--;
 }
 
 /* clean up */
index 7660312..1ed3e15 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: rde.h,v 1.292 2023/04/19 07:09:47 claudio Exp $ */
+/*     $OpenBSD: rde.h,v 1.293 2023/04/19 13:23:33 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org> and
@@ -567,9 +567,14 @@ int                 rib_dump_subtree(uint16_t, struct bgpd_addr *, uint8_t,
                    int (*)(void *));
 void            rib_dump_terminate(void *);
 
+extern struct rib flowrib;
+
 static inline struct rib *
 re_rib(struct rib_entry *re)
 {
+       if (re->prefix->aid == AID_FLOWSPECv4 ||
+           re->prefix->aid == AID_FLOWSPECv6)
+               return &flowrib;
        return rib_byid(re->rib_id);
 }
 
@@ -596,6 +601,12 @@ int                 prefix_update(struct rib *, struct rde_peer *, uint32_t,
                    uint32_t, struct filterstate *, struct bgpd_addr *, int);
 int             prefix_withdraw(struct rib *, struct rde_peer *, uint32_t,
                    struct bgpd_addr *, int);
+int             prefix_flowspec_update(struct rde_peer *, struct filterstate *,
+                   struct pt_entry *, uint32_t);
+int             prefix_flowspec_withdraw(struct rde_peer *, struct pt_entry *);
+void            prefix_flowspec_dump(uint8_t, void *,
+                   void (*)(struct rib_entry *, void *),
+                   void (*)(void *, uint8_t));
 void            prefix_add_eor(struct rde_peer *, uint8_t);
 void            prefix_adjout_update(struct prefix *, struct rde_peer *,
                    struct filterstate *, struct pt_entry *, uint32_t);
index e8871b8..4c0b570 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: rde_filter.c,v 1.134 2023/03/10 07:57:15 claudio Exp $ */
+/*     $OpenBSD: rde_filter.c,v 1.135 2023/04/19 13:23:33 claudio Exp $ */
 
 /*
  * Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org>
@@ -834,6 +834,9 @@ rde_filter(struct filter_head *rules, struct rde_peer *peer,
        if (rules == NULL)
                return (action);
 
+       if (prefix->aid == AID_FLOWSPECv4 || prefix->aid == AID_FLOWSPECv6)
+               return (ACTION_ALLOW);
+
        f = TAILQ_FIRST(rules);
        while (f != NULL) {
                RDE_FILTER_TEST_ATTRIB(
index 9248851..bbf0a9e 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: rde_peer.c,v 1.31 2023/03/10 07:57:16 claudio Exp $ */
+/*     $OpenBSD: rde_peer.c,v 1.32 2023/04/19 13:23:33 claudio Exp $ */
 
 /*
  * Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org>
@@ -562,6 +562,9 @@ peer_dump(struct rde_peer *peer, uint8_t aid)
        } else if (peer->export_type == EXPORT_DEFAULT_ROUTE) {
                up_generate_default(peer, aid);
                rde_up_dump_done(peer, aid);
+       } else if (aid == AID_FLOWSPECv4 || aid == AID_FLOWSPECv6) {
+               prefix_flowspec_dump(aid, peer, rde_up_dump_upcall,
+                   rde_up_dump_done);
        } else {
                if (rib_dump_new(peer->loc_rib_id, aid, RDE_RUNNER_ROUNDS, peer,
                    rde_up_dump_upcall, rde_up_dump_done, NULL) == -1)
index 3aa355b..f04c251 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: rde_rib.c,v 1.258 2023/04/07 13:49:03 claudio Exp $ */
+/*     $OpenBSD: rde_rib.c,v 1.259 2023/04/19 13:23:33 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org>
@@ -37,6 +37,7 @@
  */
 uint16_t rib_size;
 struct rib **ribs;
+struct rib flowrib = { .id = 1, .tree = RB_INITIALIZER(&flowrib.tree) };
 
 struct rib_entry *rib_add(struct rib *, struct pt_entry *);
 static inline int rib_compare(const struct rib_entry *,
@@ -1117,6 +1118,91 @@ prefix_withdraw(struct rib *rib, struct rde_peer *peer, uint32_t path_id,
        return (1);
 }
 
+/*
+ * Special functions for flowspec until full integration is available.
+ * This just directly feeds the prefixes into the Adj-RIB-Out bypassing
+ * Adj-RIB-In and Loc-RIB for now.
+ */
+int
+prefix_flowspec_update(struct rde_peer *peer, struct filterstate *state,
+    struct pt_entry *pte, uint32_t path_id_tx)
+{
+       struct rde_aspath *asp, *nasp;
+       struct rde_community *comm, *ncomm;
+       struct rib_entry *re;
+       struct prefix *new, *old;
+
+       re = rib_get(&flowrib, pte);
+       if (re == NULL)
+               re = rib_add(&flowrib, pte);
+
+       old = prefix_bypeer(re, peer, 0);
+       new = prefix_alloc();
+
+       nasp = &state->aspath;
+       ncomm = &state->communities;
+       if ((asp = path_lookup(nasp)) == NULL) {
+               /* Path not available, create and link a new one. */
+               asp = path_copy(path_get(), nasp);
+               path_link(asp);
+       }
+       if ((comm = communities_lookup(ncomm)) == NULL) {
+               /* Communities not available, create and link a new one. */
+               comm = communities_link(ncomm);
+       }
+
+       prefix_link(new, re, re->prefix, peer, 0, path_id_tx, asp, comm,
+           NULL, 0, 0);
+       TAILQ_INSERT_HEAD(&re->prefix_h, new, entry.list.rib);
+
+       rde_generate_updates(re, new, old, EVAL_DEFAULT);
+
+       if (old != NULL) {
+               TAILQ_REMOVE(&re->prefix_h, old, entry.list.rib);
+               prefix_unlink(old);
+               prefix_free(old);
+               return 0;
+       }
+       return 1;
+}
+
+/*
+ * Remove a possible flowspec prefix from all Adj-RIB-Outs.
+ */
+int
+prefix_flowspec_withdraw(struct rde_peer *peer, struct pt_entry *pte)
+{
+       struct rib_entry *re;
+       struct prefix *p;
+
+       re = rib_get(&flowrib, pte);
+       if (re == NULL)
+               return 0;
+       p = prefix_bypeer(re, peer, 0);
+       if (p == NULL)
+               return 0;
+       rde_generate_updates(re, NULL, p, EVAL_DEFAULT);
+       TAILQ_REMOVE(&re->prefix_h, p, entry.list.rib);
+       prefix_unlink(p);
+       prefix_free(p);
+       return 1;
+}
+
+/*
+ * Push all flowspec rules into a newly available Adj-RIB-Out.
+ */
+void
+prefix_flowspec_dump(uint8_t aid, void *arg,
+    void (*call)(struct rib_entry *, void *), void (*done)(void *, uint8_t))
+{
+       struct rib_entry *re, *next;
+
+       RB_FOREACH_SAFE(re, rib_tree, rib_tree(&flowrib), next)
+               call(re, arg);
+       if (done != NULL)
+               done(arg, aid);
+}
+
 /*
  * Insert an End-of-RIB marker into the update queue.
  */