]> git.proxmox.com Git - mirror_frr.git/blame - bgpd/bgp_rpki.c
bgpd: Break up rpki prefix revalidation by bgp structure
[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);
404 node = match;
405
406 while (node) {
407 if (bgp_dest_has_bgp_path_info_data(node)) {
408 revalidate_bgp_node(node, rrp->afi, rrp->safi);
409 }
410
411 node = bgp_route_next_until(node, match);
412 }
413
414 if (match)
415 bgp_dest_unlock_node(match);
416
417 XFREE(MTYPE_BGP_RPKI_REVALIDATE, rrp);
418}
419
cc9f21da 420static void bgpd_sync_callback(struct thread *thread)
1dacdd8b
MR
421{
422 struct bgp *bgp;
423 struct listnode *node;
fc15f734 424 struct prefix prefix;
1dacdd8b
MR
425 struct pfx_record rec;
426
d2e3f8a2
DA
427 thread_add_read(bm->master, bgpd_sync_callback, NULL,
428 rpki_sync_socket_bgpd, NULL);
4ce82676
MR
429
430 if (atomic_load_explicit(&rtr_update_overflow, memory_order_seq_cst)) {
d2e3f8a2
DA
431 while (read(rpki_sync_socket_bgpd, &rec,
432 sizeof(struct pfx_record)) != -1)
4ce82676
MR
433 ;
434
435 atomic_store_explicit(&rtr_update_overflow, 0,
436 memory_order_seq_cst);
437 revalidate_all_routes();
cc9f21da 438 return;
4ce82676
MR
439 }
440
d2e3f8a2
DA
441 int retval =
442 read(rpki_sync_socket_bgpd, &rec, sizeof(struct pfx_record));
3b128228 443 if (retval != sizeof(struct pfx_record)) {
d2e3f8a2 444 RPKI_DEBUG("Could not read from rpki_sync_socket_bgpd");
cc9f21da 445 return;
38775a3c 446 }
fc15f734 447 pfx_record_to_prefix(&rec, &prefix);
1dacdd8b
MR
448
449 afi_t afi = (rec.prefix.ver == LRTR_IPV4) ? AFI_IP : AFI_IP6;
450
451 for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) {
35a1e798 452 safi_t safi;
1dacdd8b 453
35a1e798 454 for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) {
7e7f61ed 455 struct bgp_table *table = bgp->rib[afi][safi];
7f1f9314 456 struct rpki_revalidate_prefix *rrp;
7e7f61ed
DA
457
458 if (!table)
35a1e798 459 continue;
31a2af32 460
7f1f9314
DS
461 rrp = XCALLOC(MTYPE_BGP_RPKI_REVALIDATE, sizeof(*rrp));
462 rrp->bgp = bgp;
463 rrp->prefix = prefix;
464 rrp->afi = afi;
465 rrp->safi = safi;
466 thread_add_event(bm->master, rpki_revalidate_prefix,
467 rrp, 0, &bgp->t_revalidate[afi][safi]);
1dacdd8b
MR
468 }
469 }
1dacdd8b
MR
470}
471
9bcb3eef 472static void revalidate_bgp_node(struct bgp_dest *bgp_dest, afi_t afi,
1dacdd8b
MR
473 safi_t safi)
474{
475 struct bgp_adj_in *ain;
476
9bcb3eef 477 for (ain = bgp_dest->adj_in; ain; ain = ain->next) {
2b964e86 478 struct bgp_path_info *path =
9bcb3eef 479 bgp_dest_get_bgp_path_info(bgp_dest);
1dacdd8b
MR
480 mpls_label_t *label = NULL;
481 uint32_t num_labels = 0;
482
9b6d8fcf
DS
483 if (path && path->extra) {
484 label = path->extra->label;
485 num_labels = path->extra->num_labels;
1dacdd8b 486 }
73261b47 487 (void)bgp_update(ain->peer, bgp_dest_get_prefix(bgp_dest),
b54892e0
DS
488 ain->addpath_rx_id, ain->attr, afi, safi,
489 ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, label,
490 num_labels, 1, NULL);
1dacdd8b
MR
491 }
492}
493
7651f277
DS
494/*
495 * The act of a soft reconfig in revalidation is really expensive
496 * coupled with the fact that the download of a full rpki state
497 * from a rpki server can be expensive, let's break up the revalidation
498 * to a point in time in the future to allow other bgp events
499 * to take place too.
500 */
501struct rpki_revalidate_peer {
502 afi_t afi;
503 safi_t safi;
504 struct peer *peer;
505};
506
507static void bgp_rpki_revalidate_peer(struct thread *thread)
508{
509 struct rpki_revalidate_peer *rvp = THREAD_ARG(thread);
510
511 /*
512 * Here's the expensive bit of gnomish deviousness
513 */
514 bgp_soft_reconfig_in(rvp->peer, rvp->afi, rvp->safi);
515
516 XFREE(MTYPE_BGP_RPKI_REVALIDATE, rvp);
517}
518
1dacdd8b
MR
519static void revalidate_all_routes(void)
520{
521 struct bgp *bgp;
522 struct listnode *node;
1dacdd8b
MR
523
524 for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) {
31a2af32
MR
525 struct peer *peer;
526 struct listnode *peer_listnode;
1dacdd8b 527
31a2af32 528 for (ALL_LIST_ELEMENTS_RO(bgp->peer, peer_listnode, peer)) {
8fb15d02
DS
529 afi_t afi;
530 safi_t safi;
31a2af32 531
8fb15d02 532 FOREACH_AFI_SAFI (afi, safi) {
7651f277
DS
533 struct rpki_revalidate_peer *rvp;
534
802ca11f 535 if (!bgp->rib[afi][safi])
8fb15d02 536 continue;
d2e3f8a2 537
89c73443
DS
538 if (!peer_established(peer))
539 continue;
7651f277
DS
540
541 rvp = XCALLOC(MTYPE_BGP_RPKI_REVALIDATE,
542 sizeof(*rvp));
543 rvp->peer = peer;
544 rvp->afi = afi;
545 rvp->safi = safi;
546
547 thread_add_event(
548 bm->master, bgp_rpki_revalidate_peer,
549 rvp, 0,
550 &peer->t_revalidate_all[afi][safi]);
1dacdd8b
MR
551 }
552 }
553 }
554}
555
556static void rpki_update_cb_sync_rtr(struct pfx_table *p __attribute__((unused)),
557 const struct pfx_record rec,
558 const bool added __attribute__((unused)))
559{
9ca44fc8
DA
560 if (is_stopping() ||
561 atomic_load_explicit(&rtr_update_overflow, memory_order_seq_cst))
1dacdd8b
MR
562 return;
563
564 int retval =
565 write(rpki_sync_socket_rtr, &rec, sizeof(struct pfx_record));
4ce82676
MR
566 if (retval == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
567 atomic_store_explicit(&rtr_update_overflow, 1,
568 memory_order_seq_cst);
569
570 else if (retval != sizeof(struct pfx_record))
1dacdd8b
MR
571 RPKI_DEBUG("Could not write to rpki_sync_socket_rtr");
572}
573
574static void rpki_init_sync_socket(void)
575{
576 int fds[2];
4ce82676 577 const char *msg;
1dacdd8b
MR
578
579 RPKI_DEBUG("initializing sync socket");
580 if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, fds) != 0) {
4ce82676
MR
581 msg = "could not open rpki sync socketpair";
582 goto err;
1dacdd8b
MR
583 }
584 rpki_sync_socket_rtr = fds[0];
585 rpki_sync_socket_bgpd = fds[1];
4ce82676
MR
586
587 if (set_nonblocking(rpki_sync_socket_rtr) != 0) {
588 msg = "could not set rpki_sync_socket_rtr to non blocking";
589 goto err;
590 }
591
592 if (set_nonblocking(rpki_sync_socket_bgpd) != 0) {
593 msg = "could not set rpki_sync_socket_bgpd to non blocking";
594 goto err;
595 }
596
d2e3f8a2 597
1dacdd8b 598 thread_add_read(bm->master, bgpd_sync_callback, NULL,
d2e3f8a2 599 rpki_sync_socket_bgpd, NULL);
4ce82676
MR
600
601 return;
602
603err:
604 zlog_err("RPKI: %s", msg);
605 abort();
606
1dacdd8b
MR
607}
608
dabecd7c
MR
609static int bgp_rpki_init(struct thread_master *master)
610{
0e3d96bf
DA
611 rpki_debug = false;
612 rtr_is_running = false;
613 rtr_is_stopping = false;
d67485c6 614 rtr_is_synced = false;
dabecd7c
MR
615
616 cache_list = list_new();
996c9314 617 cache_list->del = (void (*)(void *)) & free_cache;
dabecd7c
MR
618
619 polling_period = POLLING_PERIOD_DEFAULT;
620 expire_interval = EXPIRE_INTERVAL_DEFAULT;
621 retry_interval = RETRY_INTERVAL_DEFAULT;
dabecd7c 622 install_cli_commands();
1dacdd8b 623 rpki_init_sync_socket();
dabecd7c
MR
624 return 0;
625}
626
627static int bgp_rpki_fini(void)
628{
629 stop();
6a154c88 630 list_delete(&cache_list);
dabecd7c 631
1dacdd8b
MR
632 close(rpki_sync_socket_rtr);
633 close(rpki_sync_socket_bgpd);
634
dabecd7c
MR
635 return 0;
636}
637
638static int bgp_rpki_module_init(void)
639{
996c9314 640 lrtr_set_alloc_functions(malloc_wrapper, realloc_wrapper, free_wrapper);
dabecd7c 641
b5b99af8 642 hook_register(bgp_rpki_prefix_status, rpki_validate_prefix);
dabecd7c 643 hook_register(frr_late_init, bgp_rpki_init);
f714e57a 644 hook_register(frr_early_fini, bgp_rpki_fini);
dabecd7c
MR
645
646 return 0;
647}
648
d67485c6 649static void sync_expired(struct thread *thread)
1b2095d1
DA
650{
651 if (!rtr_mgr_conf_in_sync(rtr_config)) {
d67485c6
DA
652 RPKI_DEBUG("rtr_mgr is not synced, retrying.");
653 thread_add_timer(bm->master, sync_expired, NULL,
1b2095d1 654 BGP_RPKI_CACHE_SERVER_SYNC_RETRY_TIMEOUT,
d67485c6 655 &t_rpki_sync);
1b2095d1
DA
656 return;
657 }
658
d67485c6
DA
659 RPKI_DEBUG("rtr_mgr sync is done.");
660
661 rtr_is_synced = true;
1b2095d1
DA
662}
663
dabecd7c
MR
664static int start(void)
665{
dabecd7c
MR
666 int ret;
667
0e3d96bf 668 rtr_is_stopping = false;
d67485c6 669 rtr_is_synced = false;
4ce82676 670 rtr_update_overflow = 0;
1dacdd8b 671
dabecd7c
MR
672 if (list_isempty(cache_list)) {
673 RPKI_DEBUG(
674 "No caches were found in config. Prefix validation is off.");
675 return ERROR;
676 }
677 RPKI_DEBUG("Init rtr_mgr.");
678 int groups_len = listcount(cache_list);
679 struct rtr_mgr_group *groups = get_groups();
680
1dacdd8b 681 RPKI_DEBUG("Polling period: %d", polling_period);
dabecd7c 682 ret = rtr_mgr_init(&rtr_config, groups, groups_len, polling_period,
1dacdd8b 683 expire_interval, retry_interval,
d2e3f8a2 684 rpki_update_cb_sync_rtr, NULL, NULL, NULL);
dabecd7c
MR
685 if (ret == RTR_ERROR) {
686 RPKI_DEBUG("Init rtr_mgr failed.");
687 return ERROR;
688 }
689
690 RPKI_DEBUG("Starting rtr_mgr.");
691 ret = rtr_mgr_start(rtr_config);
692 if (ret == RTR_ERROR) {
693 RPKI_DEBUG("Starting rtr_mgr failed.");
694 rtr_mgr_free(rtr_config);
695 return ERROR;
696 }
1b2095d1 697
d67485c6 698 thread_add_timer(bm->master, sync_expired, NULL, 0, &t_rpki_sync);
dabecd7c
MR
699
700 XFREE(MTYPE_BGP_RPKI_CACHE_GROUP, groups);
701
0e3d96bf 702 rtr_is_running = true;
d67485c6 703
dabecd7c
MR
704 return SUCCESS;
705}
706
707static void stop(void)
708{
0e3d96bf 709 rtr_is_stopping = true;
01fcc189 710 if (is_running()) {
d67485c6 711 THREAD_OFF(t_rpki_sync);
dabecd7c
MR
712 rtr_mgr_stop(rtr_config);
713 rtr_mgr_free(rtr_config);
0e3d96bf 714 rtr_is_running = false;
dabecd7c
MR
715 }
716}
717
718static int reset(bool force)
719{
01fcc189 720 if (is_running() && !force)
dabecd7c
MR
721 return SUCCESS;
722
723 RPKI_DEBUG("Resetting RPKI Session");
724 stop();
725 return start();
726}
727
728static struct rtr_mgr_group *get_connected_group(void)
729{
18b91526 730 if (!cache_list || list_isempty(cache_list))
dabecd7c
MR
731 return NULL;
732
733 return rtr_mgr_get_first_group(rtr_config);
734}
735
dff41cc8
DA
736static void print_prefix_table_by_asn(struct vty *vty, as_t as,
737 json_object *json)
02334bb2
DA
738{
739 unsigned int number_of_ipv4_prefixes = 0;
740 unsigned int number_of_ipv6_prefixes = 0;
741 struct rtr_mgr_group *group = get_connected_group();
742 struct rpki_for_each_record_arg arg;
dff41cc8 743 json_object *json_records = NULL;
02334bb2
DA
744
745 arg.vty = vty;
746 arg.as = as;
dff41cc8 747 arg.json = NULL;
02334bb2
DA
748
749 if (!group) {
dff41cc8
DA
750 if (!json)
751 vty_out(vty, "Cannot find a connected group.\n");
02334bb2
DA
752 return;
753 }
754
755 struct pfx_table *pfx_table = group->sockets[0]->pfx_table;
756
dff41cc8
DA
757 if (!json) {
758 vty_out(vty, "RPKI/RTR prefix table\n");
759 vty_out(vty, "%-40s %s %s\n", "Prefix", "Prefix Length",
760 "Origin-AS");
761 } else {
762 json_records = json_object_new_array();
763 json_object_object_add(json, "prefixes", json_records);
764 arg.json = json_records;
765 }
02334bb2
DA
766
767 arg.prefix_amount = &number_of_ipv4_prefixes;
768 pfx_table_for_each_ipv4_record(pfx_table, print_record_by_asn, &arg);
769
770 arg.prefix_amount = &number_of_ipv6_prefixes;
771 pfx_table_for_each_ipv6_record(pfx_table, print_record_by_asn, &arg);
772
dff41cc8
DA
773 if (!json) {
774 vty_out(vty, "Number of IPv4 Prefixes: %u\n",
775 number_of_ipv4_prefixes);
776 vty_out(vty, "Number of IPv6 Prefixes: %u\n",
777 number_of_ipv6_prefixes);
778 } else {
779 json_object_int_add(json, "ipv4PrefixCount",
780 number_of_ipv4_prefixes);
781 json_object_int_add(json, "ipv6PrefixCount",
782 number_of_ipv6_prefixes);
783 }
784
785 if (json)
786 vty_json(vty, json);
02334bb2
DA
787}
788
dff41cc8 789static void print_prefix_table(struct vty *vty, json_object *json)
dabecd7c
MR
790{
791 struct rpki_for_each_record_arg arg;
792
793 unsigned int number_of_ipv4_prefixes = 0;
794 unsigned int number_of_ipv6_prefixes = 0;
795 struct rtr_mgr_group *group = get_connected_group();
dff41cc8 796 json_object *json_records = NULL;
dabecd7c
MR
797
798 arg.vty = vty;
dff41cc8 799 arg.json = NULL;
dabecd7c 800
dff41cc8
DA
801 if (!group) {
802 if (!json)
803 vty_out(vty, "Cannot find a connected group.\n");
dabecd7c 804 return;
dff41cc8 805 }
dabecd7c
MR
806
807 struct pfx_table *pfx_table = group->sockets[0]->pfx_table;
808
dff41cc8
DA
809 if (!json) {
810 vty_out(vty, "RPKI/RTR prefix table\n");
811 vty_out(vty, "%-40s %s %s\n", "Prefix", "Prefix Length",
812 "Origin-AS");
813 } else {
814 json_records = json_object_new_array();
815 json_object_object_add(json, "prefixes", json_records);
816 arg.json = json_records;
817 }
dabecd7c
MR
818
819 arg.prefix_amount = &number_of_ipv4_prefixes;
5d799192 820 pfx_table_for_each_ipv4_record(pfx_table, print_record_cb, &arg);
dabecd7c
MR
821
822 arg.prefix_amount = &number_of_ipv6_prefixes;
5d799192 823 pfx_table_for_each_ipv6_record(pfx_table, print_record_cb, &arg);
dabecd7c 824
dff41cc8
DA
825 if (!json) {
826 vty_out(vty, "Number of IPv4 Prefixes: %u\n",
827 number_of_ipv4_prefixes);
828 vty_out(vty, "Number of IPv6 Prefixes: %u\n",
829 number_of_ipv6_prefixes);
830 } else {
831 json_object_int_add(json, "ipv4PrefixCount",
832 number_of_ipv4_prefixes);
833 json_object_int_add(json, "ipv6PrefixCount",
834 number_of_ipv6_prefixes);
835 }
836
837 if (json)
838 vty_json(vty, json);
dabecd7c
MR
839}
840
841static int rpki_validate_prefix(struct peer *peer, struct attr *attr,
898c4e66 842 const struct prefix *prefix)
dabecd7c
MR
843{
844 struct assegment *as_segment;
845 as_t as_number = 0;
846 struct lrtr_ip_addr ip_addr_prefix;
847 enum pfxv_state result;
dabecd7c
MR
848
849 if (!is_synchronized())
e1a05dd4 850 return RPKI_NOT_BEING_USED;
dabecd7c
MR
851
852 // No aspath means route comes from iBGP
853 if (!attr->aspath || !attr->aspath->segments) {
854 // Set own as number
855 as_number = peer->bgp->as;
856 } else {
857 as_segment = attr->aspath->segments;
858 // Find last AsSegment
859 while (as_segment->next)
860 as_segment = as_segment->next;
861
862 if (as_segment->type == AS_SEQUENCE) {
863 // Get rightmost asn
864 as_number = as_segment->as[as_segment->length - 1];
996c9314
LB
865 } else if (as_segment->type == AS_CONFED_SEQUENCE
866 || as_segment->type == AS_CONFED_SET) {
dabecd7c
MR
867 // Set own as number
868 as_number = peer->bgp->as;
869 } else {
870 // RFC says: "Take distinguished value NONE as asn"
871 // which means state is unknown
872 return RPKI_NOTFOUND;
873 }
874 }
875
876 // Get the prefix in requested format
877 switch (prefix->family) {
878 case AF_INET:
879 ip_addr_prefix.ver = LRTR_IPV4;
880 ip_addr_prefix.u.addr4.addr = ntohl(prefix->u.prefix4.s_addr);
881 break;
882
dabecd7c
MR
883 case AF_INET6:
884 ip_addr_prefix.ver = LRTR_IPV6;
885 ipv6_addr_to_host_byte_order(prefix->u.prefix6.s6_addr32,
886 ip_addr_prefix.u.addr6.addr);
887 break;
dabecd7c
MR
888
889 default:
e1a05dd4 890 return RPKI_NOT_BEING_USED;
dabecd7c
MR
891 }
892
893 // Do the actual validation
894 rtr_mgr_validate(rtr_config, as_number, &ip_addr_prefix,
895 prefix->prefixlen, &result);
896
897 // Print Debug output
dabecd7c
MR
898 switch (result) {
899 case BGP_PFXV_STATE_VALID:
900 RPKI_DEBUG(
2dbe669b
DA
901 "Validating Prefix %pFX from asn %u Result: VALID",
902 prefix, as_number);
dabecd7c
MR
903 return RPKI_VALID;
904 case BGP_PFXV_STATE_NOT_FOUND:
905 RPKI_DEBUG(
2dbe669b
DA
906 "Validating Prefix %pFX from asn %u Result: NOT FOUND",
907 prefix, as_number);
dabecd7c
MR
908 return RPKI_NOTFOUND;
909 case BGP_PFXV_STATE_INVALID:
910 RPKI_DEBUG(
2dbe669b
DA
911 "Validating Prefix %pFX from asn %u Result: INVALID",
912 prefix, as_number);
dabecd7c
MR
913 return RPKI_INVALID;
914 default:
915 RPKI_DEBUG(
2dbe669b
DA
916 "Validating Prefix %pFX from asn %u Result: CANNOT VALIDATE",
917 prefix, as_number);
dabecd7c
MR
918 break;
919 }
e1a05dd4 920 return RPKI_NOT_BEING_USED;
dabecd7c
MR
921}
922
923static int add_cache(struct cache *cache)
924{
925 uint8_t preference = cache->preference;
926 struct rtr_mgr_group group;
927
928 group.preference = preference;
929 group.sockets_len = 1;
930 group.sockets = &cache->rtr_socket;
931
01fcc189 932 if (is_running()) {
92110aab
MR
933 init_tr_socket(cache);
934
935 if (rtr_mgr_add_group(rtr_config, &group) != RTR_SUCCESS) {
936 free_tr_socket(cache);
937 return ERROR;
938 }
dabecd7c
MR
939 }
940
6893064b
MR
941 listnode_add(cache_list, cache);
942
dabecd7c
MR
943 return SUCCESS;
944}
945
996c9314 946static int add_tcp_cache(const char *host, const char *port,
7253a7bc 947 const uint8_t preference, const char *bindaddr)
dabecd7c
MR
948{
949 struct rtr_socket *rtr_socket;
950 struct tr_tcp_config *tcp_config =
a633498e 951 XCALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct tr_tcp_config));
dabecd7c
MR
952 struct tr_socket *tr_socket =
953 XMALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct tr_socket));
954 struct cache *cache =
955 XMALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct cache));
956
957 tcp_config->host = XSTRDUP(MTYPE_BGP_RPKI_CACHE, host);
958 tcp_config->port = XSTRDUP(MTYPE_BGP_RPKI_CACHE, port);
7253a7bc
PG
959 if (bindaddr)
960 tcp_config->bindaddr = XSTRDUP(MTYPE_BGP_RPKI_CACHE, bindaddr);
961 else
962 tcp_config->bindaddr = NULL;
dabecd7c
MR
963
964 rtr_socket = create_rtr_socket(tr_socket);
965
966 cache->type = TCP;
967 cache->tr_socket = tr_socket;
968 cache->tr_config.tcp_config = tcp_config;
969 cache->rtr_socket = rtr_socket;
970 cache->preference = preference;
971
6893064b
MR
972 int ret = add_cache(cache);
973 if (ret != SUCCESS) {
974 free_cache(cache);
975 }
976
977 return ret;
dabecd7c
MR
978}
979
980#if defined(FOUND_SSH)
996c9314
LB
981static int add_ssh_cache(const char *host, const unsigned int port,
982 const char *username, const char *client_privkey_path,
dabecd7c 983 const char *server_pubkey_path,
7253a7bc 984 const uint8_t preference, const char *bindaddr)
dabecd7c
MR
985{
986 struct tr_ssh_config *ssh_config =
a633498e 987 XCALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct tr_ssh_config));
dabecd7c
MR
988 struct cache *cache =
989 XMALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct cache));
990 struct tr_socket *tr_socket =
991 XMALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct tr_socket));
992 struct rtr_socket *rtr_socket;
993
994 ssh_config->port = port;
995 ssh_config->host = XSTRDUP(MTYPE_BGP_RPKI_CACHE, host);
7253a7bc
PG
996 if (bindaddr)
997 ssh_config->bindaddr = XSTRDUP(MTYPE_BGP_RPKI_CACHE, bindaddr);
998 else
999 ssh_config->bindaddr = NULL;
dabecd7c
MR
1000
1001 ssh_config->username = XSTRDUP(MTYPE_BGP_RPKI_CACHE, username);
996c9314
LB
1002 ssh_config->client_privkey_path =
1003 XSTRDUP(MTYPE_BGP_RPKI_CACHE, client_privkey_path);
dabecd7c
MR
1004 ssh_config->server_hostkey_path =
1005 XSTRDUP(MTYPE_BGP_RPKI_CACHE, server_pubkey_path);
1006
1007 rtr_socket = create_rtr_socket(tr_socket);
1008
1009 cache->type = SSH;
1010 cache->tr_socket = tr_socket;
1011 cache->tr_config.ssh_config = ssh_config;
1012 cache->rtr_socket = rtr_socket;
1013 cache->preference = preference;
1014
6893064b
MR
1015 int ret = add_cache(cache);
1016 if (ret != SUCCESS) {
1017 free_cache(cache);
1018 }
1019
1020 return ret;
dabecd7c
MR
1021}
1022#endif
1023
1024static void free_cache(struct cache *cache)
1025{
1026 if (cache->type == TCP) {
996c9314
LB
1027 XFREE(MTYPE_BGP_RPKI_CACHE, cache->tr_config.tcp_config->host);
1028 XFREE(MTYPE_BGP_RPKI_CACHE, cache->tr_config.tcp_config->port);
c41a3cc5
DA
1029 XFREE(MTYPE_BGP_RPKI_CACHE,
1030 cache->tr_config.tcp_config->bindaddr);
dabecd7c
MR
1031 XFREE(MTYPE_BGP_RPKI_CACHE, cache->tr_config.tcp_config);
1032 }
1033#if defined(FOUND_SSH)
1034 else {
996c9314 1035 XFREE(MTYPE_BGP_RPKI_CACHE, cache->tr_config.ssh_config->host);
dabecd7c
MR
1036 XFREE(MTYPE_BGP_RPKI_CACHE,
1037 cache->tr_config.ssh_config->username);
1038 XFREE(MTYPE_BGP_RPKI_CACHE,
1039 cache->tr_config.ssh_config->client_privkey_path);
1040 XFREE(MTYPE_BGP_RPKI_CACHE,
1041 cache->tr_config.ssh_config->server_hostkey_path);
c41a3cc5
DA
1042 XFREE(MTYPE_BGP_RPKI_CACHE,
1043 cache->tr_config.ssh_config->bindaddr);
dabecd7c
MR
1044 XFREE(MTYPE_BGP_RPKI_CACHE, cache->tr_config.ssh_config);
1045 }
1046#endif
1047 XFREE(MTYPE_BGP_RPKI_CACHE, cache->tr_socket);
1048 XFREE(MTYPE_BGP_RPKI_CACHE, cache->rtr_socket);
1049 XFREE(MTYPE_BGP_RPKI_CACHE, cache);
1050}
1051
1052static int config_write(struct vty *vty)
1053{
1054 struct listnode *cache_node;
1055 struct cache *cache;
1056
708b8053
DS
1057 if (rpki_debug)
1058 vty_out(vty, "debug rpki\n");
1059
1060 vty_out(vty, "!\n");
1061 vty_out(vty, "rpki\n");
9a651153 1062
8f401cda
DA
1063 if (polling_period != POLLING_PERIOD_DEFAULT)
1064 vty_out(vty, " rpki polling_period %d\n", polling_period);
9a651153
DS
1065 if (retry_interval != RETRY_INTERVAL_DEFAULT)
1066 vty_out(vty, " rpki retry_interval %d\n", retry_interval);
1067 if (expire_interval != EXPIRE_INTERVAL_DEFAULT)
1068 vty_out(vty, " rpki expire_interval %d\n", expire_interval);
1069
708b8053
DS
1070 for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) {
1071 switch (cache->type) {
1072 struct tr_tcp_config *tcp_config;
acd4a9f4 1073#if defined(FOUND_SSH)
708b8053 1074 struct tr_ssh_config *ssh_config;
acd4a9f4 1075#endif
708b8053
DS
1076 case TCP:
1077 tcp_config = cache->tr_config.tcp_config;
484fc374 1078 vty_out(vty, " rpki cache %s %s ", tcp_config->host,
708b8053 1079 tcp_config->port);
484fc374
IR
1080 if (tcp_config->bindaddr)
1081 vty_out(vty, "source %s ",
1082 tcp_config->bindaddr);
708b8053 1083 break;
dabecd7c 1084#if defined(FOUND_SSH)
708b8053
DS
1085 case SSH:
1086 ssh_config = cache->tr_config.ssh_config;
484fc374
IR
1087 vty_out(vty, " rpki cache %s %u %s %s %s ",
1088 ssh_config->host, ssh_config->port,
1089 ssh_config->username,
708b8053
DS
1090 ssh_config->client_privkey_path,
1091 ssh_config->server_hostkey_path != NULL
1092 ? ssh_config->server_hostkey_path
1093 : " ");
484fc374
IR
1094 if (ssh_config->bindaddr)
1095 vty_out(vty, "source %s ",
1096 ssh_config->bindaddr);
708b8053 1097 break;
dabecd7c 1098#endif
708b8053
DS
1099 default:
1100 break;
dabecd7c 1101 }
708b8053
DS
1102
1103 vty_out(vty, "preference %hhu\n", cache->preference);
dabecd7c 1104 }
07679ad9 1105 vty_out(vty, "exit\n");
708b8053
DS
1106
1107 return 1;
dabecd7c
MR
1108}
1109
1110DEFUN_NOSH (rpki,
1111 rpki_cmd,
1112 "rpki",
1113 "Enable rpki and enter rpki configuration mode\n")
1114{
1115 vty->node = RPKI_NODE;
1116 return CMD_SUCCESS;
1117}
1118
8156765a
DA
1119DEFPY (no_rpki,
1120 no_rpki_cmd,
1121 "no rpki",
1122 NO_STR
1123 "Enable rpki and enter rpki configuration mode\n")
1124{
1125 rpki_delete_all_cache_nodes();
1126 stop();
1127 return CMD_SUCCESS;
1128}
1129
dabecd7c
MR
1130DEFUN (bgp_rpki_start,
1131 bgp_rpki_start_cmd,
1132 "rpki start",
1133 RPKI_OUTPUT_STRING
1134 "start rpki support\n")
1135{
1136 if (listcount(cache_list) == 0)
996c9314
LB
1137 vty_out(vty,
1138 "Could not start rpki because no caches are configured\n");
dabecd7c
MR
1139
1140 if (!is_running()) {
1141 if (start() == ERROR) {
1142 RPKI_DEBUG("RPKI failed to start");
1143 return CMD_WARNING;
1144 }
1145 }
1146 return CMD_SUCCESS;
1147}
1148
1149DEFUN (bgp_rpki_stop,
1150 bgp_rpki_stop_cmd,
1151 "rpki stop",
1152 RPKI_OUTPUT_STRING
1153 "start rpki support\n")
1154{
1155 if (is_running())
1156 stop();
1157
1158 return CMD_SUCCESS;
1159}
1160
1161DEFPY (rpki_polling_period,
1162 rpki_polling_period_cmd,
1163 "rpki polling_period (1-86400)$pp",
1164 RPKI_OUTPUT_STRING
1165 "Set polling period\n"
1166 "Polling period value\n")
1167{
1168 polling_period = pp;
1169 return CMD_SUCCESS;
1170}
1171
1172DEFUN (no_rpki_polling_period,
1173 no_rpki_polling_period_cmd,
e9f709e5 1174 "no rpki polling_period [(1-86400)]",
dabecd7c
MR
1175 NO_STR
1176 RPKI_OUTPUT_STRING
e9f709e5
DS
1177 "Set polling period back to default\n"
1178 "Polling period value\n")
dabecd7c
MR
1179{
1180 polling_period = POLLING_PERIOD_DEFAULT;
1181 return CMD_SUCCESS;
1182}
1183
1184DEFPY (rpki_expire_interval,
1185 rpki_expire_interval_cmd,
1186 "rpki expire_interval (600-172800)$tmp",
1187 RPKI_OUTPUT_STRING
1188 "Set expire interval\n"
1189 "Expire interval value\n")
1190{
6f577f58 1191 if ((unsigned int)tmp >= polling_period) {
dabecd7c
MR
1192 expire_interval = tmp;
1193 return CMD_SUCCESS;
1194 }
1195
1196 vty_out(vty, "%% Expiry interval must be polling period or larger\n");
1197 return CMD_WARNING_CONFIG_FAILED;
1198}
1199
1200DEFUN (no_rpki_expire_interval,
1201 no_rpki_expire_interval_cmd,
e9f709e5 1202 "no rpki expire_interval [(600-172800)]",
dabecd7c
MR
1203 NO_STR
1204 RPKI_OUTPUT_STRING
e9f709e5
DS
1205 "Set expire interval back to default\n"
1206 "Expire interval value\n")
dabecd7c
MR
1207{
1208 expire_interval = polling_period * 2;
1209 return CMD_SUCCESS;
1210}
1211
1212DEFPY (rpki_retry_interval,
1213 rpki_retry_interval_cmd,
1214 "rpki retry_interval (1-7200)$tmp",
1215 RPKI_OUTPUT_STRING
1216 "Set retry interval\n"
1217 "retry interval value\n")
1218{
1219 retry_interval = tmp;
1220 return CMD_SUCCESS;
1221}
1222
1223DEFUN (no_rpki_retry_interval,
1224 no_rpki_retry_interval_cmd,
e9f709e5 1225 "no rpki retry_interval [(1-7200)]",
dabecd7c
MR
1226 NO_STR
1227 RPKI_OUTPUT_STRING
e9f709e5
DS
1228 "Set retry interval back to default\n"
1229 "retry interval value\n")
dabecd7c
MR
1230{
1231 retry_interval = RETRY_INTERVAL_DEFAULT;
1232 return CMD_SUCCESS;
1233}
1234
7253a7bc 1235DEFPY(rpki_cache, rpki_cache_cmd,
2a5f5ec0 1236 "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
1237 RPKI_OUTPUT_STRING
1238 "Install a cache server to current group\n"
2a5f5ec0
DA
1239 "IP address of cache server\n"
1240 "Hostname of cache server\n"
7253a7bc
PG
1241 "TCP port number\n"
1242 "SSH port number\n"
1243 "SSH user name\n"
1244 "Path to own SSH private key\n"
7253a7bc 1245 "Path to Public key of cache server\n"
484fc374
IR
1246 "Configure source IP address of RPKI connection\n"
1247 "Define a Source IP Address\n"
7253a7bc
PG
1248 "Preference of the cache server\n"
1249 "Preference value\n")
dabecd7c 1250{
3f54c705 1251 int return_value;
a2ed7b2b
MR
1252 struct listnode *cache_node;
1253 struct cache *current_cache;
1254
1255 for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, current_cache)) {
1256 if (current_cache->preference == preference) {
1257 vty_out(vty,
1258 "Cache with preference %ld is already configured\n",
1259 preference);
1260 return CMD_WARNING;
1261 }
1262 }
1263
dabecd7c
MR
1264
1265 // use ssh connection
1266 if (ssh_uname) {
1267#if defined(FOUND_SSH)
2a5f5ec0
DA
1268 return_value =
1269 add_ssh_cache(cache, sshport, ssh_uname, ssh_privkey,
1270 server_pubkey, preference, bindaddr_str);
dabecd7c 1271#else
3f54c705 1272 return_value = SUCCESS;
dabecd7c 1273 vty_out(vty,
3efd0893 1274 "ssh sockets are not supported. Please recompile rtrlib and frr with ssh support. If you want to use it\n");
dabecd7c
MR
1275#endif
1276 } else { // use tcp connection
7253a7bc
PG
1277 return_value =
1278 add_tcp_cache(cache, tcpport, preference, bindaddr_str);
dabecd7c
MR
1279 }
1280
1281 if (return_value == ERROR) {
1282 vty_out(vty, "Could not create new rpki cache\n");
1283 return CMD_WARNING;
1284 }
1285
1286 return CMD_SUCCESS;
1287}
1288
1289DEFPY (no_rpki_cache,
1290 no_rpki_cache_cmd,
2a5f5ec0 1291 "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
1292 NO_STR
1293 RPKI_OUTPUT_STRING
cc22635a 1294 "Install a cache server to current group\n"
2a5f5ec0
DA
1295 "IP address of cache server\n"
1296 "Hostname of cache server\n"
dabecd7c
MR
1297 "TCP port number\n"
1298 "SSH port number\n"
cc22635a
DA
1299 "SSH user name\n"
1300 "Path to own SSH private key\n"
cc22635a
DA
1301 "Path to Public key of cache server\n"
1302 "Configure source IP address of RPKI connection\n"
1303 "Define a Source IP Address\n"
dabecd7c
MR
1304 "Preference of the cache server\n"
1305 "Preference value\n")
1306{
1307 struct cache *cache_p = find_cache(preference);
1308
222487fe 1309 if (!cache_p) {
11423e50
DA
1310 vty_out(vty, "Could not find cache with preference %ld\n",
1311 preference);
dabecd7c
MR
1312 return CMD_WARNING;
1313 }
1314
01fcc189 1315 if (is_running() && listcount(cache_list) == 1) {
8add1719 1316 stop();
01fcc189 1317 } else if (is_running()) {
dabecd7c 1318 if (rtr_mgr_remove_group(rtr_config, preference) == RTR_ERROR) {
11423e50
DA
1319 vty_out(vty,
1320 "Could not remove cache with preference %ld\n",
1321 preference);
dabecd7c
MR
1322 return CMD_WARNING;
1323 }
1324 }
1325
1326 listnode_delete(cache_list, cache_p);
1327 free_cache(cache_p);
1328
1329 return CMD_SUCCESS;
1330}
1331
dff41cc8 1332DEFPY (show_rpki_prefix_table,
dabecd7c 1333 show_rpki_prefix_table_cmd,
dff41cc8 1334 "show rpki prefix-table [json$uj]",
dabecd7c
MR
1335 SHOW_STR
1336 RPKI_OUTPUT_STRING
dff41cc8
DA
1337 "Show validated prefixes which were received from RPKI Cache\n"
1338 JSON_STR)
dabecd7c 1339{
dff41cc8
DA
1340 struct json_object *json = NULL;
1341
1342 if (!is_synchronized()) {
1343 if (!uj)
1344 vty_out(vty, "No connection to RPKI cache server.\n");
1345 return CMD_WARNING;
1346 }
1347
1348 if (uj)
1349 json = json_object_new_object();
dabecd7c 1350
dff41cc8 1351 print_prefix_table(vty, json);
dabecd7c
MR
1352 return CMD_SUCCESS;
1353}
1354
dff41cc8
DA
1355DEFPY (show_rpki_as_number,
1356 show_rpki_as_number_cmd,
1357 "show rpki as-number (1-4294967295)$by_asn [json$uj]",
1358 SHOW_STR
1359 RPKI_OUTPUT_STRING
1360 "Lookup by ASN in prefix table\n"
1361 "AS Number\n"
1362 JSON_STR)
02334bb2 1363{
dff41cc8
DA
1364 struct json_object *json = NULL;
1365
02334bb2 1366 if (!is_synchronized()) {
dff41cc8
DA
1367 if (!uj)
1368 vty_out(vty, "No Connection to RPKI cache server.\n");
02334bb2
DA
1369 return CMD_WARNING;
1370 }
1371
dff41cc8
DA
1372 if (uj)
1373 json = json_object_new_object();
1374
1375 print_prefix_table_by_asn(vty, by_asn, json);
02334bb2
DA
1376 return CMD_SUCCESS;
1377}
1378
5d799192
MR
1379DEFPY (show_rpki_prefix,
1380 show_rpki_prefix_cmd,
dff41cc8 1381 "show rpki prefix <A.B.C.D/M|X:X::X:X/M> [(1-4294967295)$asn] [json$uj]",
5d799192
MR
1382 SHOW_STR
1383 RPKI_OUTPUT_STRING
1384 "Lookup IP prefix and optionally ASN in prefix table\n"
1385 "IPv4 prefix\n"
1386 "IPv6 prefix\n"
dff41cc8
DA
1387 "AS Number\n"
1388 JSON_STR)
5d799192 1389{
dff41cc8
DA
1390 json_object *json = NULL;
1391 json_object *json_records = NULL;
5d799192
MR
1392
1393 if (!is_synchronized()) {
dff41cc8
DA
1394 if (!uj)
1395 vty_out(vty, "No Connection to RPKI cache server.\n");
5d799192
MR
1396 return CMD_WARNING;
1397 }
1398
1399 struct lrtr_ip_addr addr;
1400 char addr_str[INET6_ADDRSTRLEN];
1401 size_t addr_len = strchr(prefix_str, '/') - prefix_str;
1402
1403 memset(addr_str, 0, sizeof(addr_str));
1404 memcpy(addr_str, prefix_str, addr_len);
1405
1406 if (lrtr_ip_str_to_addr(addr_str, &addr) != 0) {
dff41cc8
DA
1407 if (!json)
1408 vty_out(vty, "Invalid IP prefix\n");
5d799192
MR
1409 return CMD_WARNING;
1410 }
1411
1412 struct pfx_record *matches = NULL;
1413 unsigned int match_count = 0;
1414 enum pfxv_state result;
1415
1416 if (pfx_table_validate_r(rtr_config->pfx_table, &matches, &match_count,
1417 asn, &addr, prefix->prefixlen, &result)
1418 != PFX_SUCCESS) {
dff41cc8
DA
1419 if (!json)
1420 vty_out(vty, "Prefix lookup failed\n");
5d799192
MR
1421 return CMD_WARNING;
1422 }
1423
dff41cc8
DA
1424 if (uj)
1425 json = json_object_new_object();
1426
1427 if (!json) {
1428 vty_out(vty, "%-40s %s %s\n", "Prefix", "Prefix Length",
1429 "Origin-AS");
1430 } else {
1431 json_records = json_object_new_array();
1432 json_object_object_add(json, "prefixes", json_records);
1433 }
1434
5d799192
MR
1435 for (size_t i = 0; i < match_count; ++i) {
1436 const struct pfx_record *record = &matches[i];
1437
1438 if (record->max_len >= prefix->prefixlen
c17140fd
DS
1439 && ((asn != 0 && (uint32_t)asn == record->asn)
1440 || asn == 0)) {
dff41cc8 1441 print_record(&matches[i], vty, json_records);
5d799192
MR
1442 }
1443 }
1444
dff41cc8
DA
1445 if (json)
1446 vty_json(vty, json);
1447
5d799192
MR
1448 return CMD_SUCCESS;
1449}
1450
cc4d121f 1451DEFPY (show_rpki_cache_server,
dabecd7c 1452 show_rpki_cache_server_cmd,
cc4d121f 1453 "show rpki cache-server [json$uj]",
dabecd7c
MR
1454 SHOW_STR
1455 RPKI_OUTPUT_STRING
cc4d121f
DA
1456 "Show configured cache server\n"
1457 JSON_STR)
dabecd7c 1458{
cc4d121f
DA
1459 struct json_object *json = NULL;
1460 struct json_object *json_server = NULL;
1461 struct json_object *json_servers = NULL;
dabecd7c
MR
1462 struct listnode *cache_node;
1463 struct cache *cache;
1464
cc4d121f
DA
1465 if (uj) {
1466 json = json_object_new_object();
1467 json_servers = json_object_new_array();
1468 json_object_object_add(json, "servers", json_servers);
1469 }
1470
dabecd7c 1471 for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) {
745ae9c0 1472 if (cache->type == TCP) {
cc4d121f 1473 if (!json) {
77be6d6a
DA
1474 vty_out(vty,
1475 "host: %s port: %s, preference: %hhu\n",
cc4d121f 1476 cache->tr_config.tcp_config->host,
77be6d6a
DA
1477 cache->tr_config.tcp_config->port,
1478 cache->preference);
cc4d121f
DA
1479 } else {
1480 json_server = json_object_new_object();
1481 json_object_string_add(json_server, "mode",
1482 "tcp");
1483 json_object_string_add(
1484 json_server, "host",
1485 cache->tr_config.tcp_config->host);
1486 json_object_string_add(
1487 json_server, "port",
1488 cache->tr_config.tcp_config->port);
77be6d6a
DA
1489 json_object_int_add(json_server, "preference",
1490 cache->preference);
cc4d121f
DA
1491 json_object_array_add(json_servers,
1492 json_server);
1493 }
745ae9c0 1494
fef6fafa 1495#if defined(FOUND_SSH)
745ae9c0 1496 } else if (cache->type == SSH) {
cc4d121f
DA
1497 if (!json) {
1498 vty_out(vty,
77be6d6a 1499 "host: %s port: %d username: %s server_hostkey_path: %s client_privkey_path: %s, preference: %hhu\n",
cc4d121f
DA
1500 cache->tr_config.ssh_config->host,
1501 cache->tr_config.ssh_config->port,
1502 cache->tr_config.ssh_config->username,
1503 cache->tr_config.ssh_config
1504 ->server_hostkey_path,
1505 cache->tr_config.ssh_config
77be6d6a
DA
1506 ->client_privkey_path,
1507 cache->preference);
cc4d121f
DA
1508 } else {
1509 json_server = json_object_new_object();
1510 json_object_string_add(json_server, "mode",
1511 "ssh");
1512 json_object_string_add(
1513 json_server, "host",
1514 cache->tr_config.ssh_config->host);
1515 json_object_int_add(
1516 json_server, "port",
1517 cache->tr_config.ssh_config->port);
1518 json_object_string_add(
1519 json_server, "username",
1520 cache->tr_config.ssh_config->username);
1521 json_object_string_add(
1522 json_server, "serverHostkeyPath",
1523 cache->tr_config.ssh_config
1524 ->server_hostkey_path);
1525 json_object_string_add(
1526 json_server, "clientPrivkeyPath",
1527 cache->tr_config.ssh_config
1528 ->client_privkey_path);
77be6d6a
DA
1529 json_object_int_add(json_server, "preference",
1530 cache->preference);
cc4d121f
DA
1531 json_object_array_add(json_servers,
1532 json_server);
1533 }
fef6fafa 1534#endif
745ae9c0 1535 }
dabecd7c
MR
1536 }
1537
cc4d121f
DA
1538 if (json)
1539 vty_json(vty, json);
1540
dabecd7c
MR
1541 return CMD_SUCCESS;
1542}
1543
ae872c2f 1544DEFPY (show_rpki_cache_connection,
dabecd7c 1545 show_rpki_cache_connection_cmd,
ae872c2f 1546 "show rpki cache-connection [json$uj]",
dabecd7c
MR
1547 SHOW_STR
1548 RPKI_OUTPUT_STRING
ae872c2f
DA
1549 "Show to which RPKI Cache Servers we have a connection\n"
1550 JSON_STR)
dabecd7c 1551{
ae872c2f
DA
1552 struct json_object *json = NULL;
1553 struct json_object *json_conn = NULL;
1554 struct json_object *json_conns = NULL;
1555 struct listnode *cache_node;
1556 struct cache *cache;
1557 struct rtr_mgr_group *group;
1558
1559 if (uj)
1560 json = json_object_new_object();
1561
708b8053 1562 if (!is_synchronized()) {
ae872c2f
DA
1563 if (!json)
1564 vty_out(vty, "No connection to RPKI cache server.\n");
1565 else
1566 vty_json(vty, json);
708b8053
DS
1567
1568 return CMD_SUCCESS;
1569 }
1570
ae872c2f 1571 group = get_connected_group();
708b8053 1572 if (!group) {
ae872c2f
DA
1573 if (!json)
1574 vty_out(vty, "Cannot find a connected group.\n");
1575 else
1576 vty_json(vty, json);
1577
708b8053
DS
1578 return CMD_SUCCESS;
1579 }
ae872c2f
DA
1580
1581 if (!json) {
1582 vty_out(vty, "Connected to group %d\n", group->preference);
1583 } else {
1584 json_conns = json_object_new_array();
1585 json_object_int_add(json, "connectedGroup", group->preference);
1586 json_object_object_add(json, "connections", json_conns);
1587 }
1588
708b8053 1589 for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) {
c1a68b62 1590 struct tr_tcp_config *tcp_config;
acd4a9f4 1591#if defined(FOUND_SSH)
c1a68b62 1592 struct tr_ssh_config *ssh_config;
acd4a9f4 1593#endif
c1a68b62
DA
1594 switch (cache->type) {
1595 case TCP:
1596 tcp_config = cache->tr_config.tcp_config;
ae872c2f
DA
1597
1598 if (!json) {
1599 vty_out(vty,
1600 "rpki tcp cache %s %s pref %hhu%s\n",
1601 tcp_config->host, tcp_config->port,
1602 cache->preference,
1603 cache->rtr_socket->state ==
1604 RTR_ESTABLISHED
1605 ? " (connected)"
1606 : "");
1607 } else {
1608 json_conn = json_object_new_object();
1609 json_object_string_add(json_conn, "mode",
1610 "tcp");
1611 json_object_string_add(json_conn, "host",
1612 tcp_config->host);
1613 json_object_string_add(json_conn, "port",
1614 tcp_config->port);
1615 json_object_int_add(json_conn, "preference",
1616 cache->preference);
1617 json_object_string_add(
1618 json_conn, "state",
1619 cache->rtr_socket->state ==
1620 RTR_ESTABLISHED
1621 ? "connected"
1622 : "disconnected");
1623 json_object_array_add(json_conns, json_conn);
1624 }
c1a68b62 1625 break;
dabecd7c 1626#if defined(FOUND_SSH)
c1a68b62
DA
1627 case SSH:
1628 ssh_config = cache->tr_config.ssh_config;
ae872c2f
DA
1629
1630 if (!json) {
1631 vty_out(vty,
1632 "rpki ssh cache %s %u pref %hhu%s\n",
1633 ssh_config->host, ssh_config->port,
1634 cache->preference,
1635 cache->rtr_socket->state ==
1636 RTR_ESTABLISHED
1637 ? " (connected)"
1638 : "");
1639 } else {
1640 json_conn = json_object_new_object();
1641 json_object_string_add(json_conn, "mode",
1642 "ssh");
1643 json_object_string_add(json_conn, "host",
1644 ssh_config->host);
4d4b9dc1
DA
1645 json_object_int_add(json_conn, "port",
1646 ssh_config->port);
ae872c2f
DA
1647 json_object_int_add(json_conn, "preference",
1648 cache->preference);
1649 json_object_string_add(
1650 json_conn, "state",
1651 cache->rtr_socket->state ==
1652 RTR_ESTABLISHED
1653 ? "connected"
1654 : "disconnected");
1655 json_object_array_add(json_conns, json_conn);
1656 }
c1a68b62 1657 break;
dabecd7c 1658#endif
c1a68b62
DA
1659 default:
1660 break;
dabecd7c 1661 }
dabecd7c
MR
1662 }
1663
ae872c2f
DA
1664 if (json)
1665 vty_json(vty, json);
1666
dabecd7c
MR
1667 return CMD_SUCCESS;
1668}
1669
791ded4a 1670static int config_on_exit(struct vty *vty)
dabecd7c 1671{
61a484a9 1672 reset(false);
791ded4a 1673 return 1;
dabecd7c
MR
1674}
1675
1676DEFUN (rpki_reset,
1677 rpki_reset_cmd,
1678 "rpki reset",
1679 RPKI_OUTPUT_STRING
1680 "reset rpki\n")
1681{
1682 return reset(true) == SUCCESS ? CMD_SUCCESS : CMD_WARNING;
1683}
1684
1685DEFUN (debug_rpki,
1686 debug_rpki_cmd,
1687 "debug rpki",
1688 DEBUG_STR
08f9cfb8 1689 "Enable debugging for rpki\n")
dabecd7c 1690{
0e3d96bf 1691 rpki_debug = true;
dabecd7c
MR
1692 return CMD_SUCCESS;
1693}
1694
1695DEFUN (no_debug_rpki,
1696 no_debug_rpki_cmd,
1697 "no debug rpki",
1698 NO_STR
1699 DEBUG_STR
08f9cfb8 1700 "Disable debugging for rpki\n")
dabecd7c 1701{
0e3d96bf 1702 rpki_debug = false;
dabecd7c
MR
1703 return CMD_SUCCESS;
1704}
1705
48cb7ea9 1706DEFUN_YANG (match_rpki,
dabecd7c
MR
1707 match_rpki_cmd,
1708 "match rpki <valid|invalid|notfound>",
1709 MATCH_STR
1710 RPKI_OUTPUT_STRING
1711 "Valid prefix\n"
1712 "Invalid prefix\n"
1713 "Prefix not found\n")
1714{
48cb7ea9
SP
1715 const char *xpath =
1716 "./match-condition[condition='frr-bgp-route-map:rpki']";
1717 char xpath_value[XPATH_MAXLEN];
69337c34 1718
48cb7ea9
SP
1719 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
1720 snprintf(xpath_value, sizeof(xpath_value),
1721 "%s/rmap-match-condition/frr-bgp-route-map:rpki", xpath);
1722 nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[2]->arg);
1723
1724 return nb_cli_apply_changes(vty, NULL);
dabecd7c
MR
1725}
1726
48cb7ea9 1727DEFUN_YANG (no_match_rpki,
dabecd7c
MR
1728 no_match_rpki_cmd,
1729 "no match rpki <valid|invalid|notfound>",
1730 NO_STR
1731 MATCH_STR
1732 RPKI_OUTPUT_STRING
1733 "Valid prefix\n"
1734 "Invalid prefix\n"
1735 "Prefix not found\n")
1736{
48cb7ea9
SP
1737 const char *xpath =
1738 "./match-condition[condition='frr-bgp-route-map:rpki']";
dabecd7c 1739
87c3ed1b 1740 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
48cb7ea9 1741 return nb_cli_apply_changes(vty, NULL);
dabecd7c
MR
1742}
1743
dabecd7c
MR
1744static void install_cli_commands(void)
1745{
996c9314 1746 // TODO: make config write work
612c2c15 1747 install_node(&rpki_node);
dabecd7c 1748 install_default(RPKI_NODE);
dabecd7c 1749 install_element(CONFIG_NODE, &rpki_cmd);
9593708d 1750 install_element(ENABLE_NODE, &rpki_cmd);
8156765a
DA
1751 install_element(CONFIG_NODE, &no_rpki_cmd);
1752
dabecd7c
MR
1753
1754 install_element(ENABLE_NODE, &bgp_rpki_start_cmd);
1755 install_element(ENABLE_NODE, &bgp_rpki_stop_cmd);
1756
1757 /* Install rpki reset command */
8f14ae47 1758 install_element(ENABLE_NODE, &rpki_reset_cmd);
dabecd7c
MR
1759 install_element(RPKI_NODE, &rpki_reset_cmd);
1760
1761 /* Install rpki polling period commands */
1762 install_element(RPKI_NODE, &rpki_polling_period_cmd);
1763 install_element(RPKI_NODE, &no_rpki_polling_period_cmd);
1764
1765 /* Install rpki expire interval commands */
1766 install_element(RPKI_NODE, &rpki_expire_interval_cmd);
1767 install_element(RPKI_NODE, &no_rpki_expire_interval_cmd);
1768
1769 /* Install rpki retry interval commands */
1770 install_element(RPKI_NODE, &rpki_retry_interval_cmd);
1771 install_element(RPKI_NODE, &no_rpki_retry_interval_cmd);
1772
dabecd7c
MR
1773 /* Install rpki cache commands */
1774 install_element(RPKI_NODE, &rpki_cache_cmd);
1775 install_element(RPKI_NODE, &no_rpki_cache_cmd);
1776
1777 /* Install show commands */
9593708d 1778 install_element(VIEW_NODE, &show_rpki_prefix_table_cmd);
1779 install_element(VIEW_NODE, &show_rpki_cache_connection_cmd);
1780 install_element(VIEW_NODE, &show_rpki_cache_server_cmd);
1781 install_element(VIEW_NODE, &show_rpki_prefix_cmd);
02334bb2 1782 install_element(VIEW_NODE, &show_rpki_as_number_cmd);
dabecd7c
MR
1783
1784 /* Install debug commands */
1785 install_element(CONFIG_NODE, &debug_rpki_cmd);
1786 install_element(ENABLE_NODE, &debug_rpki_cmd);
1787 install_element(CONFIG_NODE, &no_debug_rpki_cmd);
1788 install_element(ENABLE_NODE, &no_debug_rpki_cmd);
1789
1790 /* Install route match */
1791 route_map_install_match(&route_match_rpki_cmd);
1792 install_element(RMAP_NODE, &match_rpki_cmd);
1793 install_element(RMAP_NODE, &no_match_rpki_cmd);
1794}
1795
1796FRR_MODULE_SETUP(.name = "bgpd_rpki", .version = "0.3.6",
1797 .description = "Enable RPKI support for FRR.",
80413c20
DL
1798 .init = bgp_rpki_module_init,
1799);