]> git.proxmox.com Git - mirror_frr.git/blame - bgpd/bgp_rpki.c
bgpd: fix the IGP metric for best path selection on VPN import
[mirror_frr.git] / bgpd / bgp_rpki.c
CommitLineData
dabecd7c
MR
1/*
2 * BGP RPKI
3 * Copyright (C) 2013 Michael Mester (m.mester@fu-berlin.de), for FU Berlin
996c9314
LB
4 * Copyright (C) 2014-2017 Andreas Reuter (andreas.reuter@fu-berlin.de), for FU
5 * Berlin
6 * Copyright (C) 2016-2017 Colin Sames (colin.sames@haw-hamburg.de), for HAW
7 * Hamburg
e4234602
MR
8 * Copyright (C) 2017-2018 Marcel Röthke (marcel.roethke@haw-hamburg.de),
9 * for HAW Hamburg
dabecd7c
MR
10 *
11 * This file is part of FRRouting.
12 *
13 * This program is free software; you can redistribute it and/or modify it
14 * under the terms of the GNU General Public License as published by the Free
15 * Software Foundation; either version 2 of the License, or (at your option)
16 * any later version.
17 *
18 * This program is distributed in the hope that it will be useful, but WITHOUT
19 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
21 * more details.
22 *
23 * You should have received a copy of the GNU General Public License along
24 * with this program; see the file COPYING; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26 */
27
45c0beda
AK
28/* If rtrlib compiled with ssh support, don`t fail build */
29#define LIBSSH_LEGACY_0_4
30
dabecd7c
MR
31#include <zebra.h>
32#include <pthread.h>
33#include <time.h>
34#include <stdbool.h>
35#include <stdlib.h>
36#include "prefix.h"
37#include "log.h"
38#include "command.h"
39#include "linklist.h"
40#include "memory.h"
41#include "thread.h"
42#include "filter.h"
43#include "bgpd/bgpd.h"
44#include "bgpd/bgp_table.h"
45#include "bgp_advertise.h"
46#include "bgpd/bgp_debug.h"
47#include "bgpd/bgp_attr.h"
48#include "bgpd/bgp_aspath.h"
49#include "bgpd/bgp_route.h"
fdeb5a81 50#include "bgpd/bgp_rpki.h"
0d951156 51#include "northbound_cli.h"
fdeb5a81 52
4ce82676 53#include "lib/network.h"
1dacdd8b 54#include "lib/thread.h"
dabecd7c 55#include "rtrlib/rtrlib.h"
dabecd7c
MR
56#include "hook.h"
57#include "libfrr.h"
09781197 58#include "lib/version.h"
dabecd7c 59
2e4c2296 60#include "bgpd/bgp_rpki_clippy.c"
dabecd7c 61
bf8d3d6a
DL
62DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE, "BGP RPKI Cache server");
63DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE_GROUP, "BGP RPKI Cache server group");
94ff78a7 64DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_RTRLIB, "BGP RPKI RTRLib");
7651f277 65DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_REVALIDATE, "BGP RPKI Revalidation");
dabecd7c 66
dabecd7c
MR
67#define POLLING_PERIOD_DEFAULT 3600
68#define EXPIRE_INTERVAL_DEFAULT 7200
69#define RETRY_INTERVAL_DEFAULT 600
1b2095d1 70#define BGP_RPKI_CACHE_SERVER_SYNC_RETRY_TIMEOUT 3
dabecd7c 71
d67485c6
DA
72static struct thread *t_rpki_sync;
73
dabecd7c
MR
74#define RPKI_DEBUG(...) \
75 if (rpki_debug) { \
76 zlog_debug("RPKI: " __VA_ARGS__); \
77 }
78
79#define RPKI_OUTPUT_STRING "Control rpki specific settings\n"
80
81struct cache {
996c9314
LB
82 enum { TCP, SSH } type;
83 struct tr_socket *tr_socket;
84 union {
dabecd7c
MR
85 struct tr_tcp_config *tcp_config;
86 struct tr_ssh_config *ssh_config;
996c9314
LB
87 } tr_config;
88 struct rtr_socket *rtr_socket;
89 uint8_t preference;
dabecd7c
MR
90};
91
92enum return_values { SUCCESS = 0, ERROR = -1 };
93
94struct rpki_for_each_record_arg {
95 struct vty *vty;
96 unsigned int *prefix_amount;
02334bb2 97 as_t as;
dff41cc8 98 json_object *json;
dabecd7c
MR
99};
100
101static int start(void);
102static void stop(void);
103static int reset(bool force);
104static struct rtr_mgr_group *get_connected_group(void);
dff41cc8 105static void print_prefix_table(struct vty *vty, json_object *json);
dabecd7c
MR
106static void install_cli_commands(void);
107static int config_write(struct vty *vty);
791ded4a 108static int config_on_exit(struct vty *vty);
dabecd7c
MR
109static void free_cache(struct cache *cache);
110static struct rtr_mgr_group *get_groups(void);
111#if defined(FOUND_SSH)
996c9314
LB
112static int add_ssh_cache(const char *host, const unsigned int port,
113 const char *username, const char *client_privkey_path,
dabecd7c 114 const char *server_pubkey_path,
7253a7bc 115 const uint8_t preference, const char *bindaddr);
dabecd7c
MR
116#endif
117static struct rtr_socket *create_rtr_socket(struct tr_socket *tr_socket);
118static struct cache *find_cache(const uint8_t preference);
8156765a 119static void rpki_delete_all_cache_nodes(void);
996c9314 120static int add_tcp_cache(const char *host, const char *port,
7253a7bc 121 const uint8_t preference, const char *bindaddr);
dff41cc8
DA
122static void print_record(const struct pfx_record *record, struct vty *vty,
123 json_object *json);
d67485c6 124static bool is_synchronized(void);
0e3d96bf
DA
125static bool is_running(void);
126static bool is_stopping(void);
dabecd7c 127static void route_match_free(void *rule);
b68885f9
LK
128static enum route_map_cmd_result_t route_match(void *rule,
129 const struct prefix *prefix,
1782514f 130
b68885f9 131 void *object);
dabecd7c 132static void *route_match_compile(const char *arg);
9bcb3eef 133static void revalidate_bgp_node(struct bgp_dest *dest, afi_t afi, safi_t safi);
4ce82676 134static void revalidate_all_routes(void);
dabecd7c
MR
135
136static struct rtr_mgr_config *rtr_config;
137static struct list *cache_list;
0e3d96bf
DA
138static bool rtr_is_running;
139static bool rtr_is_stopping;
d67485c6 140static bool rtr_is_synced;
4ce82676 141static _Atomic int rtr_update_overflow;
0e3d96bf 142static bool rpki_debug;
dabecd7c
MR
143static unsigned int polling_period;
144static unsigned int expire_interval;
145static unsigned int retry_interval;
1dacdd8b
MR
146static int rpki_sync_socket_rtr;
147static int rpki_sync_socket_bgpd;
dabecd7c 148
62b346ee 149static struct cmd_node rpki_node = {
f4b8291f 150 .name = "rpki",
62b346ee 151 .node = RPKI_NODE,
24389580 152 .parent_node = CONFIG_NODE,
62b346ee 153 .prompt = "%s(config-rpki)# ",
612c2c15 154 .config_write = config_write,
791ded4a 155 .node_exit = config_on_exit,
62b346ee 156};
364deb04 157static const struct route_map_rule_cmd route_match_rpki_cmd = {
996c9314 158 "rpki", route_match, route_match_compile, route_match_free};
dabecd7c
MR
159
160static void *malloc_wrapper(size_t size)
161{
94ff78a7 162 return XMALLOC(MTYPE_BGP_RPKI_RTRLIB, size);
dabecd7c
MR
163}
164
165static void *realloc_wrapper(void *ptr, size_t size)
166{
94ff78a7 167 return XREALLOC(MTYPE_BGP_RPKI_RTRLIB, ptr, size);
dabecd7c
MR
168}
169
170static void free_wrapper(void *ptr)
171{
94ff78a7 172 XFREE(MTYPE_BGP_RPKI_RTRLIB, ptr);
dabecd7c
MR
173}
174
92110aab
MR
175static void init_tr_socket(struct cache *cache)
176{
177 if (cache->type == TCP)
178 tr_tcp_init(cache->tr_config.tcp_config,
179 cache->tr_socket);
180#if defined(FOUND_SSH)
181 else
182 tr_ssh_init(cache->tr_config.ssh_config,
183 cache->tr_socket);
184#endif
185}
186
187static void free_tr_socket(struct cache *cache)
188{
189 if (cache->type == TCP)
190 tr_tcp_init(cache->tr_config.tcp_config,
191 cache->tr_socket);
192#if defined(FOUND_SSH)
193 else
194 tr_ssh_init(cache->tr_config.ssh_config,
195 cache->tr_socket);
196#endif
197}
198
dabecd7c 199static int rpki_validate_prefix(struct peer *peer, struct attr *attr,
898c4e66 200 const struct prefix *prefix);
dabecd7c 201
1dacdd8b
MR
202static void ipv6_addr_to_network_byte_order(const uint32_t *src, uint32_t *dest)
203{
204 int i;
205
206 for (i = 0; i < 4; i++)
207 dest[i] = htonl(src[i]);
208}
209
e4234602
MR
210static void ipv6_addr_to_host_byte_order(const uint32_t *src, uint32_t *dest)
211{
212 int i;
213
214 for (i = 0; i < 4; i++)
215 dest[i] = ntohl(src[i]);
216}
217
b68885f9
LK
218static enum route_map_cmd_result_t route_match(void *rule,
219 const struct prefix *prefix,
b68885f9 220 void *object)
dabecd7c
MR
221{
222 int *rpki_status = rule;
9b6d8fcf 223 struct bgp_path_info *path;
dabecd7c 224
1782514f 225 path = object;
dabecd7c 226
1782514f
DS
227 if (rpki_validate_prefix(path->peer, path->attr, prefix)
228 == *rpki_status) {
229 return RMAP_MATCH;
dabecd7c 230 }
1782514f 231
dabecd7c
MR
232 return RMAP_NOMATCH;
233}
234
235static void *route_match_compile(const char *arg)
236{
237 int *rpki_status;
238
0b2c4b35 239 rpki_status = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(int));
dabecd7c
MR
240
241 if (strcmp(arg, "valid") == 0)
242 *rpki_status = RPKI_VALID;
243 else if (strcmp(arg, "invalid") == 0)
244 *rpki_status = RPKI_INVALID;
245 else
246 *rpki_status = RPKI_NOTFOUND;
247
248 return rpki_status;
249}
250
251static void route_match_free(void *rule)
252{
253 XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
254}
255
256static struct rtr_socket *create_rtr_socket(struct tr_socket *tr_socket)
257{
258 struct rtr_socket *rtr_socket =
259 XMALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct rtr_socket));
260 rtr_socket->tr_socket = tr_socket;
261 return rtr_socket;
262}
263
264static struct cache *find_cache(const uint8_t preference)
265{
266 struct listnode *cache_node;
267 struct cache *cache;
268
269 for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) {
270 if (cache->preference == preference)
271 return cache;
272 }
273 return NULL;
274}
275
8156765a
DA
276static void rpki_delete_all_cache_nodes(void)
277{
278 struct listnode *cache_node, *cache_next;
279 struct cache *cache;
280
281 for (ALL_LIST_ELEMENTS(cache_list, cache_node, cache_next, cache)) {
282 rtr_mgr_remove_group(rtr_config, cache->preference);
283 listnode_delete(cache_list, cache);
284 }
285}
286
dff41cc8
DA
287static void print_record(const struct pfx_record *record, struct vty *vty,
288 json_object *json)
dabecd7c
MR
289{
290 char ip[INET6_ADDRSTRLEN];
dff41cc8 291 json_object *json_record = NULL;
5d799192
MR
292
293 lrtr_ip_addr_to_str(&record->prefix, ip, sizeof(ip));
dff41cc8
DA
294
295 if (!json) {
296 vty_out(vty, "%-40s %3u - %3u %10u\n", ip, record->min_len,
297 record->max_len, record->asn);
298 } else {
299 json_record = json_object_new_object();
300 json_object_string_add(json_record, "prefix", ip);
301 json_object_int_add(json_record, "prefixLenMin",
302 record->min_len);
303 json_object_int_add(json_record, "prefixLenMax",
304 record->max_len);
305 json_object_int_add(json_record, "asn", record->asn);
306 json_object_array_add(json, json_record);
307 }
5d799192
MR
308}
309
02334bb2
DA
310static void print_record_by_asn(const struct pfx_record *record, void *data)
311{
312 struct rpki_for_each_record_arg *arg = data;
313 struct vty *vty = arg->vty;
314
315 if (record->asn == arg->as) {
316 (*arg->prefix_amount)++;
dff41cc8 317 print_record(record, vty, arg->json);
02334bb2
DA
318 }
319}
320
5d799192
MR
321static void print_record_cb(const struct pfx_record *record, void *data)
322{
dabecd7c
MR
323 struct rpki_for_each_record_arg *arg = data;
324 struct vty *vty = arg->vty;
325
a220aec6 326 (*arg->prefix_amount)++;
dabecd7c 327
dff41cc8 328 print_record(record, vty, arg->json);
dabecd7c
MR
329}
330
331static struct rtr_mgr_group *get_groups(void)
332{
333 struct listnode *cache_node;
334 struct rtr_mgr_group *rtr_mgr_groups;
335 struct cache *cache;
336
337 int group_count = listcount(cache_list);
338
339 if (group_count == 0)
340 return NULL;
341
342 rtr_mgr_groups = XMALLOC(MTYPE_BGP_RPKI_CACHE_GROUP,
343 group_count * sizeof(struct rtr_mgr_group));
344
345 size_t i = 0;
346
347 for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) {
348 rtr_mgr_groups[i].sockets = &cache->rtr_socket;
349 rtr_mgr_groups[i].sockets_len = 1;
350 rtr_mgr_groups[i].preference = cache->preference;
351
92110aab 352 init_tr_socket(cache);
dabecd7c
MR
353
354 i++;
355 }
356
357 return rtr_mgr_groups;
358}
359
d67485c6 360inline bool is_synchronized(void)
dabecd7c 361{
d67485c6 362 return rtr_is_synced;
dabecd7c
MR
363}
364
0e3d96bf 365inline bool is_running(void)
dabecd7c
MR
366{
367 return rtr_is_running;
368}
369
0e3d96bf 370inline bool is_stopping(void)
9ca44fc8
DA
371{
372 return rtr_is_stopping;
373}
374
fc15f734
DS
375static void pfx_record_to_prefix(struct pfx_record *record,
376 struct prefix *prefix)
1dacdd8b 377{
1dacdd8b
MR
378 prefix->prefixlen = record->min_len;
379
380 if (record->prefix.ver == LRTR_IPV4) {
381 prefix->family = AF_INET;
382 prefix->u.prefix4.s_addr = htonl(record->prefix.u.addr4.addr);
383 } else {
384 prefix->family = AF_INET6;
385 ipv6_addr_to_network_byte_order(record->prefix.u.addr6.addr,
386 prefix->u.prefix6.s6_addr32);
387 }
1dacdd8b
MR
388}
389
7f1f9314
DS
390struct rpki_revalidate_prefix {
391 struct bgp *bgp;
392 struct prefix prefix;
393 afi_t afi;
394 safi_t safi;
395};
396
397static void rpki_revalidate_prefix(struct thread *thread)
398{
399 struct rpki_revalidate_prefix *rrp = THREAD_ARG(thread);
400 struct bgp_dest *match, *node;
401
402 match = bgp_table_subtree_lookup(rrp->bgp->rib[rrp->afi][rrp->safi],
403 &rrp->prefix);
31d0363f 404
7f1f9314
DS
405 node = match;
406
407 while (node) {
408 if (bgp_dest_has_bgp_path_info_data(node)) {
409 revalidate_bgp_node(node, rrp->afi, rrp->safi);
410 }
411
412 node = bgp_route_next_until(node, match);
413 }
414
7f1f9314
DS
415 XFREE(MTYPE_BGP_RPKI_REVALIDATE, rrp);
416}
417
cc9f21da 418static void bgpd_sync_callback(struct thread *thread)
1dacdd8b
MR
419{
420 struct bgp *bgp;
421 struct listnode *node;
fc15f734 422 struct prefix prefix;
1dacdd8b
MR
423 struct pfx_record rec;
424
d2e3f8a2
DA
425 thread_add_read(bm->master, bgpd_sync_callback, NULL,
426 rpki_sync_socket_bgpd, NULL);
4ce82676
MR
427
428 if (atomic_load_explicit(&rtr_update_overflow, memory_order_seq_cst)) {
d2e3f8a2
DA
429 while (read(rpki_sync_socket_bgpd, &rec,
430 sizeof(struct pfx_record)) != -1)
4ce82676
MR
431 ;
432
433 atomic_store_explicit(&rtr_update_overflow, 0,
434 memory_order_seq_cst);
435 revalidate_all_routes();
cc9f21da 436 return;
4ce82676
MR
437 }
438
d2e3f8a2
DA
439 int retval =
440 read(rpki_sync_socket_bgpd, &rec, sizeof(struct pfx_record));
3b128228 441 if (retval != sizeof(struct pfx_record)) {
d2e3f8a2 442 RPKI_DEBUG("Could not read from rpki_sync_socket_bgpd");
cc9f21da 443 return;
38775a3c 444 }
fc15f734 445 pfx_record_to_prefix(&rec, &prefix);
1dacdd8b
MR
446
447 afi_t afi = (rec.prefix.ver == LRTR_IPV4) ? AFI_IP : AFI_IP6;
448
449 for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) {
35a1e798 450 safi_t safi;
1dacdd8b 451
35a1e798 452 for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) {
7e7f61ed 453 struct bgp_table *table = bgp->rib[afi][safi];
7f1f9314 454 struct rpki_revalidate_prefix *rrp;
7e7f61ed
DA
455
456 if (!table)
35a1e798 457 continue;
31a2af32 458
7f1f9314
DS
459 rrp = XCALLOC(MTYPE_BGP_RPKI_REVALIDATE, sizeof(*rrp));
460 rrp->bgp = bgp;
461 rrp->prefix = prefix;
462 rrp->afi = afi;
463 rrp->safi = safi;
464 thread_add_event(bm->master, rpki_revalidate_prefix,
465 rrp, 0, &bgp->t_revalidate[afi][safi]);
1dacdd8b
MR
466 }
467 }
1dacdd8b
MR
468}
469
9bcb3eef 470static void revalidate_bgp_node(struct bgp_dest *bgp_dest, afi_t afi,
1dacdd8b
MR
471 safi_t safi)
472{
473 struct bgp_adj_in *ain;
474
9bcb3eef 475 for (ain = bgp_dest->adj_in; ain; ain = ain->next) {
2b964e86 476 struct bgp_path_info *path =
9bcb3eef 477 bgp_dest_get_bgp_path_info(bgp_dest);
1dacdd8b
MR
478 mpls_label_t *label = NULL;
479 uint32_t num_labels = 0;
480
9b6d8fcf
DS
481 if (path && path->extra) {
482 label = path->extra->label;
483 num_labels = path->extra->num_labels;
1dacdd8b 484 }
73261b47 485 (void)bgp_update(ain->peer, bgp_dest_get_prefix(bgp_dest),
b54892e0
DS
486 ain->addpath_rx_id, ain->attr, afi, safi,
487 ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, label,
488 num_labels, 1, NULL);
1dacdd8b
MR
489 }
490}
491
7651f277
DS
492/*
493 * The act of a soft reconfig in revalidation is really expensive
494 * coupled with the fact that the download of a full rpki state
495 * from a rpki server can be expensive, let's break up the revalidation
496 * to a point in time in the future to allow other bgp events
497 * to take place too.
498 */
499struct rpki_revalidate_peer {
500 afi_t afi;
501 safi_t safi;
502 struct peer *peer;
503};
504
505static void bgp_rpki_revalidate_peer(struct thread *thread)
506{
507 struct rpki_revalidate_peer *rvp = THREAD_ARG(thread);
508
509 /*
510 * Here's the expensive bit of gnomish deviousness
511 */
512 bgp_soft_reconfig_in(rvp->peer, rvp->afi, rvp->safi);
513
514 XFREE(MTYPE_BGP_RPKI_REVALIDATE, rvp);
515}
516
1dacdd8b
MR
517static void revalidate_all_routes(void)
518{
519 struct bgp *bgp;
520 struct listnode *node;
1dacdd8b
MR
521
522 for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) {
31a2af32
MR
523 struct peer *peer;
524 struct listnode *peer_listnode;
1dacdd8b 525
31a2af32 526 for (ALL_LIST_ELEMENTS_RO(bgp->peer, peer_listnode, peer)) {
8fb15d02
DS
527 afi_t afi;
528 safi_t safi;
31a2af32 529
8fb15d02 530 FOREACH_AFI_SAFI (afi, safi) {
7651f277
DS
531 struct rpki_revalidate_peer *rvp;
532
802ca11f 533 if (!bgp->rib[afi][safi])
8fb15d02 534 continue;
d2e3f8a2 535
89c73443
DS
536 if (!peer_established(peer))
537 continue;
7651f277
DS
538
539 rvp = XCALLOC(MTYPE_BGP_RPKI_REVALIDATE,
540 sizeof(*rvp));
541 rvp->peer = peer;
542 rvp->afi = afi;
543 rvp->safi = safi;
544
545 thread_add_event(
546 bm->master, bgp_rpki_revalidate_peer,
547 rvp, 0,
548 &peer->t_revalidate_all[afi][safi]);
1dacdd8b
MR
549 }
550 }
551 }
552}
553
554static void rpki_update_cb_sync_rtr(struct pfx_table *p __attribute__((unused)),
555 const struct pfx_record rec,
556 const bool added __attribute__((unused)))
557{
9ca44fc8
DA
558 if (is_stopping() ||
559 atomic_load_explicit(&rtr_update_overflow, memory_order_seq_cst))
1dacdd8b
MR
560 return;
561
562 int retval =
563 write(rpki_sync_socket_rtr, &rec, sizeof(struct pfx_record));
4ce82676
MR
564 if (retval == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
565 atomic_store_explicit(&rtr_update_overflow, 1,
566 memory_order_seq_cst);
567
568 else if (retval != sizeof(struct pfx_record))
1dacdd8b
MR
569 RPKI_DEBUG("Could not write to rpki_sync_socket_rtr");
570}
571
572static void rpki_init_sync_socket(void)
573{
574 int fds[2];
4ce82676 575 const char *msg;
1dacdd8b
MR
576
577 RPKI_DEBUG("initializing sync socket");
578 if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, fds) != 0) {
4ce82676
MR
579 msg = "could not open rpki sync socketpair";
580 goto err;
1dacdd8b
MR
581 }
582 rpki_sync_socket_rtr = fds[0];
583 rpki_sync_socket_bgpd = fds[1];
4ce82676
MR
584
585 if (set_nonblocking(rpki_sync_socket_rtr) != 0) {
586 msg = "could not set rpki_sync_socket_rtr to non blocking";
587 goto err;
588 }
589
590 if (set_nonblocking(rpki_sync_socket_bgpd) != 0) {
591 msg = "could not set rpki_sync_socket_bgpd to non blocking";
592 goto err;
593 }
594
d2e3f8a2 595
1dacdd8b 596 thread_add_read(bm->master, bgpd_sync_callback, NULL,
d2e3f8a2 597 rpki_sync_socket_bgpd, NULL);
4ce82676
MR
598
599 return;
600
601err:
602 zlog_err("RPKI: %s", msg);
603 abort();
604
1dacdd8b
MR
605}
606
dabecd7c
MR
607static int bgp_rpki_init(struct thread_master *master)
608{
0e3d96bf
DA
609 rpki_debug = false;
610 rtr_is_running = false;
611 rtr_is_stopping = false;
d67485c6 612 rtr_is_synced = false;
dabecd7c
MR
613
614 cache_list = list_new();
996c9314 615 cache_list->del = (void (*)(void *)) & free_cache;
dabecd7c
MR
616
617 polling_period = POLLING_PERIOD_DEFAULT;
618 expire_interval = EXPIRE_INTERVAL_DEFAULT;
619 retry_interval = RETRY_INTERVAL_DEFAULT;
dabecd7c 620 install_cli_commands();
1dacdd8b 621 rpki_init_sync_socket();
dabecd7c
MR
622 return 0;
623}
624
625static int bgp_rpki_fini(void)
626{
627 stop();
6a154c88 628 list_delete(&cache_list);
dabecd7c 629
1dacdd8b
MR
630 close(rpki_sync_socket_rtr);
631 close(rpki_sync_socket_bgpd);
632
dabecd7c
MR
633 return 0;
634}
635
636static int bgp_rpki_module_init(void)
637{
996c9314 638 lrtr_set_alloc_functions(malloc_wrapper, realloc_wrapper, free_wrapper);
dabecd7c 639
b5b99af8 640 hook_register(bgp_rpki_prefix_status, rpki_validate_prefix);
dabecd7c 641 hook_register(frr_late_init, bgp_rpki_init);
f714e57a 642 hook_register(frr_early_fini, bgp_rpki_fini);
dabecd7c
MR
643
644 return 0;
645}
646
d67485c6 647static void sync_expired(struct thread *thread)
1b2095d1
DA
648{
649 if (!rtr_mgr_conf_in_sync(rtr_config)) {
d67485c6
DA
650 RPKI_DEBUG("rtr_mgr is not synced, retrying.");
651 thread_add_timer(bm->master, sync_expired, NULL,
1b2095d1 652 BGP_RPKI_CACHE_SERVER_SYNC_RETRY_TIMEOUT,
d67485c6 653 &t_rpki_sync);
1b2095d1
DA
654 return;
655 }
656
d67485c6
DA
657 RPKI_DEBUG("rtr_mgr sync is done.");
658
659 rtr_is_synced = true;
1b2095d1
DA
660}
661
dabecd7c
MR
662static int start(void)
663{
dabecd7c
MR
664 int ret;
665
0e3d96bf 666 rtr_is_stopping = false;
d67485c6 667 rtr_is_synced = false;
4ce82676 668 rtr_update_overflow = 0;
1dacdd8b 669
dabecd7c
MR
670 if (list_isempty(cache_list)) {
671 RPKI_DEBUG(
672 "No caches were found in config. Prefix validation is off.");
673 return ERROR;
674 }
675 RPKI_DEBUG("Init rtr_mgr.");
676 int groups_len = listcount(cache_list);
677 struct rtr_mgr_group *groups = get_groups();
678
1dacdd8b 679 RPKI_DEBUG("Polling period: %d", polling_period);
dabecd7c 680 ret = rtr_mgr_init(&rtr_config, groups, groups_len, polling_period,
1dacdd8b 681 expire_interval, retry_interval,
d2e3f8a2 682 rpki_update_cb_sync_rtr, NULL, NULL, NULL);
dabecd7c
MR
683 if (ret == RTR_ERROR) {
684 RPKI_DEBUG("Init rtr_mgr failed.");
685 return ERROR;
686 }
687
688 RPKI_DEBUG("Starting rtr_mgr.");
689 ret = rtr_mgr_start(rtr_config);
690 if (ret == RTR_ERROR) {
691 RPKI_DEBUG("Starting rtr_mgr failed.");
692 rtr_mgr_free(rtr_config);
693 return ERROR;
694 }
1b2095d1 695
d67485c6 696 thread_add_timer(bm->master, sync_expired, NULL, 0, &t_rpki_sync);
dabecd7c
MR
697
698 XFREE(MTYPE_BGP_RPKI_CACHE_GROUP, groups);
699
0e3d96bf 700 rtr_is_running = true;
d67485c6 701
dabecd7c
MR
702 return SUCCESS;
703}
704
705static void stop(void)
706{
0e3d96bf 707 rtr_is_stopping = true;
01fcc189 708 if (is_running()) {
d67485c6 709 THREAD_OFF(t_rpki_sync);
dabecd7c
MR
710 rtr_mgr_stop(rtr_config);
711 rtr_mgr_free(rtr_config);
0e3d96bf 712 rtr_is_running = false;
dabecd7c
MR
713 }
714}
715
716static int reset(bool force)
717{
01fcc189 718 if (is_running() && !force)
dabecd7c
MR
719 return SUCCESS;
720
721 RPKI_DEBUG("Resetting RPKI Session");
722 stop();
723 return start();
724}
725
726static struct rtr_mgr_group *get_connected_group(void)
727{
18b91526 728 if (!cache_list || list_isempty(cache_list))
dabecd7c
MR
729 return NULL;
730
731 return rtr_mgr_get_first_group(rtr_config);
732}
733
dff41cc8
DA
734static void print_prefix_table_by_asn(struct vty *vty, as_t as,
735 json_object *json)
02334bb2
DA
736{
737 unsigned int number_of_ipv4_prefixes = 0;
738 unsigned int number_of_ipv6_prefixes = 0;
739 struct rtr_mgr_group *group = get_connected_group();
740 struct rpki_for_each_record_arg arg;
dff41cc8 741 json_object *json_records = NULL;
02334bb2
DA
742
743 arg.vty = vty;
744 arg.as = as;
dff41cc8 745 arg.json = NULL;
02334bb2
DA
746
747 if (!group) {
dff41cc8
DA
748 if (!json)
749 vty_out(vty, "Cannot find a connected group.\n");
02334bb2
DA
750 return;
751 }
752
753 struct pfx_table *pfx_table = group->sockets[0]->pfx_table;
754
dff41cc8
DA
755 if (!json) {
756 vty_out(vty, "RPKI/RTR prefix table\n");
757 vty_out(vty, "%-40s %s %s\n", "Prefix", "Prefix Length",
758 "Origin-AS");
759 } else {
760 json_records = json_object_new_array();
761 json_object_object_add(json, "prefixes", json_records);
762 arg.json = json_records;
763 }
02334bb2
DA
764
765 arg.prefix_amount = &number_of_ipv4_prefixes;
766 pfx_table_for_each_ipv4_record(pfx_table, print_record_by_asn, &arg);
767
768 arg.prefix_amount = &number_of_ipv6_prefixes;
769 pfx_table_for_each_ipv6_record(pfx_table, print_record_by_asn, &arg);
770
dff41cc8
DA
771 if (!json) {
772 vty_out(vty, "Number of IPv4 Prefixes: %u\n",
773 number_of_ipv4_prefixes);
774 vty_out(vty, "Number of IPv6 Prefixes: %u\n",
775 number_of_ipv6_prefixes);
776 } else {
777 json_object_int_add(json, "ipv4PrefixCount",
778 number_of_ipv4_prefixes);
779 json_object_int_add(json, "ipv6PrefixCount",
780 number_of_ipv6_prefixes);
781 }
782
783 if (json)
784 vty_json(vty, json);
02334bb2
DA
785}
786
dff41cc8 787static void print_prefix_table(struct vty *vty, json_object *json)
dabecd7c
MR
788{
789 struct rpki_for_each_record_arg arg;
790
791 unsigned int number_of_ipv4_prefixes = 0;
792 unsigned int number_of_ipv6_prefixes = 0;
793 struct rtr_mgr_group *group = get_connected_group();
dff41cc8 794 json_object *json_records = NULL;
dabecd7c
MR
795
796 arg.vty = vty;
dff41cc8 797 arg.json = NULL;
dabecd7c 798
dff41cc8
DA
799 if (!group) {
800 if (!json)
801 vty_out(vty, "Cannot find a connected group.\n");
dabecd7c 802 return;
dff41cc8 803 }
dabecd7c
MR
804
805 struct pfx_table *pfx_table = group->sockets[0]->pfx_table;
806
dff41cc8
DA
807 if (!json) {
808 vty_out(vty, "RPKI/RTR prefix table\n");
809 vty_out(vty, "%-40s %s %s\n", "Prefix", "Prefix Length",
810 "Origin-AS");
811 } else {
812 json_records = json_object_new_array();
813 json_object_object_add(json, "prefixes", json_records);
814 arg.json = json_records;
815 }
dabecd7c
MR
816
817 arg.prefix_amount = &number_of_ipv4_prefixes;
5d799192 818 pfx_table_for_each_ipv4_record(pfx_table, print_record_cb, &arg);
dabecd7c
MR
819
820 arg.prefix_amount = &number_of_ipv6_prefixes;
5d799192 821 pfx_table_for_each_ipv6_record(pfx_table, print_record_cb, &arg);
dabecd7c 822
dff41cc8
DA
823 if (!json) {
824 vty_out(vty, "Number of IPv4 Prefixes: %u\n",
825 number_of_ipv4_prefixes);
826 vty_out(vty, "Number of IPv6 Prefixes: %u\n",
827 number_of_ipv6_prefixes);
828 } else {
829 json_object_int_add(json, "ipv4PrefixCount",
830 number_of_ipv4_prefixes);
831 json_object_int_add(json, "ipv6PrefixCount",
832 number_of_ipv6_prefixes);
833 }
834
835 if (json)
836 vty_json(vty, json);
dabecd7c
MR
837}
838
839static int rpki_validate_prefix(struct peer *peer, struct attr *attr,
898c4e66 840 const struct prefix *prefix)
dabecd7c
MR
841{
842 struct assegment *as_segment;
843 as_t as_number = 0;
844 struct lrtr_ip_addr ip_addr_prefix;
845 enum pfxv_state result;
dabecd7c
MR
846
847 if (!is_synchronized())
e1a05dd4 848 return RPKI_NOT_BEING_USED;
dabecd7c
MR
849
850 // No aspath means route comes from iBGP
851 if (!attr->aspath || !attr->aspath->segments) {
852 // Set own as number
853 as_number = peer->bgp->as;
854 } else {
855 as_segment = attr->aspath->segments;
856 // Find last AsSegment
857 while (as_segment->next)
858 as_segment = as_segment->next;
859
860 if (as_segment->type == AS_SEQUENCE) {
861 // Get rightmost asn
862 as_number = as_segment->as[as_segment->length - 1];
996c9314
LB
863 } else if (as_segment->type == AS_CONFED_SEQUENCE
864 || as_segment->type == AS_CONFED_SET) {
dabecd7c
MR
865 // Set own as number
866 as_number = peer->bgp->as;
867 } else {
868 // RFC says: "Take distinguished value NONE as asn"
869 // which means state is unknown
870 return RPKI_NOTFOUND;
871 }
872 }
873
874 // Get the prefix in requested format
875 switch (prefix->family) {
876 case AF_INET:
877 ip_addr_prefix.ver = LRTR_IPV4;
878 ip_addr_prefix.u.addr4.addr = ntohl(prefix->u.prefix4.s_addr);
879 break;
880
dabecd7c
MR
881 case AF_INET6:
882 ip_addr_prefix.ver = LRTR_IPV6;
883 ipv6_addr_to_host_byte_order(prefix->u.prefix6.s6_addr32,
884 ip_addr_prefix.u.addr6.addr);
885 break;
dabecd7c
MR
886
887 default:
e1a05dd4 888 return RPKI_NOT_BEING_USED;
dabecd7c
MR
889 }
890
891 // Do the actual validation
892 rtr_mgr_validate(rtr_config, as_number, &ip_addr_prefix,
893 prefix->prefixlen, &result);
894
895 // Print Debug output
dabecd7c
MR
896 switch (result) {
897 case BGP_PFXV_STATE_VALID:
898 RPKI_DEBUG(
2dbe669b
DA
899 "Validating Prefix %pFX from asn %u Result: VALID",
900 prefix, as_number);
dabecd7c
MR
901 return RPKI_VALID;
902 case BGP_PFXV_STATE_NOT_FOUND:
903 RPKI_DEBUG(
2dbe669b
DA
904 "Validating Prefix %pFX from asn %u Result: NOT FOUND",
905 prefix, as_number);
dabecd7c
MR
906 return RPKI_NOTFOUND;
907 case BGP_PFXV_STATE_INVALID:
908 RPKI_DEBUG(
2dbe669b
DA
909 "Validating Prefix %pFX from asn %u Result: INVALID",
910 prefix, as_number);
dabecd7c
MR
911 return RPKI_INVALID;
912 default:
913 RPKI_DEBUG(
2dbe669b
DA
914 "Validating Prefix %pFX from asn %u Result: CANNOT VALIDATE",
915 prefix, as_number);
dabecd7c
MR
916 break;
917 }
e1a05dd4 918 return RPKI_NOT_BEING_USED;
dabecd7c
MR
919}
920
921static int add_cache(struct cache *cache)
922{
923 uint8_t preference = cache->preference;
924 struct rtr_mgr_group group;
925
926 group.preference = preference;
927 group.sockets_len = 1;
928 group.sockets = &cache->rtr_socket;
929
01fcc189 930 if (is_running()) {
92110aab
MR
931 init_tr_socket(cache);
932
933 if (rtr_mgr_add_group(rtr_config, &group) != RTR_SUCCESS) {
934 free_tr_socket(cache);
935 return ERROR;
936 }
dabecd7c
MR
937 }
938
6893064b
MR
939 listnode_add(cache_list, cache);
940
dabecd7c
MR
941 return SUCCESS;
942}
943
996c9314 944static int add_tcp_cache(const char *host, const char *port,
7253a7bc 945 const uint8_t preference, const char *bindaddr)
dabecd7c
MR
946{
947 struct rtr_socket *rtr_socket;
948 struct tr_tcp_config *tcp_config =
a633498e 949 XCALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct tr_tcp_config));
dabecd7c
MR
950 struct tr_socket *tr_socket =
951 XMALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct tr_socket));
952 struct cache *cache =
953 XMALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct cache));
954
955 tcp_config->host = XSTRDUP(MTYPE_BGP_RPKI_CACHE, host);
956 tcp_config->port = XSTRDUP(MTYPE_BGP_RPKI_CACHE, port);
7253a7bc
PG
957 if (bindaddr)
958 tcp_config->bindaddr = XSTRDUP(MTYPE_BGP_RPKI_CACHE, bindaddr);
959 else
960 tcp_config->bindaddr = NULL;
dabecd7c
MR
961
962 rtr_socket = create_rtr_socket(tr_socket);
963
964 cache->type = TCP;
965 cache->tr_socket = tr_socket;
966 cache->tr_config.tcp_config = tcp_config;
967 cache->rtr_socket = rtr_socket;
968 cache->preference = preference;
969
6893064b
MR
970 int ret = add_cache(cache);
971 if (ret != SUCCESS) {
972 free_cache(cache);
973 }
974
975 return ret;
dabecd7c
MR
976}
977
978#if defined(FOUND_SSH)
996c9314
LB
979static int add_ssh_cache(const char *host, const unsigned int port,
980 const char *username, const char *client_privkey_path,
dabecd7c 981 const char *server_pubkey_path,
7253a7bc 982 const uint8_t preference, const char *bindaddr)
dabecd7c
MR
983{
984 struct tr_ssh_config *ssh_config =
a633498e 985 XCALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct tr_ssh_config));
dabecd7c
MR
986 struct cache *cache =
987 XMALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct cache));
988 struct tr_socket *tr_socket =
989 XMALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct tr_socket));
990 struct rtr_socket *rtr_socket;
991
992 ssh_config->port = port;
993 ssh_config->host = XSTRDUP(MTYPE_BGP_RPKI_CACHE, host);
7253a7bc
PG
994 if (bindaddr)
995 ssh_config->bindaddr = XSTRDUP(MTYPE_BGP_RPKI_CACHE, bindaddr);
996 else
997 ssh_config->bindaddr = NULL;
dabecd7c
MR
998
999 ssh_config->username = XSTRDUP(MTYPE_BGP_RPKI_CACHE, username);
996c9314
LB
1000 ssh_config->client_privkey_path =
1001 XSTRDUP(MTYPE_BGP_RPKI_CACHE, client_privkey_path);
dabecd7c
MR
1002 ssh_config->server_hostkey_path =
1003 XSTRDUP(MTYPE_BGP_RPKI_CACHE, server_pubkey_path);
1004
1005 rtr_socket = create_rtr_socket(tr_socket);
1006
1007 cache->type = SSH;
1008 cache->tr_socket = tr_socket;
1009 cache->tr_config.ssh_config = ssh_config;
1010 cache->rtr_socket = rtr_socket;
1011 cache->preference = preference;
1012
6893064b
MR
1013 int ret = add_cache(cache);
1014 if (ret != SUCCESS) {
1015 free_cache(cache);
1016 }
1017
1018 return ret;
dabecd7c
MR
1019}
1020#endif
1021
1022static void free_cache(struct cache *cache)
1023{
1024 if (cache->type == TCP) {
996c9314
LB
1025 XFREE(MTYPE_BGP_RPKI_CACHE, cache->tr_config.tcp_config->host);
1026 XFREE(MTYPE_BGP_RPKI_CACHE, cache->tr_config.tcp_config->port);
c41a3cc5
DA
1027 XFREE(MTYPE_BGP_RPKI_CACHE,
1028 cache->tr_config.tcp_config->bindaddr);
dabecd7c
MR
1029 XFREE(MTYPE_BGP_RPKI_CACHE, cache->tr_config.tcp_config);
1030 }
1031#if defined(FOUND_SSH)
1032 else {
996c9314 1033 XFREE(MTYPE_BGP_RPKI_CACHE, cache->tr_config.ssh_config->host);
dabecd7c
MR
1034 XFREE(MTYPE_BGP_RPKI_CACHE,
1035 cache->tr_config.ssh_config->username);
1036 XFREE(MTYPE_BGP_RPKI_CACHE,
1037 cache->tr_config.ssh_config->client_privkey_path);
1038 XFREE(MTYPE_BGP_RPKI_CACHE,
1039 cache->tr_config.ssh_config->server_hostkey_path);
c41a3cc5
DA
1040 XFREE(MTYPE_BGP_RPKI_CACHE,
1041 cache->tr_config.ssh_config->bindaddr);
dabecd7c
MR
1042 XFREE(MTYPE_BGP_RPKI_CACHE, cache->tr_config.ssh_config);
1043 }
1044#endif
1045 XFREE(MTYPE_BGP_RPKI_CACHE, cache->tr_socket);
1046 XFREE(MTYPE_BGP_RPKI_CACHE, cache->rtr_socket);
1047 XFREE(MTYPE_BGP_RPKI_CACHE, cache);
1048}
1049
1050static int config_write(struct vty *vty)
1051{
1052 struct listnode *cache_node;
1053 struct cache *cache;
1054
708b8053
DS
1055 if (rpki_debug)
1056 vty_out(vty, "debug rpki\n");
1057
1058 vty_out(vty, "!\n");
1059 vty_out(vty, "rpki\n");
9a651153 1060
8f401cda
DA
1061 if (polling_period != POLLING_PERIOD_DEFAULT)
1062 vty_out(vty, " rpki polling_period %d\n", polling_period);
9a651153
DS
1063 if (retry_interval != RETRY_INTERVAL_DEFAULT)
1064 vty_out(vty, " rpki retry_interval %d\n", retry_interval);
1065 if (expire_interval != EXPIRE_INTERVAL_DEFAULT)
1066 vty_out(vty, " rpki expire_interval %d\n", expire_interval);
1067
708b8053
DS
1068 for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) {
1069 switch (cache->type) {
1070 struct tr_tcp_config *tcp_config;
acd4a9f4 1071#if defined(FOUND_SSH)
708b8053 1072 struct tr_ssh_config *ssh_config;
acd4a9f4 1073#endif
708b8053
DS
1074 case TCP:
1075 tcp_config = cache->tr_config.tcp_config;
484fc374 1076 vty_out(vty, " rpki cache %s %s ", tcp_config->host,
708b8053 1077 tcp_config->port);
484fc374
IR
1078 if (tcp_config->bindaddr)
1079 vty_out(vty, "source %s ",
1080 tcp_config->bindaddr);
708b8053 1081 break;
dabecd7c 1082#if defined(FOUND_SSH)
708b8053
DS
1083 case SSH:
1084 ssh_config = cache->tr_config.ssh_config;
484fc374
IR
1085 vty_out(vty, " rpki cache %s %u %s %s %s ",
1086 ssh_config->host, ssh_config->port,
1087 ssh_config->username,
708b8053
DS
1088 ssh_config->client_privkey_path,
1089 ssh_config->server_hostkey_path != NULL
1090 ? ssh_config->server_hostkey_path
1091 : " ");
484fc374
IR
1092 if (ssh_config->bindaddr)
1093 vty_out(vty, "source %s ",
1094 ssh_config->bindaddr);
708b8053 1095 break;
dabecd7c 1096#endif
708b8053
DS
1097 default:
1098 break;
dabecd7c 1099 }
708b8053
DS
1100
1101 vty_out(vty, "preference %hhu\n", cache->preference);
dabecd7c 1102 }
07679ad9 1103 vty_out(vty, "exit\n");
708b8053
DS
1104
1105 return 1;
dabecd7c
MR
1106}
1107
1108DEFUN_NOSH (rpki,
1109 rpki_cmd,
1110 "rpki",
1111 "Enable rpki and enter rpki configuration mode\n")
1112{
1113 vty->node = RPKI_NODE;
1114 return CMD_SUCCESS;
1115}
1116
8156765a
DA
1117DEFPY (no_rpki,
1118 no_rpki_cmd,
1119 "no rpki",
1120 NO_STR
1121 "Enable rpki and enter rpki configuration mode\n")
1122{
1123 rpki_delete_all_cache_nodes();
1124 stop();
1125 return CMD_SUCCESS;
1126}
1127
dabecd7c
MR
1128DEFUN (bgp_rpki_start,
1129 bgp_rpki_start_cmd,
1130 "rpki start",
1131 RPKI_OUTPUT_STRING
1132 "start rpki support\n")
1133{
1134 if (listcount(cache_list) == 0)
996c9314
LB
1135 vty_out(vty,
1136 "Could not start rpki because no caches are configured\n");
dabecd7c
MR
1137
1138 if (!is_running()) {
1139 if (start() == ERROR) {
1140 RPKI_DEBUG("RPKI failed to start");
1141 return CMD_WARNING;
1142 }
1143 }
1144 return CMD_SUCCESS;
1145}
1146
1147DEFUN (bgp_rpki_stop,
1148 bgp_rpki_stop_cmd,
1149 "rpki stop",
1150 RPKI_OUTPUT_STRING
1151 "start rpki support\n")
1152{
1153 if (is_running())
1154 stop();
1155
1156 return CMD_SUCCESS;
1157}
1158
1159DEFPY (rpki_polling_period,
1160 rpki_polling_period_cmd,
1161 "rpki polling_period (1-86400)$pp",
1162 RPKI_OUTPUT_STRING
1163 "Set polling period\n"
1164 "Polling period value\n")
1165{
1166 polling_period = pp;
1167 return CMD_SUCCESS;
1168}
1169
1170DEFUN (no_rpki_polling_period,
1171 no_rpki_polling_period_cmd,
e9f709e5 1172 "no rpki polling_period [(1-86400)]",
dabecd7c
MR
1173 NO_STR
1174 RPKI_OUTPUT_STRING
e9f709e5
DS
1175 "Set polling period back to default\n"
1176 "Polling period value\n")
dabecd7c
MR
1177{
1178 polling_period = POLLING_PERIOD_DEFAULT;
1179 return CMD_SUCCESS;
1180}
1181
1182DEFPY (rpki_expire_interval,
1183 rpki_expire_interval_cmd,
1184 "rpki expire_interval (600-172800)$tmp",
1185 RPKI_OUTPUT_STRING
1186 "Set expire interval\n"
1187 "Expire interval value\n")
1188{
6f577f58 1189 if ((unsigned int)tmp >= polling_period) {
dabecd7c
MR
1190 expire_interval = tmp;
1191 return CMD_SUCCESS;
1192 }
1193
1194 vty_out(vty, "%% Expiry interval must be polling period or larger\n");
1195 return CMD_WARNING_CONFIG_FAILED;
1196}
1197
1198DEFUN (no_rpki_expire_interval,
1199 no_rpki_expire_interval_cmd,
e9f709e5 1200 "no rpki expire_interval [(600-172800)]",
dabecd7c
MR
1201 NO_STR
1202 RPKI_OUTPUT_STRING
e9f709e5
DS
1203 "Set expire interval back to default\n"
1204 "Expire interval value\n")
dabecd7c
MR
1205{
1206 expire_interval = polling_period * 2;
1207 return CMD_SUCCESS;
1208}
1209
1210DEFPY (rpki_retry_interval,
1211 rpki_retry_interval_cmd,
1212 "rpki retry_interval (1-7200)$tmp",
1213 RPKI_OUTPUT_STRING
1214 "Set retry interval\n"
1215 "retry interval value\n")
1216{
1217 retry_interval = tmp;
1218 return CMD_SUCCESS;
1219}
1220
1221DEFUN (no_rpki_retry_interval,
1222 no_rpki_retry_interval_cmd,
e9f709e5 1223 "no rpki retry_interval [(1-7200)]",
dabecd7c
MR
1224 NO_STR
1225 RPKI_OUTPUT_STRING
e9f709e5
DS
1226 "Set retry interval back to default\n"
1227 "retry interval value\n")
dabecd7c
MR
1228{
1229 retry_interval = RETRY_INTERVAL_DEFAULT;
1230 return CMD_SUCCESS;
1231}
1232
7253a7bc 1233DEFPY(rpki_cache, rpki_cache_cmd,
2a5f5ec0 1234 "rpki cache <A.B.C.D|WORD> <TCPPORT|(1-65535)$sshport SSH_UNAME SSH_PRIVKEY [SERVER_PUBKEY]> [source <A.B.C.D>$bindaddr] preference (1-255)",
7253a7bc
PG
1235 RPKI_OUTPUT_STRING
1236 "Install a cache server to current group\n"
2a5f5ec0
DA
1237 "IP address of cache server\n"
1238 "Hostname of cache server\n"
7253a7bc
PG
1239 "TCP port number\n"
1240 "SSH port number\n"
1241 "SSH user name\n"
1242 "Path to own SSH private key\n"
7253a7bc 1243 "Path to Public key of cache server\n"
484fc374
IR
1244 "Configure source IP address of RPKI connection\n"
1245 "Define a Source IP Address\n"
7253a7bc
PG
1246 "Preference of the cache server\n"
1247 "Preference value\n")
dabecd7c 1248{
3f54c705 1249 int return_value;
a2ed7b2b
MR
1250 struct listnode *cache_node;
1251 struct cache *current_cache;
1252
1253 for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, current_cache)) {
1254 if (current_cache->preference == preference) {
1255 vty_out(vty,
1256 "Cache with preference %ld is already configured\n",
1257 preference);
1258 return CMD_WARNING;
1259 }
1260 }
1261
dabecd7c
MR
1262
1263 // use ssh connection
1264 if (ssh_uname) {
1265#if defined(FOUND_SSH)
2a5f5ec0
DA
1266 return_value =
1267 add_ssh_cache(cache, sshport, ssh_uname, ssh_privkey,
1268 server_pubkey, preference, bindaddr_str);
dabecd7c 1269#else
3f54c705 1270 return_value = SUCCESS;
dabecd7c 1271 vty_out(vty,
3efd0893 1272 "ssh sockets are not supported. Please recompile rtrlib and frr with ssh support. If you want to use it\n");
dabecd7c
MR
1273#endif
1274 } else { // use tcp connection
7253a7bc
PG
1275 return_value =
1276 add_tcp_cache(cache, tcpport, preference, bindaddr_str);
dabecd7c
MR
1277 }
1278
1279 if (return_value == ERROR) {
1280 vty_out(vty, "Could not create new rpki cache\n");
1281 return CMD_WARNING;
1282 }
1283
1284 return CMD_SUCCESS;
1285}
1286
1287DEFPY (no_rpki_cache,
1288 no_rpki_cache_cmd,
2a5f5ec0 1289 "no rpki cache <A.B.C.D|WORD> <TCPPORT|(1-65535)$sshport SSH_UNAME SSH_PRIVKEY [SERVER_PUBKEY]> [source <A.B.C.D>$bindaddr] preference (1-255)",
dabecd7c
MR
1290 NO_STR
1291 RPKI_OUTPUT_STRING
cc22635a 1292 "Install a cache server to current group\n"
2a5f5ec0
DA
1293 "IP address of cache server\n"
1294 "Hostname of cache server\n"
dabecd7c
MR
1295 "TCP port number\n"
1296 "SSH port number\n"
cc22635a
DA
1297 "SSH user name\n"
1298 "Path to own SSH private key\n"
cc22635a
DA
1299 "Path to Public key of cache server\n"
1300 "Configure source IP address of RPKI connection\n"
1301 "Define a Source IP Address\n"
dabecd7c
MR
1302 "Preference of the cache server\n"
1303 "Preference value\n")
1304{
1305 struct cache *cache_p = find_cache(preference);
1306
222487fe 1307 if (!cache_p) {
11423e50
DA
1308 vty_out(vty, "Could not find cache with preference %ld\n",
1309 preference);
dabecd7c
MR
1310 return CMD_WARNING;
1311 }
1312
01fcc189 1313 if (is_running() && listcount(cache_list) == 1) {
8add1719 1314 stop();
01fcc189 1315 } else if (is_running()) {
dabecd7c 1316 if (rtr_mgr_remove_group(rtr_config, preference) == RTR_ERROR) {
11423e50
DA
1317 vty_out(vty,
1318 "Could not remove cache with preference %ld\n",
1319 preference);
dabecd7c
MR
1320 return CMD_WARNING;
1321 }
1322 }
1323
1324 listnode_delete(cache_list, cache_p);
1325 free_cache(cache_p);
1326
1327 return CMD_SUCCESS;
1328}
1329
dff41cc8 1330DEFPY (show_rpki_prefix_table,
dabecd7c 1331 show_rpki_prefix_table_cmd,
dff41cc8 1332 "show rpki prefix-table [json$uj]",
dabecd7c
MR
1333 SHOW_STR
1334 RPKI_OUTPUT_STRING
dff41cc8
DA
1335 "Show validated prefixes which were received from RPKI Cache\n"
1336 JSON_STR)
dabecd7c 1337{
dff41cc8
DA
1338 struct json_object *json = NULL;
1339
1340 if (!is_synchronized()) {
1341 if (!uj)
1342 vty_out(vty, "No connection to RPKI cache server.\n");
1343 return CMD_WARNING;
1344 }
1345
1346 if (uj)
1347 json = json_object_new_object();
dabecd7c 1348
dff41cc8 1349 print_prefix_table(vty, json);
dabecd7c
MR
1350 return CMD_SUCCESS;
1351}
1352
dff41cc8
DA
1353DEFPY (show_rpki_as_number,
1354 show_rpki_as_number_cmd,
1355 "show rpki as-number (1-4294967295)$by_asn [json$uj]",
1356 SHOW_STR
1357 RPKI_OUTPUT_STRING
1358 "Lookup by ASN in prefix table\n"
1359 "AS Number\n"
1360 JSON_STR)
02334bb2 1361{
dff41cc8
DA
1362 struct json_object *json = NULL;
1363
02334bb2 1364 if (!is_synchronized()) {
dff41cc8
DA
1365 if (!uj)
1366 vty_out(vty, "No Connection to RPKI cache server.\n");
02334bb2
DA
1367 return CMD_WARNING;
1368 }
1369
dff41cc8
DA
1370 if (uj)
1371 json = json_object_new_object();
1372
1373 print_prefix_table_by_asn(vty, by_asn, json);
02334bb2
DA
1374 return CMD_SUCCESS;
1375}
1376
5d799192
MR
1377DEFPY (show_rpki_prefix,
1378 show_rpki_prefix_cmd,
dff41cc8 1379 "show rpki prefix <A.B.C.D/M|X:X::X:X/M> [(1-4294967295)$asn] [json$uj]",
5d799192
MR
1380 SHOW_STR
1381 RPKI_OUTPUT_STRING
1382 "Lookup IP prefix and optionally ASN in prefix table\n"
1383 "IPv4 prefix\n"
1384 "IPv6 prefix\n"
dff41cc8
DA
1385 "AS Number\n"
1386 JSON_STR)
5d799192 1387{
dff41cc8
DA
1388 json_object *json = NULL;
1389 json_object *json_records = NULL;
5d799192
MR
1390
1391 if (!is_synchronized()) {
dff41cc8
DA
1392 if (!uj)
1393 vty_out(vty, "No Connection to RPKI cache server.\n");
5d799192
MR
1394 return CMD_WARNING;
1395 }
1396
1397 struct lrtr_ip_addr addr;
1398 char addr_str[INET6_ADDRSTRLEN];
1399 size_t addr_len = strchr(prefix_str, '/') - prefix_str;
1400
1401 memset(addr_str, 0, sizeof(addr_str));
1402 memcpy(addr_str, prefix_str, addr_len);
1403
1404 if (lrtr_ip_str_to_addr(addr_str, &addr) != 0) {
dff41cc8
DA
1405 if (!json)
1406 vty_out(vty, "Invalid IP prefix\n");
5d799192
MR
1407 return CMD_WARNING;
1408 }
1409
1410 struct pfx_record *matches = NULL;
1411 unsigned int match_count = 0;
1412 enum pfxv_state result;
1413
1414 if (pfx_table_validate_r(rtr_config->pfx_table, &matches, &match_count,
1415 asn, &addr, prefix->prefixlen, &result)
1416 != PFX_SUCCESS) {
dff41cc8
DA
1417 if (!json)
1418 vty_out(vty, "Prefix lookup failed\n");
5d799192
MR
1419 return CMD_WARNING;
1420 }
1421
dff41cc8
DA
1422 if (uj)
1423 json = json_object_new_object();
1424
1425 if (!json) {
1426 vty_out(vty, "%-40s %s %s\n", "Prefix", "Prefix Length",
1427 "Origin-AS");
1428 } else {
1429 json_records = json_object_new_array();
1430 json_object_object_add(json, "prefixes", json_records);
1431 }
1432
5d799192
MR
1433 for (size_t i = 0; i < match_count; ++i) {
1434 const struct pfx_record *record = &matches[i];
1435
1436 if (record->max_len >= prefix->prefixlen
c17140fd
DS
1437 && ((asn != 0 && (uint32_t)asn == record->asn)
1438 || asn == 0)) {
dff41cc8 1439 print_record(&matches[i], vty, json_records);
5d799192
MR
1440 }
1441 }
1442
dff41cc8
DA
1443 if (json)
1444 vty_json(vty, json);
1445
5d799192
MR
1446 return CMD_SUCCESS;
1447}
1448
cc4d121f 1449DEFPY (show_rpki_cache_server,
dabecd7c 1450 show_rpki_cache_server_cmd,
cc4d121f 1451 "show rpki cache-server [json$uj]",
dabecd7c
MR
1452 SHOW_STR
1453 RPKI_OUTPUT_STRING
cc4d121f
DA
1454 "Show configured cache server\n"
1455 JSON_STR)
dabecd7c 1456{
cc4d121f
DA
1457 struct json_object *json = NULL;
1458 struct json_object *json_server = NULL;
1459 struct json_object *json_servers = NULL;
dabecd7c
MR
1460 struct listnode *cache_node;
1461 struct cache *cache;
1462
cc4d121f
DA
1463 if (uj) {
1464 json = json_object_new_object();
1465 json_servers = json_object_new_array();
1466 json_object_object_add(json, "servers", json_servers);
1467 }
1468
dabecd7c 1469 for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) {
745ae9c0 1470 if (cache->type == TCP) {
cc4d121f 1471 if (!json) {
77be6d6a
DA
1472 vty_out(vty,
1473 "host: %s port: %s, preference: %hhu\n",
cc4d121f 1474 cache->tr_config.tcp_config->host,
77be6d6a
DA
1475 cache->tr_config.tcp_config->port,
1476 cache->preference);
cc4d121f
DA
1477 } else {
1478 json_server = json_object_new_object();
1479 json_object_string_add(json_server, "mode",
1480 "tcp");
1481 json_object_string_add(
1482 json_server, "host",
1483 cache->tr_config.tcp_config->host);
1484 json_object_string_add(
1485 json_server, "port",
1486 cache->tr_config.tcp_config->port);
77be6d6a
DA
1487 json_object_int_add(json_server, "preference",
1488 cache->preference);
cc4d121f
DA
1489 json_object_array_add(json_servers,
1490 json_server);
1491 }
745ae9c0 1492
fef6fafa 1493#if defined(FOUND_SSH)
745ae9c0 1494 } else if (cache->type == SSH) {
cc4d121f
DA
1495 if (!json) {
1496 vty_out(vty,
77be6d6a 1497 "host: %s port: %d username: %s server_hostkey_path: %s client_privkey_path: %s, preference: %hhu\n",
cc4d121f
DA
1498 cache->tr_config.ssh_config->host,
1499 cache->tr_config.ssh_config->port,
1500 cache->tr_config.ssh_config->username,
1501 cache->tr_config.ssh_config
1502 ->server_hostkey_path,
1503 cache->tr_config.ssh_config
77be6d6a
DA
1504 ->client_privkey_path,
1505 cache->preference);
cc4d121f
DA
1506 } else {
1507 json_server = json_object_new_object();
1508 json_object_string_add(json_server, "mode",
1509 "ssh");
1510 json_object_string_add(
1511 json_server, "host",
1512 cache->tr_config.ssh_config->host);
1513 json_object_int_add(
1514 json_server, "port",
1515 cache->tr_config.ssh_config->port);
1516 json_object_string_add(
1517 json_server, "username",
1518 cache->tr_config.ssh_config->username);
1519 json_object_string_add(
1520 json_server, "serverHostkeyPath",
1521 cache->tr_config.ssh_config
1522 ->server_hostkey_path);
1523 json_object_string_add(
1524 json_server, "clientPrivkeyPath",
1525 cache->tr_config.ssh_config
1526 ->client_privkey_path);
77be6d6a
DA
1527 json_object_int_add(json_server, "preference",
1528 cache->preference);
cc4d121f
DA
1529 json_object_array_add(json_servers,
1530 json_server);
1531 }
fef6fafa 1532#endif
745ae9c0 1533 }
dabecd7c
MR
1534 }
1535
cc4d121f
DA
1536 if (json)
1537 vty_json(vty, json);
1538
dabecd7c
MR
1539 return CMD_SUCCESS;
1540}
1541
ae872c2f 1542DEFPY (show_rpki_cache_connection,
dabecd7c 1543 show_rpki_cache_connection_cmd,
ae872c2f 1544 "show rpki cache-connection [json$uj]",
dabecd7c
MR
1545 SHOW_STR
1546 RPKI_OUTPUT_STRING
ae872c2f
DA
1547 "Show to which RPKI Cache Servers we have a connection\n"
1548 JSON_STR)
dabecd7c 1549{
ae872c2f
DA
1550 struct json_object *json = NULL;
1551 struct json_object *json_conn = NULL;
1552 struct json_object *json_conns = NULL;
1553 struct listnode *cache_node;
1554 struct cache *cache;
1555 struct rtr_mgr_group *group;
1556
1557 if (uj)
1558 json = json_object_new_object();
1559
708b8053 1560 if (!is_synchronized()) {
ae872c2f
DA
1561 if (!json)
1562 vty_out(vty, "No connection to RPKI cache server.\n");
1563 else
1564 vty_json(vty, json);
708b8053
DS
1565
1566 return CMD_SUCCESS;
1567 }
1568
ae872c2f 1569 group = get_connected_group();
708b8053 1570 if (!group) {
ae872c2f
DA
1571 if (!json)
1572 vty_out(vty, "Cannot find a connected group.\n");
1573 else
1574 vty_json(vty, json);
1575
708b8053
DS
1576 return CMD_SUCCESS;
1577 }
ae872c2f
DA
1578
1579 if (!json) {
1580 vty_out(vty, "Connected to group %d\n", group->preference);
1581 } else {
1582 json_conns = json_object_new_array();
1583 json_object_int_add(json, "connectedGroup", group->preference);
1584 json_object_object_add(json, "connections", json_conns);
1585 }
1586
708b8053 1587 for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) {
c1a68b62 1588 struct tr_tcp_config *tcp_config;
acd4a9f4 1589#if defined(FOUND_SSH)
c1a68b62 1590 struct tr_ssh_config *ssh_config;
acd4a9f4 1591#endif
c1a68b62
DA
1592 switch (cache->type) {
1593 case TCP:
1594 tcp_config = cache->tr_config.tcp_config;
ae872c2f
DA
1595
1596 if (!json) {
1597 vty_out(vty,
1598 "rpki tcp cache %s %s pref %hhu%s\n",
1599 tcp_config->host, tcp_config->port,
1600 cache->preference,
1601 cache->rtr_socket->state ==
1602 RTR_ESTABLISHED
1603 ? " (connected)"
1604 : "");
1605 } else {
1606 json_conn = json_object_new_object();
1607 json_object_string_add(json_conn, "mode",
1608 "tcp");
1609 json_object_string_add(json_conn, "host",
1610 tcp_config->host);
1611 json_object_string_add(json_conn, "port",
1612 tcp_config->port);
1613 json_object_int_add(json_conn, "preference",
1614 cache->preference);
1615 json_object_string_add(
1616 json_conn, "state",
1617 cache->rtr_socket->state ==
1618 RTR_ESTABLISHED
1619 ? "connected"
1620 : "disconnected");
1621 json_object_array_add(json_conns, json_conn);
1622 }
c1a68b62 1623 break;
dabecd7c 1624#if defined(FOUND_SSH)
c1a68b62
DA
1625 case SSH:
1626 ssh_config = cache->tr_config.ssh_config;
ae872c2f
DA
1627
1628 if (!json) {
1629 vty_out(vty,
1630 "rpki ssh cache %s %u pref %hhu%s\n",
1631 ssh_config->host, ssh_config->port,
1632 cache->preference,
1633 cache->rtr_socket->state ==
1634 RTR_ESTABLISHED
1635 ? " (connected)"
1636 : "");
1637 } else {
1638 json_conn = json_object_new_object();
1639 json_object_string_add(json_conn, "mode",
1640 "ssh");
1641 json_object_string_add(json_conn, "host",
1642 ssh_config->host);
4d4b9dc1
DA
1643 json_object_int_add(json_conn, "port",
1644 ssh_config->port);
ae872c2f
DA
1645 json_object_int_add(json_conn, "preference",
1646 cache->preference);
1647 json_object_string_add(
1648 json_conn, "state",
1649 cache->rtr_socket->state ==
1650 RTR_ESTABLISHED
1651 ? "connected"
1652 : "disconnected");
1653 json_object_array_add(json_conns, json_conn);
1654 }
c1a68b62 1655 break;
dabecd7c 1656#endif
c1a68b62
DA
1657 default:
1658 break;
dabecd7c 1659 }
dabecd7c
MR
1660 }
1661
ae872c2f
DA
1662 if (json)
1663 vty_json(vty, json);
1664
dabecd7c
MR
1665 return CMD_SUCCESS;
1666}
1667
791ded4a 1668static int config_on_exit(struct vty *vty)
dabecd7c 1669{
61a484a9 1670 reset(false);
791ded4a 1671 return 1;
dabecd7c
MR
1672}
1673
1674DEFUN (rpki_reset,
1675 rpki_reset_cmd,
1676 "rpki reset",
1677 RPKI_OUTPUT_STRING
1678 "reset rpki\n")
1679{
1680 return reset(true) == SUCCESS ? CMD_SUCCESS : CMD_WARNING;
1681}
1682
1683DEFUN (debug_rpki,
1684 debug_rpki_cmd,
1685 "debug rpki",
1686 DEBUG_STR
08f9cfb8 1687 "Enable debugging for rpki\n")
dabecd7c 1688{
0e3d96bf 1689 rpki_debug = true;
dabecd7c
MR
1690 return CMD_SUCCESS;
1691}
1692
1693DEFUN (no_debug_rpki,
1694 no_debug_rpki_cmd,
1695 "no debug rpki",
1696 NO_STR
1697 DEBUG_STR
08f9cfb8 1698 "Disable debugging for rpki\n")
dabecd7c 1699{
0e3d96bf 1700 rpki_debug = false;
dabecd7c
MR
1701 return CMD_SUCCESS;
1702}
1703
48cb7ea9 1704DEFUN_YANG (match_rpki,
dabecd7c
MR
1705 match_rpki_cmd,
1706 "match rpki <valid|invalid|notfound>",
1707 MATCH_STR
1708 RPKI_OUTPUT_STRING
1709 "Valid prefix\n"
1710 "Invalid prefix\n"
1711 "Prefix not found\n")
1712{
48cb7ea9
SP
1713 const char *xpath =
1714 "./match-condition[condition='frr-bgp-route-map:rpki']";
1715 char xpath_value[XPATH_MAXLEN];
69337c34 1716
48cb7ea9
SP
1717 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
1718 snprintf(xpath_value, sizeof(xpath_value),
1719 "%s/rmap-match-condition/frr-bgp-route-map:rpki", xpath);
1720 nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[2]->arg);
1721
1722 return nb_cli_apply_changes(vty, NULL);
dabecd7c
MR
1723}
1724
48cb7ea9 1725DEFUN_YANG (no_match_rpki,
dabecd7c
MR
1726 no_match_rpki_cmd,
1727 "no match rpki <valid|invalid|notfound>",
1728 NO_STR
1729 MATCH_STR
1730 RPKI_OUTPUT_STRING
1731 "Valid prefix\n"
1732 "Invalid prefix\n"
1733 "Prefix not found\n")
1734{
48cb7ea9
SP
1735 const char *xpath =
1736 "./match-condition[condition='frr-bgp-route-map:rpki']";
dabecd7c 1737
87c3ed1b 1738 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
48cb7ea9 1739 return nb_cli_apply_changes(vty, NULL);
dabecd7c
MR
1740}
1741
dabecd7c
MR
1742static void install_cli_commands(void)
1743{
996c9314 1744 // TODO: make config write work
612c2c15 1745 install_node(&rpki_node);
dabecd7c 1746 install_default(RPKI_NODE);
dabecd7c 1747 install_element(CONFIG_NODE, &rpki_cmd);
9593708d 1748 install_element(ENABLE_NODE, &rpki_cmd);
8156765a
DA
1749 install_element(CONFIG_NODE, &no_rpki_cmd);
1750
dabecd7c
MR
1751
1752 install_element(ENABLE_NODE, &bgp_rpki_start_cmd);
1753 install_element(ENABLE_NODE, &bgp_rpki_stop_cmd);
1754
1755 /* Install rpki reset command */
8f14ae47 1756 install_element(ENABLE_NODE, &rpki_reset_cmd);
dabecd7c
MR
1757 install_element(RPKI_NODE, &rpki_reset_cmd);
1758
1759 /* Install rpki polling period commands */
1760 install_element(RPKI_NODE, &rpki_polling_period_cmd);
1761 install_element(RPKI_NODE, &no_rpki_polling_period_cmd);
1762
1763 /* Install rpki expire interval commands */
1764 install_element(RPKI_NODE, &rpki_expire_interval_cmd);
1765 install_element(RPKI_NODE, &no_rpki_expire_interval_cmd);
1766
1767 /* Install rpki retry interval commands */
1768 install_element(RPKI_NODE, &rpki_retry_interval_cmd);
1769 install_element(RPKI_NODE, &no_rpki_retry_interval_cmd);
1770
dabecd7c
MR
1771 /* Install rpki cache commands */
1772 install_element(RPKI_NODE, &rpki_cache_cmd);
1773 install_element(RPKI_NODE, &no_rpki_cache_cmd);
1774
1775 /* Install show commands */
9593708d 1776 install_element(VIEW_NODE, &show_rpki_prefix_table_cmd);
1777 install_element(VIEW_NODE, &show_rpki_cache_connection_cmd);
1778 install_element(VIEW_NODE, &show_rpki_cache_server_cmd);
1779 install_element(VIEW_NODE, &show_rpki_prefix_cmd);
02334bb2 1780 install_element(VIEW_NODE, &show_rpki_as_number_cmd);
dabecd7c
MR
1781
1782 /* Install debug commands */
1783 install_element(CONFIG_NODE, &debug_rpki_cmd);
1784 install_element(ENABLE_NODE, &debug_rpki_cmd);
1785 install_element(CONFIG_NODE, &no_debug_rpki_cmd);
1786 install_element(ENABLE_NODE, &no_debug_rpki_cmd);
1787
1788 /* Install route match */
1789 route_map_install_match(&route_match_rpki_cmd);
1790 install_element(RMAP_NODE, &match_rpki_cmd);
1791 install_element(RMAP_NODE, &no_match_rpki_cmd);
1792}
1793
1794FRR_MODULE_SETUP(.name = "bgpd_rpki", .version = "0.3.6",
1795 .description = "Enable RPKI support for FRR.",
80413c20
DL
1796 .init = bgp_rpki_module_init,
1797);