]> git.puffer.fish Git - matthieu/frr.git/commitdiff
bgpd: Add RPKI/RTR support
authorMarcel Röthke <marcel.roethke@haw-hamburg.de>
Fri, 10 Nov 2017 12:56:24 +0000 (13:56 +0100)
committerMarcel Röthke <marcel.roethke@haw-hamburg.de>
Fri, 10 Nov 2017 13:37:52 +0000 (14:37 +0100)
This commit adds support for the RTR protocol to receive ROA
information from a RPKI cache server. That information can than be used
to validate the BGP origin AS of IP prefixes.
Both features are implemented using [rtrlib](http://rtrlib.realmv6.org/).

Signed-off-by: Marcel Röthke <marcel.roethke@haw-hamburg.de>
bgpd/Makefile.am
bgpd/bgp_rpki.c [new file with mode: 0644]
configure.ac
doc/bgpd.texi
doc/rpki.texi [new file with mode: 0644]
lib/command.c
lib/command.h
vtysh/Makefile.am
vtysh/vtysh.c

index 1f2602c05991c645cdc10d9115b3517d8ee4245b..8633cf9fc507adc895655b83bd7e8f9f0fd2d843 100644 (file)
@@ -114,6 +114,18 @@ bgpd_snmp_la_CFLAGS = $(WERROR) $(SNMP_CFLAGS)
 bgpd_snmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
 bgpd_snmp_la_LIBADD = ../lib/libfrrsnmp.la
 
+if RPKI
+module_LTLIBRARIES += bgpd_rpki.la
+endif
+
+bgpd_rpki_la-bgp_rpki.o: bgp_rpki_clippy.c
+bgp_rpki.o: bgp_rpki_clippy.c
+
+bgpd_rpki_la_SOURCES = bgp_rpki.c
+bgpd_rpki_la_CFLAGS = $(WERROR) $(RTRLIB_CFLAGS)
+bgpd_rpki_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+bgpd_rpki_la_LIBADD = $(RTRLIB_LIBS)
+
 examplesdir = $(exampledir)
 dist_examples_DATA = bgpd.conf.sample bgpd.conf.sample2 \
        bgpd.conf.vnc.sample
diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c
new file mode 100644 (file)
index 0000000..a3971f8
--- /dev/null
@@ -0,0 +1,1207 @@
+/*
+ * BGP RPKI
+ * Copyright (C) 2013 Michael Mester (m.mester@fu-berlin.de), for FU Berlin
+ * Copyright (C) 2014-2017 Andreas Reuter (andreas.reuter@fu-berlin.de), for FU Berlin
+ * Copyright (C) 2016-2017 Colin Sames (colin.sames@haw-hamburg.de), for HAW Hamburg
+ * Copyright (C) 2017 Marcel Röthke (marcel.roethke@haw-hamburg.de), for HAW Hamburg
+ *
+ * This file is part of FRRouting.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+#include <pthread.h>
+#include <time.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include "prefix.h"
+#include "log.h"
+#include "command.h"
+#include "linklist.h"
+#include "memory.h"
+#include "thread.h"
+#include "filter.h"
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgp_advertise.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_route.h"
+#include "rtrlib/rtrlib.h"
+#include "rtrlib/rtr_mgr.h"
+#include "rtrlib/lib/ip.h"
+#include "rtrlib/transport/tcp/tcp_transport.h"
+#if defined(FOUND_SSH)
+#include "rtrlib/transport/ssh/ssh_transport.h"
+#endif
+#include "hook.h"
+#include "libfrr.h"
+#include "version.h"
+
+#include "bgp_rpki_clippy.c"
+
+DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE, "BGP RPKI Cache server")
+DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE_GROUP, "BGP RPKI Cache server group")
+
+#define RPKI_VALID      1
+#define RPKI_NOTFOUND   2
+#define RPKI_INVALID    3
+
+#define POLLING_PERIOD_DEFAULT 3600
+#define EXPIRE_INTERVAL_DEFAULT 7200
+#define RETRY_INTERVAL_DEFAULT 600
+#define TIMEOUT_DEFAULT 600
+#define INITIAL_SYNCHRONISATION_TIMEOUT_DEFAULT 30
+
+#define RPKI_DEBUG(...)                                                        \
+       if (rpki_debug) {                                                      \
+               zlog_debug("RPKI: " __VA_ARGS__);                              \
+       }
+
+#define RPKI_OUTPUT_STRING "Control rpki specific settings\n"
+
+struct cache {
+       enum { TCP, SSH } type;
+       struct tr_socket *tr_socket;
+       union {
+               struct tr_tcp_config *tcp_config;
+               struct tr_ssh_config *ssh_config;
+       } tr_config;
+       struct rtr_socket *rtr_socket;
+       uint8_t preference;
+};
+
+enum return_values { SUCCESS = 0, ERROR = -1 };
+
+struct rpki_for_each_record_arg {
+       struct vty *vty;
+       unsigned int *prefix_amount;
+};
+
+static int start(void);
+static void stop(void);
+static int reset(bool force);
+static struct rtr_mgr_group *get_connected_group(void);
+static void print_prefix_table(struct vty *vty);
+static void install_cli_commands(void);
+static int config_write(struct vty *vty);
+static void overwrite_exit_commands(void);
+static void free_cache(struct cache *cache);
+static struct rtr_mgr_group *get_groups(void);
+#if defined(FOUND_SSH)
+static int add_ssh_cache(const char *host,
+                        const unsigned int port,
+                        const char *username,
+                        const char *client_privkey_path,
+                        const char *client_pubkey_path,
+                        const char *server_pubkey_path,
+                        const uint8_t preference);
+#endif
+static struct rtr_socket *create_rtr_socket(struct tr_socket *tr_socket);
+static struct cache *find_cache(const uint8_t preference);
+static int add_tcp_cache(const char *host,
+                        const char *port,
+                        const uint8_t preference);
+static void print_record(const struct pfx_record *record, void *data);
+static int is_synchronized(void);
+static int is_running(void);
+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 struct rtr_mgr_config *rtr_config;
+static struct list *cache_list;
+static int rtr_is_running;
+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 struct cmd_node rpki_node = {RPKI_NODE, "%s(config-rpki)# ", 1};
+static struct route_map_rule_cmd route_match_rpki_cmd = {"rpki", route_match,
+                                                 route_match_compile,
+                                                 route_match_free};
+
+static void *malloc_wrapper(size_t size)
+{
+       return XMALLOC(MTYPE_BGP_RPKI_CACHE, size);
+}
+
+static void *realloc_wrapper(void *ptr, size_t size)
+{
+       return XREALLOC(MTYPE_BGP_RPKI_CACHE, ptr, size);
+}
+
+static void free_wrapper(void *ptr)
+{
+       XFREE(MTYPE_BGP_RPKI_CACHE, ptr);
+}
+
+static int rpki_validate_prefix(struct peer *peer, struct attr *attr,
+                               struct prefix *prefix);
+
+static route_map_result_t route_match(void *rule, struct prefix *prefix,
+                                     route_map_object_t type,
+                                     void *object)
+{
+       int *rpki_status = rule;
+       struct bgp_info *bgp_info;
+
+       if (type == RMAP_BGP) {
+               bgp_info = object;
+
+               if (rpki_validate_prefix(bgp_info->peer, bgp_info->attr, prefix)
+                   == *rpki_status) {
+                       return RMAP_MATCH;
+               }
+       }
+       return RMAP_NOMATCH;
+}
+
+static void *route_match_compile(const char *arg)
+{
+       int *rpki_status;
+
+       rpki_status = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(u_char));
+
+       if (strcmp(arg, "valid") == 0)
+               *rpki_status = RPKI_VALID;
+       else if (strcmp(arg, "invalid") == 0)
+               *rpki_status = RPKI_INVALID;
+       else
+               *rpki_status = RPKI_NOTFOUND;
+
+       return rpki_status;
+}
+
+static void route_match_free(void *rule)
+{
+       XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static struct rtr_socket *create_rtr_socket(struct tr_socket *tr_socket)
+{
+       struct rtr_socket *rtr_socket =
+               XMALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct rtr_socket));
+       rtr_socket->tr_socket = tr_socket;
+       return rtr_socket;
+}
+
+static struct cache *find_cache(const uint8_t preference)
+{
+       struct listnode *cache_node;
+       struct cache *cache;
+
+       for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) {
+               if (cache->preference == preference)
+                       return cache;
+       }
+       return NULL;
+}
+
+static void print_record(const struct pfx_record *record, void *data)
+{
+       char ip[INET6_ADDRSTRLEN];
+       struct rpki_for_each_record_arg *arg = data;
+       struct vty *vty = arg->vty;
+
+       arg->prefix_amount++;
+
+       lrtr_ip_addr_to_str(&record->prefix, ip, sizeof(ip));
+       vty_out(vty, "%-40s   %3u - %3u   %10u\n", ip, record->min_len,
+               record->max_len, record->asn);
+}
+
+static struct rtr_mgr_group *get_groups(void)
+{
+       struct listnode *cache_node;
+       struct rtr_mgr_group *rtr_mgr_groups;
+       struct cache *cache;
+
+       int group_count = listcount(cache_list);
+
+       if (group_count == 0)
+               return NULL;
+
+       rtr_mgr_groups = XMALLOC(MTYPE_BGP_RPKI_CACHE_GROUP,
+                                group_count * sizeof(struct rtr_mgr_group));
+
+       size_t i = 0;
+
+       for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) {
+               rtr_mgr_groups[i].sockets = &cache->rtr_socket;
+               rtr_mgr_groups[i].sockets_len = 1;
+               rtr_mgr_groups[i].preference = cache->preference;
+
+               if (cache->type == TCP)
+                       tr_tcp_init(cache->tr_config.tcp_config,
+                                   cache->tr_socket);
+               else
+                       tr_ssh_init(cache->tr_config.ssh_config,
+                                   cache->tr_socket);
+
+               i++;
+       }
+
+       return rtr_mgr_groups;
+}
+
+inline int is_synchronized(void)
+{
+       return rtr_is_running && rtr_mgr_conf_in_sync(rtr_config);
+}
+
+inline int is_running(void)
+{
+       return rtr_is_running;
+}
+
+static int bgp_rpki_init(struct thread_master *master)
+{
+       rpki_debug = 0;
+       rtr_is_running = 0;
+
+       cache_list = list_new();
+       cache_list->del = (void (*)(void *)) &free_cache;
+
+       polling_period = POLLING_PERIOD_DEFAULT;
+       expire_interval = EXPIRE_INTERVAL_DEFAULT;
+       retry_interval = RETRY_INTERVAL_DEFAULT;
+       timeout = TIMEOUT_DEFAULT;
+       initial_synchronisation_timeout =
+               INITIAL_SYNCHRONISATION_TIMEOUT_DEFAULT;
+       install_cli_commands();
+       return 0;
+}
+
+static int bgp_rpki_fini(void)
+{
+       stop();
+       list_delete_and_null(&cache_list);
+
+       return 0;
+}
+
+static int bgp_rpki_module_init(void)
+{
+       lrtr_set_alloc_functions(malloc_wrapper,
+                                realloc_wrapper,
+                                free_wrapper);
+
+       hook_register(frr_late_init, bgp_rpki_init);
+       hook_register(frr_early_fini, &bgp_rpki_fini);
+
+       return 0;
+}
+
+static int start(void)
+{
+       unsigned int waiting_time = 0;
+       int ret;
+
+       if (list_isempty(cache_list)) {
+               RPKI_DEBUG(
+                       "No caches were found in config. Prefix validation is off.");
+               return ERROR;
+       }
+       RPKI_DEBUG("Init rtr_mgr.");
+       int groups_len = listcount(cache_list);
+       struct rtr_mgr_group *groups = get_groups();
+
+       ret = rtr_mgr_init(&rtr_config, groups, groups_len, polling_period,
+                          expire_interval, retry_interval,
+                          NULL, NULL, NULL, NULL);
+       if (ret == RTR_ERROR) {
+               RPKI_DEBUG("Init rtr_mgr failed.");
+               return ERROR;
+       }
+
+       RPKI_DEBUG("Starting rtr_mgr.");
+       ret = rtr_mgr_start(rtr_config);
+       if (ret == RTR_ERROR) {
+               RPKI_DEBUG("Starting rtr_mgr failed.");
+               rtr_mgr_free(rtr_config);
+               return ERROR;
+       }
+       rtr_is_running = 1;
+       RPKI_DEBUG("Waiting for rtr connection to synchronize.");
+       while (waiting_time++ <= initial_synchronisation_timeout) {
+               if (rtr_mgr_conf_in_sync(rtr_config))
+                       break;
+
+               sleep(1);
+       }
+       if (rtr_mgr_conf_in_sync(rtr_config)) {
+               RPKI_DEBUG("Got synchronisation with at least one RPKI cache!");
+       } else {
+               RPKI_DEBUG(
+                       "Timeout expired! Proceeding without RPKI validation data.");
+       }
+
+       XFREE(MTYPE_BGP_RPKI_CACHE_GROUP, groups);
+
+       return SUCCESS;
+}
+
+static void stop(void)
+{
+       if (rtr_is_running) {
+               rtr_mgr_stop(rtr_config);
+               rtr_mgr_free(rtr_config);
+               rtr_is_running = 0;
+       }
+}
+
+static int reset(bool force)
+{
+       if (rtr_is_running && !force)
+               return SUCCESS;
+
+       RPKI_DEBUG("Resetting RPKI Session");
+       stop();
+       return start();
+}
+
+static struct rtr_mgr_group *get_connected_group(void)
+{
+       if (list_isempty(cache_list))
+               return NULL;
+
+       return rtr_mgr_get_first_group(rtr_config);
+}
+
+static void print_prefix_table(struct vty *vty)
+{
+       struct rpki_for_each_record_arg arg;
+
+       unsigned int number_of_ipv4_prefixes = 0;
+       unsigned int number_of_ipv6_prefixes = 0;
+       struct rtr_mgr_group *group = get_connected_group();
+
+       arg.vty = vty;
+
+       if (!group)
+               return;
+
+       struct pfx_table *pfx_table = group->sockets[0]->pfx_table;
+
+       vty_out(vty, "RPKI/RTR prefix table\n");
+       vty_out(vty, "%-40s %s  %s\n", "Prefix", "Prefix Length", "Origin-AS");
+
+       arg.prefix_amount = &number_of_ipv4_prefixes;
+       pfx_table_for_each_ipv4_record(pfx_table, print_record, &arg);
+
+       arg.prefix_amount = &number_of_ipv6_prefixes;
+       pfx_table_for_each_ipv6_record(pfx_table, print_record, &arg);
+
+       vty_out(vty, "Number of IPv4 Prefixes: %u\n", number_of_ipv4_prefixes);
+       vty_out(vty, "Number of IPv6 Prefixes: %u\n", number_of_ipv6_prefixes);
+}
+
+static int rpki_validate_prefix(struct peer *peer, struct attr *attr,
+                               struct prefix *prefix)
+{
+       struct assegment *as_segment;
+       as_t as_number = 0;
+       struct lrtr_ip_addr ip_addr_prefix;
+       enum pfxv_state result;
+       char buf[BUFSIZ];
+       const char *prefix_string;
+
+       if (!is_synchronized())
+               return 0;
+
+       // No aspath means route comes from iBGP
+       if (!attr->aspath || !attr->aspath->segments) {
+               // Set own as number
+               as_number = peer->bgp->as;
+       } else {
+               as_segment = attr->aspath->segments;
+               // Find last AsSegment
+               while (as_segment->next)
+                       as_segment = as_segment->next;
+
+               if (as_segment->type == AS_SEQUENCE) {
+                       // Get rightmost asn
+                       as_number = as_segment->as[as_segment->length - 1];
+               } else if (as_segment->type == AS_CONFED_SEQUENCE ||
+                          as_segment->type == AS_CONFED_SET) {
+                       // Set own as number
+                       as_number = peer->bgp->as;
+               } else {
+                       // RFC says: "Take distinguished value NONE as asn"
+                       // which means state is unknown
+                       return RPKI_NOTFOUND;
+               }
+       }
+
+       // Get the prefix in requested format
+       switch (prefix->family) {
+       case AF_INET:
+               ip_addr_prefix.ver = LRTR_IPV4;
+               ip_addr_prefix.u.addr4.addr = ntohl(prefix->u.prefix4.s_addr);
+               break;
+
+#ifdef HAVE_IPV6
+       case AF_INET6:
+               ip_addr_prefix.ver = LRTR_IPV6;
+               ipv6_addr_to_host_byte_order(prefix->u.prefix6.s6_addr32,
+                                            ip_addr_prefix.u.addr6.addr);
+               break;
+#endif /* HAVE_IPV6 */
+
+       default:
+               return 0;
+       }
+
+       // Do the actual validation
+       rtr_mgr_validate(rtr_config, as_number, &ip_addr_prefix,
+                        prefix->prefixlen, &result);
+
+       // Print Debug output
+       prefix_string =
+               inet_ntop(prefix->family, &prefix->u.prefix, buf, BUFSIZ);
+       switch (result) {
+       case BGP_PFXV_STATE_VALID:
+               RPKI_DEBUG(
+                       "Validating Prefix %s/%hhu from asn %u    Result: VALID",
+                       prefix_string, prefix->prefixlen, as_number);
+               return RPKI_VALID;
+       case BGP_PFXV_STATE_NOT_FOUND:
+               RPKI_DEBUG(
+                       "Validating Prefix %s/%hhu from asn %u    Result: NOT FOUND",
+                       prefix_string, prefix->prefixlen, as_number);
+               return RPKI_NOTFOUND;
+       case BGP_PFXV_STATE_INVALID:
+               RPKI_DEBUG(
+                       "Validating Prefix %s/%hhu from asn %u    Result: INVALID",
+                       prefix_string, prefix->prefixlen, as_number);
+               return RPKI_INVALID;
+       default:
+               RPKI_DEBUG(
+                       "Validating Prefix %s/%hhu from asn %u    Result: CANNOT VALIDATE",
+                       prefix_string, prefix->prefixlen, as_number);
+               break;
+       }
+       return 0;
+}
+
+static int add_cache(struct cache *cache)
+{
+       uint8_t preference = cache->preference;
+       struct rtr_mgr_group group;
+
+       group.preference = preference;
+       group.sockets_len = 1;
+       group.sockets = &cache->rtr_socket;
+
+       listnode_add(cache_list, cache);
+
+       if (rtr_is_running &&
+           rtr_mgr_add_group(rtr_config, &group) != RTR_SUCCESS) {
+               return ERROR;
+       }
+
+       return SUCCESS;
+}
+
+static int add_tcp_cache(const char *host,
+                        const char *port,
+                        const uint8_t preference)
+{
+       struct rtr_socket *rtr_socket;
+       struct tr_tcp_config *tcp_config =
+               XMALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct tr_tcp_config));
+       struct tr_socket *tr_socket =
+               XMALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct tr_socket));
+       struct cache *cache =
+               XMALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct cache));
+
+       tcp_config->host = XSTRDUP(MTYPE_BGP_RPKI_CACHE, host);
+       tcp_config->port = XSTRDUP(MTYPE_BGP_RPKI_CACHE, port);
+       tcp_config->bindaddr = NULL;
+
+       rtr_socket = create_rtr_socket(tr_socket);
+
+       cache->type = TCP;
+       cache->tr_socket = tr_socket;
+       cache->tr_config.tcp_config = tcp_config;
+       cache->rtr_socket = rtr_socket;
+       cache->preference = preference;
+
+       return add_cache(cache);
+}
+
+#if defined(FOUND_SSH)
+static int add_ssh_cache(const char *host,
+                        const unsigned int port,
+                        const char *username,
+                        const char *client_privkey_path,
+                        const char *client_pubkey_path,
+                        const char *server_pubkey_path,
+                        const uint8_t preference)
+{
+       struct tr_ssh_config *ssh_config =
+               XMALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct tr_ssh_config));
+       struct cache *cache =
+               XMALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct cache));
+       struct tr_socket *tr_socket =
+               XMALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct tr_socket));
+       struct rtr_socket *rtr_socket;
+
+       ssh_config->port = port;
+       ssh_config->host = XSTRDUP(MTYPE_BGP_RPKI_CACHE, host);
+       ssh_config->bindaddr = NULL;
+
+       ssh_config->username = XSTRDUP(MTYPE_BGP_RPKI_CACHE, username);
+       ssh_config->client_privkey_path = XSTRDUP(
+                       MTYPE_BGP_RPKI_CACHE, client_privkey_path);
+       ssh_config->server_hostkey_path =
+               XSTRDUP(MTYPE_BGP_RPKI_CACHE, server_pubkey_path);
+
+       rtr_socket = create_rtr_socket(tr_socket);
+
+       cache->type = SSH;
+       cache->tr_socket = tr_socket;
+       cache->tr_config.ssh_config = ssh_config;
+       cache->rtr_socket = rtr_socket;
+       cache->preference = preference;
+
+       return add_cache(cache);
+}
+#endif
+
+static void free_cache(struct cache *cache)
+{
+       if (cache->type == TCP) {
+               XFREE(MTYPE_BGP_RPKI_CACHE,
+                     cache->tr_config.tcp_config->host);
+               XFREE(MTYPE_BGP_RPKI_CACHE,
+                     cache->tr_config.tcp_config->port);
+               XFREE(MTYPE_BGP_RPKI_CACHE, cache->tr_config.tcp_config);
+       }
+#if defined(FOUND_SSH)
+       else {
+               XFREE(MTYPE_BGP_RPKI_CACHE,
+                     cache->tr_config.ssh_config->host);
+               XFREE(MTYPE_BGP_RPKI_CACHE,
+                     cache->tr_config.ssh_config->username);
+               XFREE(MTYPE_BGP_RPKI_CACHE,
+                     cache->tr_config.ssh_config->client_privkey_path);
+               XFREE(MTYPE_BGP_RPKI_CACHE,
+                     cache->tr_config.ssh_config->server_hostkey_path);
+               XFREE(MTYPE_BGP_RPKI_CACHE, cache->tr_config.ssh_config);
+       }
+#endif
+       XFREE(MTYPE_BGP_RPKI_CACHE, cache->tr_socket);
+       XFREE(MTYPE_BGP_RPKI_CACHE, cache->rtr_socket);
+       XFREE(MTYPE_BGP_RPKI_CACHE, cache);
+}
+
+static int config_write(struct vty *vty)
+{
+       struct listnode *cache_node;
+       struct cache *cache;
+
+       if (listcount(cache_list)) {
+               if (rpki_debug)
+                       vty_out(vty, "debug rpki\n");
+
+               vty_out(vty, "!\n");
+               vty_out(vty, "rpki\n");
+               vty_out(vty, "  rpki polling_period %d\n", polling_period);
+               vty_out(vty, "  rpki timeout %d\n", timeout);
+               vty_out(vty, "  rpki initial-synchronisation-timeout %d\n",
+                       initial_synchronisation_timeout);
+               for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) {
+                       switch (cache->type) {
+                               struct tr_tcp_config *tcp_config;
+                               struct tr_ssh_config *ssh_config;
+                       case TCP:
+                               tcp_config = cache->tr_config.tcp_config;
+                               vty_out(vty,
+                                       "  rpki cache %s %s ",
+                                       tcp_config->host,
+                                       tcp_config->port);
+                               break;
+#if defined(FOUND_SSH)
+                       case SSH:
+                               ssh_config = cache->tr_config.ssh_config;
+                               vty_out(vty,
+                                       "  rpki cache %s %u %s %s %s ",
+                                       ssh_config->host,
+                                       ssh_config->port,
+                                       ssh_config->username,
+                                       ssh_config->client_privkey_path,
+                                       ssh_config->server_hostkey_path
+                                                       != NULL
+                                               ? ssh_config
+                                                         ->server_hostkey_path
+                                               : " ");
+                               break;
+#endif
+                       default:
+                               break;
+                       }
+
+                       vty_out(vty, "preference %hhu\n", cache->preference);
+               }
+               vty_out(vty, "  exit\n");
+               return 1;
+       } else {
+               return 0;
+       }
+}
+
+DEFUN_NOSH (rpki,
+           rpki_cmd,
+           "rpki",
+           "Enable rpki and enter rpki configuration mode\n")
+{
+       vty->node = RPKI_NODE;
+       return CMD_SUCCESS;
+}
+
+DEFUN (bgp_rpki_start,
+       bgp_rpki_start_cmd,
+       "rpki start",
+       RPKI_OUTPUT_STRING
+       "start rpki support\n")
+{
+       if (listcount(cache_list) == 0)
+               vty_out(vty, "Could not start rpki because no caches are configured\n");
+
+       if (!is_running()) {
+               if (start() == ERROR) {
+                       RPKI_DEBUG("RPKI failed to start");
+                       return CMD_WARNING;
+               }
+       }
+       return CMD_SUCCESS;
+}
+
+DEFUN (bgp_rpki_stop,
+       bgp_rpki_stop_cmd,
+       "rpki stop",
+       RPKI_OUTPUT_STRING
+       "start rpki support\n")
+{
+       if (is_running())
+               stop();
+
+       return CMD_SUCCESS;
+}
+
+DEFPY (rpki_polling_period,
+       rpki_polling_period_cmd,
+       "rpki polling_period (1-86400)$pp",
+       RPKI_OUTPUT_STRING
+       "Set polling period\n"
+       "Polling period value\n")
+{
+       polling_period = pp;
+       return CMD_SUCCESS;
+}
+
+DEFUN (no_rpki_polling_period,
+       no_rpki_polling_period_cmd,
+       "no rpki polling_period",
+       NO_STR
+       RPKI_OUTPUT_STRING
+       "Set polling period back to default\n")
+{
+       polling_period = POLLING_PERIOD_DEFAULT;
+       return CMD_SUCCESS;
+}
+
+DEFPY (rpki_expire_interval,
+       rpki_expire_interval_cmd,
+       "rpki expire_interval (600-172800)$tmp",
+       RPKI_OUTPUT_STRING
+       "Set expire interval\n"
+       "Expire interval value\n")
+{
+       if (tmp >= polling_period) {
+               expire_interval = tmp;
+               return CMD_SUCCESS;
+       }
+
+       vty_out(vty, "%% Expiry interval must be polling period or larger\n");
+       return CMD_WARNING_CONFIG_FAILED;
+}
+
+DEFUN (no_rpki_expire_interval,
+       no_rpki_expire_interval_cmd,
+       "no rpki expire_interval",
+       NO_STR
+       RPKI_OUTPUT_STRING
+       "Set expire interval back to default\n")
+{
+       expire_interval = polling_period * 2;
+       return CMD_SUCCESS;
+}
+
+DEFPY (rpki_retry_interval,
+       rpki_retry_interval_cmd,
+       "rpki retry_interval (1-7200)$tmp",
+       RPKI_OUTPUT_STRING
+       "Set retry interval\n"
+       "retry interval value\n")
+{
+       retry_interval = tmp;
+       return CMD_SUCCESS;
+}
+
+DEFUN (no_rpki_retry_interval,
+       no_rpki_retry_interval_cmd,
+       "no rpki retry_interval",
+       NO_STR
+       RPKI_OUTPUT_STRING
+       "Set retry interval back to default\n")
+{
+       retry_interval = RETRY_INTERVAL_DEFAULT;
+       return CMD_SUCCESS;
+}
+
+DEFPY (rpki_timeout,
+       rpki_timeout_cmd,
+       "rpki timeout (1-4294967295)$to_arg",
+       RPKI_OUTPUT_STRING
+       "Set timeout\n"
+       "Timeout value\n")
+{
+       timeout = to_arg;
+       return CMD_SUCCESS;
+}
+
+DEFUN (no_rpki_timeout,
+       no_rpki_timeout_cmd,
+       "no rpki timeout",
+       NO_STR
+       RPKI_OUTPUT_STRING
+       "Set timeout back to default\n")
+{
+       timeout = TIMEOUT_DEFAULT;
+       return CMD_SUCCESS;
+}
+
+DEFPY (rpki_synchronisation_timeout,
+       rpki_synchronisation_timeout_cmd,
+       "rpki initial-synchronisation-timeout (1-4294967295)$ito_arg",
+       RPKI_OUTPUT_STRING
+       "Set a timeout for the initial synchronisation of prefix validation data\n"
+       "Timeout value\n")
+{
+       initial_synchronisation_timeout = ito_arg;
+       return CMD_SUCCESS;
+}
+
+DEFUN (no_rpki_synchronisation_timeout,
+       no_rpki_synchronisation_timeout_cmd,
+       "no rpki initial-synchronisation-timeout",
+       NO_STR
+       RPKI_OUTPUT_STRING
+       "Set the initial synchronisation timeout back to default (30 sec.)\n")
+{
+       initial_synchronisation_timeout =
+               INITIAL_SYNCHRONISATION_TIMEOUT_DEFAULT;
+       return CMD_SUCCESS;
+}
+
+DEFPY (rpki_cache,
+       rpki_cache_cmd,
+       "rpki cache <A.B.C.D|WORD>"
+       "<TCPPORT|(1-65535)$sshport SSH_UNAME SSH_PRIVKEY SSH_PUBKEY [SERVER_PUBKEY]> "
+       "preference (1-255)",
+       RPKI_OUTPUT_STRING
+       "Install a cache server to current group\n"
+       "IP address of cache server\n Hostname of cache server\n"
+       "TCP port number\n"
+       "SSH port number\n"
+       "SSH user name\n"
+       "Path to own SSH private key\n"
+       "Path to own SSH public key\n"
+       "Path to Public key of cache server\n"
+       "Preference of the cache server\n"
+       "Preference value\n")
+{
+       int return_value = SUCCESS;
+
+       // use ssh connection
+       if (ssh_uname) {
+#if defined(FOUND_SSH)
+               return_value = add_ssh_cache(
+                       cache, sshport, ssh_uname, ssh_privkey, ssh_pubkey,
+                       server_pubkey, preference);
+#else
+               vty_out(vty,
+                       "ssh sockets are not supported. "
+                       "Please recompile rtrlib and frr with ssh support. "
+                       "If you want to use it");
+#endif
+       } else { // use tcp connection
+               return_value = add_tcp_cache(cache, tcpport, preference);
+       }
+
+       if (return_value == ERROR) {
+               vty_out(vty, "Could not create new rpki cache\n");
+               return CMD_WARNING;
+       }
+
+       return CMD_SUCCESS;
+}
+
+DEFPY (no_rpki_cache,
+       no_rpki_cache_cmd,
+       "no rpki cache <A.B.C.D|WORD> <TCPPORT|(1-65535)$sshport> preference (1-255)$preference",
+       NO_STR
+       RPKI_OUTPUT_STRING
+       "Remove a cache server\n"
+       "IP address of cache server\n Hostname of cache server\n"
+       "TCP port number\n"
+       "SSH port number\n"
+       "Preference of the cache server\n"
+       "Preference value\n")
+{
+       struct cache *cache_p = find_cache(preference);
+
+       if (!cache) {
+               vty_out(vty, "Could not find cache %ld\n", preference);
+               return CMD_WARNING;
+       }
+
+       if (rtr_is_running) {
+               if (rtr_mgr_remove_group(rtr_config, preference) == RTR_ERROR) {
+                       vty_out(vty, "Could not remove cache %ld", preference);
+                       if (listcount(cache_list) == 1)
+                               vty_out(vty, " because it is the last cache");
+
+                       vty_out(vty, "\n");
+                       return CMD_WARNING;
+               }
+       }
+
+       listnode_delete(cache_list, cache_p);
+       free_cache(cache_p);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN (show_rpki_prefix_table,
+       show_rpki_prefix_table_cmd,
+       "show rpki prefix-table",
+       SHOW_STR
+       RPKI_OUTPUT_STRING
+       "Show validated prefixes which were received from RPKI Cache")
+{
+       struct listnode *cache_node;
+       struct cache *cache;
+
+       for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) {
+               vty_out(vty,
+                       "host: %s port: %s\n",
+                       cache->tr_config.tcp_config->host,
+                       cache->tr_config.tcp_config->port);
+       }
+       if (is_synchronized())
+               print_prefix_table(vty);
+       else
+               vty_out(vty, "No connection to RPKI cache server.\n");
+
+       return CMD_SUCCESS;
+}
+
+DEFUN (show_rpki_cache_server,
+       show_rpki_cache_server_cmd,
+       "show rpki cache-server",
+       SHOW_STR
+       RPKI_OUTPUT_STRING
+       "SHOW configured cache server")
+{
+       struct listnode *cache_node;
+       struct cache *cache;
+
+       for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) {
+               vty_out(vty,
+                       "host: %s port: %s\n",
+                       cache->tr_config.tcp_config->host,
+                       cache->tr_config.tcp_config->port);
+       }
+
+       return CMD_SUCCESS;
+}
+
+DEFUN (show_rpki_cache_connection,
+       show_rpki_cache_connection_cmd,
+       "show rpki cache-connection",
+       SHOW_STR
+       RPKI_OUTPUT_STRING
+       "Show to which RPKI Cache Servers we have a connection")
+{
+       if (is_synchronized()) {
+               struct listnode *cache_node;
+               struct cache *cache;
+               struct rtr_mgr_group *group = get_connected_group();
+
+               if (!group) {
+                       vty_out(vty, "Cannot find a connected group.\n");
+                       return CMD_SUCCESS;
+               }
+               vty_out(vty, "Connected to group %d\n", group->preference);
+               for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node,
+                                         cache)) {
+                       if (cache->preference == group->preference) {
+                               struct tr_tcp_config *tcp_config;
+                               struct tr_ssh_config *ssh_config;
+
+                               switch (cache->type) {
+                               case TCP:
+                                       tcp_config =
+                                               cache->tr_config
+                                                       .tcp_config;
+                                       vty_out(vty,
+                                               "rpki tcp cache %s %s pref %hhu\n",
+                                               tcp_config->host,
+                                               tcp_config->port,
+                                               cache->preference);
+                                       break;
+
+#if defined(FOUND_SSH)
+                               case SSH:
+                                       ssh_config =
+                                               cache->tr_config
+                                                       .ssh_config;
+                                       vty_out(vty,
+                                               "rpki ssh cache %s %u pref %hhu\n",
+                                               ssh_config->host,
+                                               ssh_config->port,
+                                               cache->preference);
+                                       break;
+#endif
+
+                               default:
+                                       break;
+                               }
+                       }
+               }
+       } else {
+               vty_out(vty, "No connection to RPKI cache server.\n");
+       }
+
+       return CMD_SUCCESS;
+}
+
+DEFUN_NOSH (rpki_exit,
+           rpki_exit_cmd,
+           "exit",
+           "Exit rpki configuration and restart rpki session")
+{
+       int ret = reset(false);
+
+       vty->node = CONFIG_NODE;
+       return ret == SUCCESS ? CMD_SUCCESS : CMD_WARNING;
+}
+
+DEFUN_NOSH (rpki_quit,
+           rpki_quit_cmd,
+           "quit",
+           "Exit rpki configuration mode")
+{
+       return rpki_exit(self, vty, argc, argv);
+}
+
+DEFUN_NOSH (rpki_end,
+           rpki_end_cmd,
+           "end",
+           "End rpki configuration, restart rpki session and change to enable mode.")
+{
+       int ret = reset(false);
+
+       vty_config_unlock(vty);
+       vty->node = ENABLE_NODE;
+       return ret == SUCCESS ? CMD_SUCCESS : CMD_WARNING;
+}
+
+DEFUN (rpki_reset,
+       rpki_reset_cmd,
+       "rpki reset",
+       RPKI_OUTPUT_STRING
+       "reset rpki\n")
+{
+       return reset(true) == SUCCESS ? CMD_SUCCESS : CMD_WARNING;
+}
+
+DEFUN (debug_rpki,
+       debug_rpki_cmd,
+       "debug rpki",
+       DEBUG_STR
+       "Enable debugging for rpki")
+{
+       rpki_debug = 1;
+       return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_rpki,
+       no_debug_rpki_cmd,
+       "no debug rpki",
+       NO_STR
+       DEBUG_STR
+       "Disable debugging for rpki")
+{
+       rpki_debug = 0;
+       return CMD_SUCCESS;
+}
+
+DEFUN (match_rpki,
+       match_rpki_cmd,
+       "match rpki <valid|invalid|notfound>",
+       MATCH_STR
+       RPKI_OUTPUT_STRING
+       "Valid prefix\n"
+       "Invalid prefix\n"
+       "Prefix not found\n")
+{
+       VTY_DECLVAR_CONTEXT(route_map_index, index);
+       int ret;
+
+       ret = route_map_add_match(index, "rpki", argv[2]->arg);
+       if (ret) {
+               switch (ret) {
+               case RMAP_RULE_MISSING:
+                       vty_out(vty, "%% BGP Can't find rule.\n");
+                       return CMD_WARNING_CONFIG_FAILED;
+               case RMAP_COMPILE_ERROR:
+                       vty_out(vty, "%% BGP Argument is malformed.\n");
+                       return CMD_WARNING_CONFIG_FAILED;
+               }
+       }
+       return CMD_SUCCESS;
+}
+
+DEFUN (no_match_rpki,
+       no_match_rpki_cmd,
+       "no match rpki <valid|invalid|notfound>",
+       NO_STR
+       MATCH_STR
+       RPKI_OUTPUT_STRING
+       "Valid prefix\n"
+       "Invalid prefix\n"
+       "Prefix not found\n")
+{
+       VTY_DECLVAR_CONTEXT(route_map_index, index);
+       int ret;
+
+       ret = route_map_delete_match(index, "rpki", argv[3]->arg);
+       if (ret) {
+               switch (ret) {
+               case RMAP_RULE_MISSING:
+                       vty_out(vty, "%% BGP Can't find rule.\n");
+                       break;
+               case RMAP_COMPILE_ERROR:
+                       vty_out(vty, "%% BGP Argument is malformed.\n");
+                       break;
+               }
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       return CMD_SUCCESS;
+}
+
+static void overwrite_exit_commands(void)
+{
+       unsigned int i;
+       vector cmd_vector = rpki_node.cmd_vector;
+
+       for (i = 0; i < cmd_vector->active; ++i) {
+               struct cmd_element *cmd = vector_lookup(cmd_vector, i);
+
+               if (strcmp(cmd->string, "exit") == 0 ||
+                   strcmp(cmd->string, "quit") == 0 ||
+                   strcmp(cmd->string, "end") == 0) {
+                       uninstall_element(RPKI_NODE, cmd);
+               }
+       }
+
+       install_element(RPKI_NODE, &rpki_exit_cmd);
+       install_element(RPKI_NODE, &rpki_quit_cmd);
+       install_element(RPKI_NODE, &rpki_end_cmd);
+}
+
+static void install_cli_commands(void)
+{
+       //TODO: make config write work
+       install_node(&rpki_node, &config_write);
+       install_default(RPKI_NODE);
+       overwrite_exit_commands();
+       install_element(CONFIG_NODE, &rpki_cmd);
+       install_element(VIEW_NODE, &rpki_cmd);
+
+       install_element(ENABLE_NODE, &bgp_rpki_start_cmd);
+       install_element(ENABLE_NODE, &bgp_rpki_stop_cmd);
+
+       /* Install rpki reset command */
+       install_element(RPKI_NODE, &rpki_reset_cmd);
+
+       /* Install rpki polling period commands */
+       install_element(RPKI_NODE, &rpki_polling_period_cmd);
+       install_element(RPKI_NODE, &no_rpki_polling_period_cmd);
+
+       /* Install rpki expire interval commands */
+       install_element(RPKI_NODE, &rpki_expire_interval_cmd);
+       install_element(RPKI_NODE, &no_rpki_expire_interval_cmd);
+
+       /* Install rpki retry interval commands */
+       install_element(RPKI_NODE, &rpki_retry_interval_cmd);
+       install_element(RPKI_NODE, &no_rpki_retry_interval_cmd);
+
+       /* Install rpki timeout commands */
+       install_element(RPKI_NODE, &rpki_timeout_cmd);
+       install_element(RPKI_NODE, &no_rpki_timeout_cmd);
+
+       /* Install rpki synchronisation timeout commands */
+       install_element(RPKI_NODE, &rpki_synchronisation_timeout_cmd);
+       install_element(RPKI_NODE, &no_rpki_synchronisation_timeout_cmd);
+
+       /* Install rpki cache commands */
+       install_element(RPKI_NODE, &rpki_cache_cmd);
+       install_element(RPKI_NODE, &no_rpki_cache_cmd);
+
+       /* Install show commands */
+       install_element(ENABLE_NODE, &show_rpki_prefix_table_cmd);
+       install_element(ENABLE_NODE, &show_rpki_cache_connection_cmd);
+       install_element(ENABLE_NODE, &show_rpki_cache_server_cmd);
+
+       /* Install debug commands */
+       install_element(CONFIG_NODE, &debug_rpki_cmd);
+       install_element(ENABLE_NODE, &debug_rpki_cmd);
+       install_element(CONFIG_NODE, &no_debug_rpki_cmd);
+       install_element(ENABLE_NODE, &no_debug_rpki_cmd);
+
+       /* Install route match */
+       route_map_install_match(&route_match_rpki_cmd);
+       install_element(RMAP_NODE, &match_rpki_cmd);
+       install_element(RMAP_NODE, &no_match_rpki_cmd);
+}
+
+FRR_MODULE_SETUP(.name = "bgpd_rpki", .version = "0.3.6",
+                .description = "Enable RPKI support for FRR.",
+        .init = bgp_rpki_module_init)
index cae50611225f1c9afb37a0975242af54556e95f3..41ebab6a16cad06265a8e74093a7c3e9f3c2611a 100755 (executable)
@@ -387,6 +387,8 @@ AC_ARG_ENABLE([protobuf],
   AS_HELP_STRING([--enable-protobuf], [Enable experimental protobuf support]))
 AC_ARG_ENABLE([oldvpn_commands],
   AS_HELP_STRING([--enable-oldvpn-commands], [Keep old vpn commands]))
+AC_ARG_ENABLE(rpki,
+  AS_HELP_STRING([--enable-rpki], [enable RPKI prefix validation support]))
 
 AC_CHECK_HEADERS(json-c/json.h)
 AC_CHECK_LIB(json-c, json_object_get, LIBS="$LIBS -ljson-c", [], [-lm])
@@ -1796,6 +1798,30 @@ AC_SUBST(CFG_STATE)
 AC_SUBST(CFG_MODULE)
 AC_DEFINE_UNQUOTED(MODULE_PATH, "$CFG_MODULE", path to modules)
 
+dnl ------------------------------------
+dnl Enable RPKI and add librtr to libs
+dnl ------------------------------------
+if test "${enable_rpki}" = "yes"; then
+  PKG_CHECK_MODULES(RTRLIB,[rtrlib >= 0.5.0],
+      [AC_DEFINE(HAVE_RPKI,1,Enable RPKI prefix validation for BGP)
+      RPKI=true],
+      [RPKI=false
+      AC_MSG_ERROR([rtrlib was not found on your system or is too old.])]
+  )
+fi
+AM_CONDITIONAL([RPKI], test "x$RPKI" = "xtrue")
+
+dnl ------------------------------------------
+dnl Check whether rtrlib was build with ssh support
+dnl ------------------------------------------
+AC_MSG_CHECKING([whether the RTR Library is compiled with SSH])
+AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include "rtrlib/rtrlib.h"]],
+                       [[struct tr_ssh_config config;]])],
+       [AC_MSG_RESULT(yes)
+       AC_DEFINE(FOUND_SSH,,found_ssh)],
+       AC_MSG_RESULT(no)
+)
+
 dnl ---------------------------
 dnl Check htonl works correctly
 dnl ---------------------------
