]> git.proxmox.com Git - mirror_frr.git/commitdiff
bgpd: add support for rpki revalidation on rpki updates
authorMarcel Röthke <marcel.roethke@haw-hamburg.de>
Thu, 29 Mar 2018 09:15:18 +0000 (11:15 +0200)
committerMarcel Röthke <marcel.roethke@haw-hamburg.de>
Thu, 5 Jul 2018 14:06:37 +0000 (16:06 +0200)
Signed-off-by: Marcel Röthke <marcel.roethke@haw-hamburg.de>
bgpd/bgp_rpki.c
bgpd/bgp_table.c
bgpd/bgp_table.h
doc/user/rpki.rst

index db182eef986846070693526a3009364dcc75fc6a..d73744950f4f0ee7844abdcec487215a61c2c1c0 100644 (file)
@@ -47,6 +47,7 @@
 #include "bgpd/bgp_attr.h"
 #include "bgpd/bgp_aspath.h"
 #include "bgpd/bgp_route.h"
+#include "lib/thread.h"
 #include "rtrlib/rtrlib.h"
 #include "rtrlib/rtr_mgr.h"
 #include "rtrlib/lib/ip.h"
@@ -128,16 +129,22 @@ static void route_match_free(void *rule);
 static route_map_result_t route_match(void *rule, struct prefix *prefix,
                                      route_map_object_t type, void *object);
 static void *route_match_compile(const char *arg);
+static void revalidate_bgp_node(struct bgp_node *bgp_node, afi_t afi,
+                               safi_t safi);
 
 static struct rtr_mgr_config *rtr_config;
 static struct list *cache_list;
 static int rtr_is_running;
+static int rtr_is_stopping;
+static int rtr_is_starting;
 static int rpki_debug;
 static unsigned int polling_period;
 static unsigned int expire_interval;
 static unsigned int retry_interval;
 static unsigned int timeout;
 static unsigned int initial_synchronisation_timeout;
