mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2025-01-03 07:31:54 +00:00
RPKI protocol with one cache server per protocol
The RPKI protocol (RFC 6810) using the RTRLib (http://rpki.realmv6.org/) that is integrated inside the BIRD's code. Implemeted transports are: - unprotected transport over TCP - secure transport over SSHv2 Example configuration of bird.conf: ... roa4 table r4; roa6 table r6; protocol rpki { debug all; # Import both IPv4 and IPv6 ROAs roa4 { table r4; }; roa6 { table r6; }; # Set cache server (validator) address, # overwrite default port 323 remote "rpki-validator.realmv6.org" port 8282; # Overwrite default time intervals retry 10; # Default 600 seconds refresh 60; # Default 3600 seconds expire 600; # Default 7200 seconds } protocol rpki { debug all; # Import only IPv4 routes roa4 { table r4; }; # Set cache server address to localhost, # use default ports tcp => 323 or ssh => 22 remote 127.0.0.1; # Use SSH transport instead of unprotected transport over TCP ssh encryption { bird private key "/home/birdgeek/.ssh/id_rsa"; remote public key "/home/birdgeek/.ssh/known_hosts"; user "birdgeek"; }; } ...
This commit is contained in:
parent
dc9f0826d6
commit
4661035431
@ -10,6 +10,7 @@ CPPFLAGS=-I$(objdir) -I$(srcdir) @CPPFLAGS@
|
|||||||
CFLAGS=$(CPPFLAGS) @CFLAGS@
|
CFLAGS=$(CPPFLAGS) @CFLAGS@
|
||||||
LDFLAGS=@LDFLAGS@
|
LDFLAGS=@LDFLAGS@
|
||||||
LIBS=@LIBS@
|
LIBS=@LIBS@
|
||||||
|
DAEMON_LIBS=@DAEMON_LIBS@
|
||||||
CLIENT_LIBS=@CLIENT_LIBS@
|
CLIENT_LIBS=@CLIENT_LIBS@
|
||||||
CC=@CC@
|
CC=@CC@
|
||||||
M4=@M4@
|
M4=@M4@
|
||||||
@ -55,6 +56,8 @@ all: daemon cli
|
|||||||
daemon: $(daemon)
|
daemon: $(daemon)
|
||||||
cli: $(client)
|
cli: $(client)
|
||||||
|
|
||||||
|
$(daemon): LIBS += $(DAEMON_LIBS)
|
||||||
|
|
||||||
# Include directories
|
# Include directories
|
||||||
dirs := client conf doc filter lib nest $(addprefix proto/,$(protocols)) @sysdep_dirs@
|
dirs := client conf doc filter lib nest $(addprefix proto/,$(protocols)) @sysdep_dirs@
|
||||||
|
|
||||||
|
@ -168,7 +168,7 @@ fi
|
|||||||
AC_SUBST(iproutedir)
|
AC_SUBST(iproutedir)
|
||||||
|
|
||||||
# all_protocols="$proto_bfd bgp ospf pipe radv rip static"
|
# all_protocols="$proto_bfd bgp ospf pipe radv rip static"
|
||||||
all_protocols="$proto_bfd ospf pipe radv rip static"
|
all_protocols="$proto_bfd ospf pipe radv rip rpki static"
|
||||||
|
|
||||||
all_protocols=`echo $all_protocols | sed 's/ /,/g'`
|
all_protocols=`echo $all_protocols | sed 's/ /,/g'`
|
||||||
|
|
||||||
@ -226,6 +226,10 @@ if test "$enable_debug" = yes ; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
DAEMON_LIBS=
|
||||||
|
AC_CHECK_LIB(dl, dlopen, DAEMON_LIBS="-ldl")
|
||||||
|
AC_SUBST(DAEMON_LIBS)
|
||||||
|
|
||||||
CLIENT=birdcl
|
CLIENT=birdcl
|
||||||
CLIENT_LIBS=
|
CLIENT_LIBS=
|
||||||
if test "$enable_client" = yes ; then
|
if test "$enable_client" = yes ; then
|
||||||
|
132
doc/bird.sgml
132
doc/bird.sgml
@ -464,7 +464,7 @@ protocol rip {
|
|||||||
Create a new ROA (Route Origin Authorization) table. ROA tables can be
|
Create a new ROA (Route Origin Authorization) table. ROA tables can be
|
||||||
used to validate route origination of BGP routes. A ROA table contains
|
used to validate route origination of BGP routes. A ROA table contains
|
||||||
ROA entries, each consist of a network prefix, a max prefix length and
|
ROA entries, each consist of a network prefix, a max prefix length and
|
||||||
an AS number. A ROA entry specifies prefixes which could be originated
|
an AS (Autonomous System) number. A ROA entry specifies prefixes which could be originated
|
||||||
by that AS number. ROA tables could be filled with data from RPKI (RFC
|
by that AS number. ROA tables could be filled with data from RPKI (RFC
|
||||||
6480) or from public databases like Whois. ROA tables are examined by
|
6480) or from public databases like Whois. ROA tables are examined by
|
||||||
<cf/roa_check()/ operator in filters.
|
<cf/roa_check()/ operator in filters.
|
||||||
@ -3494,6 +3494,136 @@ protocol rip {
|
|||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
<sect>RPKI
|
||||||
|
|
||||||
|
<p>The Resource Public Key Infrastructure (RPKI) to Router Protocol (RFC 6810)
|
||||||
|
is a protocol for receiving Route Origin Authorizations (ROAs) from trusted
|
||||||
|
caches (RPKI validators). ROAs are documents used to link a set of prefixes
|
||||||
|
with an origin ASN.
|
||||||
|
The ROAs could be
|
||||||
|
In BIRD, it is implemented only receiving prefix origin data from a trusted cache.
|
||||||
|
RPKI-based origin validation uses some of the RPKI data to allow a router
|
||||||
|
to version ... FIXME! srify that the autonomous system announcing an IP address prefix is in fact
|
||||||
|
authorized to do so. This is not crypto checked so can be violated. But it
|
||||||
|
should prevent the vast majority of accidental 'hijackings' on the internet
|
||||||
|
|
||||||
|
It is possible to configure only one remote cache server per protocol yet.
|
||||||
|
|
||||||
|
<code>
|
||||||
|
protocol rpki [<name>] {
|
||||||
|
remote <ip> | "<domain>" [port <num>];
|
||||||
|
port <num>;
|
||||||
|
retry <num>;
|
||||||
|
refresh <num>;
|
||||||
|
expire <num>;
|
||||||
|
ssh encryption {
|
||||||
|
bird private key "</path/to/id_rsa>";
|
||||||
|
remote public key "</path/to/known_host>";
|
||||||
|
user "<name>";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
|
||||||
|
<sect1>RPKI protocol options
|
||||||
|
<descrip>
|
||||||
|
<tag>roa table <m/name/</tag>
|
||||||
|
Specifies the roa table into which will import the routes from cache.
|
||||||
|
This option is required.
|
||||||
|
|
||||||
|
<tag>remote <m/ip/ | "<m/hostname/" [port <num>]</tag>
|
||||||
|
Specifies a destination address of the cache server.
|
||||||
|
Can be specified by an IP address or by full domain name.
|
||||||
|
Only one cache can be specified per protocol.
|
||||||
|
|
||||||
|
<tag>port <m/num/</tag>
|
||||||
|
Specifies the port number.
|
||||||
|
The default port number is 323 for transport without any encryption
|
||||||
|
and 22 for transport with SSH encryption.
|
||||||
|
|
||||||
|
<tag>retry <m/num/</tag>
|
||||||
|
Time period in seconds between a failed query and the next attempt.
|
||||||
|
Default: 600 seconds
|
||||||
|
|
||||||
|
<tag>refresh <m/num/</tag>
|
||||||
|
Time period in seconds.
|
||||||
|
Tells how long to wait before next attempting to poll the cache, using
|
||||||
|
a Serial Query or Reset Query PDU. Must be lower than 1 hour.
|
||||||
|
Default: 3600 seconds
|
||||||
|
|
||||||
|
<tag>expire <m/num/</tag>
|
||||||
|
Time period in seconds.
|
||||||
|
Received records are deleted if the client was unable to refresh data
|
||||||
|
for this time period.
|
||||||
|
Default: 7200 seconds
|
||||||
|
|
||||||
|
<tag>ssh encryption { <m/ssh encryption options.../ }</tag>
|
||||||
|
This enables a SSH encryption.
|
||||||
|
Default: off
|
||||||
|
</descrip>
|
||||||
|
|
||||||
|
<sect1>SSH encryption options in RPKI protocol
|
||||||
|
<descrip>
|
||||||
|
<tag>bird private key "<m///path/to/id_rsa"</tag>
|
||||||
|
A path to the BIRD's private SSH key for authentication.
|
||||||
|
It can be a <cf/id_rsa/ file.
|
||||||
|
|
||||||
|
<tag>remote public key "<m///path/to/known_host"</tag>
|
||||||
|
A path to the cache's public SSH key for verification identity
|
||||||
|
of the cache server. It could be a <cf/known_host/ file.
|
||||||
|
|
||||||
|
<tag>user "<m/name/"</tag>
|
||||||
|
A SSH user name for authentication. This option is a required.
|
||||||
|
</descrip>
|
||||||
|
|
||||||
|
<sect1>Examples:
|
||||||
|
<p>Typical RPKI configuration with BGP origin validation
|
||||||
|
<code>
|
||||||
|
roa4 table r4;
|
||||||
|
roa6 table r6;
|
||||||
|
|
||||||
|
protocol rpki {
|
||||||
|
debug all;
|
||||||
|
roa4 { table r4; };
|
||||||
|
roa6 { table r6; };
|
||||||
|
remote "rpki-validator.realmv6.org";
|
||||||
|
}
|
||||||
|
|
||||||
|
filter peer_in {
|
||||||
|
if (roa_check(r4, net, bgp_path.last) = ROA_INVALID ||
|
||||||
|
roa_check(r6, net, bgp_path.last) = ROA_INVALID) then
|
||||||
|
{
|
||||||
|
print "ROA check failed for ", net, " ASN ", bgp_path.last;
|
||||||
|
reject;
|
||||||
|
}
|
||||||
|
accept;
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol bgp my_peer {
|
||||||
|
local as 65000;
|
||||||
|
neighbor 192.0.2.1 as 65001;
|
||||||
|
import filter peer_in;
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
|
||||||
|
<p>A configuration using SSHv2 transport encryption:
|
||||||
|
<code>
|
||||||
|
roa4 table r4;
|
||||||
|
roa6 table r6;
|
||||||
|
|
||||||
|
protocol rpki {
|
||||||
|
debug all;
|
||||||
|
roa4 { table r4; };
|
||||||
|
roa6 { table r6; };
|
||||||
|
remote 127.0.0.1 port 2345;
|
||||||
|
ssh encryption {
|
||||||
|
bird private key "/home/birdgeek/.ssh/id_rsa";
|
||||||
|
remote public key "/home/birdgeek/.ssh/known_hosts";
|
||||||
|
user "birdgeek";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<sect>Static
|
<sect>Static
|
||||||
|
|
||||||
|
@ -1277,11 +1277,15 @@ interpret(struct f_inst *what)
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct rtable *table = ((struct f_inst_roa_check *) what)->rtc->table;
|
struct rtable *table = ((struct f_inst_roa_check *) what)->rtc->table;
|
||||||
if (!table || table->addr_type != (v1.val.net->type == NET_IP4 ? NET_ROA4 : NET_ROA6))
|
if (!table || (table->addr_type != NET_ROA4 && table->addr_type != NET_ROA6))
|
||||||
runtime("Missing ROA table");
|
runtime("Missing ROA table");
|
||||||
|
|
||||||
res.type = T_ENUM_ROA;
|
res.type = T_ENUM_ROA;
|
||||||
res.val.i = net_roa_check(table, v1.val.net, as);
|
|
||||||
|
if (table->addr_type != (v1.val.net->type == NET_IP4 ? NET_ROA4 : NET_ROA6))
|
||||||
|
res.val.i = ROA_UNKNOWN; /* Prefix and table type mismatch */
|
||||||
|
else
|
||||||
|
res.val.i = net_roa_check(table, v1.val.net, as);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -2,6 +2,6 @@ src := bitops.c checksum.c ip.c lists.c md5.c net.c patmatch.c printf.c sha1.c s
|
|||||||
obj := $(src-o-files)
|
obj := $(src-o-files)
|
||||||
$(all-client)
|
$(all-client)
|
||||||
|
|
||||||
src := bitops.c checksum.c event.c idm.c ip.c lists.c md5.c mempool.c net.c patmatch.c printf.c resource.c sha1.c sha256.c sha512.c slab.c slists.c tbf.c xmalloc.c
|
src := bitops.c checksum.c event.c idm.c ip.c libssh.c lists.c md5.c mempool.c net.c patmatch.c printf.c resource.c sha1.c sha256.c sha512.c slab.c slists.c tbf.c xmalloc.c
|
||||||
obj := $(src-o-files)
|
obj := $(src-o-files)
|
||||||
$(all-daemon)
|
$(all-daemon)
|
||||||
|
150
lib/libssh.c
Normal file
150
lib/libssh.c
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
* BIRD -- Mockup of SSH Library for loading LibSSH using dlopen
|
||||||
|
*
|
||||||
|
* (c) 2015 CZ.NIC
|
||||||
|
*
|
||||||
|
* This file was part of SSH Library: http://www.libssh.org/
|
||||||
|
* (c) 2003-2009 by Aris Adamantiadis (SSH Library)
|
||||||
|
*
|
||||||
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include "nest/bird.h"
|
||||||
|
#include "lib/libssh.h"
|
||||||
|
|
||||||
|
#define FILENAME_OF_SHARED_OBJECT_LIBSSH "libssh.so"
|
||||||
|
|
||||||
|
ssh_session (*ssh_new)(void);
|
||||||
|
void (*ssh_set_blocking)(ssh_session session, int blocking);
|
||||||
|
int (*ssh_options_set)(ssh_session session, enum ssh_options_e type, const void *value);
|
||||||
|
int (*ssh_connect)(ssh_session session);
|
||||||
|
socket_t (*ssh_get_fd)(ssh_session session);
|
||||||
|
int (*ssh_is_server_known)(ssh_session session);
|
||||||
|
int (*ssh_userauth_publickey_auto)(ssh_session session, const char *username, const char *passphrase);
|
||||||
|
const char * (*ssh_get_error)(void *error);
|
||||||
|
int (*ssh_get_error_code)(void *error);
|
||||||
|
void (*ssh_disconnect)(ssh_session session);
|
||||||
|
void (*ssh_free)(ssh_session session);
|
||||||
|
|
||||||
|
ssh_channel (*ssh_channel_new)(ssh_session session);
|
||||||
|
int (*ssh_channel_is_open)(ssh_channel channel);
|
||||||
|
int (*ssh_channel_close)(ssh_channel channel);
|
||||||
|
void (*ssh_channel_free)(ssh_channel channel);
|
||||||
|
int (*ssh_channel_open_session)(ssh_channel channel);
|
||||||
|
int (*ssh_channel_request_subsystem)(ssh_channel channel, const char *subsystem);
|
||||||
|
int (*ssh_channel_read_nonblocking)(ssh_channel channel, void *dest, uint32_t count, int is_stderr);
|
||||||
|
int (*ssh_channel_is_eof)(ssh_channel channel);
|
||||||
|
int (*ssh_channel_select)(ssh_channel *readchans, ssh_channel *writechans, ssh_channel *exceptchans, struct timeval * timeout);
|
||||||
|
int (*ssh_channel_write)(ssh_channel channel, const void *data, uint32_t len);
|
||||||
|
|
||||||
|
|
||||||
|
static void *libssh;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @return NULL if success
|
||||||
|
* @return string with error if failed
|
||||||
|
*/
|
||||||
|
const char *
|
||||||
|
load_libssh(void)
|
||||||
|
{
|
||||||
|
char *err_buf;
|
||||||
|
|
||||||
|
libssh = dlopen(FILENAME_OF_SHARED_OBJECT_LIBSSH, RTLD_LAZY);
|
||||||
|
if (!libssh)
|
||||||
|
{
|
||||||
|
/* This would be probably often repeated problem */
|
||||||
|
char *help_msg = "You have to install libssh library.";
|
||||||
|
err_buf = mb_alloc(&root_pool, 512); /* FIXME: free memory */
|
||||||
|
bsnprintf(err_buf, 512, "%s. %s", dlerror(), help_msg);
|
||||||
|
return err_buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
dlerror(); /* Clear any existing error */
|
||||||
|
|
||||||
|
ssh_new = (ssh_session (*)(void)) dlsym(libssh, "ssh_new");
|
||||||
|
if ((err_buf = dlerror()) != NULL)
|
||||||
|
return err_buf;
|
||||||
|
|
||||||
|
ssh_set_blocking = (void (*)(ssh_session, int)) dlsym(libssh, "ssh_set_blocking");
|
||||||
|
if ((err_buf = dlerror()) != NULL)
|
||||||
|
return err_buf;
|
||||||
|
|
||||||
|
ssh_options_set = (int (*)(ssh_session, enum ssh_options_e, const void *)) dlsym(libssh, "ssh_options_set");
|
||||||
|
if ((err_buf = dlerror()) != NULL)
|
||||||
|
return err_buf;
|
||||||
|
|
||||||
|
ssh_connect = (int (*)(ssh_session)) dlsym(libssh, "ssh_connect");
|
||||||
|
if ((err_buf = dlerror()) != NULL)
|
||||||
|
return err_buf;
|
||||||
|
|
||||||
|
ssh_get_fd = (socket_t (*)(ssh_session)) dlsym(libssh, "ssh_get_fd");
|
||||||
|
if ((err_buf = dlerror()) != NULL)
|
||||||
|
return err_buf;
|
||||||
|
|
||||||
|
ssh_is_server_known = (int (*)(ssh_session)) dlsym(libssh, "ssh_is_server_known");
|
||||||
|
if ((err_buf = dlerror()) != NULL)
|
||||||
|
return err_buf;
|
||||||
|
|
||||||
|
ssh_userauth_publickey_auto = (int (*)(ssh_session, const char *, const char *)) dlsym(libssh, "ssh_userauth_publickey_auto");
|
||||||
|
if ((err_buf = dlerror()) != NULL)
|
||||||
|
return err_buf;
|
||||||
|
|
||||||
|
ssh_get_error = (const char * (*)(void *)) dlsym(libssh, "ssh_get_error");
|
||||||
|
if ((err_buf = dlerror()) != NULL)
|
||||||
|
return err_buf;
|
||||||
|
|
||||||
|
ssh_get_error_code = (int (*)(void *)) dlsym(libssh, "ssh_get_error_code");
|
||||||
|
if ((err_buf = dlerror()) != NULL)
|
||||||
|
return err_buf;
|
||||||
|
|
||||||
|
ssh_disconnect = (void (*)(ssh_session)) dlsym(libssh, "ssh_disconnect");
|
||||||
|
if ((err_buf = dlerror()) != NULL)
|
||||||
|
return err_buf;
|
||||||
|
|
||||||
|
ssh_free = (void (*)(ssh_session)) dlsym(libssh, "ssh_free");
|
||||||
|
if ((err_buf = dlerror()) != NULL)
|
||||||
|
return err_buf;
|
||||||
|
|
||||||
|
ssh_channel_new = (ssh_channel (*)(ssh_session)) dlsym(libssh, "ssh_channel_new");
|
||||||
|
if ((err_buf = dlerror()) != NULL)
|
||||||
|
return err_buf;
|
||||||
|
|
||||||
|
ssh_channel_is_open = (int (*)(ssh_channel)) dlsym(libssh, "ssh_channel_is_open");
|
||||||
|
if ((err_buf = dlerror()) != NULL)
|
||||||
|
return err_buf;
|
||||||
|
|
||||||
|
ssh_channel_close = (int (*)(ssh_channel)) dlsym(libssh, "ssh_channel_close");
|
||||||
|
if ((err_buf = dlerror()) != NULL)
|
||||||
|
return err_buf;
|
||||||
|
|
||||||
|
ssh_channel_free = (void (*)(ssh_channel)) dlsym(libssh, "ssh_channel_free");
|
||||||
|
if ((err_buf = dlerror()) != NULL)
|
||||||
|
return err_buf;
|
||||||
|
|
||||||
|
ssh_channel_open_session = (int (*)(ssh_channel)) dlsym(libssh, "ssh_channel_open_session");
|
||||||
|
if ((err_buf = dlerror()) != NULL)
|
||||||
|
return err_buf;
|
||||||
|
|
||||||
|
ssh_channel_request_subsystem = (int (*)(ssh_channel, const char *)) dlsym(libssh, "ssh_channel_request_subsystem");
|
||||||
|
if ((err_buf = dlerror()) != NULL)
|
||||||
|
return err_buf;
|
||||||
|
|
||||||
|
ssh_channel_read_nonblocking = (int (*)(ssh_channel, void *, uint32_t, int)) dlsym(libssh, "ssh_channel_read_nonblocking");
|
||||||
|
if ((err_buf = dlerror()) != NULL)
|
||||||
|
return err_buf;
|
||||||
|
|
||||||
|
ssh_channel_is_eof = (int (*)(ssh_channel)) dlsym(libssh, "ssh_channel_is_eof");
|
||||||
|
if ((err_buf = dlerror()) != NULL)
|
||||||
|
return err_buf;
|
||||||
|
|
||||||
|
ssh_channel_select = (int (*)(ssh_channel *, ssh_channel *, ssh_channel *, struct timeval *)) dlsym(libssh, "ssh_channel_select");
|
||||||
|
if ((err_buf = dlerror()) != NULL)
|
||||||
|
return err_buf;
|
||||||
|
|
||||||
|
ssh_channel_write = (int (*)(ssh_channel, const void *, uint32_t)) dlsym(libssh, "ssh_channel_write");
|
||||||
|
if ((err_buf = dlerror()) != NULL)
|
||||||
|
return err_buf;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
133
lib/libssh.h
Normal file
133
lib/libssh.h
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
* BIRD -- Mockup headers of SSH Library for loading LibSSH using dlopen
|
||||||
|
*
|
||||||
|
* (c) 2015 CZ.NIC
|
||||||
|
*
|
||||||
|
* This file was part of SSH Library: http://www.libssh.org/
|
||||||
|
* (c) 2003-2009 by Aris Adamantiadis (SSH Library)
|
||||||
|
*
|
||||||
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _BIRD_LIBSSH_H_
|
||||||
|
#define _BIRD_LIBSSH_H_
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
typedef struct ssh_session_struct* ssh_session;
|
||||||
|
typedef struct ssh_channel_struct* ssh_channel;
|
||||||
|
|
||||||
|
/* Error return codes */
|
||||||
|
#define SSH_OK 0 /* No error */
|
||||||
|
#define SSH_ERROR -1 /* Error of some kind */
|
||||||
|
#define SSH_AGAIN -2 /* The nonblocking call must be repeated */
|
||||||
|
#define SSH_EOF -127 /* We have already a eof */
|
||||||
|
|
||||||
|
enum ssh_server_known_e {
|
||||||
|
SSH_SERVER_ERROR=-1,
|
||||||
|
SSH_SERVER_NOT_KNOWN=0,
|
||||||
|
SSH_SERVER_KNOWN_OK,
|
||||||
|
SSH_SERVER_KNOWN_CHANGED,
|
||||||
|
SSH_SERVER_FOUND_OTHER,
|
||||||
|
SSH_SERVER_FILE_NOT_FOUND
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ssh_auth_e {
|
||||||
|
SSH_AUTH_SUCCESS=0,
|
||||||
|
SSH_AUTH_DENIED,
|
||||||
|
SSH_AUTH_PARTIAL,
|
||||||
|
SSH_AUTH_INFO,
|
||||||
|
SSH_AUTH_AGAIN,
|
||||||
|
SSH_AUTH_ERROR=-1
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ssh_error_types_e {
|
||||||
|
SSH_NO_ERROR=0,
|
||||||
|
SSH_REQUEST_DENIED,
|
||||||
|
SSH_FATAL,
|
||||||
|
SSH_EINTR
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ssh_options_e {
|
||||||
|
SSH_OPTIONS_HOST,
|
||||||
|
SSH_OPTIONS_PORT,
|
||||||
|
SSH_OPTIONS_PORT_STR,
|
||||||
|
SSH_OPTIONS_FD,
|
||||||
|
SSH_OPTIONS_USER,
|
||||||
|
SSH_OPTIONS_SSH_DIR,
|
||||||
|
SSH_OPTIONS_IDENTITY,
|
||||||
|
SSH_OPTIONS_ADD_IDENTITY,
|
||||||
|
SSH_OPTIONS_KNOWNHOSTS,
|
||||||
|
SSH_OPTIONS_TIMEOUT,
|
||||||
|
SSH_OPTIONS_TIMEOUT_USEC,
|
||||||
|
SSH_OPTIONS_SSH1,
|
||||||
|
SSH_OPTIONS_SSH2,
|
||||||
|
SSH_OPTIONS_LOG_VERBOSITY,
|
||||||
|
SSH_OPTIONS_LOG_VERBOSITY_STR,
|
||||||
|
SSH_OPTIONS_CIPHERS_C_S,
|
||||||
|
SSH_OPTIONS_CIPHERS_S_C,
|
||||||
|
SSH_OPTIONS_COMPRESSION_C_S,
|
||||||
|
SSH_OPTIONS_COMPRESSION_S_C,
|
||||||
|
SSH_OPTIONS_PROXYCOMMAND,
|
||||||
|
SSH_OPTIONS_BINDADDR,
|
||||||
|
SSH_OPTIONS_STRICTHOSTKEYCHECK,
|
||||||
|
SSH_OPTIONS_COMPRESSION,
|
||||||
|
SSH_OPTIONS_COMPRESSION_LEVEL,
|
||||||
|
SSH_OPTIONS_KEY_EXCHANGE,
|
||||||
|
SSH_OPTIONS_HOSTKEYS,
|
||||||
|
SSH_OPTIONS_GSSAPI_SERVER_IDENTITY,
|
||||||
|
SSH_OPTIONS_GSSAPI_CLIENT_IDENTITY,
|
||||||
|
SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS,
|
||||||
|
SSH_OPTIONS_HMAC_C_S,
|
||||||
|
SSH_OPTIONS_HMAC_S_C,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
/** No logging at all
|
||||||
|
*/
|
||||||
|
SSH_LOG_NOLOG=0,
|
||||||
|
/** Only warnings
|
||||||
|
*/
|
||||||
|
SSH_LOG_WARNING,
|
||||||
|
/** High level protocol information
|
||||||
|
*/
|
||||||
|
SSH_LOG_PROTOCOL,
|
||||||
|
/** Lower level protocol infomations, packet level
|
||||||
|
*/
|
||||||
|
SSH_LOG_PACKET,
|
||||||
|
/** Every function path
|
||||||
|
*/
|
||||||
|
SSH_LOG_FUNCTIONS
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef socket_t
|
||||||
|
typedef int socket_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern ssh_session (*ssh_new)(void);
|
||||||
|
extern void (*ssh_set_blocking)(ssh_session session, int blocking);
|
||||||
|
extern int (*ssh_options_set)(ssh_session session, enum ssh_options_e type, const void *value);
|
||||||
|
extern int (*ssh_connect)(ssh_session session);
|
||||||
|
extern socket_t (*ssh_get_fd)(ssh_session session);
|
||||||
|
extern int (*ssh_is_server_known)(ssh_session session);
|
||||||
|
extern int (*ssh_userauth_publickey_auto)(ssh_session session, const char *username, const char *passphrase);
|
||||||
|
extern const char * (*ssh_get_error)(void *error);
|
||||||
|
extern int (*ssh_get_error_code)(void *error);
|
||||||
|
extern void (*ssh_disconnect)(ssh_session session);
|
||||||
|
extern void (*ssh_free)(ssh_session session);
|
||||||
|
|
||||||
|
extern ssh_channel (*ssh_channel_new)(ssh_session session);
|
||||||
|
extern int (*ssh_channel_is_open)(ssh_channel channel);
|
||||||
|
extern int (*ssh_channel_close)(ssh_channel channel);
|
||||||
|
extern void (*ssh_channel_free)(ssh_channel channel);
|
||||||
|
extern int (*ssh_channel_open_session)(ssh_channel channel);
|
||||||
|
extern int (*ssh_channel_request_subsystem)(ssh_channel channel, const char *subsystem);
|
||||||
|
extern int (*ssh_channel_read_nonblocking)(ssh_channel channel, void *dest, uint32_t count, int is_stderr);
|
||||||
|
extern int (*ssh_channel_is_eof)(ssh_channel channel);
|
||||||
|
extern int (*ssh_channel_select)(ssh_channel *readchans, ssh_channel *writechans, ssh_channel *exceptchans, struct timeval * timeout);
|
||||||
|
extern int (*ssh_channel_write)(ssh_channel channel, const void *data, uint32_t len);
|
||||||
|
|
||||||
|
const char *load_libssh(void);
|
||||||
|
|
||||||
|
#endif /* _BIRD_LIBSSH_H_ */
|
@ -8,7 +8,9 @@ const char * const net_label[] = {
|
|||||||
[NET_IP4] = "ipv4",
|
[NET_IP4] = "ipv4",
|
||||||
[NET_IP6] = "ipv6",
|
[NET_IP6] = "ipv6",
|
||||||
[NET_VPN4] = "vpn4",
|
[NET_VPN4] = "vpn4",
|
||||||
[NET_VPN6] = "vpn6"
|
[NET_VPN6] = "vpn6",
|
||||||
|
[NET_ROA4] = "roa4",
|
||||||
|
[NET_ROA6] = "roa6",
|
||||||
};
|
};
|
||||||
|
|
||||||
const u16 net_addr_length[] = {
|
const u16 net_addr_length[] = {
|
||||||
|
@ -25,6 +25,8 @@
|
|||||||
#define NB_IP6 (1 << NET_IP6)
|
#define NB_IP6 (1 << NET_IP6)
|
||||||
#define NB_VPN4 (1 << NET_VPN4)
|
#define NB_VPN4 (1 << NET_VPN4)
|
||||||
#define NB_VPN6 (1 << NET_VPN6)
|
#define NB_VPN6 (1 << NET_VPN6)
|
||||||
|
#define NB_ROA4 (1 << NET_ROA4)
|
||||||
|
#define NB_ROA6 (1 << NET_ROA6)
|
||||||
|
|
||||||
#define NB_IP (NB_IP4 | NB_IP6)
|
#define NB_IP (NB_IP4 | NB_IP6)
|
||||||
#define NB_ANY 0xffffffff
|
#define NB_ANY 0xffffffff
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
struct pool {
|
struct pool {
|
||||||
resource r;
|
resource r;
|
||||||
list inside;
|
list inside;
|
||||||
char *name;
|
const char *name;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void pool_dump(resource *);
|
static void pool_dump(resource *);
|
||||||
@ -61,7 +61,7 @@ static int indent;
|
|||||||
* parent pool.
|
* parent pool.
|
||||||
*/
|
*/
|
||||||
pool *
|
pool *
|
||||||
rp_new(pool *p, char *name)
|
rp_new(pool *p, const char *name)
|
||||||
{
|
{
|
||||||
pool *z = ralloc(p, &pool_class);
|
pool *z = ralloc(p, &pool_class);
|
||||||
z->name = name;
|
z->name = name;
|
||||||
|
@ -37,7 +37,7 @@ struct resclass {
|
|||||||
typedef struct pool pool;
|
typedef struct pool pool;
|
||||||
|
|
||||||
void resource_init(void);
|
void resource_init(void);
|
||||||
pool *rp_new(pool *, char *); /* Create new pool */
|
pool *rp_new(pool *, const char *); /* Create new pool */
|
||||||
void rfree(void *); /* Free single resource */
|
void rfree(void *); /* Free single resource */
|
||||||
void rdump(void *); /* Dump to debug output */
|
void rdump(void *); /* Dump to debug output */
|
||||||
size_t rmemsize(void *res); /* Return size of memory used by the resource */
|
size_t rmemsize(void *res); /* Return size of memory used by the resource */
|
||||||
|
24
lib/socket.h
24
lib/socket.h
@ -12,6 +12,24 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#include "lib/resource.h"
|
#include "lib/resource.h"
|
||||||
|
#include "lib/libssh.h"
|
||||||
|
|
||||||
|
struct ssh_sock {
|
||||||
|
const char *username; /* (Required) SSH user name */
|
||||||
|
const char *server_hostkey_path; /* (Optional) Filepath to the SSH public key of remote side, can be knownhost file */
|
||||||
|
const char *client_privkey_path; /* (Optional) Filepath to the SSH private key of BIRD */
|
||||||
|
const char *subsystem; /* (Optional) Name of SSH subsytem */
|
||||||
|
ssh_session session; /* Internal */
|
||||||
|
ssh_channel channel; /* Internal */
|
||||||
|
int state; /* Internal */
|
||||||
|
#define SK_SSH_CONNECT 0 /* Start state */
|
||||||
|
#define SK_SSH_SERVER_KNOWN 1 /* Internal */
|
||||||
|
#define SK_SSH_USERAUTH 2 /* Internal */
|
||||||
|
#define SK_SSH_CHANNEL 3 /* Internal */
|
||||||
|
#define SK_SSH_SESSION 4 /* Internal */
|
||||||
|
#define SK_SSH_SUBSYSTEM 5 /* Internal */
|
||||||
|
#define SK_SSH_ESTABLISHED 6 /* Final state */
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct birdsock {
|
typedef struct birdsock {
|
||||||
resource r;
|
resource r;
|
||||||
@ -19,6 +37,7 @@ typedef struct birdsock {
|
|||||||
int type; /* Socket type */
|
int type; /* Socket type */
|
||||||
void *data; /* User data */
|
void *data; /* User data */
|
||||||
ip_addr saddr, daddr; /* IPA_NONE = unspecified */
|
ip_addr saddr, daddr; /* IPA_NONE = unspecified */
|
||||||
|
const char *host; /* Alternative to daddr, NULL = unspecified */
|
||||||
uint sport, dport; /* 0 = unspecified (for IP: protocol type) */
|
uint sport, dport; /* 0 = unspecified (for IP: protocol type) */
|
||||||
int tos; /* TOS / traffic class, -1 = default */
|
int tos; /* TOS / traffic class, -1 = default */
|
||||||
int priority; /* Local socket priority, -1 = default */
|
int priority; /* Local socket priority, -1 = default */
|
||||||
@ -51,7 +70,8 @@ typedef struct birdsock {
|
|||||||
node n;
|
node n;
|
||||||
void *rbuf_alloc, *tbuf_alloc;
|
void *rbuf_alloc, *tbuf_alloc;
|
||||||
char *password; /* Password for MD5 authentication */
|
char *password; /* Password for MD5 authentication */
|
||||||
char *err; /* Error message */
|
const char *err; /* Error message */
|
||||||
|
struct ssh_sock *ssh; /* Used in SK_SSH */
|
||||||
} sock;
|
} sock;
|
||||||
|
|
||||||
sock *sock_new(pool *); /* Allocate new socket */
|
sock *sock_new(pool *); /* Allocate new socket */
|
||||||
@ -115,6 +135,8 @@ extern int sk_priority_control; /* Suggested priority for control traffic, shou
|
|||||||
#define SK_MAGIC 7 /* Internal use by sysdep code */
|
#define SK_MAGIC 7 /* Internal use by sysdep code */
|
||||||
#define SK_UNIX_PASSIVE 8
|
#define SK_UNIX_PASSIVE 8
|
||||||
#define SK_UNIX 9
|
#define SK_UNIX 9
|
||||||
|
#define SK_SSH_ACTIVE 10 /* - - * * - ? - DA = host */
|
||||||
|
#define SK_SSH 11
|
||||||
|
|
||||||
/* Socket families */
|
/* Socket families */
|
||||||
|
|
||||||
|
13
nest/proto.c
13
nest/proto.c
@ -39,12 +39,10 @@ static int graceful_restart_state;
|
|||||||
static u32 graceful_restart_locks;
|
static u32 graceful_restart_locks;
|
||||||
|
|
||||||
static char *p_states[] = { "DOWN", "START", "UP", "STOP" };
|
static char *p_states[] = { "DOWN", "START", "UP", "STOP" };
|
||||||
static char *cs_states[] = {
|
|
||||||
[CS_DOWN] = "DOWN",
|
#if defined(LOCAL_DEBUG) || defined(GLOBAL_DEBUG)
|
||||||
[CS_START] = "START",
|
static char *cs_states[] = { "DOWN", "START", "UP", "FLUSHING" };
|
||||||
[CS_UP] = "UP",
|
#endif
|
||||||
[CS_FLUSHING] = "FLUSHING"
|
|
||||||
};
|
|
||||||
|
|
||||||
extern struct protocol proto_unix_iface;
|
extern struct protocol proto_unix_iface;
|
||||||
|
|
||||||
@ -1263,6 +1261,9 @@ protos_build(void)
|
|||||||
proto_build(&proto_bfd);
|
proto_build(&proto_bfd);
|
||||||
bfd_init_all();
|
bfd_init_all();
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef CONFIG_RPKI
|
||||||
|
proto_build(&proto_rpki);
|
||||||
|
#endif
|
||||||
|
|
||||||
proto_pool = rp_new(&root_pool, "Protocols");
|
proto_pool = rp_new(&root_pool, "Protocols");
|
||||||
proto_shutdown_timer = tm_new(proto_pool);
|
proto_shutdown_timer = tm_new(proto_pool);
|
||||||
|
@ -81,7 +81,7 @@ void protos_dump_all(void);
|
|||||||
|
|
||||||
extern struct protocol
|
extern struct protocol
|
||||||
proto_device, proto_radv, proto_rip, proto_static,
|
proto_device, proto_radv, proto_rip, proto_static,
|
||||||
proto_ospf, proto_pipe, proto_bgp, proto_bfd;
|
proto_ospf, proto_pipe, proto_bgp, proto_bfd, proto_rpki;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Routing Protocol Instance
|
* Routing Protocol Instance
|
||||||
@ -271,7 +271,7 @@ proto_get_router_id(struct proto_config *pc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Moved from route.h to avoid dependency conflicts */
|
/* Moved from route.h to avoid dependency conflicts */
|
||||||
static inline void rte_update(struct proto *p, net_addr *n, rte *new) { rte_update2(p->main_channel, n, new, p->main_source); }
|
static inline void rte_update(struct proto *p, const net_addr *n, rte *new) { rte_update2(p->main_channel, n, new, p->main_source); }
|
||||||
|
|
||||||
extern list proto_list;
|
extern list proto_list;
|
||||||
|
|
||||||
@ -562,11 +562,9 @@ int proto_configure_channel(struct proto *p, struct channel **c, struct channel_
|
|||||||
|
|
||||||
void channel_set_state(struct channel *c, uint state);
|
void channel_set_state(struct channel *c, uint state);
|
||||||
|
|
||||||
/*
|
|
||||||
static inline void channel_init(struct channel *c) { channel_set_state(c, CS_START); }
|
static inline void channel_init(struct channel *c) { channel_set_state(c, CS_START); }
|
||||||
static inline void channel_open(struct channel *c) { channel_set_state(c, CS_UP); }
|
static inline void channel_open(struct channel *c) { channel_set_state(c, CS_UP); }
|
||||||
static inline void channel_close(struct channel *c) { channel_set_state(c, CS_FLUSHING); }
|
static inline void channel_close(struct channel *c) { channel_set_state(c, CS_FLUSHING); }
|
||||||
*/
|
|
||||||
|
|
||||||
void channel_request_feeding(struct channel *c);
|
void channel_request_feeding(struct channel *c);
|
||||||
void *channel_config_new(const struct channel_class *cc, uint net_type, struct proto_config *proto);
|
void *channel_config_new(const struct channel_class *cc, uint net_type, struct proto_config *proto);
|
||||||
|
@ -276,7 +276,7 @@ static inline net *net_get(rtable *tab, const net_addr *addr) { return (net *) f
|
|||||||
|
|
||||||
rte *rte_find(net *net, struct rte_src *src);
|
rte *rte_find(net *net, struct rte_src *src);
|
||||||
rte *rte_get_temp(struct rta *);
|
rte *rte_get_temp(struct rta *);
|
||||||
void rte_update2(struct channel *c, net_addr *n, rte *new, struct rte_src *src);
|
void rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src);
|
||||||
/* rte_update() moved to protocol.h to avoid dependency conflicts */
|
/* rte_update() moved to protocol.h to avoid dependency conflicts */
|
||||||
void rte_discard(rtable *tab, rte *old);
|
void rte_discard(rtable *tab, rte *old);
|
||||||
int rt_examine(rtable *t, net_addr *a, struct proto *p, struct filter *filter);
|
int rt_examine(rtable *t, net_addr *a, struct proto *p, struct filter *filter);
|
||||||
@ -376,6 +376,7 @@ typedef struct rta {
|
|||||||
#define RTS_OSPF_EXT2 10 /* OSPF external route type 2 */
|
#define RTS_OSPF_EXT2 10 /* OSPF external route type 2 */
|
||||||
#define RTS_BGP 11 /* BGP route */
|
#define RTS_BGP 11 /* BGP route */
|
||||||
#define RTS_PIPE 12 /* Inter-table wormhole */
|
#define RTS_PIPE 12 /* Inter-table wormhole */
|
||||||
|
#define RTS_RPKI 13 /* Route Origin Authorization */
|
||||||
|
|
||||||
#define RTC_UNICAST 0
|
#define RTC_UNICAST 0
|
||||||
#define RTC_BROADCAST 1
|
#define RTC_BROADCAST 1
|
||||||
@ -551,6 +552,7 @@ extern struct protocol *attr_class_to_protocol[EAP_MAX];
|
|||||||
#define DEF_PREF_OSPF 150 /* OSPF intra-area, inter-area and type 1 external routes */
|
#define DEF_PREF_OSPF 150 /* OSPF intra-area, inter-area and type 1 external routes */
|
||||||
#define DEF_PREF_RIP 120 /* RIP */
|
#define DEF_PREF_RIP 120 /* RIP */
|
||||||
#define DEF_PREF_BGP 100 /* BGP */
|
#define DEF_PREF_BGP 100 /* BGP */
|
||||||
|
#define DEF_PREF_RPKI 80 /* RPKI */
|
||||||
#define DEF_PREF_INHERITED 10 /* Routes inherited from other routing daemons */
|
#define DEF_PREF_INHERITED 10 /* Routes inherited from other routing daemons */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1267,7 +1267,7 @@ rte_unhide_dummy_routes(net *net, rte **dummy)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
void
|
void
|
||||||
rte_update2(struct channel *c, net_addr *n, rte *new, struct rte_src *src)
|
rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
|
||||||
{
|
{
|
||||||
struct proto *p = c->proto;
|
struct proto *p = c->proto;
|
||||||
struct proto_stats *stats = &c->stats;
|
struct proto_stats *stats = &c->stats;
|
||||||
|
@ -3,7 +3,8 @@ C bfd
|
|||||||
C bgp
|
C bgp
|
||||||
C ospf
|
C ospf
|
||||||
C pipe
|
C pipe
|
||||||
C rip
|
|
||||||
C radv
|
C radv
|
||||||
|
C rip
|
||||||
|
C rpki
|
||||||
C static
|
C static
|
||||||
S ../nest/rt-dev.c
|
S ../nest/rt-dev.c
|
||||||
|
5
proto/rpki/Doc
Normal file
5
proto/rpki/Doc
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
S rpki.c
|
||||||
|
S packets.c
|
||||||
|
S transport.c
|
||||||
|
S tcp_transport.c
|
||||||
|
S ssh_transport.c
|
4
proto/rpki/Makefile
Normal file
4
proto/rpki/Makefile
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
src := rpki.c packets.c tcp_transport.c ssh_transport.c transport.c
|
||||||
|
obj := $(src-o-files)
|
||||||
|
$(all-daemon)
|
||||||
|
$(cf-local)
|
118
proto/rpki/config.Y
Normal file
118
proto/rpki/config.Y
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* BIRD -- The Resource Public Key Infrastructure (RPKI) to Router Protocol
|
||||||
|
*
|
||||||
|
* (c) 2015 CZ.NIC
|
||||||
|
*
|
||||||
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
CF_HDR
|
||||||
|
|
||||||
|
#include "proto/rpki/rpki.h"
|
||||||
|
|
||||||
|
CF_DEFINES
|
||||||
|
|
||||||
|
#define RPKI_CFG ((struct rpki_config *) this_proto)
|
||||||
|
|
||||||
|
static void
|
||||||
|
rpki_check_unused_hostname(void)
|
||||||
|
{
|
||||||
|
if (RPKI_CFG->hostname != NULL)
|
||||||
|
cf_error("Only one remote cache server per protocol allowed");
|
||||||
|
}
|
||||||
|
|
||||||
|
CF_DECLS
|
||||||
|
|
||||||
|
CF_KEYWORDS(RPKI, REMOTE, BIRD, PRIVATE, PUBLIC, KEY, SSH, ENCRYPTION, USER,
|
||||||
|
RETRY, REFRESH, EXPIRE)
|
||||||
|
|
||||||
|
CF_GRAMMAR
|
||||||
|
|
||||||
|
CF_ADDTO(proto, rpki_proto)
|
||||||
|
|
||||||
|
rpki_proto_start: proto_start RPKI {
|
||||||
|
this_proto = proto_config_new(&proto_rpki, $1);
|
||||||
|
RPKI_CFG->retry_interval = RPKI_DEFAULT_RETRY_INTERVAL;
|
||||||
|
RPKI_CFG->refresh_interval = RPKI_DEFAULT_REFRESH_INTERVAL;
|
||||||
|
RPKI_CFG->expire_interval = RPKI_DEFAULT_EXPIRE_INTERVAL;
|
||||||
|
};
|
||||||
|
|
||||||
|
rpki_proto: rpki_proto_start proto_name '{' rpki_proto_opts '}' { rpki_check_config(RPKI_CFG); };
|
||||||
|
|
||||||
|
rpki_proto_opts:
|
||||||
|
/* empty */
|
||||||
|
| rpki_proto_opts rpki_proto_item ';'
|
||||||
|
;
|
||||||
|
|
||||||
|
rpki_proto_item:
|
||||||
|
proto_item
|
||||||
|
| proto_channel
|
||||||
|
| REMOTE rpki_cache_addr
|
||||||
|
| REMOTE rpki_cache_addr rpki_proto_item_port
|
||||||
|
| rpki_proto_item_port
|
||||||
|
| SSH ENCRYPTION rpki_transport_ssh
|
||||||
|
| REFRESH expr {
|
||||||
|
if (rpki_check_refresh_interval($2))
|
||||||
|
cf_error(rpki_check_refresh_interval($2));
|
||||||
|
RPKI_CFG->refresh_interval = $2;
|
||||||
|
}
|
||||||
|
| RETRY expr {
|
||||||
|
if (rpki_check_retry_interval($2))
|
||||||
|
cf_error(rpki_check_retry_interval($2));
|
||||||
|
RPKI_CFG->retry_interval = $2;
|
||||||
|
}
|
||||||
|
| EXPIRE expr {
|
||||||
|
if (rpki_check_expire_interval($2))
|
||||||
|
cf_error(rpki_check_expire_interval($2));
|
||||||
|
RPKI_CFG->expire_interval = $2;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
rpki_transport_ssh:
|
||||||
|
OFF { RPKI_CFG->ssh = NULL; }
|
||||||
|
| rpki_transport_ssh_init '{' rpki_transport_ssh_opts '}' rpki_transport_ssh_check
|
||||||
|
;
|
||||||
|
|
||||||
|
rpki_proto_item_port: PORT expr { check_u16($2); RPKI_CFG->port = $2; };
|
||||||
|
|
||||||
|
rpki_cache_addr:
|
||||||
|
text {
|
||||||
|
rpki_check_unused_hostname();
|
||||||
|
RPKI_CFG->hostname = $1;
|
||||||
|
}
|
||||||
|
| ipa {
|
||||||
|
rpki_check_unused_hostname();
|
||||||
|
RPKI_CFG->ip = $1;
|
||||||
|
char *hostname = cfg_allocz(sizeof(INET6_ADDRSTRLEN+1));
|
||||||
|
bsnprintf(hostname, INET6_ADDRSTRLEN+1, "%I", RPKI_CFG->ip);
|
||||||
|
RPKI_CFG->hostname = hostname;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
rpki_transport_ssh_init:
|
||||||
|
{
|
||||||
|
/* allow extending ssh configuration */
|
||||||
|
if (RPKI_CFG->ssh == NULL)
|
||||||
|
RPKI_CFG->ssh = cfg_allocz(sizeof(struct rpki_config_ssh));
|
||||||
|
};
|
||||||
|
|
||||||
|
rpki_transport_ssh_opts:
|
||||||
|
/* empty */
|
||||||
|
| rpki_transport_ssh_opts rpki_transport_ssh_item ';'
|
||||||
|
;
|
||||||
|
|
||||||
|
rpki_transport_ssh_item:
|
||||||
|
BIRD PRIVATE KEY text { RPKI_CFG->ssh->bird_private_key = $4; }
|
||||||
|
| REMOTE PUBLIC KEY text { RPKI_CFG->ssh->cache_public_key = $4; }
|
||||||
|
| USER text { RPKI_CFG->ssh->user = $2; }
|
||||||
|
;
|
||||||
|
|
||||||
|
rpki_transport_ssh_check:
|
||||||
|
{
|
||||||
|
if (RPKI_CFG->ssh->user == NULL)
|
||||||
|
cf_error("User must be set");
|
||||||
|
};
|
||||||
|
|
||||||
|
CF_CODE
|
||||||
|
|
||||||
|
CF_END
|
1043
proto/rpki/packets.c
Normal file
1043
proto/rpki/packets.c
Normal file
File diff suppressed because it is too large
Load Diff
37
proto/rpki/packets.h
Normal file
37
proto/rpki/packets.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* BIRD -- The Resource Public Key Infrastructure (RPKI) to Router Protocol
|
||||||
|
*
|
||||||
|
* (c) 2015 CZ.NIC
|
||||||
|
*
|
||||||
|
* This file was a part of RTRlib: http://rpki.realmv6.org/
|
||||||
|
*
|
||||||
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _BIRD_RPKI_PACKETS_H_
|
||||||
|
#define _BIRD_RPKI_PACKETS_H_
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#define RPKI_RX_BUFFER_SIZE 65536
|
||||||
|
#define RPKI_TX_BUFFER_SIZE 65536
|
||||||
|
#define RPKI_PDU_HEADER_LEN 8
|
||||||
|
|
||||||
|
/* Error PDU size is the biggest (has encapsulate PDU inside):
|
||||||
|
* Header size 8 Bytes +
|
||||||
|
* Length of Encapsulated PDU 4 Bytes +
|
||||||
|
* Encapsulated PDU IPv6 32 Bytes +
|
||||||
|
* Length of Text 4 Bytes +
|
||||||
|
* UTF-8 Text 400*2 Bytes
|
||||||
|
* = 848 Bytes
|
||||||
|
*/
|
||||||
|
#define RPKI_PDU_MAX_LEN 848
|
||||||
|
|
||||||
|
int rpki_send_serial_query(struct rpki_cache *cache);
|
||||||
|
int rpki_send_reset_query(struct rpki_cache *cache);
|
||||||
|
int rpki_rx_hook(sock *sk, int size);
|
||||||
|
void rpki_connected_hook(sock *sk);
|
||||||
|
void rpki_err_hook(sock *sk, int size);
|
||||||
|
void rpki_table_remove_all(struct rpki_cache *cache);
|
||||||
|
|
||||||
|
#endif
|
737
proto/rpki/rpki.c
Normal file
737
proto/rpki/rpki.c
Normal file
@ -0,0 +1,737 @@
|
|||||||
|
/*
|
||||||
|
* BIRD -- The Resource Public Key Infrastructure (RPKI) to Router Protocol
|
||||||
|
*
|
||||||
|
* (c) 2015 CZ.NIC
|
||||||
|
*
|
||||||
|
* Using RTRlib: http://rpki.realmv6.org/
|
||||||
|
*
|
||||||
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DOC: RPKI to Router Protocol
|
||||||
|
*
|
||||||
|
* The Resource Public Key Infrastructure (RPKI) to Router Protocol (RFC 6810)
|
||||||
|
* is protocol for communication between router (BIRD) and RPKI cache server
|
||||||
|
* (RPKI validator). Validator sends implementation
|
||||||
|
* is based on the RTRlib (http://rpki.realmv6.org/). The BIRD takes over
|
||||||
|
* |packets.c|, |rtr.c| (inside |rpki.c|), |transport.c|, |tcp_transport.c| and |ssh_transport.c| files
|
||||||
|
* from RTRlib.
|
||||||
|
*
|
||||||
|
* A SSH transport requires LibSSH library. LibSSH is loading dynamically using dlopen
|
||||||
|
* function.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO list
|
||||||
|
* - Receive Router Key PDU with End-Entity certificate
|
||||||
|
* https://tools.ietf.org/html/draft-ietf-sidr-rpki-rtr-rfc6810-bis-07#section-5.10
|
||||||
|
* It's implemented in RTRlib.
|
||||||
|
* - Saving EE Certificate
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
|
||||||
|
#undef LOCAL_DEBUG
|
||||||
|
|
||||||
|
#include "rpki.h"
|
||||||
|
#include "lib/string.h"
|
||||||
|
#include "nest/cli.h"
|
||||||
|
|
||||||
|
static const char *str_cache_states[] = {
|
||||||
|
[RPKI_CS_CONNECTING] = "CONNECTING",
|
||||||
|
[RPKI_CS_ESTABLISHED] = "ESTABLISHED",
|
||||||
|
[RPKI_CS_RESET] = "RESET",
|
||||||
|
[RPKI_CS_SYNC] = "SYNC",
|
||||||
|
[RPKI_CS_FAST_RECONNECT] = "FAST_RECONNECT",
|
||||||
|
[RPKI_CS_ERROR_NO_DATA_AVAIL] = "ERROR_NO_DATA_AVAIL",
|
||||||
|
[RPKI_CS_ERROR_NO_INCR_UPDATE_AVAIL] = "ERROR_NO_INCR_UPDATE_AVAIL",
|
||||||
|
[RPKI_CS_ERROR_FATAL] = "ERROR_FATAL",
|
||||||
|
[RPKI_CS_ERROR_TRANSPORT] = "ERROR_TRANSPORT",
|
||||||
|
[RPKI_CS_SHUTDOWN] = "SHUTDOWN"
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *
|
||||||
|
rpki_cache_state_to_str(enum rpki_cache_state state)
|
||||||
|
{
|
||||||
|
return str_cache_states[state];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return 0 if non-valid transition,
|
||||||
|
* return 1 if valid transition */
|
||||||
|
static int
|
||||||
|
rpki_is_allowed_transition_cache_state(const enum rpki_cache_state old, const enum rpki_cache_state new)
|
||||||
|
{
|
||||||
|
switch (new)
|
||||||
|
{
|
||||||
|
case RPKI_CS_CONNECTING: return old == RPKI_CS_SHUTDOWN || old == RPKI_CS_ERROR_TRANSPORT || old == RPKI_CS_FAST_RECONNECT;
|
||||||
|
case RPKI_CS_ESTABLISHED: return old == RPKI_CS_SYNC;
|
||||||
|
case RPKI_CS_RESET: return old == RPKI_CS_ERROR_NO_DATA_AVAIL || old == RPKI_CS_ERROR_NO_INCR_UPDATE_AVAIL;
|
||||||
|
case RPKI_CS_SYNC: return old == RPKI_CS_RESET || old == RPKI_CS_CONNECTING || old == RPKI_CS_ESTABLISHED;
|
||||||
|
case RPKI_CS_FAST_RECONNECT: return old == RPKI_CS_ESTABLISHED || old == RPKI_CS_SYNC;
|
||||||
|
case RPKI_CS_ERROR_NO_DATA_AVAIL: return old == RPKI_CS_SYNC;
|
||||||
|
case RPKI_CS_ERROR_NO_INCR_UPDATE_AVAIL: return old == RPKI_CS_SYNC;
|
||||||
|
case RPKI_CS_ERROR_FATAL: return 1;
|
||||||
|
case RPKI_CS_ERROR_TRANSPORT: return 1;
|
||||||
|
case RPKI_CS_SHUTDOWN: return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct proto *
|
||||||
|
rpki_init(struct proto_config *CF)
|
||||||
|
{
|
||||||
|
struct proto *P = proto_new(CF);
|
||||||
|
|
||||||
|
return P;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
rpki_get_cache_ident(struct rpki_cache *cache)
|
||||||
|
{
|
||||||
|
return rpki_tr_ident(cache->tr_sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Timers
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
rpki_schedule_next_refresh(struct rpki_cache *cache)
|
||||||
|
{
|
||||||
|
if (cache->state == RPKI_CS_SHUTDOWN)
|
||||||
|
{
|
||||||
|
CACHE_DBG(cache, "Stop refreshing");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned time_to_wait = cache->refresh_interval;
|
||||||
|
|
||||||
|
CACHE_DBG(cache, "Scheduling next refresh after %u seconds", time_to_wait);
|
||||||
|
tm_start(cache->refresh_timer, time_to_wait);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rpki_schedule_next_retry(struct rpki_cache *cache)
|
||||||
|
{
|
||||||
|
uint time_to_wait = cache->retry_interval;
|
||||||
|
|
||||||
|
switch (cache->state)
|
||||||
|
{
|
||||||
|
case RPKI_CS_ESTABLISHED:
|
||||||
|
case RPKI_CS_SYNC:
|
||||||
|
case RPKI_CS_RESET:
|
||||||
|
CACHE_DBG(cache, "Stop retrying connection");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
CACHE_DBG(cache, "Scheduling next retry after %u seconds", time_to_wait);
|
||||||
|
tm_start(cache->retry_timer, time_to_wait);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rpki_schedule_next_expire_check(struct rpki_cache *cache)
|
||||||
|
{
|
||||||
|
/* minimum time to wait is 1 second */
|
||||||
|
unsigned time_to_wait = MAX(((int)cache->expire_interval - (int)(now - cache->last_update)), 1);
|
||||||
|
|
||||||
|
CACHE_DBG(cache, "Scheduling next expiration check after %u seconds", time_to_wait);
|
||||||
|
tm_start(cache->expire_timer, time_to_wait);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rpki_refresh_hook(struct timer *tm)
|
||||||
|
{
|
||||||
|
struct rpki_cache *cache = tm->data;
|
||||||
|
|
||||||
|
CACHE_DBG(cache, "%s", rpki_cache_state_to_str(cache->state));
|
||||||
|
|
||||||
|
switch (cache->state)
|
||||||
|
{
|
||||||
|
case RPKI_CS_ESTABLISHED:
|
||||||
|
rpki_cache_change_state(cache, RPKI_CS_SYNC);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RPKI_CS_SYNC:
|
||||||
|
rpki_cache_change_state(cache, RPKI_CS_ERROR_TRANSPORT);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
rpki_schedule_next_refresh(cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rpki_retry_hook(struct timer *tm)
|
||||||
|
{
|
||||||
|
struct rpki_cache *cache = tm->data;
|
||||||
|
|
||||||
|
CACHE_DBG(cache, "%s", rpki_cache_state_to_str(cache->state));
|
||||||
|
|
||||||
|
switch (cache->state)
|
||||||
|
{
|
||||||
|
case RPKI_CS_ESTABLISHED:
|
||||||
|
case RPKI_CS_CONNECTING:
|
||||||
|
case RPKI_CS_SYNC:
|
||||||
|
case RPKI_CS_SHUTDOWN:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
rpki_cache_change_state(cache, RPKI_CS_CONNECTING);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
rpki_schedule_next_retry(cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rpki_purge_records_if_outdated(struct rpki_cache *cache)
|
||||||
|
{
|
||||||
|
if (cache->last_update == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ((cache->last_update + cache->expire_interval) < now)
|
||||||
|
{
|
||||||
|
CACHE_TRACE(D_EVENTS, cache, "All routes expired");
|
||||||
|
rpki_table_remove_all(cache);
|
||||||
|
cache->request_session_id = 1;
|
||||||
|
cache->serial_number = 0;
|
||||||
|
cache->last_update = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CACHE_DBG(cache, "No outdated records, remains %d seconds to become obsolete", (int)cache->expire_interval - (int)(now - cache->last_update));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rpki_expire_hook(struct timer *tm)
|
||||||
|
{
|
||||||
|
struct rpki_cache *cache = tm->data;
|
||||||
|
|
||||||
|
if (cache->last_update == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
CACHE_DBG(cache, ""); /* Show name of function */
|
||||||
|
|
||||||
|
rpki_purge_records_if_outdated(cache);
|
||||||
|
rpki_schedule_next_expire_check(cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
rpki_open_connection(struct rpki_cache *cache)
|
||||||
|
{
|
||||||
|
CACHE_TRACE(D_EVENTS, cache, "Opening a connection");
|
||||||
|
|
||||||
|
if (rpki_tr_open(cache->tr_sock) == TR_ERROR)
|
||||||
|
{
|
||||||
|
rpki_cache_change_state(cache, RPKI_CS_ERROR_TRANSPORT);
|
||||||
|
return TR_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rpki_close_connection(struct rpki_cache *cache)
|
||||||
|
{
|
||||||
|
CACHE_TRACE(D_EVENTS, cache, "Closing a connection");
|
||||||
|
rpki_tr_close(cache->tr_sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rpki_cache_change_state - check and change cache state
|
||||||
|
* @cache: RPKI cache instance
|
||||||
|
* @new_state: suggested new state
|
||||||
|
*
|
||||||
|
* Validates and makes transition. Does appropriate actions after change
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
rpki_cache_change_state(struct rpki_cache *cache, const enum rpki_cache_state new_state)
|
||||||
|
{
|
||||||
|
const enum rpki_cache_state old_state = cache->state;
|
||||||
|
|
||||||
|
if (old_state == new_state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!rpki_is_allowed_transition_cache_state(old_state, new_state))
|
||||||
|
{
|
||||||
|
CACHE_TRACE(D_EVENTS, cache, "Change state %s -> %s is not allowed", rpki_cache_state_to_str(old_state), rpki_cache_state_to_str(new_state));
|
||||||
|
ASSERT(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CACHE_TRACE(D_EVENTS, cache, "Change state %s -> %s", rpki_cache_state_to_str(old_state), rpki_cache_state_to_str(new_state));
|
||||||
|
cache->state = new_state;
|
||||||
|
|
||||||
|
switch (new_state)
|
||||||
|
{
|
||||||
|
case RPKI_CS_CONNECTING:
|
||||||
|
{
|
||||||
|
sock *sk = cache->tr_sock->sk;
|
||||||
|
|
||||||
|
if (sk == NULL || sk->fd < 0)
|
||||||
|
rpki_open_connection(cache);
|
||||||
|
else
|
||||||
|
rpki_cache_change_state(cache, RPKI_CS_SYNC);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case RPKI_CS_ESTABLISHED:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RPKI_CS_RESET:
|
||||||
|
/* Resetting RTR connection. */
|
||||||
|
cache->request_session_id = 1;
|
||||||
|
cache->serial_number = 0;
|
||||||
|
rpki_cache_change_state(cache, RPKI_CS_SYNC);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RPKI_CS_SYNC:
|
||||||
|
/* Requesting for receive validation records from the RTR server. */
|
||||||
|
if (cache->request_session_id)
|
||||||
|
{
|
||||||
|
/* Change to state RESET, if socket dont has a session_id */
|
||||||
|
if (rpki_send_reset_query(cache) != RPKI_SUCCESS)
|
||||||
|
rpki_cache_change_state(cache, RPKI_CS_ERROR_FATAL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* if we already have a session_id, send a serial query and start to sync */
|
||||||
|
if (rpki_send_serial_query(cache) != RPKI_SUCCESS)
|
||||||
|
rpki_cache_change_state(cache, RPKI_CS_ERROR_FATAL);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RPKI_CS_ERROR_NO_INCR_UPDATE_AVAIL:
|
||||||
|
/* Server was unable to answer the last serial or reset query. */
|
||||||
|
rpki_purge_records_if_outdated(cache);
|
||||||
|
rpki_cache_change_state(cache, RPKI_CS_RESET);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RPKI_CS_ERROR_NO_DATA_AVAIL:
|
||||||
|
/* No validation records are available on the RTR server. */
|
||||||
|
rpki_cache_change_state(cache, RPKI_CS_RESET);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RPKI_CS_ERROR_FATAL:
|
||||||
|
/* Fatal protocol error occurred. */
|
||||||
|
cache->request_session_id = 1;
|
||||||
|
cache->serial_number = 0;
|
||||||
|
cache->last_update = 0;
|
||||||
|
rpki_table_remove_all(cache);
|
||||||
|
/* Fall through */
|
||||||
|
|
||||||
|
case RPKI_CS_ERROR_TRANSPORT:
|
||||||
|
/* Error on the transport socket occurred. */
|
||||||
|
rpki_close_connection(cache);
|
||||||
|
rpki_schedule_next_retry(cache);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RPKI_CS_FAST_RECONNECT:
|
||||||
|
/* Reconnect without any waiting period */
|
||||||
|
rpki_close_connection(cache);
|
||||||
|
rpki_cache_change_state(cache, RPKI_CS_CONNECTING);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RPKI_CS_SHUTDOWN:
|
||||||
|
/* RTR Socket is stopped. */
|
||||||
|
rpki_close_connection(cache);
|
||||||
|
cache->request_session_id = 1;
|
||||||
|
cache->serial_number = 0;
|
||||||
|
cache->last_update = 0;
|
||||||
|
rpki_table_remove_all(cache);
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rpki_check_refresh_interval - check validity of refresh interval value
|
||||||
|
* @seconds: suggested value
|
||||||
|
*
|
||||||
|
* Validate value and return NULL if check passed or error message if check failed.
|
||||||
|
*/
|
||||||
|
const char *
|
||||||
|
rpki_check_refresh_interval(uint seconds)
|
||||||
|
{
|
||||||
|
if (seconds < 1)
|
||||||
|
return "Minimum allowed refresh interval is 1 second";
|
||||||
|
if (seconds > 86400)
|
||||||
|
return "Maximum allowed refresh interval is 86400 seconds";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rpki_check_retry_interval - check validity of retry interval value
|
||||||
|
* @seconds: suggested value
|
||||||
|
*
|
||||||
|
* Validate value and return NULL if check passed or error message if check failed.
|
||||||
|
*/
|
||||||
|
const char *
|
||||||
|
rpki_check_retry_interval(uint seconds)
|
||||||
|
{
|
||||||
|
if (seconds < 1)
|
||||||
|
return "Minimum allowed retry interval is 1 second";
|
||||||
|
if (seconds > 7200)
|
||||||
|
return "Maximum allowed retry interval is 7200 seconds";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rpki_check_expire_interval - check validity of expire interval value
|
||||||
|
* @seconds: suggested value
|
||||||
|
*
|
||||||
|
* Validate value and return NULL if check passed or error message if check failed.
|
||||||
|
*/
|
||||||
|
const char *
|
||||||
|
rpki_check_expire_interval(uint seconds)
|
||||||
|
{
|
||||||
|
if (seconds < 600)
|
||||||
|
return "Minimum allowed expire interval is 600 seconds";
|
||||||
|
if (seconds > 172800)
|
||||||
|
return "Maximum allowed expire interval is 172800 seconds";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct rpki_cache *
|
||||||
|
rpki_init_cache(struct rpki_proto *p, struct rpki_config *cf)
|
||||||
|
{
|
||||||
|
pool *pool = rp_new(p->p.pool, cf->hostname);
|
||||||
|
|
||||||
|
struct rpki_cache *cache = mb_allocz(pool, sizeof(struct rpki_cache));
|
||||||
|
|
||||||
|
cache->pool = pool;
|
||||||
|
cache->p = p;
|
||||||
|
|
||||||
|
proto_configure_channel(&p->p, &cache->roa4_channel, proto_cf_find_channel(p->p.cf, NET_ROA4));
|
||||||
|
proto_configure_channel(&p->p, &cache->roa6_channel, proto_cf_find_channel(p->p.cf, NET_ROA6));
|
||||||
|
|
||||||
|
cache->state = RPKI_CS_SHUTDOWN;
|
||||||
|
cache->request_session_id = 1;
|
||||||
|
cache->serial_number = 0;
|
||||||
|
cache->last_update = 0;
|
||||||
|
cache->version = RPKI_MAX_VERSION;
|
||||||
|
|
||||||
|
cache->refresh_interval = cf->refresh_interval;
|
||||||
|
cache->retry_interval = cf->retry_interval;
|
||||||
|
cache->expire_interval = cf->expire_interval;
|
||||||
|
cache->retry_timer = tm_new_set(pool, &rpki_retry_hook, cache, 0, 0);
|
||||||
|
cache->refresh_timer = tm_new_set(pool, &rpki_refresh_hook, cache, 0, 0);
|
||||||
|
cache->expire_timer = tm_new_set(pool, &rpki_expire_hook, cache, 0, 0);
|
||||||
|
|
||||||
|
cache->tr_sock = mb_allocz(pool, sizeof(struct rpki_tr_sock));
|
||||||
|
cache->tr_sock->cache = cache;
|
||||||
|
|
||||||
|
if (cf->ssh)
|
||||||
|
rpki_tr_ssh_init(cache->tr_sock);
|
||||||
|
else
|
||||||
|
rpki_tr_tcp_init(cache->tr_sock);
|
||||||
|
|
||||||
|
CACHE_TRACE(D_EVENTS, cache, "Created");
|
||||||
|
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rpki_free_cache(struct rpki_cache *cache)
|
||||||
|
{
|
||||||
|
struct rpki_proto *p = cache->p;
|
||||||
|
|
||||||
|
rpki_table_remove_all(cache);
|
||||||
|
|
||||||
|
CACHE_TRACE(D_EVENTS, p->cache, "Destroyed");
|
||||||
|
rfree(cache->pool);
|
||||||
|
p->cache = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
rpki_shutdown(struct proto *P)
|
||||||
|
{
|
||||||
|
struct rpki_proto *p = (void *) P;
|
||||||
|
p->cache = NULL;
|
||||||
|
|
||||||
|
/* protocol memory pool will be automatically freed */
|
||||||
|
return PS_DOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rpki_start_cache(struct rpki_cache *cache)
|
||||||
|
{
|
||||||
|
rpki_cache_change_state(cache, RPKI_CS_CONNECTING);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rpki_replace_cache(struct rpki_cache *cache, struct rpki_config *new, struct rpki_config *old)
|
||||||
|
{
|
||||||
|
struct rpki_proto *p = cache->p;
|
||||||
|
|
||||||
|
rpki_free_cache(cache);
|
||||||
|
|
||||||
|
p->cache = rpki_init_cache(p, new);
|
||||||
|
rpki_start_cache(p->cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rpki_fast_reconnect_cache(struct rpki_cache *cache, struct rpki_config *new, struct rpki_config *old)
|
||||||
|
{
|
||||||
|
if (cache->state == RPKI_CS_ESTABLISHED)
|
||||||
|
rpki_cache_change_state(cache, RPKI_CS_FAST_RECONNECT);
|
||||||
|
else
|
||||||
|
rpki_replace_cache(cache, old, new);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return 0 if need to restart
|
||||||
|
* Return 1 if reconfiguration finished successful
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
rpki_reconfigure_cache(struct rpki_proto *p, struct rpki_cache *cache, struct rpki_config *new, struct rpki_config *old)
|
||||||
|
{
|
||||||
|
u8 try_fast_reconnect = 0;
|
||||||
|
|
||||||
|
if (!proto_configure_channel(&p->p, &cache->roa4_channel, proto_cf_find_channel(p->p.cf, NET_ROA4)) ||
|
||||||
|
!proto_configure_channel(&p->p, &cache->roa6_channel, proto_cf_find_channel(p->p.cf, NET_ROA6)))
|
||||||
|
{
|
||||||
|
CACHE_TRACE(D_EVENTS, cache, "Channels changed");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(old->hostname, new->hostname) != 0)
|
||||||
|
{
|
||||||
|
CACHE_TRACE(D_EVENTS, cache, "Remote cache server address changed to %s", new->hostname);
|
||||||
|
goto hard_cache_replace;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (old->port != new->port)
|
||||||
|
{
|
||||||
|
CACHE_TRACE(D_EVENTS, cache, "Remote cache server port changed to %u", new->port);
|
||||||
|
goto hard_cache_replace;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!!old->ssh != !!new->ssh)
|
||||||
|
{
|
||||||
|
CACHE_TRACE(D_EVENTS, cache, "SSH encryption toggled");
|
||||||
|
goto hard_cache_replace;
|
||||||
|
}
|
||||||
|
else if (old->ssh && new->ssh)
|
||||||
|
{
|
||||||
|
if ((strcmp(old->ssh->bird_private_key, new->ssh->bird_private_key) != 0) ||
|
||||||
|
(strcmp(old->ssh->cache_public_key, new->ssh->cache_public_key) != 0) ||
|
||||||
|
(strcmp(old->ssh->user, new->ssh->user) != 0))
|
||||||
|
{
|
||||||
|
CACHE_TRACE(D_EVENTS, cache, "Settings of SSH transport encryption changed");
|
||||||
|
try_fast_reconnect = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cache->expire_interval != new->expire_interval)
|
||||||
|
{
|
||||||
|
cache->expire_interval = new->expire_interval;
|
||||||
|
CACHE_TRACE(D_EVENTS, cache, "Expire interval changed to %u seconds", cache->expire_interval);
|
||||||
|
try_fast_reconnect = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cache->refresh_interval != new->refresh_interval)
|
||||||
|
{
|
||||||
|
cache->refresh_interval = new->refresh_interval;
|
||||||
|
CACHE_TRACE(D_EVENTS, cache, "Refresh interval changed to %u seconds", cache->refresh_interval);
|
||||||
|
try_fast_reconnect = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cache->retry_interval != new->retry_interval)
|
||||||
|
{
|
||||||
|
cache->retry_interval = new->retry_interval;
|
||||||
|
CACHE_TRACE(D_EVENTS, cache, "Retry interval changed to %u seconds", cache->retry_interval);
|
||||||
|
try_fast_reconnect = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (try_fast_reconnect)
|
||||||
|
rpki_fast_reconnect_cache(cache, new, old);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
hard_cache_replace:
|
||||||
|
rpki_replace_cache(cache, new, old);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return 0 if need to restart
|
||||||
|
* Return 1 if reconfiguration finished successful
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
rpki_reconfigure_proto(struct rpki_proto *p, struct rpki_config *new_cf, struct rpki_config *old_cf)
|
||||||
|
{
|
||||||
|
u8 new = new_cf && new_cf->hostname;
|
||||||
|
u8 old = old_cf && old_cf->hostname;
|
||||||
|
struct rpki_cache *cache = p->cache;
|
||||||
|
|
||||||
|
if (new && !old)
|
||||||
|
{
|
||||||
|
p->cache = rpki_init_cache(p, new_cf);
|
||||||
|
rpki_start_cache(p->cache);
|
||||||
|
}
|
||||||
|
else if (!new && old && cache)
|
||||||
|
rpki_free_cache(cache);
|
||||||
|
else if (new && old && cache)
|
||||||
|
return rpki_reconfigure_cache(p, cache, new_cf, old_cf);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return 0 if need to restart
|
||||||
|
* Return 1 if reconfiguration finished successful
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
rpki_reconfigure(struct proto *P, struct proto_config *CF)
|
||||||
|
{
|
||||||
|
struct rpki_proto *p = (void *) P;
|
||||||
|
struct rpki_config *new = (void *) CF;
|
||||||
|
struct rpki_config *old = (void *) p->p.cf;
|
||||||
|
|
||||||
|
P->cf = CF;
|
||||||
|
if (rpki_reconfigure_proto(p, new, old))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
P->cf = (void *) old;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rpki_get_status(struct proto *P, byte *buf)
|
||||||
|
{
|
||||||
|
struct rpki_proto *p = (struct rpki_proto *) P;
|
||||||
|
|
||||||
|
if (P->proto_state == PS_DOWN)
|
||||||
|
{
|
||||||
|
*buf = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p->cache)
|
||||||
|
bsprintf(buf, "%s", rpki_cache_state_to_str(p->cache->state));
|
||||||
|
else
|
||||||
|
bsprintf(buf, "No cache server configured");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rpki_show_proto_info_timer(const char *name, uint num, timer *t)
|
||||||
|
{
|
||||||
|
if (t->expires)
|
||||||
|
cli_msg(-1006, " %-17s %us (remains %us)", name, num, tm_remains(t));
|
||||||
|
else
|
||||||
|
cli_msg(-1006, " %-17s %us", name, num);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rpki_show_proto_info(struct proto *P)
|
||||||
|
{
|
||||||
|
struct rpki_proto *p = (struct rpki_proto *) P;
|
||||||
|
struct rpki_config *cf = (void *) p->p.cf;
|
||||||
|
struct rpki_cache *cache = p->cache;
|
||||||
|
|
||||||
|
if (cache)
|
||||||
|
{
|
||||||
|
cli_msg(-1006, " Remote server: %s", rpki_get_cache_ident(cache));
|
||||||
|
cli_msg(-1006, " Status: %s", rpki_cache_state_to_str(cache->state));
|
||||||
|
cli_msg(-1006, " Transport: %s", cf->ssh ? "SSHv2" : "Unprotected over TCP");
|
||||||
|
cli_msg(-1006, " Protocol version: %u", cache->version);
|
||||||
|
|
||||||
|
if (cache->last_update)
|
||||||
|
cli_msg(-1006, " Last update: before %us", now - cache->last_update);
|
||||||
|
else
|
||||||
|
cli_msg(-1006, " Last update: ---");
|
||||||
|
|
||||||
|
rpki_show_proto_info_timer("Retry interval:", cache->retry_interval, cache->retry_timer);
|
||||||
|
rpki_show_proto_info_timer("Refresh interval:", cache->refresh_interval, cache->refresh_timer);
|
||||||
|
rpki_show_proto_info_timer("Expire interval:", cache->expire_interval, cache->expire_timer);
|
||||||
|
|
||||||
|
if (cache->roa4_channel)
|
||||||
|
channel_show_info(cache->roa4_channel);
|
||||||
|
else
|
||||||
|
cli_msg(-1006, " No roa4 channel");
|
||||||
|
|
||||||
|
if (cache->roa6_channel)
|
||||||
|
channel_show_info(cache->roa6_channel);
|
||||||
|
else
|
||||||
|
cli_msg(-1006, " No roa6 channel");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
rpki_start(struct proto *P)
|
||||||
|
{
|
||||||
|
struct rpki_proto *p = (void *) P;
|
||||||
|
struct rpki_config *cf = (void *) P->cf;
|
||||||
|
|
||||||
|
rpki_reconfigure_proto(p, cf, NULL);
|
||||||
|
|
||||||
|
return PS_UP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rpki_postconfig(struct proto_config *CF)
|
||||||
|
{
|
||||||
|
/* Define default channel */
|
||||||
|
if (EMPTY_LIST(CF->channels))
|
||||||
|
channel_config_new(NULL, CF->net_type, CF);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rpki_copy_config(struct proto_config *dest, struct proto_config *src)
|
||||||
|
{
|
||||||
|
struct rpki_config *d = (void *) dest;
|
||||||
|
struct rpki_config *s = (void *) src;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make a deep copy.
|
||||||
|
* The SSH configuration block can be reopened and extended.
|
||||||
|
*/
|
||||||
|
if (s->ssh)
|
||||||
|
{
|
||||||
|
d->ssh = cfg_alloc(sizeof(struct rpki_config_ssh));
|
||||||
|
memcpy(d->ssh, s->ssh, sizeof(struct rpki_config_ssh));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rpki_check_config(struct rpki_config *cf)
|
||||||
|
{
|
||||||
|
/* Do not check templates at all */
|
||||||
|
if (cf->c.class == SYM_TEMPLATE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (cf->hostname == NULL)
|
||||||
|
cf_error("Address or hostname of remote cache server must be set");
|
||||||
|
|
||||||
|
if (cf->port == 0)
|
||||||
|
{
|
||||||
|
if (cf->ssh != NULL)
|
||||||
|
cf->port = RPKI_SSH_PORT;
|
||||||
|
else
|
||||||
|
cf->port = RPKI_PORT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct protocol proto_rpki = {
|
||||||
|
.name = "RPKI",
|
||||||
|
.template = "rpki%d",
|
||||||
|
.preference = DEF_PREF_RPKI,
|
||||||
|
.proto_size = sizeof(struct rpki_proto),
|
||||||
|
.config_size = sizeof(struct rpki_config),
|
||||||
|
.init = rpki_init,
|
||||||
|
.start = rpki_start,
|
||||||
|
.postconfig = rpki_postconfig,
|
||||||
|
.channel_mask = (NB_ROA4 | NB_ROA6),
|
||||||
|
.show_proto_info = rpki_show_proto_info,
|
||||||
|
.shutdown = rpki_shutdown,
|
||||||
|
.copy_config = rpki_copy_config,
|
||||||
|
.reconfigure = rpki_reconfigure,
|
||||||
|
.get_status = rpki_get_status,
|
||||||
|
};
|
158
proto/rpki/rpki.h
Normal file
158
proto/rpki/rpki.h
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
/*
|
||||||
|
* BIRD -- The Resource Public Key Infrastructure (RPKI) to Router Protocol
|
||||||
|
*
|
||||||
|
* (c) 2015 CZ.NIC
|
||||||
|
*
|
||||||
|
* Using RTRlib: http://rpki.realmv6.org/
|
||||||
|
*
|
||||||
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _BIRD_RPKI_H_
|
||||||
|
#define _BIRD_RPKI_H_
|
||||||
|
|
||||||
|
#include "nest/bird.h"
|
||||||
|
#include "nest/route.h"
|
||||||
|
#include "nest/protocol.h"
|
||||||
|
#include "lib/socket.h"
|
||||||
|
#include "lib/ip.h"
|
||||||
|
|
||||||
|
#include "ssh_transport.h"
|
||||||
|
#include "tcp_transport.h"
|
||||||
|
#include "packets.h"
|
||||||
|
|
||||||
|
#define RPKI_PORT 323
|
||||||
|
#define RPKI_SSH_PORT 22
|
||||||
|
#define RPKI_DEFAULT_RETRY_INTERVAL 600
|
||||||
|
#define RPKI_DEFAULT_REFRESH_INTERVAL 3600
|
||||||
|
#define RPKI_DEFAULT_EXPIRE_INTERVAL 7200
|
||||||
|
|
||||||
|
#define RPKI_VERSION_0 0
|
||||||
|
#define RPKI_VERSION_1 1
|
||||||
|
#define RPKI_MIN_VERSION 0
|
||||||
|
#define RPKI_MAX_VERSION 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Used in parsing of configuration file
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct rpki_config_ssh {
|
||||||
|
const char *bird_private_key; /* Filepath to the BIRD server private key */
|
||||||
|
const char *cache_public_key; /* Filepath to the public key of cache server, can be file known_hosts */
|
||||||
|
const char *user; /* Username for SSH connection */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Cache server
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum rpki_cache_state {
|
||||||
|
RPKI_CS_CONNECTING, /* Socket is establishing the transport connection. */
|
||||||
|
RPKI_CS_ESTABLISHED, /* Connection is established, socket is waiting for a Serial Notify or expiration of the refresh_interval timer */
|
||||||
|
RPKI_CS_RESET, /* Resetting RTR connection. */
|
||||||
|
RPKI_CS_SYNC, /* Receiving validation records from the RTR server. */
|
||||||
|
RPKI_CS_FAST_RECONNECT, /* Reconnect without any waiting period */
|
||||||
|
RPKI_CS_ERROR_NO_DATA_AVAIL, /* No validation records are available on the RTR server. */
|
||||||
|
RPKI_CS_ERROR_NO_INCR_UPDATE_AVAIL, /* Server was unable to answer the last serial or reset query. */
|
||||||
|
RPKI_CS_ERROR_FATAL, /* Fatal protocol error occurred. */
|
||||||
|
RPKI_CS_ERROR_TRANSPORT, /* Error on the transport socket occurred. */
|
||||||
|
RPKI_CS_SHUTDOWN, /* RTR Socket is stopped. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* return values */
|
||||||
|
enum rpki_rtvals {
|
||||||
|
RPKI_SUCCESS = 0,
|
||||||
|
RPKI_ERROR = -1
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rpki_cache {
|
||||||
|
pool *pool; /* Pool containing cache objects */
|
||||||
|
struct rpki_proto *p;
|
||||||
|
|
||||||
|
struct channel *roa4_channel;
|
||||||
|
struct channel *roa6_channel;
|
||||||
|
u8 refresh_channels; /* For non-incremental updates using rt_refresh_begin(), rt_refresh_end() */
|
||||||
|
|
||||||
|
struct rpki_tr_sock *tr_sock; /* Transport specific socket */
|
||||||
|
enum rpki_cache_state state; /* RPKI_CS_* */
|
||||||
|
u32 session_id;
|
||||||
|
u8 request_session_id; /* 1 => have to request new session id; 0 => we have already session id */
|
||||||
|
u32 serial_number;
|
||||||
|
uint version; /* Protocol version */
|
||||||
|
bird_clock_t last_update; /* Last successful synchronization with cache server */
|
||||||
|
|
||||||
|
/* Intervals can be changed by remote cache server on the fly */
|
||||||
|
uint refresh_interval;
|
||||||
|
uint expire_interval;
|
||||||
|
uint retry_interval;
|
||||||
|
timer *retry_timer;
|
||||||
|
timer *refresh_timer;
|
||||||
|
timer *expire_timer;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Rest of RPKI
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct rpki_config {
|
||||||
|
struct proto_config c;
|
||||||
|
const char *hostname; /* Full domain name of remote cache server */
|
||||||
|
ip_addr ip; /* IP address of cache server or IPA_NONE */
|
||||||
|
u16 port; /* Port of cache server */
|
||||||
|
uint refresh_interval; /* Time interval (in seconds) for refreshing ROA from server */
|
||||||
|
uint expire_interval; /* Time interval (in seconds) */
|
||||||
|
uint retry_interval; /* Time interval (in seconds) for an unreachable server */
|
||||||
|
struct rpki_config_ssh *ssh; /* SSH configuration or NULL */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rpki_proto {
|
||||||
|
struct proto p;
|
||||||
|
struct rpki_cache *cache;
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *rpki_get_cache_ident(struct rpki_cache *cache);
|
||||||
|
|
||||||
|
void rpki_check_config(struct rpki_config *cf);
|
||||||
|
const char *rpki_check_refresh_interval(uint seconds);
|
||||||
|
const char *rpki_check_retry_interval(uint seconds);
|
||||||
|
const char *rpki_check_expire_interval(uint seconds);
|
||||||
|
|
||||||
|
void rpki_schedule_next_refresh(struct rpki_cache *cache);
|
||||||
|
void rpki_schedule_next_retry(struct rpki_cache *cache);
|
||||||
|
void rpki_schedule_next_expire_check(struct rpki_cache *cache);
|
||||||
|
|
||||||
|
void rpki_cache_change_state(struct rpki_cache *cache, const enum rpki_cache_state new_state);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Debug/log outputs
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define RPKI_LOG(log_level, rpki, msg, args...) \
|
||||||
|
do { \
|
||||||
|
log(log_level "%s: " msg, (rpki)->p.name , ## args); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#if defined(LOCAL_DEBUG) || defined(GLOBAL_DEBUG)
|
||||||
|
#define CACHE_DBG(cache,msg,args...) \
|
||||||
|
do { \
|
||||||
|
RPKI_LOG(L_DEBUG, (cache)->p, "%s: %s: " msg, rpki_get_cache_ident(cache), __func__, ## args); \
|
||||||
|
} while(0)
|
||||||
|
#else
|
||||||
|
#define CACHE_DBG(cache,msg,args...) do { } while(0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define RPKI_TRACE(level,rpki,msg,args...) \
|
||||||
|
do { \
|
||||||
|
if ((rpki)->p.debug & level) \
|
||||||
|
RPKI_LOG(L_TRACE, rpki, msg, ## args); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define CACHE_TRACE(level,cache,msg,args...) \
|
||||||
|
do { \
|
||||||
|
if ((cache)->p->p.debug & level) \
|
||||||
|
RPKI_LOG(L_TRACE, (cache)->p, "%s: " msg, rpki_get_cache_ident(cache), ## args); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define RPKI_WARN(p, msg, args...) RPKI_LOG(L_WARN, p, msg, ## args);
|
||||||
|
|
||||||
|
#endif /* _BIRD_RPKI_H_ */
|
100
proto/rpki/ssh_transport.c
Normal file
100
proto/rpki/ssh_transport.c
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* BIRD -- An implementation of the SSH protocol for the RPKI transport
|
||||||
|
*
|
||||||
|
* (c) 2015 CZ.NIC
|
||||||
|
*
|
||||||
|
* This file was a part of RTRlib: http://rpki.realmv6.org/
|
||||||
|
* This transport implementation uses libssh (http://www.libssh.org/)
|
||||||
|
*
|
||||||
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#include "rpki.h"
|
||||||
|
#include "ssh_transport.h"
|
||||||
|
#include "lib/libssh.h"
|
||||||
|
|
||||||
|
static int
|
||||||
|
rpki_tr_ssh_open(struct rpki_tr_sock *tr)
|
||||||
|
{
|
||||||
|
struct rpki_cache *cache = tr->cache;
|
||||||
|
struct rpki_config *cf = (void *) cache->p->p.cf;
|
||||||
|
sock *sk = tr->sk;
|
||||||
|
|
||||||
|
const char *err_msg;
|
||||||
|
if ((err_msg = load_libssh()) != NULL)
|
||||||
|
{
|
||||||
|
CACHE_TRACE(D_EVENTS, cache, "%s", err_msg);
|
||||||
|
return TR_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
sk->type = SK_SSH_ACTIVE;
|
||||||
|
sk->ssh = mb_allocz(sk->pool, sizeof(struct ssh_sock));
|
||||||
|
sk->ssh->username = cf->ssh->user;
|
||||||
|
sk->ssh->client_privkey_path = cf->ssh->bird_private_key;
|
||||||
|
sk->ssh->server_hostkey_path = cf->ssh->cache_public_key;
|
||||||
|
sk->ssh->subsystem = "rpki-rtr";
|
||||||
|
sk->ssh->state = SK_SSH_CONNECT;
|
||||||
|
|
||||||
|
if (sk_open(sk) != 0)
|
||||||
|
return TR_ERROR;
|
||||||
|
|
||||||
|
return TR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rpki_tr_ssh_close(struct rpki_tr_sock *tr)
|
||||||
|
{
|
||||||
|
struct rpki_tr_ssh *ssh = tr->data;
|
||||||
|
|
||||||
|
if (ssh && ssh->ident != NULL)
|
||||||
|
{
|
||||||
|
mb_free((char *) ssh->ident);
|
||||||
|
ssh->ident = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tr->sk is closed in tr_close() */
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
rpki_tr_ssh_ident(struct rpki_tr_sock *tr)
|
||||||
|
{
|
||||||
|
ASSERT(tr != NULL);
|
||||||
|
|
||||||
|
struct rpki_cache *cache = tr->cache;
|
||||||
|
struct rpki_config *cf = (void *) cache->p->p.cf;
|
||||||
|
struct rpki_tr_ssh *ssh = tr->data;
|
||||||
|
|
||||||
|
if (ssh->ident != NULL)
|
||||||
|
return ssh->ident;
|
||||||
|
|
||||||
|
const char *username = cf->ssh->user;
|
||||||
|
const char *host = cf->hostname;
|
||||||
|
u16 port = cf->port;
|
||||||
|
|
||||||
|
size_t len = strlen(username) + 1 + strlen(host) + 1 + 5 + 1; /* <user> + '@' + <host> + ':' + <port> + '\0' */
|
||||||
|
char *ident = mb_alloc(cache->pool, len);
|
||||||
|
bsnprintf(ident, len, "%s@%s:%u", username, host, port);
|
||||||
|
ssh->ident = ident;
|
||||||
|
|
||||||
|
return ssh->ident;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initializes the rpki_tr_sock struct for a SSH connection.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
rpki_tr_ssh_init(struct rpki_tr_sock *tr)
|
||||||
|
{
|
||||||
|
struct rpki_cache *cache = tr->cache;
|
||||||
|
|
||||||
|
tr->close_fp = &rpki_tr_ssh_close;
|
||||||
|
tr->open_fp = &rpki_tr_ssh_open;
|
||||||
|
tr->ident_fp = &rpki_tr_ssh_ident;
|
||||||
|
|
||||||
|
tr->data = mb_allocz(cache->pool, sizeof(struct rpki_tr_ssh));
|
||||||
|
}
|
24
proto/rpki/ssh_transport.h
Normal file
24
proto/rpki/ssh_transport.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* BIRD -- An implementation of the SSH protocol for the RPKI transport
|
||||||
|
*
|
||||||
|
* This transport implementation uses libssh (http://www.libssh.org/)
|
||||||
|
*
|
||||||
|
* (c) 2015 CZ.NIC
|
||||||
|
*
|
||||||
|
* This file was a part of RTRlib: http://rpki.realmv6.org/
|
||||||
|
*
|
||||||
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _BIRD_RPKI_SSH_TRANSPORT_H_
|
||||||
|
#define _BIRD_RPKI_SSH_TRANSPORT_H_
|
||||||
|
|
||||||
|
#include "transport.h"
|
||||||
|
|
||||||
|
struct rpki_tr_ssh {
|
||||||
|
const char *ident;
|
||||||
|
};
|
||||||
|
|
||||||
|
void rpki_tr_ssh_init(struct rpki_tr_sock *tr);
|
||||||
|
|
||||||
|
#endif
|
95
proto/rpki/tcp_transport.c
Normal file
95
proto/rpki/tcp_transport.c
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* BIRD -- An implementation of the TCP protocol for the RPKI protocol transport
|
||||||
|
*
|
||||||
|
* (c) 2015 CZ.NIC
|
||||||
|
*
|
||||||
|
* This file was a part of RTRlib: http://rpki.realmv6.org/
|
||||||
|
*
|
||||||
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "rpki.h"
|
||||||
|
#include "tcp_transport.h"
|
||||||
|
#include "sysdep/unix/unix.h"
|
||||||
|
|
||||||
|
static int
|
||||||
|
rpki_tr_tcp_open(struct rpki_tr_sock *tr)
|
||||||
|
{
|
||||||
|
sock *sk = tr->sk;
|
||||||
|
|
||||||
|
sk->type = SK_TCP_ACTIVE;
|
||||||
|
|
||||||
|
if (sk_open(sk) != 0)
|
||||||
|
return TR_ERROR;
|
||||||
|
|
||||||
|
return TR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rpki_tr_tcp_close(struct rpki_tr_sock *tr)
|
||||||
|
{
|
||||||
|
struct rpki_tr_tcp *tcp = tr->data;
|
||||||
|
|
||||||
|
if (tcp && tcp->ident != NULL)
|
||||||
|
{
|
||||||
|
mb_free((char *) tcp->ident);
|
||||||
|
tcp->ident = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tr->sk is closed in tr_close() */
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
rpki_tr_tcp_ident(struct rpki_tr_sock *tr)
|
||||||
|
{
|
||||||
|
ASSERT(tr != NULL);
|
||||||
|
|
||||||
|
struct rpki_cache *cache = tr->cache;
|
||||||
|
struct rpki_config *cf = (void *) cache->p->p.cf;
|
||||||
|
struct rpki_tr_tcp *tcp = tr->data;
|
||||||
|
|
||||||
|
if (tcp->ident != NULL)
|
||||||
|
return tcp->ident;
|
||||||
|
|
||||||
|
const char *host = cf->hostname;
|
||||||
|
ip_addr ip = cf->ip;
|
||||||
|
u16 port = cf->port;
|
||||||
|
|
||||||
|
size_t colon_and_port_len = 6; /* max ":65535" */
|
||||||
|
size_t ident_len;
|
||||||
|
if (host)
|
||||||
|
ident_len = strlen(host) + colon_and_port_len + 1;
|
||||||
|
else
|
||||||
|
ident_len = IPA_MAX_TEXT_LENGTH + colon_and_port_len + 1;
|
||||||
|
|
||||||
|
char *ident = mb_alloc(cache->pool, ident_len);
|
||||||
|
if (host)
|
||||||
|
bsnprintf(ident, ident_len, "%s:%u", host, port);
|
||||||
|
else
|
||||||
|
bsnprintf(ident, ident_len, "%I:%u", ip, port);
|
||||||
|
|
||||||
|
tcp->ident = ident;
|
||||||
|
return tcp->ident;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initializes the rpki_tr_sock struct for a TCP connection. */
|
||||||
|
void
|
||||||
|
rpki_tr_tcp_init(struct rpki_tr_sock *tr)
|
||||||
|
{
|
||||||
|
struct rpki_cache *cache = tr->cache;
|
||||||
|
|
||||||
|
tr->close_fp = &rpki_tr_tcp_close;
|
||||||
|
tr->open_fp = &rpki_tr_tcp_open;
|
||||||
|
tr->ident_fp = &rpki_tr_tcp_ident;
|
||||||
|
|
||||||
|
tr->data = mb_allocz(cache->pool, sizeof(struct rpki_tr_tcp));
|
||||||
|
}
|
22
proto/rpki/tcp_transport.h
Normal file
22
proto/rpki/tcp_transport.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* BIRD -- An implementation of the TCP protocol for the RPKI protocol transport
|
||||||
|
*
|
||||||
|
* (c) 2015 CZ.NIC
|
||||||
|
*
|
||||||
|
* This file was a part of RTRlib: http://rpki.realmv6.org/
|
||||||
|
*
|
||||||
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _BIRD_RPKI_TCP_TRANSPORT_H_
|
||||||
|
#define _BIRD_RPKI_TCP_TRANSPORT_H_
|
||||||
|
|
||||||
|
#include "transport.h"
|
||||||
|
|
||||||
|
struct rpki_tr_tcp {
|
||||||
|
const char *ident;
|
||||||
|
};
|
||||||
|
|
||||||
|
void rpki_tr_tcp_init(struct rpki_tr_sock *tr);
|
||||||
|
|
||||||
|
#endif
|
109
proto/rpki/transport.c
Normal file
109
proto/rpki/transport.c
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* BIRD -- The Resource Public Key Infrastructure (RPKI) to Router Protocol
|
||||||
|
*
|
||||||
|
* (c) 2015 CZ.NIC
|
||||||
|
*
|
||||||
|
* This file was a part of RTRlib: http://rpki.realmv6.org/
|
||||||
|
*
|
||||||
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
|
||||||
|
#include "rpki.h"
|
||||||
|
#include "transport.h"
|
||||||
|
#include "sysdep/unix/unix.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fulfill sock->af and sock->daddr if sock->daddr is empty and hostname is defined
|
||||||
|
* Return TR_SUCCESS or TR_ERROR
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
rpki_hostname_autoresolv(sock *sk)
|
||||||
|
{
|
||||||
|
if (ipa_zero(sk->daddr) && sk->host)
|
||||||
|
{
|
||||||
|
struct addrinfo *res;
|
||||||
|
struct addrinfo hints = {
|
||||||
|
.ai_family = AF_UNSPEC,
|
||||||
|
.ai_socktype = SOCK_STREAM,
|
||||||
|
.ai_flags = AI_ADDRCONFIG,
|
||||||
|
};
|
||||||
|
|
||||||
|
char port[6]; /* max is "65535" + '\0' */
|
||||||
|
bsnprintf(port, sizeof(port), "%u", sk->dport);
|
||||||
|
|
||||||
|
if (getaddrinfo(sk->host, port, &hints, &res) != 0)
|
||||||
|
{
|
||||||
|
CACHE_TRACE(D_EVENTS, (struct rpki_cache *) sk->data, "getaddrinfo error, %s", gai_strerror(errno));
|
||||||
|
return TR_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res->ai_family == AF_INET)
|
||||||
|
sk->fam = SK_FAM_IPV4;
|
||||||
|
else
|
||||||
|
sk->fam = SK_FAM_IPV6;
|
||||||
|
|
||||||
|
sockaddr sa = {
|
||||||
|
.sa = *res->ai_addr,
|
||||||
|
};
|
||||||
|
|
||||||
|
uint unused;
|
||||||
|
sockaddr_read(&sa, res->ai_family, &sk->daddr, NULL, &unused);
|
||||||
|
|
||||||
|
freeaddrinfo(res);
|
||||||
|
}
|
||||||
|
else if (ipa_zero(sk->daddr) && !sk->host)
|
||||||
|
return TR_ERROR;
|
||||||
|
else
|
||||||
|
sk->fam = ip6_is_v4mapped(sk->daddr) ? SK_FAM_IPV4 : SK_FAM_IPV6;
|
||||||
|
|
||||||
|
return TR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Establish the connection.
|
||||||
|
* Returns TR_SUCCESS or TR_ERROR
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
rpki_tr_open(struct rpki_tr_sock *tr)
|
||||||
|
{
|
||||||
|
struct rpki_cache *cache = tr->cache;
|
||||||
|
struct rpki_config *cf = (void *) cache->p->p.cf;
|
||||||
|
|
||||||
|
ASSERT(tr->sk == NULL);
|
||||||
|
tr->sk = sk_new(cache->pool);
|
||||||
|
sock *sk = tr->sk;
|
||||||
|
|
||||||
|
sk->tx_hook = rpki_connected_hook;
|
||||||
|
sk->err_hook = rpki_err_hook;
|
||||||
|
sk->data = cache;
|
||||||
|
sk->daddr = cf->ip;
|
||||||
|
sk->dport = cf->port;
|
||||||
|
sk->host = cf->hostname;
|
||||||
|
sk->rbsize = RPKI_RX_BUFFER_SIZE;
|
||||||
|
sk->tbsize = RPKI_TX_BUFFER_SIZE;
|
||||||
|
sk->tos = IP_PREC_INTERNET_CONTROL;
|
||||||
|
sk->type = -1; /* must be set in the specific transport layer in tr_open() */
|
||||||
|
rpki_hostname_autoresolv(sk);
|
||||||
|
|
||||||
|
return tr->open_fp(tr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close socket and prepare it for possible next open */
|
||||||
|
inline void
|
||||||
|
rpki_tr_close(struct rpki_tr_sock *tr)
|
||||||
|
{
|
||||||
|
tr->close_fp(tr);
|
||||||
|
|
||||||
|
rfree(tr->sk);
|
||||||
|
tr->sk = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns a \0 terminated string identifier for the socket endpoint, eg host:port */
|
||||||
|
inline const char *
|
||||||
|
rpki_tr_ident(struct rpki_tr_sock *tr)
|
||||||
|
{
|
||||||
|
return tr->ident_fp(tr);
|
||||||
|
}
|
51
proto/rpki/transport.h
Normal file
51
proto/rpki/transport.h
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* BIRD -- The Resource Public Key Infrastructure (RPKI) to Router Protocol
|
||||||
|
*
|
||||||
|
* (c) 2015 CZ.NIC
|
||||||
|
*
|
||||||
|
* This file was a part of RTRlib: http://rpki.realmv6.org/
|
||||||
|
*
|
||||||
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The RPKI transport sockets implement the communication channel
|
||||||
|
* (e.g., SSH, TCP, TCP-AO) between an RPKI server and client.
|
||||||
|
*
|
||||||
|
* Before using the transport socket, a tr_socket must be
|
||||||
|
* initialized based on a protocol-dependent init function (e.g.,
|
||||||
|
* rpki_tr_tcp_init()).
|
||||||
|
*
|
||||||
|
* The rpki_tr_* functions call the corresponding function pointers, which are
|
||||||
|
* passed in the rpki_tr_sock struct, and forward the remaining arguments.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _BIRD_RPKI_TRANSPORT_H_
|
||||||
|
#define _BIRD_RPKI_TRANSPORT_H_
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
/* The return values for tr_ functions */
|
||||||
|
enum tr_rtvals {
|
||||||
|
TR_SUCCESS = 0, /* Operation was successfull */
|
||||||
|
TR_ERROR = -1, /* Error occured */
|
||||||
|
TR_WOULDBLOCK = -2, /* No data is available on the socket */
|
||||||
|
TR_INTR = -3, /* Call was interrupted from a signal */
|
||||||
|
TR_CLOSED = -4 /* Connection closed */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* A transport socket datastructure */
|
||||||
|
struct rpki_tr_sock {
|
||||||
|
void *data; /* Technology specific data */
|
||||||
|
sock *sk; /* Standard BIRD socket */
|
||||||
|
struct rpki_cache *cache; /* Cache server */
|
||||||
|
int (*open_fp)(struct rpki_tr_sock *); /* Pointer to a function that establishes the socket connection */
|
||||||
|
void (*close_fp)(struct rpki_tr_sock *); /* Pointer to a function that close and frees all memory allocated with this socket */
|
||||||
|
const char *(*ident_fp)(struct rpki_tr_sock *); /* Pointer to a function that returns an identifier for the socket endpoint */
|
||||||
|
};
|
||||||
|
|
||||||
|
int rpki_tr_open(struct rpki_tr_sock *tr);
|
||||||
|
void rpki_tr_close(struct rpki_tr_sock *tr);
|
||||||
|
const char *rpki_tr_ident(struct rpki_tr_sock *tr);
|
||||||
|
|
||||||
|
#endif
|
@ -43,6 +43,7 @@
|
|||||||
#undef CONFIG_BGP
|
#undef CONFIG_BGP
|
||||||
#undef CONFIG_OSPF
|
#undef CONFIG_OSPF
|
||||||
#undef CONFIG_PIPE
|
#undef CONFIG_PIPE
|
||||||
|
#undef CONFIG_RPKI
|
||||||
|
|
||||||
/* We use multithreading */
|
/* We use multithreading */
|
||||||
#undef USE_PTHREADS
|
#undef USE_PTHREADS
|
||||||
|
359
sysdep/unix/io.c
359
sysdep/unix/io.c
@ -36,6 +36,7 @@
|
|||||||
#include "lib/socket.h"
|
#include "lib/socket.h"
|
||||||
#include "lib/event.h"
|
#include "lib/event.h"
|
||||||
#include "lib/string.h"
|
#include "lib/string.h"
|
||||||
|
#include "lib/libssh.h"
|
||||||
#include "nest/iface.h"
|
#include "nest/iface.h"
|
||||||
|
|
||||||
#include "sysdep/unix/unix.h"
|
#include "sysdep/unix/unix.h"
|
||||||
@ -1060,26 +1061,58 @@ sk_free_bufs(sock *s)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sk_ssh_free(sock *s)
|
||||||
|
{
|
||||||
|
struct ssh_sock *ssh = s->ssh;
|
||||||
|
|
||||||
|
if (s->ssh == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
s->ssh = NULL;
|
||||||
|
|
||||||
|
if (ssh->channel)
|
||||||
|
{
|
||||||
|
ssh_channel_close(ssh->channel);
|
||||||
|
ssh_channel_free(ssh->channel);
|
||||||
|
ssh->channel = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ssh->session)
|
||||||
|
{
|
||||||
|
ssh_disconnect(ssh->session);
|
||||||
|
ssh_free(ssh->session);
|
||||||
|
ssh->session = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sk_free(resource *r)
|
sk_free(resource *r)
|
||||||
{
|
{
|
||||||
sock *s = (sock *) r;
|
sock *s = (sock *) r;
|
||||||
|
|
||||||
sk_free_bufs(s);
|
sk_free_bufs(s);
|
||||||
if (s->fd >= 0)
|
|
||||||
|
if (s->type == SK_SSH || s->type == SK_SSH_ACTIVE)
|
||||||
|
sk_ssh_free(s);
|
||||||
|
|
||||||
|
if (s->fd < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* FIXME: we should call sk_stop() for SKF_THREAD sockets */
|
||||||
|
if (!(s->flags & SKF_THREAD))
|
||||||
{
|
{
|
||||||
close(s->fd);
|
|
||||||
|
|
||||||
/* FIXME: we should call sk_stop() for SKF_THREAD sockets */
|
|
||||||
if (s->flags & SKF_THREAD)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (s == current_sock)
|
if (s == current_sock)
|
||||||
current_sock = sk_next(s);
|
current_sock = sk_next(s);
|
||||||
if (s == stored_sock)
|
if (s == stored_sock)
|
||||||
stored_sock = sk_next(s);
|
stored_sock = sk_next(s);
|
||||||
rem_node(&s->n);
|
rem_node(&s->n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (s->type != SK_SSH && s->type != SK_SSH_ACTIVE)
|
||||||
|
close(s->fd);
|
||||||
|
|
||||||
|
s->fd = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -1181,6 +1214,9 @@ sk_setup(sock *s)
|
|||||||
int y = 1;
|
int y = 1;
|
||||||
int fd = s->fd;
|
int fd = s->fd;
|
||||||
|
|
||||||
|
if (s->type == SK_SSH_ACTIVE)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
|
if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
|
||||||
ERR("O_NONBLOCK");
|
ERR("O_NONBLOCK");
|
||||||
|
|
||||||
@ -1293,6 +1329,14 @@ sk_tcp_connected(sock *s)
|
|||||||
s->tx_hook(s);
|
s->tx_hook(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sk_ssh_connected(sock *s)
|
||||||
|
{
|
||||||
|
sk_alloc_bufs(s);
|
||||||
|
s->type = SK_SSH;
|
||||||
|
s->tx_hook(s);
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
sk_passive_connected(sock *s, int type)
|
sk_passive_connected(sock *s, int type)
|
||||||
{
|
{
|
||||||
@ -1345,6 +1389,194 @@ sk_passive_connected(sock *s, int type)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return SSH_OK or SSH_AGAIN or SSH_ERROR
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
sk_ssh_connect(sock *s)
|
||||||
|
{
|
||||||
|
s->fd = ssh_get_fd(s->ssh->session);
|
||||||
|
|
||||||
|
/* Big fall thru automata */
|
||||||
|
switch (s->ssh->state)
|
||||||
|
{
|
||||||
|
case SK_SSH_CONNECT:
|
||||||
|
{
|
||||||
|
switch (ssh_connect(s->ssh->session))
|
||||||
|
{
|
||||||
|
case SSH_AGAIN:
|
||||||
|
return SSH_AGAIN;
|
||||||
|
|
||||||
|
case SSH_OK:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case SK_SSH_SERVER_KNOWN:
|
||||||
|
{
|
||||||
|
s->ssh->state = SK_SSH_SERVER_KNOWN;
|
||||||
|
|
||||||
|
if (s->ssh->server_hostkey_path)
|
||||||
|
{
|
||||||
|
int server_identity_is_ok = 1;
|
||||||
|
|
||||||
|
/* Check server identity */
|
||||||
|
switch (ssh_is_server_known(s->ssh->session))
|
||||||
|
{
|
||||||
|
#define LOG_WARN_ABOUT_SSH_SERVER_VALIDATION(s,msg,args...) log(L_WARN "SSH Identity %s@%s:%u: " msg, (s)->ssh->username, (s)->host, (s)->dport, ## args);
|
||||||
|
case SSH_SERVER_KNOWN_OK:
|
||||||
|
/* The server is known and has not changed. */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SSH_SERVER_NOT_KNOWN:
|
||||||
|
LOG_WARN_ABOUT_SSH_SERVER_VALIDATION(s, "The server is unknown, its public key was not found in the known host file %s", s->ssh->server_hostkey_path);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SSH_SERVER_KNOWN_CHANGED:
|
||||||
|
LOG_WARN_ABOUT_SSH_SERVER_VALIDATION(s, "The server key has changed. Either you are under attack or the administrator changed the key.");
|
||||||
|
server_identity_is_ok = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SSH_SERVER_FILE_NOT_FOUND:
|
||||||
|
LOG_WARN_ABOUT_SSH_SERVER_VALIDATION(s, "The known host file %s does not exist", s->ssh->server_hostkey_path);
|
||||||
|
server_identity_is_ok = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SSH_SERVER_ERROR:
|
||||||
|
LOG_WARN_ABOUT_SSH_SERVER_VALIDATION(s, "Some error happened");
|
||||||
|
server_identity_is_ok = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SSH_SERVER_FOUND_OTHER:
|
||||||
|
LOG_WARN_ABOUT_SSH_SERVER_VALIDATION(s, "The server gave use a key of a type while we had an other type recorded. " \
|
||||||
|
"It is a possible attack.");
|
||||||
|
server_identity_is_ok = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!server_identity_is_ok)
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case SK_SSH_USERAUTH:
|
||||||
|
{
|
||||||
|
s->ssh->state = SK_SSH_USERAUTH;
|
||||||
|
switch (ssh_userauth_publickey_auto(s->ssh->session, NULL, NULL))
|
||||||
|
{
|
||||||
|
case SSH_AUTH_AGAIN:
|
||||||
|
return SSH_AGAIN;
|
||||||
|
|
||||||
|
case SSH_AUTH_SUCCESS:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case SK_SSH_CHANNEL:
|
||||||
|
{
|
||||||
|
s->ssh->state = SK_SSH_CHANNEL;
|
||||||
|
s->ssh->channel = ssh_channel_new(s->ssh->session);
|
||||||
|
if (s->ssh->channel == NULL)
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SK_SSH_SESSION:
|
||||||
|
{
|
||||||
|
s->ssh->state = SK_SSH_SESSION;
|
||||||
|
switch (ssh_channel_open_session(s->ssh->channel))
|
||||||
|
{
|
||||||
|
case SSH_AGAIN:
|
||||||
|
return SSH_AGAIN;
|
||||||
|
|
||||||
|
case SSH_OK:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case SK_SSH_SUBSYSTEM:
|
||||||
|
{
|
||||||
|
s->ssh->state = SK_SSH_SUBSYSTEM;
|
||||||
|
if (s->ssh->subsystem)
|
||||||
|
{
|
||||||
|
switch (ssh_channel_request_subsystem(s->ssh->channel, s->ssh->subsystem))
|
||||||
|
{
|
||||||
|
case SSH_AGAIN:
|
||||||
|
return SSH_AGAIN;
|
||||||
|
|
||||||
|
case SSH_OK:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case SK_SSH_ESTABLISHED:
|
||||||
|
s->ssh->state = SK_SSH_ESTABLISHED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SSH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return file descriptor number if success
|
||||||
|
* Return -1 if failed
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
sk_open_ssh(sock *s)
|
||||||
|
{
|
||||||
|
if (!s->ssh)
|
||||||
|
bug("sk_open() sock->ssh is not allocated");
|
||||||
|
|
||||||
|
ssh_session sess = ssh_new();
|
||||||
|
if (sess == NULL)
|
||||||
|
ERR2("Cannot create a ssh session");
|
||||||
|
s->ssh->session = sess;
|
||||||
|
|
||||||
|
const int verbosity = SSH_LOG_NOLOG;
|
||||||
|
ssh_options_set(sess, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
|
||||||
|
ssh_options_set(sess, SSH_OPTIONS_HOST, s->host);
|
||||||
|
ssh_options_set(sess, SSH_OPTIONS_PORT, &(s->dport));
|
||||||
|
ssh_options_set(sess, SSH_OPTIONS_USER, s->ssh->username);
|
||||||
|
|
||||||
|
if (s->ssh->server_hostkey_path)
|
||||||
|
ssh_options_set(sess, SSH_OPTIONS_KNOWNHOSTS, s->ssh->server_hostkey_path);
|
||||||
|
|
||||||
|
if (s->ssh->client_privkey_path)
|
||||||
|
ssh_options_set(sess, SSH_OPTIONS_IDENTITY, s->ssh->client_privkey_path);
|
||||||
|
|
||||||
|
ssh_set_blocking(sess, 0);
|
||||||
|
|
||||||
|
switch (sk_ssh_connect(s))
|
||||||
|
{
|
||||||
|
case SSH_AGAIN:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SSH_OK:
|
||||||
|
sk_ssh_connected(s);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SSH_ERROR:
|
||||||
|
ERR2(ssh_get_error(sess));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ssh_get_fd(sess);
|
||||||
|
|
||||||
|
err:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sk_open - open a socket
|
* sk_open - open a socket
|
||||||
* @s: socket
|
* @s: socket
|
||||||
@ -1376,6 +1608,11 @@ sk_open(sock *s)
|
|||||||
do_bind = bind_port || ipa_nonzero(bind_addr);
|
do_bind = bind_port || ipa_nonzero(bind_addr);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SK_SSH_ACTIVE:
|
||||||
|
s->ttx = ""; /* Force s->ttx != s->tpos */
|
||||||
|
fd = sk_open_ssh(s);
|
||||||
|
break;
|
||||||
|
|
||||||
case SK_UDP:
|
case SK_UDP:
|
||||||
fd = socket(fam_to_af[s->fam], SOCK_DGRAM, IPPROTO_UDP);
|
fd = socket(fam_to_af[s->fam], SOCK_DGRAM, IPPROTO_UDP);
|
||||||
bind_port = s->sport;
|
bind_port = s->sport;
|
||||||
@ -1455,6 +1692,7 @@ sk_open(sock *s)
|
|||||||
ERR2("listen");
|
ERR2("listen");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SK_SSH_ACTIVE:
|
||||||
case SK_MAGIC:
|
case SK_MAGIC:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1464,6 +1702,7 @@ sk_open(sock *s)
|
|||||||
|
|
||||||
if (!(s->flags & SKF_THREAD))
|
if (!(s->flags & SKF_THREAD))
|
||||||
sk_insert(s);
|
sk_insert(s);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
@ -1647,6 +1886,26 @@ sk_maybe_write(sock *s)
|
|||||||
reset_tx_buffer(s);
|
reset_tx_buffer(s);
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
case SK_SSH:
|
||||||
|
while (s->ttx != s->tpos)
|
||||||
|
{
|
||||||
|
e = ssh_channel_write(s->ssh->channel, s->ttx, s->tpos - s->ttx);
|
||||||
|
|
||||||
|
if (e < 0)
|
||||||
|
{
|
||||||
|
s->err = ssh_get_error(s->ssh->session);
|
||||||
|
s->err_hook(s, ssh_get_error_code(s->ssh->session));
|
||||||
|
|
||||||
|
reset_tx_buffer(s);
|
||||||
|
/* EPIPE is just a connection close notification during TX */
|
||||||
|
s->err_hook(s, (errno != EPIPE) ? errno : 0);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
s->ttx += e;
|
||||||
|
}
|
||||||
|
reset_tx_buffer(s);
|
||||||
|
return 1;
|
||||||
|
|
||||||
case SK_UDP:
|
case SK_UDP:
|
||||||
case SK_IP:
|
case SK_IP:
|
||||||
{
|
{
|
||||||
@ -1671,6 +1930,7 @@ sk_maybe_write(sock *s)
|
|||||||
reset_tx_buffer(s);
|
reset_tx_buffer(s);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
bug("sk_maybe_write: unknown socket type %d", s->type);
|
bug("sk_maybe_write: unknown socket type %d", s->type);
|
||||||
}
|
}
|
||||||
@ -1750,6 +2010,62 @@ sk_send_full(sock *s, unsigned len, struct iface *ifa,
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
static void
|
||||||
|
call_rx_hook(sock *s, int size)
|
||||||
|
{
|
||||||
|
if (s->rx_hook(s, size))
|
||||||
|
{
|
||||||
|
/* We need to be careful since the socket could have been deleted by the hook */
|
||||||
|
if (current_sock == s)
|
||||||
|
s->rpos = s->rbuf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
sk_read_ssh(sock *s)
|
||||||
|
{
|
||||||
|
ssh_channel rchans[2] = { s->ssh->channel, NULL };
|
||||||
|
struct timeval timev = { 1, 0 };
|
||||||
|
|
||||||
|
if (ssh_channel_select(rchans, NULL, NULL, &timev) == SSH_EINTR)
|
||||||
|
return 1; /* Try again */
|
||||||
|
|
||||||
|
if (ssh_channel_is_eof(s->ssh->channel) != 0)
|
||||||
|
{
|
||||||
|
/* The remote side is closing the connection */
|
||||||
|
s->err_hook(s, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rchans[0] == NULL)
|
||||||
|
return 0; /* No data is available on the socket */
|
||||||
|
|
||||||
|
const uint used_bytes = s->rpos - s->rbuf;
|
||||||
|
const int read_bytes = ssh_channel_read_nonblocking(s->ssh->channel, s->rpos, s->rbsize - used_bytes, 0);
|
||||||
|
if (read_bytes > 0)
|
||||||
|
{
|
||||||
|
/* Received data */
|
||||||
|
s->rpos += read_bytes;
|
||||||
|
call_rx_hook(s, used_bytes + read_bytes);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else if (read_bytes == 0)
|
||||||
|
{
|
||||||
|
if (ssh_channel_is_eof(s->ssh->channel) != 0)
|
||||||
|
{
|
||||||
|
/* The remote side is closing the connection */
|
||||||
|
s->err_hook(s, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s->err = ssh_get_error(s->ssh->session);
|
||||||
|
s->err_hook(s, ssh_get_error_code(s->ssh->session));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0; /* No data is available on the socket */
|
||||||
|
}
|
||||||
|
|
||||||
/* sk_read() and sk_write() are called from BFD's event loop */
|
/* sk_read() and sk_write() are called from BFD's event loop */
|
||||||
|
|
||||||
int
|
int
|
||||||
@ -1783,17 +2099,15 @@ sk_read(sock *s, int revents)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
s->rpos += c;
|
s->rpos += c;
|
||||||
if (s->rx_hook(s, s->rpos - s->rbuf))
|
call_rx_hook(s, s->rpos - s->rbuf);
|
||||||
{
|
|
||||||
/* We need to be careful since the socket could have been deleted by the hook */
|
|
||||||
if (current_sock == s)
|
|
||||||
s->rpos = s->rbuf;
|
|
||||||
}
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case SK_SSH:
|
||||||
|
return sk_read_ssh(s);
|
||||||
|
|
||||||
case SK_MAGIC:
|
case SK_MAGIC:
|
||||||
return s->rx_hook(s, 0);
|
return s->rx_hook(s, 0);
|
||||||
|
|
||||||
@ -1832,6 +2146,25 @@ sk_write(sock *s)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case SK_SSH_ACTIVE:
|
||||||
|
{
|
||||||
|
switch (sk_ssh_connect(s))
|
||||||
|
{
|
||||||
|
case SSH_OK:
|
||||||
|
sk_ssh_connected(s);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SSH_AGAIN:
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
case SSH_ERROR:
|
||||||
|
s->err = ssh_get_error(s->ssh->session);
|
||||||
|
s->err_hook(s, ssh_get_error_code(s->ssh->session));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (s->ttx != s->tpos && sk_maybe_write(s) > 0)
|
if (s->ttx != s->tpos && sk_maybe_write(s) > 0)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user