index 9371bcda9550519893423fdf29fc87daa919613a..d62c3a7516128a4610d109b5327c53050e53751e 100644 (file)
@@ -37,6 +37,7 @@ BGP-4.
 * How to set up a 6-Bone connection::  
 * Dump BGP packets and table::  
 * BGP Configuration Examples::
+* Prefix Origin Validation Using RPKI::
 @end menu
 
 @node Starting BGP
@@ -2144,3 +2145,5 @@ route-map rm-peer-out permit 10
 route-map rm-peer-in permit 10
  set community additive 64512:3200
 @end example
+
+@include rpki.texi
diff --git a/doc/rpki.texi b/doc/rpki.texi
new file mode 100644 (file)
index 0000000..c1c8a8a
--- /dev/null
@@ -0,0 +1,256 @@
+@c -*-texinfo-*-
+@c This is part of the FRR Manual.
+@c @value{COPYRIGHT_STR}
+@c See file frr.texi for copying conditions.
+@node Prefix Origin Validation Using RPKI
+@section Prefix Origin Validation Using RPKI
+
+Prefix Origin Validation allows BGP routers to verify if the origin AS of
+an IP prefix is legitimate to announce this IP prefix. The required
+attestation objects are stored in the Resource Public Key Infrastructure
+(@acronym{RPKI}).  However, RPKI-enabled routers do not store cryptographic
+data itself but only validation information. The validation of the
+cryptographic data (so called Route Origin Authorization, or short
+@acronym{ROA}, objects) will be performed by trusted cache servers. The
+RPKI/RTR protocol defines a standard mechanism to maintain the exchange of
+the prefix/origin AS mapping between the cache server and routers.
+In combination with a  BGP Prefix Origin Validation scheme a router is able
+to verify received BGP updates without suffering from cryptographic
+complexity.
+
+
+The RPKI/RTR protocol is defined in @cite{RFC6810, The Resource Public Key
+Infrastructure (RPKI) to Router Protocol}, and the validation scheme in
+@cite{RFC6811, BGP Prefix Origin Validation}. The current version of Prefix
+Origin Validation in FRR implements both RFCs.
+
+For a more detailed but still easy-to-read background, we suggest the
+following two articles:
+@enumerate
+@item @cite{Geoff Huston, Randy Bush: Securing BGP, In: The Internet
+Protocol Journal, Volume 14, No. 2, 2011.}
+@uref{http://www.cisco.com/web/about/ac123/ac147/archived_issues/ipj_14-2/142_bgp.html}
+
+@item @cite{Geoff Huston: Resource Certification, In: The Internet Protocol
+Journal, Volume 12, No.1, 2009.}
+@uref{http://www.cisco.com/web/about/ac123/ac147/archived_issues/ipj_12-1/121_resource.html}
+@end enumerate
+
+@menu
+* Features of the Current Implementation::
+* Enabling RPKI::
+* Configuring RPKI/RTR Cache Servers::
+* Validating BGP Updates::
+* Debugging::
+* Displaying RPKI::
+* RPKI Configuration Example::
+@end menu
+
+@node Features of the Current Implementation
+@subsection Features of the Current Implementation
+
+In a nutshell, the current implementation provides the following features
+@itemize @bullet
+@item The BGP router can connect to one or more RPKI cache servers to
+receive validated prefix to origin AS mappings.
+Advanced failover can be implemented by server sockets with different
+preference values.
+
+@item If no connection to an RPKI cache server can be established after a
+pre-defined timeout, the router will process routes without prefix origin
+validation. It still will try to establish a connection to an RPKI cache
+server in the background.
+
+@item By default, enabling RPKI does not change best path selection. In
+particular, invalid prefixes will still be considered during best path
+selection.  However, the router can be configured to ignore all invalid
+prefixes.
+
+@item 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.
+
+@c @item When the router receives updated validation information from the RPKI
+@c cache server, all routes in the local routing table will be re-evaluated.
+
+@end itemize
+
+
+@node Enabling RPKI
+@subsection Enabling RPKI
+@deffn {Command} {rpki} {}
+This command enables the RPKI configuration mode. Most commands that start
+with @command{rpki} can only be used in this mode.
+
+When it is used in a telnet session, leaving of this mode cause rpki to be initialized.
+
+Executing this command alone does not activate prefix
+validation. You need to configure at least one reachable cache server. See section
+@ref{Configuring RPKI/RTR Cache Servers} for configuring a cache server.
+@end deffn
+
+@node Configuring RPKI/RTR Cache Servers
+@subsection Configuring RPKI/RTR Cache Servers
+
+The following commands are independent of a specific cache server.
+
+@deffn {RPKI Command} {rpki polling_period <1-3600>} {}
+@deffnx {RPKI Command} {no rpki polling_period} {}
+Set the number of seconds the router waits until the router asks the cache again
+for updated data.
+
+The default value is 300 seconds.
+@end deffn
+
+@deffn {RPKI Command} {rpki timeout <1-4,294,967,296>} {}
+@deffnx {RPKI Command} {no rpki timeout} {}
+Set the number of seconds the router waits for the cache reply. If the
+cache server is not replying within this time period, the router deletes
+all received prefix records from the prefix table.
+
+The default value is 600 seconds.
+@end deffn
+
+@deffn {RPKI Command} {rpki initial-synchronisation-timeout <1-4,294,967,296>} {}
+@deffnx {RPKI Command} {no rpki initial-synchronisation-timeout} {}
+Set the number of seconds until the first synchronization with the cache
+server needs to be completed. If the timeout expires, BGP routing is
+started without RPKI. The router will try to establish the cache server
+connection in the background.
+
+The default value is 30 seconds.
+@end deffn
+
+@noindent The following commands configure one or multiple cache servers.
+
+@deffn {RPKI Socket Command} {rpki cache (@var{A.B.C.D}|@var{WORD}) @var{PORT} [@var{SSH_USERNAME}] [@var{SSH_PRIVKEY_PATH}] [@var{SSH_PUBKEY_PATH}] [@var{KNOWN_HOSTS_PATH}] @var{PREFERENCE}} {}
+@deffnx {RPKI Socket Command} {no rpki cache (@var{A.B.C.D}|@var{WORD}) [@var{PORT}] @var{PREFERENCE}} {}
+Add a cache server to the socket. By default, the connection between
+router and cache server is based on plain TCP. Protecting the connection
+between router and cache server by SSH is optional.
+Deleting a socket removes the associated cache server and
+terminates the existing connection.
+@end deffn
+
+@table @code
+@item @var{A.B.C.D}|@var{WORD}
+Address of the cache server.
+
+@item @var{PORT}
+Port number to connect to the cache server
+
+@item @var{SSH_USERNAME}
+SSH username to establish an SSH connection to the cache server.
+
+@item @var{SSH_PRIVKEY_PATH}
+Local path that includes the private key file of the router.
+
+@item @var{SSH_PUBKEY_PATH}
+Local path that includes the public key file of the router.
+
+@item @var{KNOWN_HOSTS_PATH}
+Local path that includes the known hosts file. The default value depends on the
+configuration of the operating system environment, usually
+@file{~/.ssh/known_hosts}.
+
+@end table
+
+@node Validating BGP Updates
+@subsection Validating BGP Updates
+
+@deffn {Route Map Command} {match rpki @{notfound|invalid|valid@}} {}
+@deffnx {Route Map Command} {no match rpki @{notfound|invalid|valid@}} {}
+Create a clause for a route map to match prefixes with the specified RPKI state.
+
+@strong{Note} that the matching of invalid prefixes requires that invalid
+prefixes are considered for best path selection, i.e., @command{bgp
+bestpath prefix-validate disallow-invalid} is not enabled.
+
+In the following example, the router prefers valid routes over invalid
+prefixes because invalid routes have a lower local preference.
+@example
+  ! Allow for invalid routes in route selection process
+  route bgp 60001
+  !
+  ! Set local preference of invalid prefixes to 10
+  route-map rpki permit 10
+   match rpki invalid
+   set local-preference 10
+  !
+  ! Set local preference of valid prefixes to 500
+  route-map rpki permit 500
+   match rpki valid
+   set local-preference 500
+@end example
+
+@end deffn
+
+@node Debugging
+@subsection Debugging
+
+@deffn {Command} {debug rpki} {}
+@deffnx {Command} {no debug rpki} {}
+Enable or disable debugging output for RPKI.
+@end deffn
+
+
+@node Displaying RPKI
+@subsection Displaying RPKI
+
+@deffn {Command} {show rpki prefix-table} {}
+Display all validated prefix to origin AS mappings/records which have been
+received from the cache servers and stored in the router. Based on this data,
+the router validates BGP Updates.
+@end deffn
+
+@deffn {Command} {show rpki cache-connection} {}
+Display all configured cache servers, whether active or not.
+@end deffn
+
+@node RPKI Configuration Example
+@subsection RPKI Configuration Example
+
+
+@example
+hostname bgpd1
+password zebra
+! log stdout
+debug bgp updates
+debug bgp keepalives
+debug rpki
+!
+rpki
+ rpki polling_period 1000
+ rpki timeout 10
+  ! SSH Example:
+  rpki cache example.com 22 rtr-ssh ./ssh_key/id_rsa ./ssh_key/id_rsa.pub preference 1
+  ! TCP Example:
+  rpki cache rpki-validator.realmv6.org 8282 preference 2
+  exit
+!
+router bgp 60001
+ bgp router-id 141.22.28.223
+ network 192.168.0.0/16
+ neighbor 123.123.123.0 remote-as 60002
+ neighbor 123.123.123.0 route-map rpki in
+!
+ address-family ipv6
+  neighbor 123.123.123.0 activate
+   neighbor 123.123.123.0 route-map rpki in
+ exit-address-family
+!
+route-map rpki permit 10
+ match rpki invalid
+ set local-preference 10
+!
+route-map rpki permit 20
+ match rpki notfound
+ set local-preference 20
+!
+route-map rpki permit 30
+ match rpki valid
+ set local-preference 30
+!
+route-map rpki permit 40
+!
+@end example
index 2e91d84bf3b304fdcf76340eea015b5c46376d0d..a4d1190539d2236659c9d32326757b330379cf31 100644 (file)
@@ -117,6 +117,7 @@ const char *node_names[] = {
        "vty",                      // VTY_NODE,
        "link-params",              // LINK_PARAMS_NODE,
        "bgp evpn vni",             // BGP_EVPN_VNI_NODE,
+       "rpki",                     // RPKI_NODE
 };
 
 /* Command vector which includes some level of command lists. Normally
index 9e6c954f151f7edd64044de281955b2869e17928..42dd1c5325d9e517bb19e0421de9f228b0668290 100644 (file)
@@ -140,6 +140,7 @@ enum node_type {
        VTY_NODE,               /* Vty node. */
        LINK_PARAMS_NODE,       /* Link-parameters node */
        BGP_EVPN_VNI_NODE,      /* BGP EVPN VNI */
+       RPKI_NODE,              /* RPKI node for configuration of RPKI cache server connections.*/
        NODE_TYPE_MAX,          /* maximum */
 };
 
index d179ed1d9984c77ae26319ff0f0fb8e94f5cefcd..5156d336b31d71c3e250d1eb3033cda31414f79e 100644 (file)
@@ -56,6 +56,10 @@ vtysh_scan += $(top_srcdir)/bgpd/bgp_routemap.c
 vtysh_scan += $(top_srcdir)/bgpd/bgp_vty.c
 endif
 
+if RPKI
+vtysh_scan += $(top_srcdir)/bgpd/bgp_rpki.c
+endif
+
 if ISISD
 vtysh_scan += $(top_srcdir)/isisd/isis_redist.c
 vtysh_scan += $(top_srcdir)/isisd/isis_spf.c
index 6fdd7167ce3d1986f78c15074c390f9a6ae1de8e..b0216072401343fe335155eaae09c4b3b0ce02bd 100644 (file)
@@ -1045,6 +1045,8 @@ struct cmd_node link_params_node = {
        LINK_PARAMS_NODE, "%s(config-link-params)# ",
 };
 
+static struct cmd_node rpki_node = {RPKI_NODE, "%s(config-rpki)# ", 1};
+
 /* Defined in lib/vty.c */
 extern struct cmd_node vty_node;
 
@@ -1181,6 +1183,35 @@ DEFUNSH(VTYSH_BGPD, address_family_ipv6_labeled_unicast,
        return CMD_SUCCESS;
 }
 
+DEFUNSH(VTYSH_BGPD,
+       rpki,
+       rpki_cmd,
+       "rpki",
+       "Enable rpki and enter rpki configuration mode\n")
+{
+       vty->node = RPKI_NODE;
+       return CMD_SUCCESS;
+}
+
+DEFUNSH(VTYSH_BGPD,
+       rpki_exit,
+       rpki_exit_cmd,
+       "exit",
+       "Exit current mode and down to previous mode\n")
+{
+       vty->node = CONFIG_NODE;
+       return CMD_SUCCESS;
+}
+
+DEFUNSH(VTYSH_BGPD,
+       rpki_quit,
+       rpki_quit_cmd,
+       "quit",
+       "Exit current mode and down to previous mode\n")
+{
+       return rpki_exit(self, vty, argc, argv);
+}
+
 DEFUNSH(VTYSH_BGPD, address_family_evpn, address_family_evpn_cmd,
        "address-family <l2vpn evpn>",
        "Enter Address Family command mode\n"
@@ -2979,6 +3010,7 @@ void vtysh_init_vty(void)
        install_node(&keychain_key_node, NULL);
        install_node(&isis_node, NULL);
        install_node(&vty_node, NULL);
+       install_node(&rpki_node, NULL);
 
        struct cmd_node *node;
        for (unsigned int i = 0; i < vector_active(cmdvec); i++) {
@@ -3177,6 +3209,11 @@ void vtysh_init_vty(void)
        install_element(BGP_EVPN_NODE, &exit_address_family_cmd);
        install_element(BGP_IPV6L_NODE, &exit_address_family_cmd);
 
+       install_element(CONFIG_NODE, &rpki_cmd);
+       install_element(RPKI_NODE, &rpki_exit_cmd);
+       install_element(RPKI_NODE, &rpki_quit_cmd);
+       install_element(RPKI_NODE, &vtysh_end_all_cmd);
+
        /* EVPN commands */
        install_element(BGP_EVPN_NODE, &bgp_evpn_vni_cmd);
        install_element(BGP_EVPN_VNI_NODE, &exit_vni_cmd);