+static int rpki_sync_socket_rtr;
+static int rpki_sync_socket_bgpd;
 
 static struct cmd_node rpki_node = {RPKI_NODE, "%s(config-rpki)# ", 1};
 static struct route_map_rule_cmd route_match_rpki_cmd = {
@@ -185,6 +192,14 @@ static void free_tr_socket(struct cache *cache)
 static int rpki_validate_prefix(struct peer *peer, struct attr *attr,
                                struct prefix *prefix);
 
+static void ipv6_addr_to_network_byte_order(const uint32_t *src, uint32_t *dest)
+{
+       int i;
+
+       for (i = 0; i < 4; i++)
+               dest[i] = htonl(src[i]);
+}
+
 static void ipv6_addr_to_host_byte_order(const uint32_t *src, uint32_t *dest)
 {
        int i;
@@ -303,10 +318,159 @@ inline int is_running(void)
        return rtr_is_running;
 }
 
+static struct prefix *pfx_record_to_prefix(struct pfx_record *record)
+{
+       struct prefix *prefix = prefix_new();
+
+       prefix->prefixlen = record->min_len;
+
+       if (record->prefix.ver == LRTR_IPV4) {
+               prefix->family = AF_INET;
+               prefix->u.prefix4.s_addr = htonl(record->prefix.u.addr4.addr);
+       } else {
+               prefix->family = AF_INET6;
+               ipv6_addr_to_network_byte_order(record->prefix.u.addr6.addr,
+                                               prefix->u.prefix6.s6_addr32);
+       }
+
+       return prefix;
+}
+
+static int bgpd_sync_callback(struct thread *thread)
+{
+       struct bgp *bgp;
+       struct listnode *node;
+       struct prefix *prefix;
+       struct pfx_record rec;
+
+       thread_add_read(bm->master, bgpd_sync_callback, NULL,
+                       rpki_sync_socket_bgpd, NULL);
+       int retval =
+               read(rpki_sync_socket_bgpd, &rec, sizeof(struct pfx_record));
+       if (retval != sizeof(struct pfx_record)) {
+               RPKI_DEBUG("Could not read from rpki_sync_socket_bgpd");
+               return retval;
+       }
+       prefix = pfx_record_to_prefix(&rec);
+
+       afi_t afi = (rec.prefix.ver == LRTR_IPV4) ? AFI_IP : AFI_IP6;
+
+       for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) {
+               safi_t safi;
+
+               for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) {
+                       if (!bgp->rib[afi][safi])
+                               continue;
+
+                       struct list *matches = list_new();
+
+                       matches->del = (void (*)(void *))bgp_unlock_node;
+
+                       bgp_table_range_lookup(bgp->rib[afi][safi], prefix,
+                                              rec.max_len, matches);
+
+
+                       struct bgp_node *bgp_node;
+
+                       for (ALL_LIST_ELEMENTS_RO(matches, node, bgp_node))
+                               revalidate_bgp_node(bgp_node, afi, safi);
+
+                       list_delete_and_null(&matches);
+               }
+       }
+
+       prefix_free(prefix);
+       return 0;
+}
+
+static void revalidate_bgp_node(struct bgp_node *bgp_node, afi_t afi,
+                               safi_t safi)
+{
+       struct bgp_adj_in *ain;
+
+       for (ain = bgp_node->adj_in; ain; ain = ain->next) {
+               int ret;
+               struct bgp_info *bgp_info = bgp_node->info;
+               mpls_label_t *label = NULL;
+               uint32_t num_labels = 0;
+
+               if (bgp_info && bgp_info->extra) {
+                       label = bgp_info->extra->label;
+                       num_labels = bgp_info->extra->num_labels;
+               }
+               ret = bgp_update(ain->peer, &bgp_node->p, 0, ain->attr, afi,
+                                safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL,
+                                label, num_labels, 1, NULL);
+
+               if (ret < 0) {
+                       bgp_unlock_node(bgp_node);
+                       return;
+               }
+       }
+}
+
+static void revalidate_all_routes(void)
+{
+       struct bgp *bgp;
+       struct listnode *node;
+       struct bgp_node *bgp_node;
+
+       for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) {
+               for (size_t i = 0; i < 2; i++) {
+                       safi_t safi;
+                       afi_t afi = (i == 0) ? AFI_IP : AFI_IP6;
+
+                       for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) {
+                               if (!bgp->rib[afi][safi])
+                                       continue;
+
+                               for (bgp_node =
+                                            bgp_table_top(bgp->rib[afi][safi]);
+                                    bgp_node;
+                                    bgp_node = bgp_route_next(bgp_node)) {
+                                       if (bgp_node->info != NULL) {
+                                               revalidate_bgp_node(bgp_node,
+                                                                   afi, safi);
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+static void rpki_update_cb_sync_rtr(struct pfx_table *p __attribute__((unused)),
+                                   const struct pfx_record rec,
+                                   const bool added __attribute__((unused)))
+{
+       if (rtr_is_stopping || rtr_is_starting)
+               return;
+
+       int retval =
+               write(rpki_sync_socket_rtr, &rec, sizeof(struct pfx_record));
+       if (retval != sizeof(struct pfx_record))
+               RPKI_DEBUG("Could not write to rpki_sync_socket_rtr");
+}
+
+static void rpki_init_sync_socket(void)
+{
+       int fds[2];
+
+       RPKI_DEBUG("initializing sync socket");
+       if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, fds) != 0) {
+               RPKI_DEBUG("Could not open rpki sync socket");
+               return;
+       }
+       rpki_sync_socket_rtr = fds[0];
+       rpki_sync_socket_bgpd = fds[1];
+       thread_add_read(bm->master, bgpd_sync_callback, NULL,
+                       rpki_sync_socket_bgpd, NULL);
+}
+
 static int bgp_rpki_init(struct thread_master *master)
 {
        rpki_debug = 0;
        rtr_is_running = 0;
+       rtr_is_stopping = 0;
 
        cache_list = list_new();
        cache_list->del = (void (*)(void *)) & free_cache;
@@ -318,6 +482,7 @@ static int bgp_rpki_init(struct thread_master *master)
        initial_synchronisation_timeout =
                INITIAL_SYNCHRONISATION_TIMEOUT_DEFAULT;
        install_cli_commands();
+       rpki_init_sync_socket();
        return 0;
 }
 
@@ -326,6 +491,9 @@ static int bgp_rpki_fini(void)
        stop();
        list_delete_and_null(&cache_list);
 
+       close(rpki_sync_socket_rtr);
+       close(rpki_sync_socket_bgpd);
+
        return 0;
 }
 
@@ -344,6 +512,9 @@ static int start(void)
        unsigned int waiting_time = 0;
        int ret;
 
+       rtr_is_stopping = 0;
+       rtr_is_starting = 1;
+
        if (list_isempty(cache_list)) {
                RPKI_DEBUG(
                        "No caches were found in config. Prefix validation is off.");
@@ -353,9 +524,10 @@ static int start(void)
        int groups_len = listcount(cache_list);
        struct rtr_mgr_group *groups = get_groups();
 
+       RPKI_DEBUG("Polling period: %d", polling_period);
        ret = rtr_mgr_init(&rtr_config, groups, groups_len, polling_period,
-                          expire_interval, retry_interval, NULL, NULL, NULL,
-                          NULL);
+                          expire_interval, retry_interval,
+                          rpki_update_cb_sync_rtr, NULL, NULL, NULL);
        if (ret == RTR_ERROR) {
                RPKI_DEBUG("Init rtr_mgr failed.");
                return ERROR;
@@ -378,9 +550,13 @@ static int start(void)
        }
        if (rtr_mgr_conf_in_sync(rtr_config)) {
                RPKI_DEBUG("Got synchronisation with at least one RPKI cache!");
+               RPKI_DEBUG("Forcing revalidation.");
+               rtr_is_starting = 0;
+               revalidate_all_routes();
        } else {
                RPKI_DEBUG(
                        "Timeout expired! Proceeding without RPKI validation data.");
+               rtr_is_starting = 0;
        }
 
        XFREE(MTYPE_BGP_RPKI_CACHE_GROUP, groups);
@@ -390,6 +566,7 @@ static int start(void)
 
 static void stop(void)
 {
+       rtr_is_stopping = 1;
        if (rtr_is_running) {
                rtr_mgr_stop(rtr_config);
                rtr_mgr_free(rtr_config);
index 613b924d0d7ace099115d19cb4672a6496cd98c9..94e2d83cfe1ff287f99fbb6430c0a3a1e72f66c3 100644 (file)
@@ -114,3 +114,65 @@ struct bgp_table *bgp_table_init(struct bgp *bgp, afi_t afi, safi_t safi)
 
        return rt;
 }
+
+static struct bgp_node *
+bgp_route_next_until_maxlen(struct bgp_node *node, const struct bgp_node *limit,
+                           const uint8_t maxlen)
+{
+       if (node->l_left && node->p.prefixlen < maxlen
+           && node->l_left->p.prefixlen <= maxlen) {
+               return bgp_node_from_rnode(node->l_left);
+       }
+       if (node->l_right && node->p.prefixlen < maxlen
+           && node->l_right->p.prefixlen <= maxlen) {
+               return bgp_node_from_rnode(node->l_right);
+       }
+
+       while (node->parent && node != limit) {
+               if (bgp_node_from_rnode(node->parent->l_left) == node
+                   && node->parent->l_right) {
+                       return bgp_node_from_rnode(node->parent->l_right);
+               }
+               node = bgp_node_from_rnode(node->parent);
+       }
+       return NULL;
+}
+
+void bgp_table_range_lookup(const struct bgp_table *table, struct prefix *p,
+                           uint8_t maxlen, struct list *matches)
+{
+       struct bgp_node *node = bgp_node_from_rnode(table->route_table->top);
+       struct bgp_node *matched = NULL;
+
+       if (node == NULL)
+               return;
+
+       while (node && node->p.prefixlen <= p->prefixlen
+              && prefix_match(&node->p, p)) {
+               if (node->info && node->p.prefixlen == p->prefixlen) {
+                       matched = node;
+                       break;
+               }
+               node = bgp_node_from_rnode(node->link[prefix_bit(
+                       &p->u.prefix, node->p.prefixlen)]);
+       }
+
+       if ((matched == NULL && node->p.prefixlen > maxlen) || !node->parent)
+               return;
+       else if (matched == NULL)
+               matched = node = bgp_node_from_rnode(node->parent);
+
+       if (matched->info) {
+               bgp_lock_node(matched);
+               listnode_add(matches, matched);
+       }
+
+       while ((node = bgp_route_next_until_maxlen(node, matched, maxlen))) {
+               if (prefix_match(p, &node->p)) {
+                       if (node->info) {
+                               bgp_lock_node(node);
+                               listnode_add(matches, node);
+                       }
+               }
+       }
+}
index 388c2472270572ded9e058ff8c50815f29915611..60c2cbd4a483c3e646595ebc4434b2f16badd6ff 100644 (file)
@@ -24,6 +24,7 @@
 #include "mpls.h"
 #include "table.h"
 #include "queue.h"
+#include "linklist.h"
 
 struct bgp_table {
        /* table belongs to this instance */
@@ -309,4 +310,7 @@ static inline uint64_t bgp_table_version(struct bgp_table *table)
        return table->version;
 }
 
+void bgp_table_range_lookup(const struct bgp_table *table, struct prefix *p,
+                           uint8_t maxlen, struct list *matches);
+
 #endif /* _QUAGGA_BGP_TABLE_H */
index 93a8e4396a261d113ea2e13f992ebcb530fe8447..c35928db0793be846d2fc0bef96ab4f331d9252d 100644 (file)
@@ -44,6 +44,9 @@ In a nutshell, the current implementation provides the following features
 - Route maps can be configured to match a specific RPKI validation state. This
   allows the creation of local policies, which handle BGP routes based on the
   outcome of the Prefix Origin Validation.
+- Updates from the RPKI cache servers are directly applied and path selection
+  is updated accordingly. (Soft reconfiguration **must** be enabled for this
+  to work).
 
 
 .. _enabling-rpki: