Development with RTRlib¶
Overview¶
The RTRlib shared library is installed to /usr/local/lib
by default,
and its headers files to /usr/local/include
, respectively.
To write an application in C/C++ using the RTRlib, include the main header file
into the code:
#include "rtrlib/rtrlib.h"
The name of the corresponding shared library is rtr. To link an application against the RTRlib, pass the following parameter to the compiler:
-lrtr
If the linker reports an error such as cannot find -lrtr
, probably the
RTRlib was not installed to a standard location.
In this case, pass its location as an absolute path to the compiler,
add parameter:
-L</path/to/librtr/>
On Linux you can alternatively try to update the linker cache instead, run:
ldconfig
# verify with
ldconfig -p | grep rtr
Step-by-Step Example¶
The RTRlib package includes two command line tools, the rtrclient
and
the rpki-rov
, see also tools.
The former connects to a single RTR cache server via TCP or SSH and prints
validated prefix origin data to STDOUT. You can use this tool to get first
experiences with the RPKI-RTR protocol. With the latter you can validate
arbitrary prefix origin AS relations against records received from a connected
RPKI cache. Both tools are located in the tools/
directory. Having a look
into the source code of these tools will help to understand and integrate the
RTRlib into applications.
Any application using the RTRlib will have to setup a RTR connection manager that handles synchronization with one (or multiple) trusted RPKI cache server(s). The following provides an overview on important code segments.
First, create a RTR transport socket, for instance using TCP as shown in Listing 1.
1 2 3 4 5 6 7 8 9 10 11 12 13 | struct tr_socket tr_tcp;
struct rtr_socket rtr_tcp;
char tcp_host[] = "rpki-validator.realmv6.org";
char tcp_port[] = "8282";
struct tr_tcp_config tcp_config = {
tcp_host, // cache server host
tcp_port, // cache server port
NULL // source address, empty
};
tr_tcp_init(&tcp_config, &tr_tcp);
rtr_tcp.tr_socket = &tr_tcp;
|
Afterwards, create a group of RTR cache servers with preference 1. In this example (see Listing 2), it includes only a single cache instance.
1 2 3 4 5 | rtr_mgr_group groups[1];
groups[0].sockets = malloc(sizeof(struct rtr_socket*));
groups[0].sockets_len = 1;
groups[0].sockets[0] = &rtr_tcp;
groups[0].preference = 1;
|
Now initialize the RTR connection manager (Listing 3) providing a pointer to a configuration object, the preconfigured group(s), number of groups, a refresh interval, an expiration interval, and retry interval, as well as distinct callback functions. In this case, a refresh interval of 30 seconds, a 600s expiration timeout, and a 600s retry interval will be defined. Afterwards, start the RTR Connection Manager.
1 2 3 4 5 | struct rtr_mgr_config *conf;
int ret = rtr_mgr_init(&conf, groups, 1, 30, 600, 600,
pfx_update_fp, spki_update_fp, status_fp, NULL);
rtr_mgr_start(conf);
|
As soon as an update has been received from the RTR-Server, the callback function will be invoked. In this example, update_cb (see Listing 4) is called which prints the prefix, its minimum, and maximum length, as well as the corresponding origin AS.
1 2 3 4 5 6 7 8 9 | static void update_cb(struct pfx_table* p, const pfx_record rec, const bool added){
char ip[INET6_ADDRSTRLEN];
if(added)
printf("+ ");
else
printf("- ");
ip_addr_to_str(&(rec.prefix), ip, sizeof(ip));
printf("%-18s %3u-%-3u %10u\n", ip, rec.min_len, rec.max_len, rec.asn);
}
|
With a running RTR connection manager, you can also execute validation queries. For instance, validate the relation of prefix 10.10.0.0/24 and its origin AS 12345 as shown in Listing 5.
1 2 3 4 5 | struct lrtr_ip_addr pref;
lrtr_ip_str_to_addr("10.10.0.0", &pref);
enum pfxv_state result;
const uint8_t mask = 24;
rtr_mgr_validate(conf, 12345, &pref, mask, &result);
|
For a clean shutdown and exit of the application, first stop the RTR Connection Manager, and secondly release any memory allocated (see Listing 6).
1 2 3 | rtr_mgr_stop(conf);
rtr_mgr_free(conf);
free(groups[0].sockets);
|
Complete RTRlib Example¶
The code in Listing 7 shows a fully functional RPKI validator using the RTRlib. It includes all parts explained in the previous section, and shows how to setup multiple RPKI cache server connections using either TCP or SSH transport sockets. For the latter, the RTRlib has to be build and installed with libssh support.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | #include <stdio.h>
#include <stdlib.h>
#include "rtrlib/rtrlib.h"
int main(){
//create a SSH transport socket
char ssh_host[] = "123.231.123.221";
char ssh_user[] = "rpki_user";
char ssh_hostkey[] = "/etc/rpki-rtr/hostkey";
char ssh_privkey[] = "/etc/rpki-rtr/client.priv";
struct tr_socket tr_ssh;
struct tr_ssh_config config = {
ssh_host, //IP
22, //Port
NULL, //Source address
ssh_user,
ssh_hostkey, //Server hostkey
ssh_privkey, //Private key
};
tr_ssh_init(&config, &tr_ssh);
//create a TCP transport socket
struct tr_socket tr_tcp;
char tcp_host[] = "rpki-validator.realmv6.org";
char tcp_port[] = "8282";
struct tr_tcp_config tcp_config = {
tcp_host, //IP
tcp_port, //Port
NULL //Source address
};
tr_tcp_init(&tcp_config, &tr_tcp);
//create 3 rtr_sockets and associate them with the transprort sockets
struct rtr_socket rtr_ssh, rtr_tcp;
rtr_ssh.tr_socket = &tr_ssh;
rtr_tcp.tr_socket = &tr_tcp;
//create a rtr_mgr_group array with 2 elements
struct rtr_mgr_group groups[2];
//The first group contains both TCP RTR sockets
groups[0].sockets = malloc(sizeof(struct rtr_socket*));
groups[0].sockets_len = 1;
groups[0].sockets[0] = &rtr_tcp;
groups[0].preference = 1; //Preference value of this group
//The seconds group contains only the SSH RTR socket
groups[1].sockets = malloc(1 * sizeof(struct rtr_socket*));
groups[1].sockets_len = 1;
groups[1].sockets[0] = &rtr_ssh;
groups[1].preference = 2;
//create a rtr_mgr_config struct that stores the group
struct rtr_mgr_config *conf;
//initialize all rtr_sockets in the server pool with the same settings
int ret = rtr_mgr_init(&conf, groups, 2, 30, 600, 600, NULL, NULL, NULL, NULL);
//start the connection manager
rtr_mgr_start(conf);
//wait till at least one rtr_mgr_group is fully synchronized with the server
while(!rtr_mgr_conf_in_sync(conf)) {
sleep(1);
}
//validate the BGP-Route 10.10.0.0/24, origin ASN: 12345
struct lrtr_ip_addr pref;
lrtr_ip_str_to_addr("10.10.0.0", &pref);
enum pfxv_state result;
const uint8_t mask = 24;
rtr_mgr_validate(conf, 12345, &pref, mask, &result);
//output the result of the prefix validation above
//to showcase the returned states.
char buffer[INET_ADDRSTRLEN];
lrtr_ip_addr_to_str(&pref, buffer, sizeof(buffer));
printf("RESULT: The prefix %s/%i ", buffer, mask);
switch(result) {
case BGP_PFXV_STATE_VALID:
printf("is valid.\n");
break;
case BGP_PFXV_STATE_INVALID:
printf("is invalid.\n");
break;
case BGP_PFXV_STATE_NOT_FOUND:
printf("was not found.\n");
break;
default:
break;
}
// cleanup before exit
rtr_mgr_stop(conf);
rtr_mgr_free(conf);
free(groups[0].sockets);
free(groups[1].sockets);
}
|