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