Merge branch 'int-new-rpki-squashed' (early part) into int-new
This commit is contained in:
commit
ad88b94bca
31 changed files with 3475 additions and 66 deletions
|
@ -10,6 +10,7 @@ CPPFLAGS=-I$(objdir) -I$(srcdir) @CPPFLAGS@
|
|||
CFLAGS=$(CPPFLAGS) @CFLAGS@
|
||||
LDFLAGS=@LDFLAGS@
|
||||
LIBS=@LIBS@
|
||||
DAEMON_LIBS=@DAEMON_LIBS@
|
||||
CLIENT_LIBS=@CLIENT_LIBS@
|
||||
CC=@CC@
|
||||
M4=@M4@
|
||||
|
@ -58,6 +59,8 @@ all: daemon cli
|
|||
daemon: $(daemon)
|
||||
cli: $(client)
|
||||
|
||||
$(daemon): LIBS += $(DAEMON_LIBS)
|
||||
|
||||
# Include directories
|
||||
dirs := client conf doc filter lib nest test $(addprefix proto/,$(protocols)) @sysdep_dirs@
|
||||
|
||||
|
|
23
configure.in
23
configure.in
|
@ -10,6 +10,7 @@ AC_ARG_ENABLE(debug, [ --enable-debug enable internal debugging routin
|
|||
AC_ARG_ENABLE(memcheck, [ --enable-memcheck check memory allocations when debugging (default: enabled)],,enable_memcheck=yes)
|
||||
AC_ARG_ENABLE(client, [ --enable-client enable building of BIRD client (default: enabled)],,enable_client=yes)
|
||||
AC_ARG_ENABLE(pthreads, [ --enable-pthreads enable POSIX threads support (default: detect)],,enable_pthreads=try)
|
||||
AC_ARG_ENABLE(libssh, [ --enable-libssh enable LibSSH support together with RPKI protocol (default: detect)],,enable_libssh=try)
|
||||
AC_ARG_WITH(sysconfig, [ --with-sysconfig=FILE use specified BIRD system configuration file])
|
||||
AC_ARG_WITH(protocols, [ --with-protocols=LIST include specified routing protocols (default: all)],,[with_protocols="all"])
|
||||
AC_ARG_WITH(sysinclude, [ --with-sysinclude=PATH search for system includes on specified place])
|
||||
|
@ -86,6 +87,21 @@ if test "$enable_pthreads" != no ; then
|
|||
fi
|
||||
fi
|
||||
|
||||
if test "$enable_libssh" != no ; then
|
||||
AC_CHECK_LIB(ssh, ssh_connect)
|
||||
if test $ac_cv_lib_ssh_ssh_connect = yes ; then
|
||||
proto_rpki=rpki
|
||||
enable_libssh=yes
|
||||
AC_DEFINE(HAVE_LIBSSH)
|
||||
else
|
||||
if test "$enable_libssh" = yes ; then
|
||||
AC_MSG_ERROR([LibSSH not available.])
|
||||
else
|
||||
enable_libssh=no
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$bird_cflags_default" = yes ; then
|
||||
BIRD_CHECK_GCC_OPTION(bird_cv_c_option_wno_pointer_sign, -Wno-pointer-sign, -Wall)
|
||||
BIRD_CHECK_GCC_OPTION(bird_cv_c_option_wno_missing_init, -Wno-missing-field-initializers, -Wall -Wextra)
|
||||
|
@ -169,8 +185,8 @@ fi
|
|||
|
||||
AC_SUBST(iproutedir)
|
||||
|
||||
# all_protocols="$proto_bfd babel bgp ospf pipe radv rip static"
|
||||
all_protocols="$proto_bfd bgp ospf pipe radv rip static"
|
||||
# all_protocols="$proto_bfd babel bgp ospf pipe radv rip $proto_rpki static"
|
||||
all_protocols="$proto_bfd bgp ospf pipe radv rip $proto_rpki static "
|
||||
|
||||
all_protocols=`echo $all_protocols | sed 's/ /,/g'`
|
||||
|
||||
|
@ -231,6 +247,9 @@ if test "$enable_debug" = yes ; then
|
|||
fi
|
||||
fi
|
||||
|
||||
DAEMON_LIBS=
|
||||
AC_SUBST(DAEMON_LIBS)
|
||||
|
||||
CLIENT=birdcl
|
||||
CLIENT_LIBS=
|
||||
if test "$enable_client" = yes ; then
|
||||
|
|
182
doc/bird.sgml
182
doc/bird.sgml
|
@ -3788,8 +3788,8 @@ protocol rip [<name>] {
|
|||
<p>RIP defines two route attributes:
|
||||
|
||||
<descrip>
|
||||
<tag><label id="rta-rip-metric">int rip_metric/</tag>
|
||||
RIP metric of the route (ranging from 0 to <cf/infinity/). When routes
|
||||
<tag>int <cf/rip_metric/</tag>
|
||||
RIP metric of the route (ranging from 0 to <cf/infinity/). When routes
|
||||
from different RIP instances are available and all of them have the same
|
||||
preference, BIRD prefers the route with lowest <cf/rip_metric/. When a
|
||||
non-RIP route is exported to RIP, the default metric is 1.
|
||||
|
@ -3819,6 +3819,184 @@ protocol rip {
|
|||
}
|
||||
</code>
|
||||
|
||||
<sect>RPKI
|
||||
|
||||
<sect1>Introduction
|
||||
|
||||
<p>The Resource Public Key Infrastructure (RPKI) is mechanism for origin
|
||||
validation of BGP routes (RFC 6480). BIRD supports only so-called RPKI-based
|
||||
origin validation. There is implemented RPKI to Router (RPKI-RTR) protocol (RFC
|
||||
6810). It uses some of the RPKI data to allow a router to verify 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 today, e.g. the famous
|
||||
Pakastani accidental announcement of YouTube's address space.
|
||||
|
||||
<p>The RPKI-RTR protocol receives and maintains a set of ROAs from a cache
|
||||
server (also called validator). You can validate routes (RFC 6483) using
|
||||
function <cf/roa_check()/ in filter and set it as import filter at the BGP
|
||||
protocol. BIRD should re-validate all of affected routes after RPKI update by
|
||||
RFC 6811, but we don't support it yet! You can use a BIRD's client command
|
||||
<cf>reload in <m/bgp_protocol_name/</cf> for manual call of revalidation of all
|
||||
routes.
|
||||
|
||||
<sect1>Supported transports
|
||||
<itemize>
|
||||
<item>Unprotected transport over TCP uses a port 323. The cache server
|
||||
and BIRD router should be on the same trusted and controlled network
|
||||
for security reasons.
|
||||
<item>SSHv2 encrypted transport connection uses the normal SSH port
|
||||
22.
|
||||
</itemize>
|
||||
|
||||
<sect1>Configuration
|
||||
|
||||
<p>We currently support just one cache server per protocol. However you can
|
||||
define more RPKI protocols generally.
|
||||
|
||||
<code>
|
||||
protocol rpki [<name>] {
|
||||
roa4 { table <tab>; };
|
||||
roa6 { table <tab>; };
|
||||
remote <ip> | "<domain>" [port <num>];
|
||||
port <num>;
|
||||
refresh [keep] <num>;
|
||||
retry [keep] <num>;
|
||||
expire [keep] <num>;
|
||||
transport tcp;
|
||||
transport ssh {
|
||||
bird private key "</path/to/id_rsa>";
|
||||
remote public key "</path/to/known_host>";
|
||||
user "<name>";
|
||||
};
|
||||
}
|
||||
</code>
|
||||
|
||||
<p>Alse note that you have to specify ROA table into which will be imported
|
||||
routes from a cache server. If you want to import only IPv4 prefixes you have
|
||||
to specify only roa4 table. Similarly with IPv6 prefixes only. If you want to
|
||||
fetch both IPv4 and even IPv6 ROAs you have to specify both types of ROA
|
||||
tables.
|
||||
|
||||
<sect2>RPKI protocol options
|
||||
|
||||
<descrip>
|
||||
<tag>remote <m/ip/ | "<m/hostname/" [port <m/num/]</tag> Specifies
|
||||
a destination address of the cache server. Can be specified by an IP
|
||||
address or by full domain name string. Only one cache can be specified
|
||||
per protocol. This option is required.
|
||||
|
||||
<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>refresh [keep] <m/num/</tag> Time period in seconds. Tells how
|
||||
long to wait before next attempting to poll the cache using a Serial
|
||||
Query or a Reset Query packet. Must be lower than 86400 seconds (one
|
||||
day). Too low value can caused a false positive detection of
|
||||
network connection problems. A keyword <cf/keep/ suppresses updating
|
||||
this value by a cache server.
|
||||
Default: 3600 seconds
|
||||
|
||||
<tag>retry [keep] <m/num/</tag> Time period in seconds between a failed
|
||||
Serial/Reset Query and a next attempt. Maximum allowed value is 7200
|
||||
seconds (two hours). Too low value can caused a false positive
|
||||
detection of network connection problems. A keyword <cf/keep/
|
||||
suppresses updating this value by a cache server.
|
||||
Default: 600 seconds
|
||||
|
||||
<tag>expire [keep] <m/num/</tag> Time period in seconds. Received
|
||||
records are deleted if the client was unable to successfully refresh
|
||||
data for this time period. Must be in range from 600 seconds (ten
|
||||
minutes) to 172800 seconds (two days). A keyword <cf/keep/
|
||||
suppresses updating this value by a cache server.
|
||||
Default: 7200 seconds
|
||||
|
||||
<tag>transport tcp</tag> Unprotected transport over TCP. It's a default
|
||||
transport. Should be used only on secure private networks.
|
||||
Default: tcp
|
||||
|
||||
<tag>transport ssh { <m/SSH transport options.../ }</tag> It enables a
|
||||
SSHv2 transport encryption. Cannot be combined with a TCP transport.
|
||||
Default: off
|
||||
</descrip>
|
||||
|
||||
<sect3>SSH transport options
|
||||
<descrip>
|
||||
<tag>bird private key "<m>/path/to/id_rsa</m>"</tag>
|
||||
A path to the BIRD's private SSH key for authentication.
|
||||
It can be a <cf><m>id_rsa</m></cf> file.
|
||||
|
||||
<tag>remote public key "<m>/path/to/known_host</m>"</tag>
|
||||
A path to the cache's public SSH key for verification identity
|
||||
of the cache server. It could be a path to <cf><m>known_host</m></cf> file.
|
||||
|
||||
<tag>user "<m/name/"</tag>
|
||||
A SSH user name for authentication. This option is a required.
|
||||
</descrip>
|
||||
|
||||
<sect1>Examples
|
||||
<sect2>BGP origin validation
|
||||
<p>Policy: Don't import <cf/ROA_INVALID/ routes.
|
||||
<code>
|
||||
roa4 table r4;
|
||||
roa6 table r6;
|
||||
|
||||
protocol rpki {
|
||||
debug all;
|
||||
|
||||
roa4 { table r4; };
|
||||
roa6 { table r6; };
|
||||
|
||||
# Please, do not use rpki-validator.realmv6.org in production
|
||||
remote "rpki-validator.realmv6.org" port 8282;
|
||||
|
||||
retry keep 5;
|
||||
refresh keep 30;
|
||||
expire 600;
|
||||
}
|
||||
|
||||
filter peer_in {
|
||||
if (roa_check(r4, net, bgp_path.last) = ROA_INVALID ||
|
||||
roa_check(r6, net, bgp_path.last) = ROA_INVALID) then
|
||||
{
|
||||
print "Ignore invalid ROA ", net, " for ASN ", bgp_path.last;
|
||||
reject;
|
||||
}
|
||||
accept;
|
||||
}
|
||||
|
||||
protocol bgp {
|
||||
debug all;
|
||||
local as 65000;
|
||||
neighbor 192.168.2.1 as 65001;
|
||||
import filter peer_in;
|
||||
}
|
||||
</code>
|
||||
|
||||
<sect2>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;
|
||||
transport ssh {
|
||||
bird private key "/home/birdgeek/.ssh/id_rsa";
|
||||
remote public key "/home/birdgeek/.ssh/known_hosts";
|
||||
user "birdgeek";
|
||||
};
|
||||
|
||||
# Default interval values
|
||||
}
|
||||
</code>
|
||||
|
||||
|
||||
|
||||
<sect>Static
|
||||
<label id="static">
|
||||
|
|
|
@ -399,8 +399,8 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
|
|||
TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
|
||||
FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, CAST, DEST, IFNAME, IFINDEX,
|
||||
PREFERENCE,
|
||||
ROA_CHECK,
|
||||
LEN,
|
||||
ROA_CHECK, ASN,
|
||||
LEN, MAXLEN,
|
||||
DEFINED,
|
||||
ADD, DELETE, CONTAINS, RESET,
|
||||
PREPEND, FIRST, LAST, LAST_NONAGGREGATED, MATCH,
|
||||
|
@ -891,6 +891,8 @@ term:
|
|||
|
||||
| term '.' IP { $$ = f_new_inst(); $$->code = P('c','p'); $$->a1.p = $1; $$->aux = T_IP; }
|
||||
| term '.' LEN { $$ = f_new_inst(); $$->code = 'L'; $$->a1.p = $1; }
|
||||
| term '.' MAXLEN { $$ = f_new_inst(); $$->code = P('R','m'); $$->a1.p = $1; }
|
||||
| term '.' ASN { $$ = f_new_inst(); $$->code = P('R','a'); $$->a1.p = $1; }
|
||||
| term '.' MASK '(' term ')' { $$ = f_new_inst(); $$->code = P('i','M'); $$->a1.p = $1; $$->a2.p = $5; }
|
||||
| term '.' FIRST { $$ = f_new_inst(); $$->code = P('a','f'); $$->a1.p = $1; }
|
||||
| term '.' LAST { $$ = f_new_inst(); $$->code = P('a','l'); $$->a1.p = $1; }
|
||||
|
|
|
@ -1183,6 +1183,26 @@ interpret(struct f_inst *what)
|
|||
default: runtime( "Prefix, path, clist or eclist expected" );
|
||||
}
|
||||
break;
|
||||
case P('R','m'): /* Get ROA max prefix length */
|
||||
ONEARG;
|
||||
if (v1.type != T_NET || !net_is_roa(v1.val.net))
|
||||
runtime( "ROA expected" );
|
||||
|
||||
res.type = T_INT;
|
||||
res.val.i = (v1.val.net->type == NET_ROA4) ?
|
||||
((net_addr_roa4 *) v1.val.net)->max_pxlen :
|
||||
((net_addr_roa6 *) v1.val.net)->max_pxlen;
|
||||
break;
|
||||
case P('R','a'): /* Get ROA ASN */
|
||||
ONEARG;
|
||||
if (v1.type != T_NET || !net_is_roa(v1.val.net))
|
||||
runtime( "ROA expected" );
|
||||
|
||||
res.type = T_INT;
|
||||
res.val.i = (v1.val.net->type == NET_ROA4) ?
|
||||
((net_addr_roa4 *) v1.val.net)->asn :
|
||||
((net_addr_roa6 *) v1.val.net)->asn;
|
||||
break;
|
||||
case P('c','p'): /* Convert prefix to ... */
|
||||
ONEARG;
|
||||
if (v1.type != T_NET)
|
||||
|
@ -1476,12 +1496,15 @@ interpret(struct f_inst *what)
|
|||
if (!table)
|
||||
runtime("Missing ROA table");
|
||||
|
||||
/* Table type is either NET_ROA4 or NET_ROA6, checked in parser */
|
||||
if (v1.val.net->type != ((table->addr_type == NET_ROA4) ? NET_IP4 : NET_IP6))
|
||||
runtime("Incompatible net type");
|
||||
if (table->addr_type != NET_ROA4 && table->addr_type != NET_ROA6)
|
||||
runtime("Table type must be either ROA4 or ROA6");
|
||||
|
||||
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;
|
||||
|
||||
|
|
|
@ -1139,30 +1139,80 @@ int j;
|
|||
accept "ok I take that";
|
||||
}
|
||||
|
||||
/*
|
||||
roa table rl
|
||||
roa4 table r4;
|
||||
roa6 table r6;
|
||||
|
||||
protocol static
|
||||
{
|
||||
roa 10.110.0.0/16 max 16 as 1000;
|
||||
roa 10.120.0.0/16 max 24 as 1000;
|
||||
roa 10.130.0.0/16 max 24 as 2000;
|
||||
roa 10.130.128.0/18 max 24 as 3000;
|
||||
roa4 { table r4; };
|
||||
route 10.110.0.0/16 max 16 as 1000 blackhole;
|
||||
route 10.120.0.0/16 max 24 as 1000 blackhole ;
|
||||
route 10.130.0.0/16 max 24 as 2000 blackhole;
|
||||
route 10.130.128.0/18 max 24 as 3000 blackhole;
|
||||
}
|
||||
|
||||
function test_roa()
|
||||
protocol static
|
||||
{
|
||||
roa6 { table r6; };
|
||||
route 2001:0db8:85a3:8a2e::/64 max 96 as 1000 blackhole;
|
||||
}
|
||||
|
||||
function test_roa_check()
|
||||
{
|
||||
# cannot be tested in __startup(), sorry
|
||||
print "Testing ROA";
|
||||
print "Should be true: ", roa_check(rl, 10.10.0.0/16, 1000) = ROA_UNKNOWN,
|
||||
" ", roa_check(rl, 10.0.0.0/8, 1000) = ROA_UNKNOWN,
|
||||
" ", roa_check(rl, 10.110.0.0/16, 1000) = ROA_VALID,
|
||||
" ", roa_check(rl, 10.110.0.0/16, 2000) = ROA_INVALID,
|
||||
" ", roa_check(rl, 10.110.32.0/20, 1000) = ROA_INVALID,
|
||||
" ", roa_check(rl, 10.120.32.0/20, 1000) = ROA_VALID;
|
||||
print "Should be true: ", roa_check(rl, 10.120.32.0/20, 2000) = ROA_INVALID,
|
||||
" ", roa_check(rl, 10.120.32.32/28, 1000) = ROA_INVALID,
|
||||
" ", roa_check(rl, 10.130.130.0/24, 1000) = ROA_INVALID,
|
||||
" ", roa_check(rl, 10.130.130.0/24, 2000) = ROA_VALID,
|
||||
" ", roa_check(rl, 10.130.30.0/24, 3000) = ROA_INVALID,
|
||||
" ", roa_check(rl, 10.130.130.0/24, 3000) = ROA_VALID;
|
||||
print "Should be true: ", roa_check(r4, 10.10.0.0/16, 1000) = ROA_UNKNOWN,
|
||||
" ", roa_check(r4, 10.0.0.0/8, 1000) = ROA_UNKNOWN,
|
||||
" ", roa_check(r4, 10.110.0.0/16, 1000) = ROA_VALID,
|
||||
" ", roa_check(r4, 10.110.0.0/16, 2000) = ROA_INVALID,
|
||||
" ", roa_check(r4, 10.110.32.0/20, 1000) = ROA_INVALID,
|
||||
" ", roa_check(r4, 10.120.32.0/20, 1000) = ROA_VALID;
|
||||
print "Should be true: ", roa_check(r4, 10.120.32.0/20, 2000) = ROA_INVALID,
|
||||
" ", roa_check(r4, 10.120.32.32/28, 1000) = ROA_INVALID,
|
||||
" ", roa_check(r4, 10.130.130.0/24, 1000) = ROA_INVALID,
|
||||
" ", roa_check(r4, 10.130.130.0/24, 2000) = ROA_VALID,
|
||||
" ", roa_check(r4, 10.130.30.0/24, 3000) = ROA_INVALID,
|
||||
" ", roa_check(r4, 10.130.130.0/24, 3000) = ROA_VALID;
|
||||
print "Should be true: ", roa_check(r6, 2001:0db8:85a3:8a2e:1234::/80, 1000) = ROA_VALID,
|
||||
" ", roa_check(r6, 2001:0db8:85a3:8a2e:1234::/97, 1000) = ROA_INVALID,
|
||||
" ", roa_check(r6, 2001:0db8:85a3:8a2e::/64, 1000) = ROA_VALID,
|
||||
" ", roa_check(r6, 2001:0db8:85a3::/48, 1000) = ROA_UNKNOWN;
|
||||
|
||||
print "Should be true: ", roa_check(r4, 10.10.0.0/16, 1000) = ROA_UNKNOWN,
|
||||
" ", roa_check(r4, 10.0.0.0/8, 1000) = ROA_UNKNOWN,
|
||||
" ", roa_check(r4, 10.110.0.0/16, 1000) = ROA_VALID,
|
||||
" ", roa_check(r4, 10.110.0.0/16, 2000) = ROA_INVALID,
|
||||
" ", roa_check(r4, 10.110.32.0/20, 1000) = ROA_INVALID,
|
||||
" ", roa_check(r4, 10.120.32.0/20, 1000) = ROA_VALID;
|
||||
|
||||
print "Should be true: ", roa_check(r6, 2001:0db8:85a3:8a2e:1234::/80, 1000) = ROA_VALID,
|
||||
" ", roa_check(r6, 2001:0db8:85a3:8a2e:1234::/97, 1000) = ROA_INVALID,
|
||||
" ", roa_check(r6, 2001:0db8:85a3:8a2e::/64, 1000) = ROA_VALID,
|
||||
" ", roa_check(r6, 2001:0db8:85a3::/48, 1000) = ROA_UNKNOWN;
|
||||
|
||||
print "Should be true: ", roa_check(r4, 2001:0db8:85a3:8a2e:1234::/97, 1000) = ROA_INVALID ||
|
||||
roa_check(r6, 2001:0db8:85a3:8a2e:1234::/97, 1000) = ROA_INVALID;
|
||||
|
||||
print "Should be false: ", roa_check(r4, 2001:0db8:85a3:8a2e:1234::/80, 1000) = ROA_INVALID ||
|
||||
roa_check(r6, 2001:0db8:85a3:8a2e:1234::/80, 1000) = ROA_INVALID,
|
||||
" ", roa_check(r4, 2001:0db8:85a3::/48, 1000) = ROA_INVALID ||
|
||||
roa_check(r6, 2001:0db8:85a3::/48, 1000) = ROA_INVALID;
|
||||
|
||||
print "Should be true: ", 10.130.130.0/24 ~ 0.0.0.0/0,
|
||||
" ", 2001:0db8:85a3:8a2e::/64 ~ ::/0;
|
||||
print "Should be false: ", 10.130.130.0/24 ~ ::/0,
|
||||
" ", 2001:0db8:85a3:8a2e::/64 ~ 0.0.0.0/0;
|
||||
}
|
||||
|
||||
function roa_operators_test()
|
||||
prefix pfx;
|
||||
{
|
||||
print "Testing ROA prefix operators '.maxlen' and '.asn':";
|
||||
|
||||
pfx = 12.13.0.0/16 max 24 as 1234;
|
||||
print pfx;
|
||||
print "Should be true: ", pfx.len = 16, " ", pfx.maxlen = 24, " ", pfx.asn = 1234;
|
||||
|
||||
pfx = 1000::/8 max 32 as 1234;
|
||||
print pfx;
|
||||
print "Should be true: ", pfx.len = 8, " ", pfx.maxlen = 32, " ", pfx.asn = 1234;
|
||||
}
|
||||
*/
|
|
@ -1,3 +1,7 @@
|
|||
src := bitops.c checksum.c ip.c lists.c md5.c net.c patmatch.c printf.c sha1.c sha256.c sha512.c slists.c xmalloc.c
|
||||
obj := $(src-o-files)
|
||||
$(all-client)
|
||||
|
||||
src := bitops.c checksum.c event.c idm.c ip.c lists.c mac.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)
|
||||
$(all-daemon)
|
||||
|
|
|
@ -171,6 +171,9 @@ static inline int net_type_match(const net_addr *a, u32 mask)
|
|||
static inline int net_is_ip(const net_addr *a)
|
||||
{ return (a->type == NET_IP4) || (a->type == NET_IP6); }
|
||||
|
||||
static inline int net_is_roa(const net_addr *a)
|
||||
{ return (a->type == NET_ROA4) || (a->type == NET_ROA6); }
|
||||
|
||||
|
||||
static inline ip4_addr net4_prefix(const net_addr *a)
|
||||
{ return ((net_addr_ip4 *) a)->prefix; }
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
struct pool {
|
||||
resource r;
|
||||
list inside;
|
||||
char *name;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
static void pool_dump(resource *);
|
||||
|
@ -61,7 +61,7 @@ static int indent;
|
|||
* parent pool.
|
||||
*/
|
||||
pool *
|
||||
rp_new(pool *p, char *name)
|
||||
rp_new(pool *p, const char *name)
|
||||
{
|
||||
pool *z = ralloc(p, &pool_class);
|
||||
z->name = name;
|
||||
|
|
|
@ -37,7 +37,7 @@ struct resclass {
|
|||
typedef struct pool pool;
|
||||
|
||||
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 rdump(void *); /* Dump to debug output */
|
||||
size_t rmemsize(void *res); /* Return size of memory used by the resource */
|
||||
|
|
29
lib/socket.h
29
lib/socket.h
|
@ -12,6 +12,29 @@
|
|||
#include <errno.h>
|
||||
|
||||
#include "lib/resource.h"
|
||||
#ifdef HAVE_LIBSSH
|
||||
#define LIBSSH_LEGACY_0_4
|
||||
#include <libssh/libssh.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBSSH
|
||||
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 */
|
||||
};
|
||||
#endif
|
||||
|
||||
typedef struct birdsock {
|
||||
resource r;
|
||||
|
@ -20,6 +43,7 @@ typedef struct birdsock {
|
|||
int subtype; /* Socket subtype */
|
||||
void *data; /* User data */
|
||||
ip_addr saddr, daddr; /* IPA_NONE = unspecified */
|
||||
const char *host; /* Alternative to daddr, NULL = unspecified */
|
||||
uint sport, dport; /* 0 = unspecified (for IP: protocol type) */
|
||||
int tos; /* TOS / traffic class, -1 = default */
|
||||
int priority; /* Local socket priority, -1 = default */
|
||||
|
@ -52,7 +76,8 @@ typedef struct birdsock {
|
|||
node n;
|
||||
void *rbuf_alloc, *tbuf_alloc;
|
||||
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_new(pool *); /* Allocate new socket */
|
||||
|
@ -115,6 +140,8 @@ extern int sk_priority_control; /* Suggested priority for control traffic, shou
|
|||
#define SK_MAGIC 7 /* Internal use by sysdep code */
|
||||
#define SK_UNIX_PASSIVE 8
|
||||
#define SK_UNIX 9
|
||||
#define SK_SSH_ACTIVE 10 /* - - * * - ? - DA = host */
|
||||
#define SK_SSH 11
|
||||
|
||||
/*
|
||||
* Socket subtypes
|
||||
|
|
|
@ -1265,6 +1265,9 @@ protos_build(void)
|
|||
#ifdef CONFIG_BABEL
|
||||
proto_build(&proto_babel);
|
||||
#endif
|
||||
#ifdef CONFIG_RPKI
|
||||
proto_build(&proto_rpki);
|
||||
#endif
|
||||
|
||||
proto_pool = rp_new(&root_pool, "Protocols");
|
||||
proto_shutdown_timer = tm_new(proto_pool);
|
||||
|
|
|
@ -81,7 +81,7 @@ void protos_dump_all(void);
|
|||
|
||||
extern struct protocol
|
||||
proto_device, proto_radv, proto_rip, proto_static,
|
||||
proto_ospf, proto_pipe, proto_bgp, proto_bfd, proto_babel;
|
||||
proto_ospf, proto_pipe, proto_bgp, proto_bfd, proto_babel, proto_rpki;
|
||||
|
||||
/*
|
||||
* Routing Protocol Instance
|
||||
|
@ -271,7 +271,7 @@ proto_get_router_id(struct proto_config *pc)
|
|||
}
|
||||
|
||||
/* 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 pool *proto_pool;
|
||||
extern list proto_list;
|
||||
|
@ -568,11 +568,9 @@ int proto_configure_channel(struct proto *p, struct channel **c, struct channel_
|
|||
|
||||
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_open(struct channel *c) { channel_set_state(c, CS_UP); }
|
||||
static inline void channel_close(struct channel *c) { channel_set_state(c, CS_FLUSHING); }
|
||||
*/
|
||||
|
||||
void channel_request_feeding(struct channel *c);
|
||||
void *channel_config_new(const struct channel_class *cc, uint net_type, struct proto_config *proto);
|
||||
|
|
|
@ -283,7 +283,7 @@ void *net_route(rtable *tab, const net_addr *n);
|
|||
int net_roa_check(rtable *tab, const net_addr *n, u32 asn);
|
||||
rte *rte_find(net *net, struct rte_src *src);
|
||||
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 */
|
||||
int rt_examine(rtable *t, net_addr *a, struct proto *p, struct filter *filter);
|
||||
rte *rt_export_merged(struct channel *c, net *net, rte **rt_free, struct ea_list **tmpa, linpool *pool, int silent);
|
||||
|
@ -383,6 +383,8 @@ typedef struct rta {
|
|||
#define RTS_BGP 11 /* BGP route */
|
||||
#define RTS_PIPE 12 /* Inter-table wormhole */
|
||||
#define RTS_BABEL 13 /* Babel route */
|
||||
#define RTS_RPKI 14 /* Route Origin Authorization */
|
||||
|
||||
|
||||
#define RTC_UNICAST 0
|
||||
#define RTC_BROADCAST 1
|
||||
|
@ -572,6 +574,7 @@ extern struct protocol *attr_class_to_protocol[EAP_MAX];
|
|||
#define DEF_PREF_BABEL 130 /* Babel */
|
||||
#define DEF_PREF_RIP 120 /* RIP */
|
||||
#define DEF_PREF_BGP 100 /* BGP */
|
||||
#define DEF_PREF_RPKI 100 /* RPKI */
|
||||
#define DEF_PREF_INHERITED 10 /* Routes inherited from other routing daemons */
|
||||
|
||||
/*
|
||||
|
|
|
@ -1301,7 +1301,7 @@ rte_unhide_dummy_routes(net *net, rte **dummy)
|
|||
*/
|
||||
|
||||
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_stats *stats = &c->stats;
|
||||
|
@ -2695,6 +2695,12 @@ rt_show(struct rt_show_data *d)
|
|||
}
|
||||
}
|
||||
|
||||
if (d->table->addr_type != d->addr->type)
|
||||
{
|
||||
cli_msg(8001, "Incompatible type of prefix/ip with table");
|
||||
return;
|
||||
}
|
||||
|
||||
if (d->show_for)
|
||||
n = net_route(d->table, d->addr);
|
||||
else
|
||||
|
|
|
@ -4,7 +4,8 @@ C bfd
|
|||
C bgp
|
||||
C ospf
|
||||
C pipe
|
||||
C rip
|
||||
C radv
|
||||
C rip
|
||||
C rpki
|
||||
C static
|
||||
S ../nest/rt-dev.c
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
source=babel.c packets.c
|
||||
root-rel=../../
|
||||
dir-name=proto/babel
|
||||
|
||||
include ../../Rules
|
||||
src := babel.c packets.c
|
||||
obj := $(src-o-files)
|
||||
$(all-daemon)
|
||||
$(cf-local)
|
||||
|
|
|
@ -21,11 +21,7 @@
|
|||
#include "lib/lists.h"
|
||||
#include "lib/socket.h"
|
||||
#include "lib/string.h"
|
||||
#include "lib/timer.h"
|
||||
|
||||
#ifndef IPV6
|
||||
#error "The Babel protocol only speaks IPv6"
|
||||
#endif
|
||||
#include "sysdep/unix/timer.h"
|
||||
|
||||
#define EA_BABEL_METRIC EA_CODE(EAP_BABEL, 0)
|
||||
#define EA_BABEL_ROUTER_ID EA_CODE(EAP_BABEL, 1)
|
||||
|
|
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)
|
144
proto/rpki/config.Y
Normal file
144
proto/rpki/config.Y
Normal file
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* 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)
|
||||
#define RPKI_TR_SSH_CFG ((struct rpki_tr_ssh_config *) RPKI_CFG->tr_config.spec)
|
||||
|
||||
static void
|
||||
rpki_check_unused_hostname(void)
|
||||
{
|
||||
if (RPKI_CFG->hostname != NULL)
|
||||
cf_error("Only one cache server per protocol allowed");
|
||||
}
|
||||
|
||||
static void
|
||||
rpki_check_unused_transport(void)
|
||||
{
|
||||
if (RPKI_CFG->tr_config.spec != NULL)
|
||||
cf_error("At the most one transport per protocol allowed");
|
||||
}
|
||||
|
||||
CF_DECLS
|
||||
|
||||
CF_KEYWORDS(RPKI, REMOTE, BIRD, PRIVATE, PUBLIC, KEY, TCP, SSH, TRANSPORT, USER,
|
||||
RETRY, REFRESH, EXPIRE, KEEP)
|
||||
|
||||
%type <i> rpki_keep_interval
|
||||
|
||||
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_RETRY_INTERVAL;
|
||||
RPKI_CFG->refresh_interval = RPKI_REFRESH_INTERVAL;
|
||||
RPKI_CFG->expire_interval = RPKI_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
|
||||
| TRANSPORT rpki_transport
|
||||
| REFRESH rpki_keep_interval expr {
|
||||
if (rpki_check_refresh_interval($3))
|
||||
cf_error(rpki_check_refresh_interval($3));
|
||||
RPKI_CFG->refresh_interval = $3;
|
||||
RPKI_CFG->keep_refresh_interval = $2;
|
||||
}
|
||||
| RETRY rpki_keep_interval expr {
|
||||
if (rpki_check_retry_interval($3))
|
||||
cf_error(rpki_check_retry_interval($3));
|
||||
RPKI_CFG->retry_interval = $3;
|
||||
RPKI_CFG->keep_retry_interval = $2;
|
||||
}
|
||||
| EXPIRE rpki_keep_interval expr {
|
||||
if (rpki_check_expire_interval($3))
|
||||
cf_error(rpki_check_expire_interval($3));
|
||||
RPKI_CFG->expire_interval = $3;
|
||||
RPKI_CFG->keep_expire_interval = $2;
|
||||
}
|
||||
;
|
||||
|
||||
rpki_keep_interval:
|
||||
/* empty */ { $$ = 0; }
|
||||
| KEEP { $$ = 1; }
|
||||
;
|
||||
|
||||
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;
|
||||
/* Ensure hostname is filled */
|
||||
char *hostname = cfg_allocz(sizeof(INET6_ADDRSTRLEN + 1));
|
||||
bsnprintf(hostname, INET6_ADDRSTRLEN+1, "%I", RPKI_CFG->ip);
|
||||
RPKI_CFG->hostname = hostname;
|
||||
}
|
||||
;
|
||||
|
||||
rpki_transport:
|
||||
TCP rpki_transport_tcp_init
|
||||
| SSH rpki_transport_ssh_init '{' rpki_transport_ssh_opts '}' rpki_transport_ssh_check
|
||||
;
|
||||
|
||||
rpki_transport_tcp_init:
|
||||
{
|
||||
rpki_check_unused_transport();
|
||||
RPKI_CFG->tr_config.spec = cfg_allocz(sizeof(struct rpki_tr_tcp_config));
|
||||
RPKI_CFG->tr_config.type = RPKI_TR_TCP;
|
||||
};
|
||||
|
||||
rpki_transport_ssh_init:
|
||||
{
|
||||
rpki_check_unused_transport();
|
||||
RPKI_CFG->tr_config.spec = cfg_allocz(sizeof(struct rpki_tr_ssh_config));
|
||||
RPKI_CFG->tr_config.type = RPKI_TR_SSH;
|
||||
};
|
||||
|
||||
rpki_transport_ssh_opts:
|
||||
/* empty */
|
||||
| rpki_transport_ssh_opts rpki_transport_ssh_item ';'
|
||||
;
|
||||
|
||||
rpki_transport_ssh_item:
|
||||
BIRD PRIVATE KEY text { RPKI_TR_SSH_CFG->bird_private_key = $4; }
|
||||
| REMOTE PUBLIC KEY text { RPKI_TR_SSH_CFG->cache_public_key = $4; }
|
||||
| USER text { RPKI_TR_SSH_CFG->user = $2; }
|
||||
;
|
||||
|
||||
rpki_transport_ssh_check:
|
||||
{
|
||||
if (RPKI_TR_SSH_CFG->user == NULL)
|
||||
cf_error("User must be set");
|
||||
};
|
||||
|
||||
CF_CODE
|
||||
|
||||
CF_END
|
1077
proto/rpki/packets.c
Normal file
1077
proto/rpki/packets.c
Normal file
File diff suppressed because it is too large
Load diff
45
proto/rpki/packets.h
Normal file
45
proto/rpki/packets.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* BIRD -- The Resource Public Key Infrastructure (RPKI) to Router Protocol
|
||||
*
|
||||
* (c) 2015 CZ.NIC
|
||||
* (c) 2015 Pavel Tvrdik <pawel.tvrdik@gmail.com>
|
||||
*
|
||||
* 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_PDU_HEADER_LEN 8
|
||||
|
||||
/* A Error PDU size is the biggest (has encapsulate PDU inside):
|
||||
* +8 bytes (Header size)
|
||||
* +4 bytes (Length of Encapsulated PDU)
|
||||
* +32 bytes (Encapsulated PDU IPv6 32)
|
||||
* +4 bytes (Length of inserted text)
|
||||
* +800 bytes (UTF-8 text 400*2 bytes)
|
||||
* ------------
|
||||
* = 848 bytes (Maximal expected PDU size) */
|
||||
#define RPKI_PDU_MAX_LEN 848
|
||||
|
||||
/* RX buffer size has a great impact to scheduler granularity */
|
||||
#define RPKI_RX_BUFFER_SIZE 4096
|
||||
#define RPKI_TX_BUFFER_SIZE RPKI_PDU_MAX_LEN
|
||||
|
||||
/* Return values */
|
||||
enum rpki_rtvals {
|
||||
RPKI_SUCCESS = 0,
|
||||
RPKI_ERROR = -1
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
#endif
|
928
proto/rpki/rpki.c
Normal file
928
proto/rpki/rpki.c
Normal file
|
@ -0,0 +1,928 @@
|
|||
/*
|
||||
* BIRD -- The Resource Public Key Infrastructure (RPKI) to Router Protocol
|
||||
*
|
||||
* (c) 2015 CZ.NIC
|
||||
* (c) 2015 Pavel Tvrdik <pawel.tvrdik@gmail.com>
|
||||
*
|
||||
* Using RTRlib: http://rpki.realmv6.org/
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
/**
|
||||
* DOC: RPKI To Router (RPKI-RTR)
|
||||
*
|
||||
* The RPKI-RTR protocol is implemented in several files: |rpki.c| containing
|
||||
* the routes handling, protocol logic, timer events, cache connection,
|
||||
* reconfiguration, configuration and protocol glue with BIRD core, |packets.c|
|
||||
* containing the RPKI packets handling and finally all transports files:
|
||||
* |transport.c|, |tcp_transport.c| and |ssh_transport.c|.
|
||||
*
|
||||
* The |transport.c| is a middle layer and interface for each specific
|
||||
* transport. Transport is a way how to wrap a communication with a cache
|
||||
* server. There is supported an unprotected TCP transport and an encrypted
|
||||
* SSHv2 transport. The SSH transport requires LibSSH library. LibSSH is
|
||||
* loading dynamically using |dlopen()| function. SSH support is integrated in
|
||||
* |sysdep/unix/io.c|. Each transport must implement an initialization
|
||||
* function, an open function and a socket identification function. That's all.
|
||||
*
|
||||
* This implementation is based on the RTRlib (http://rpki.realmv6.org/). The
|
||||
* BIRD takes over files |packets.c|, |rtr.c| (inside |rpki.c|), |transport.c|,
|
||||
* |tcp_transport.c| and |ssh_transport.c| from RTRlib.
|
||||
*
|
||||
* A RPKI-RTR connection is described by a structure &rpki_cache. The main
|
||||
* logic is located in |rpki_cache_change_state()| function. There is a state
|
||||
* machine. The standard starting state flow looks like |Down| ~> |Connecting|
|
||||
* ~> |Sync-Start| ~> |Sync-Running| ~> |Established| and then the last three
|
||||
* states are periodically repeated.
|
||||
*
|
||||
* |Connecting| state establishes the transport connection. The state from a
|
||||
* call |rpki_cache_change_state(CONNECTING)| to a call |rpki_connected_hook()|
|
||||
*
|
||||
* |Sync-Start| state starts with sending |Reset Query| or |Serial Query| and
|
||||
* then waits for |Cache Response|. The state from |rpki_connected_hook()| to
|
||||
* |rpki_handle_cache_response_pdu()|
|
||||
*
|
||||
* During |Sync-Running| BIRD receives data with IPv4/IPv6 Prefixes from cache
|
||||
* server. The state starts from |rpki_handle_cache_response_pdu()| and ends
|
||||
* in |rpki_handle_end_of_data_pdu()|.
|
||||
*
|
||||
* |Established| state means that BIRD has synced all data with cache server.
|
||||
* Schedules a refresh timer event that invokes |Sync-Start|. Schedules Expire
|
||||
* timer event and stops a Retry timer event.
|
||||
*
|
||||
* |Transport Error| state means that we have some troubles with a network
|
||||
* connection. We cannot connect to a cache server or we wait too long for some
|
||||
* expected PDU for received - |Cache Response| or |End of Data|. It closes
|
||||
* current connection and schedules a Retry timer event.
|
||||
*
|
||||
* |Fatal Protocol Error| is occurred e.g. by received a bad Session ID. We
|
||||
* restart a protocol, so all ROAs are flushed immediately.
|
||||
*
|
||||
* The RPKI-RTR protocol (RFC 6810 bis) defines configurable refresh, retry and
|
||||
* expire intervals. For maintaining a connection are used timer events that
|
||||
* are scheduled by |rpki_schedule_next_refresh()|,
|
||||
* |rpki_schedule_next_retry()| and |rpki_schedule_next_expire()| functions.
|
||||
*
|
||||
* A Refresh timer event performs a sync of |Established| connection. So it
|
||||
* shifts state to |Sync-Start|. If at the beginning of second call of a
|
||||
* refresh event is connection in |Sync-Start| state then we didn't receive a
|
||||
* |Cache Response| from a cache server and we invoke |Transport Error| state.
|
||||
*
|
||||
* A Retry timer event attempts to connect cache server. It is activated after
|
||||
* |Transport Error| state and terminated by reaching |Established| state.
|
||||
* If cache connection is still connecting to the cache server at the beginning
|
||||
* of an event call then the Retry timer event invokes |Transport Error| state.
|
||||
*
|
||||
* An Expire timer event checks expiration of ROAs. If a last successful sync
|
||||
* was more ago than the expire interval then the Expire timer event invokes a
|
||||
* protocol restart thereby removes all ROAs learned from that cache server and
|
||||
* continue trying to connect to cache server. The Expire event is activated
|
||||
* by initial successful loading of ROAs, receiving End of Data PDU.
|
||||
*
|
||||
* A reconfiguration of cache connection works well without restarting when we
|
||||
* change only intervals values.
|
||||
*
|
||||
* Supported standards:
|
||||
* - RFC 6810 - main RPKI-RTR standard
|
||||
* - RFC 6810 bis - an explicit timing parameters and protocol version number negotiation
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#undef LOCAL_DEBUG
|
||||
|
||||
#include "rpki.h"
|
||||
#include "lib/string.h"
|
||||
#include "nest/cli.h"
|
||||
|
||||
/* Return values for reconfiguration functions */
|
||||
#define NEED_RESTART 0
|
||||
#define SUCCESSFUL_RECONF 1
|
||||
|
||||
static int rpki_open_connection(struct rpki_cache *cache);
|
||||
static void rpki_close_connection(struct rpki_cache *cache);
|
||||
static void rpki_schedule_next_refresh(struct rpki_cache *cache);
|
||||
static void rpki_schedule_next_retry(struct rpki_cache *cache);
|
||||
static void rpki_schedule_next_expire_check(struct rpki_cache *cache);
|
||||
static void rpki_stop_refresh_timer_event(struct rpki_cache *cache);
|
||||
static void rpki_stop_retry_timer_event(struct rpki_cache *cache);
|
||||
static void rpki_stop_expire_timer_event(struct rpki_cache *cache);
|
||||
|
||||
|
||||
/*
|
||||
* Routes handling
|
||||
*/
|
||||
|
||||
void
|
||||
rpki_table_add_roa(struct rpki_cache *cache, struct channel *channel, const net_addr_union *pfxr)
|
||||
{
|
||||
struct rpki_proto *p = cache->p;
|
||||
|
||||
rta a0 = {
|
||||
.src = p->p.main_source,
|
||||
.source = RTS_RPKI,
|
||||
.scope = SCOPE_UNIVERSE,
|
||||
.cast = RTC_UNICAST,
|
||||
.dest = RTD_BLACKHOLE,
|
||||
};
|
||||
|
||||
rta *a = rta_lookup(&a0);
|
||||
rte *e = rte_get_temp(a);
|
||||
|
||||
e->pflags = 0;
|
||||
|
||||
rte_update2(channel, &pfxr->n, e, a0.src);
|
||||
}
|
||||
|
||||
void
|
||||
rpki_table_remove_roa(struct rpki_cache *cache, struct channel *channel, const net_addr_union *pfxr)
|
||||
{
|
||||
struct rpki_proto *p = cache->p;
|
||||
rte_update2(channel, &pfxr->n, NULL, p->p.main_source);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* RPKI Protocol Logic
|
||||
*/
|
||||
|
||||
static const char *str_cache_states[] = {
|
||||
[RPKI_CS_CONNECTING] = "Connecting",
|
||||
[RPKI_CS_ESTABLISHED] = "Established",
|
||||
[RPKI_CS_RESET] = "Reseting",
|
||||
[RPKI_CS_SYNC_START] = "Sync-Start",
|
||||
[RPKI_CS_SYNC_RUNNING] = "Sync-Running",
|
||||
[RPKI_CS_FAST_RECONNECT] = "Fast-Reconnect",
|
||||
[RPKI_CS_NO_INCR_UPDATE_AVAIL]= "No-Increment-Update-Available",
|
||||
[RPKI_CS_ERROR_NO_DATA_AVAIL] = "Cache-Error-No-Data-Available",
|
||||
[RPKI_CS_ERROR_FATAL] = "Fatal-Protocol-Error",
|
||||
[RPKI_CS_ERROR_TRANSPORT] = "Transport-Error",
|
||||
[RPKI_CS_SHUTDOWN] = "Down"
|
||||
};
|
||||
|
||||
/**
|
||||
* rpki_cache_state_to_str - give a text representation of cache state
|
||||
* @state: A cache state
|
||||
*
|
||||
* The function converts logic cache state into string.
|
||||
*/
|
||||
const char *
|
||||
rpki_cache_state_to_str(enum rpki_cache_state state)
|
||||
{
|
||||
return str_cache_states[state];
|
||||
}
|
||||
|
||||
/**
|
||||
* rpki_start_cache - connect to a cache server
|
||||
* @cache: RPKI connection instance
|
||||
*
|
||||
* This function is a high level method to kick up a connection to a cache server.
|
||||
*/
|
||||
static void
|
||||
rpki_start_cache(struct rpki_cache *cache)
|
||||
{
|
||||
rpki_cache_change_state(cache, RPKI_CS_CONNECTING);
|
||||
}
|
||||
|
||||
/**
|
||||
* rpki_force_restart_proto - force shutdown and start protocol again
|
||||
* @p: RPKI protocol instance
|
||||
*
|
||||
* This function calls shutdown and frees all protocol resources as well.
|
||||
* After calling this function should be no operations with protocol data,
|
||||
* they could be freed already.
|
||||
*/
|
||||
static void
|
||||
rpki_force_restart_proto(struct rpki_proto *p)
|
||||
{
|
||||
if (p->cache)
|
||||
{
|
||||
CACHE_DBG(p->cache, "Connection object destroying");
|
||||
}
|
||||
|
||||
/* Sign as freed */
|
||||
p->cache = NULL;
|
||||
|
||||
proto_notify_state(&p->p, PS_DOWN);
|
||||
}
|
||||
|
||||
/**
|
||||
* rpki_cache_change_state - check and change cache state
|
||||
* @cache: RPKI cache instance
|
||||
* @new_state: suggested new state
|
||||
*
|
||||
* This function makes transitions between internal states.
|
||||
* It represents the core of logic management of RPKI protocol.
|
||||
* Cannot transit into the same state as cache is in already.
|
||||
*/
|
||||
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;
|
||||
|
||||
cache->state = new_state;
|
||||
CACHE_TRACE(D_EVENTS, cache, "Changing from %s to %s state", rpki_cache_state_to_str(old_state), rpki_cache_state_to_str(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_START);
|
||||
|
||||
rpki_schedule_next_retry(cache);
|
||||
break;
|
||||
}
|
||||
|
||||
case RPKI_CS_ESTABLISHED:
|
||||
rpki_schedule_next_refresh(cache);
|
||||
rpki_schedule_next_expire_check(cache);
|
||||
rpki_stop_retry_timer_event(cache);
|
||||
break;
|
||||
|
||||
case RPKI_CS_RESET:
|
||||
/* Resetting cache connection. */
|
||||
cache->request_session_id = 1;
|
||||
cache->serial_num = 0;
|
||||
rpki_cache_change_state(cache, RPKI_CS_SYNC_START);
|
||||
break;
|
||||
|
||||
case RPKI_CS_SYNC_START:
|
||||
/* Requesting for receive ROAs from a cache server. */
|
||||
if (cache->request_session_id)
|
||||
{
|
||||
/* Send request for Session ID */
|
||||
if (rpki_send_reset_query(cache) != RPKI_SUCCESS)
|
||||
rpki_cache_change_state(cache, RPKI_CS_ERROR_TRANSPORT);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We have already a session_id. So send a Serial Query and start an incremental sync */
|
||||
if (rpki_send_serial_query(cache) != RPKI_SUCCESS)
|
||||
rpki_cache_change_state(cache, RPKI_CS_ERROR_TRANSPORT);
|
||||
}
|
||||
break;
|
||||
|
||||
case RPKI_CS_SYNC_RUNNING:
|
||||
/* The state between Cache Response and End of Data. Only waiting for
|
||||
* receiving all IP Prefix PDUs and finally a End of Data PDU. */
|
||||
break;
|
||||
|
||||
case RPKI_CS_NO_INCR_UPDATE_AVAIL:
|
||||
/* Server was unable to answer the last Serial Query and sent Cache Reset. */
|
||||
rpki_cache_change_state(cache, RPKI_CS_RESET);
|
||||
break;
|
||||
|
||||
case RPKI_CS_ERROR_NO_DATA_AVAIL:
|
||||
/* No validation records are available on the cache server. */
|
||||
rpki_cache_change_state(cache, RPKI_CS_RESET);
|
||||
break;
|
||||
|
||||
case RPKI_CS_ERROR_FATAL:
|
||||
/* Fatal protocol error occurred. */
|
||||
rpki_force_restart_proto(cache->p);
|
||||
break;
|
||||
|
||||
case RPKI_CS_ERROR_TRANSPORT:
|
||||
/* Error on the transport socket occurred. */
|
||||
rpki_close_connection(cache);
|
||||
rpki_schedule_next_retry(cache);
|
||||
rpki_stop_refresh_timer_event(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:
|
||||
bug("This isn't never really called.");
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* RPKI Timer Events
|
||||
*/
|
||||
|
||||
static void
|
||||
rpki_schedule_next_refresh(struct rpki_cache *cache)
|
||||
{
|
||||
uint time_to_wait = cache->refresh_interval;
|
||||
|
||||
CACHE_DBG(cache, "after %u seconds", time_to_wait);
|
||||
tm_start(cache->refresh_timer, time_to_wait);
|
||||
}
|
||||
|
||||
static void
|
||||
rpki_schedule_next_retry(struct rpki_cache *cache)
|
||||
{
|
||||
uint time_to_wait = cache->retry_interval;
|
||||
|
||||
CACHE_DBG(cache, "after %u seconds", time_to_wait);
|
||||
tm_start(cache->retry_timer, time_to_wait);
|
||||
}
|
||||
|
||||
static void
|
||||
rpki_schedule_next_expire_check(struct rpki_cache *cache)
|
||||
{
|
||||
/* A minimum time to wait is 1 second */
|
||||
uint time_to_wait = MAX(((int)cache->expire_interval - (int)(now - cache->last_update)), 1);
|
||||
|
||||
CACHE_DBG(cache, "after %u seconds", time_to_wait);
|
||||
tm_start(cache->expire_timer, time_to_wait);
|
||||
}
|
||||
|
||||
static void
|
||||
rpki_stop_refresh_timer_event(struct rpki_cache *cache)
|
||||
{
|
||||
CACHE_DBG(cache, "Stop");
|
||||
tm_stop(cache->refresh_timer);
|
||||
}
|
||||
|
||||
static void
|
||||
rpki_stop_retry_timer_event(struct rpki_cache *cache)
|
||||
{
|
||||
CACHE_DBG(cache, "Stop");
|
||||
tm_stop(cache->retry_timer);
|
||||
}
|
||||
|
||||
static void
|
||||
rpki_stop_expire_timer_event(struct rpki_cache *cache)
|
||||
{
|
||||
CACHE_DBG(cache, "Stop");
|
||||
tm_stop(cache->expire_timer);
|
||||
}
|
||||
|
||||
static int
|
||||
rpki_do_we_recv_prefix_pdu_in_last_seconds(struct rpki_cache *cache)
|
||||
{
|
||||
if (cache->last_rx_prefix == 0)
|
||||
return 0;
|
||||
|
||||
return ((now - cache->last_rx_prefix) <= 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* rpki_refresh_hook - control a scheduling of downloading data from cache server
|
||||
* @tm: refresh timer with cache connection instance in data
|
||||
*
|
||||
* This function is periodically called during &ESTABLISHED or &SYNC* state
|
||||
* cache connection. The first refresh schedule is invoked after receiving a
|
||||
* |End of Data| PDU and has run by some &ERROR is occurred.
|
||||
*/
|
||||
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_START);
|
||||
break;
|
||||
|
||||
case RPKI_CS_SYNC_START:
|
||||
/* We sent Serial/Reset Query in last refresh hook call
|
||||
* and didn't receive Cache Response yet. It is probably
|
||||
* troubles with network. */
|
||||
case RPKI_CS_SYNC_RUNNING:
|
||||
/* We sent Serial/Reset Query in last refresh hook call
|
||||
* and we got Cache Response but didn't get End-Of-Data yet.
|
||||
* It could be a trouble with network or only too long synchronization. */
|
||||
if (!rpki_do_we_recv_prefix_pdu_in_last_seconds(cache))
|
||||
{
|
||||
CACHE_TRACE(D_EVENTS, cache, "Sync takes more time than refresh interval %us, resetting connection", cache->refresh_interval);
|
||||
rpki_cache_change_state(cache, RPKI_CS_ERROR_TRANSPORT);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (cache->state != RPKI_CS_SHUTDOWN && cache->state != RPKI_CS_ERROR_TRANSPORT)
|
||||
rpki_schedule_next_refresh(cache);
|
||||
else
|
||||
rpki_stop_refresh_timer_event(cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* rpki_retry_hook - control a scheduling of retrying connection to cache server
|
||||
* @tm: retry timer with cache connection instance in data
|
||||
*
|
||||
* This function is periodically called during &ERROR* state cache connection.
|
||||
* The first retry schedule is invoked after any &ERROR* state occurred and
|
||||
* ends by reaching of &ESTABLISHED state again.
|
||||
*/
|
||||
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_SHUTDOWN:
|
||||
break;
|
||||
|
||||
case RPKI_CS_CONNECTING:
|
||||
case RPKI_CS_SYNC_START:
|
||||
case RPKI_CS_SYNC_RUNNING:
|
||||
if (!rpki_do_we_recv_prefix_pdu_in_last_seconds(cache))
|
||||
{
|
||||
/* We tried to establish a connection in last retry hook call and haven't done
|
||||
* yet. It looks like troubles with network. We are aggressive here. */
|
||||
CACHE_TRACE(D_EVENTS, cache, "Sync takes more time than retry interval %us, resetting connection.", cache->retry_interval);
|
||||
rpki_cache_change_state(cache, RPKI_CS_ERROR_TRANSPORT);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
rpki_cache_change_state(cache, RPKI_CS_CONNECTING);
|
||||
break;
|
||||
}
|
||||
|
||||
if (cache->state != RPKI_CS_ESTABLISHED)
|
||||
rpki_schedule_next_retry(cache);
|
||||
else
|
||||
rpki_stop_retry_timer_event(cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* rpki_expire_hook - control a expiration of ROA entries
|
||||
* @tm: expire timer with cache connection instance in data
|
||||
*
|
||||
* This function is scheduled after received a |End of Data| PDU.
|
||||
* A waiting interval is calculated dynamically by last update.
|
||||
* If we reach an expiration time then we invoke a restarting
|
||||
* of the protocol.
|
||||
*/
|
||||
static void
|
||||
rpki_expire_hook(struct timer *tm)
|
||||
{
|
||||
struct rpki_cache *cache = tm->data;
|
||||
|
||||
if (cache->last_update == 0)
|
||||
return;
|
||||
|
||||
CACHE_DBG(cache, "%s", rpki_cache_state_to_str(cache->state));
|
||||
|
||||
if ((cache->last_update + cache->expire_interval) < now)
|
||||
{
|
||||
CACHE_TRACE(D_EVENTS, cache, "All ROAs expired");
|
||||
rpki_force_restart_proto(cache->p);
|
||||
}
|
||||
else
|
||||
{
|
||||
CACHE_DBG(cache, "Remains %d seconds to become ROAs obsolete", (int)cache->expire_interval - (int)(now - cache->last_update));
|
||||
rpki_schedule_next_expire_check(cache);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* rpki_check_refresh_interval - check validity of refresh interval value
|
||||
* @seconds: suggested value
|
||||
*
|
||||
* This function validates value and should return |NULL|.
|
||||
* If the check doesn't pass then returns error message.
|
||||
*/
|
||||
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
|
||||
*
|
||||
* This function validates value and should return |NULL|.
|
||||
* If the check doesn't pass then returns error message.
|
||||
*/
|
||||
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
|
||||
*
|
||||
* This function validates value and should return |NULL|.
|
||||
* If the check doesn't pass then returns error message.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* RPKI Cache
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
cache->state = RPKI_CS_SHUTDOWN;
|
||||
cache->request_session_id = 1;
|
||||
cache->version = RPKI_MAX_VERSION;
|
||||
|
||||
cache->refresh_interval = cf->refresh_interval;
|
||||
cache->retry_interval = cf->retry_interval;
|
||||
cache->expire_interval = cf->expire_interval;
|
||||
cache->refresh_timer = tm_new_set(pool, &rpki_refresh_hook, cache, 0, 0);
|
||||
cache->retry_timer = tm_new_set(pool, &rpki_retry_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;
|
||||
|
||||
switch (cf->tr_config.type)
|
||||
{
|
||||
case RPKI_TR_TCP: rpki_tr_tcp_init(cache->tr_sock); break;
|
||||
case RPKI_TR_SSH: rpki_tr_ssh_init(cache->tr_sock); break;
|
||||
};
|
||||
|
||||
CACHE_DBG(cache, "Connection object created");
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpki_get_cache_ident - give a text representation of cache server name
|
||||
* @cache: RPKI connection instance
|
||||
*
|
||||
* The function converts cache connection into string.
|
||||
*/
|
||||
const char *
|
||||
rpki_get_cache_ident(struct rpki_cache *cache)
|
||||
{
|
||||
return rpki_tr_ident(cache->tr_sock);
|
||||
}
|
||||
|
||||
static int
|
||||
rpki_open_connection(struct rpki_cache *cache)
|
||||
{
|
||||
CACHE_TRACE(D_EVENTS, cache, "Opening a connection");
|
||||
|
||||
if (rpki_tr_open(cache->tr_sock) == RPKI_TR_ERROR)
|
||||
{
|
||||
rpki_cache_change_state(cache, RPKI_CS_ERROR_TRANSPORT);
|
||||
return RPKI_TR_ERROR;
|
||||
}
|
||||
|
||||
return RPKI_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);
|
||||
proto_notify_state(&cache->p->p, PS_START);
|
||||
}
|
||||
|
||||
static int
|
||||
rpki_shutdown(struct proto *P)
|
||||
{
|
||||
struct rpki_proto *p = (void *) P;
|
||||
|
||||
rpki_force_restart_proto(p);
|
||||
|
||||
/* Protocol memory pool will be automatically freed */
|
||||
return PS_DOWN;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* RPKI Reconfiguration
|
||||
*/
|
||||
|
||||
static int
|
||||
rpki_try_fast_reconnect(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);
|
||||
return SUCCESSFUL_RECONF;
|
||||
}
|
||||
|
||||
return NEED_RESTART;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpki_reconfigure_cache - a cache reconfiguration
|
||||
* @p: RPKI protocol instance
|
||||
* @cache: a cache connection
|
||||
* @new: new RPKI configuration
|
||||
* @old: old RPKI configuration
|
||||
*
|
||||
* This function reconfigures existing single cache server connection with new
|
||||
* existing configuration. Generally, a change of time intervals could be
|
||||
* reconfigured without restarting and all others changes requires a restart of
|
||||
* protocol. Returns |NEED_TO_RESTART| or |SUCCESSFUL_RECONF|.
|
||||
*/
|
||||
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 (strcmp(old->hostname, new->hostname) != 0)
|
||||
{
|
||||
CACHE_TRACE(D_EVENTS, cache, "Cache server address changed to %s", new->hostname);
|
||||
return NEED_RESTART;
|
||||
}
|
||||
|
||||
if (old->port != new->port)
|
||||
{
|
||||
CACHE_TRACE(D_EVENTS, cache, "Cache server port changed to %u", new->port);
|
||||
return NEED_RESTART;
|
||||
}
|
||||
|
||||
if (old->tr_config.type != new->tr_config.type)
|
||||
{
|
||||
CACHE_TRACE(D_EVENTS, cache, "Transport type changed");
|
||||
return NEED_RESTART;
|
||||
}
|
||||
else if (new->tr_config.type == RPKI_TR_SSH)
|
||||
{
|
||||
struct rpki_tr_ssh_config *ssh_old = (void *) old->tr_config.spec;
|
||||
struct rpki_tr_ssh_config *ssh_new = (void *) new->tr_config.spec;
|
||||
if ((strcmp(ssh_old->bird_private_key, ssh_new->bird_private_key) != 0) ||
|
||||
(strcmp(ssh_old->cache_public_key, ssh_new->cache_public_key) != 0) ||
|
||||
(strcmp(ssh_old->user, ssh_new->user) != 0))
|
||||
{
|
||||
CACHE_TRACE(D_EVENTS, cache, "Settings of SSH transport configuration changed");
|
||||
try_fast_reconnect = 1;
|
||||
}
|
||||
}
|
||||
|
||||
#define TEST_INTERVAL(name, Name) \
|
||||
if (cache->name##_interval != new->name##_interval || \
|
||||
old->keep_##name##_interval != new->keep_##name##_interval) \
|
||||
{ \
|
||||
cache->name##_interval = new->name##_interval; \
|
||||
CACHE_TRACE(D_EVENTS, cache, #Name " interval changed to %u seconds %s", cache->name##_interval, (new->keep_##name##_interval ? "and keep it" : "")); \
|
||||
try_fast_reconnect = 1; \
|
||||
}
|
||||
TEST_INTERVAL(refresh, Refresh);
|
||||
TEST_INTERVAL(retry, Retry);
|
||||
TEST_INTERVAL(expire, Expire);
|
||||
#undef TEST_INTERVAL
|
||||
|
||||
if (try_fast_reconnect)
|
||||
return rpki_try_fast_reconnect(cache, new, old);
|
||||
|
||||
return SUCCESSFUL_RECONF;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpki_reconfigure - a protocol reconfiguration hook
|
||||
* @P: a protocol instance
|
||||
* @CF: a new protocol configuration
|
||||
*
|
||||
* This function reconfigures whole protocol.
|
||||
* It sets new protocol configuration into a protocol structure.
|
||||
* Returns |NEED_TO_RESTART| or |SUCCESSFUL_RECONF|.
|
||||
*/
|
||||
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;
|
||||
struct rpki_cache *cache = p->cache;
|
||||
|
||||
if (!proto_configure_channel(&p->p, &p->roa4_channel, proto_cf_find_channel(CF, NET_ROA4)) ||
|
||||
!proto_configure_channel(&p->p, &p->roa6_channel, proto_cf_find_channel(CF, NET_ROA6)))
|
||||
return NEED_RESTART;
|
||||
|
||||
if (rpki_reconfigure_cache(p, cache, new, old) != SUCCESSFUL_RECONF)
|
||||
return NEED_RESTART;
|
||||
|
||||
return SUCCESSFUL_RECONF;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* RPKI Protocol Glue
|
||||
*/
|
||||
|
||||
static struct proto *
|
||||
rpki_init(struct proto_config *CF)
|
||||
{
|
||||
struct proto *P = proto_new(CF);
|
||||
struct rpki_proto *p = (void *) P;
|
||||
|
||||
proto_configure_channel(&p->p, &p->roa4_channel, proto_cf_find_channel(CF, NET_ROA4));
|
||||
proto_configure_channel(&p->p, &p->roa6_channel, proto_cf_find_channel(CF, NET_ROA6));
|
||||
|
||||
return P;
|
||||
}
|
||||
|
||||
static int
|
||||
rpki_start(struct proto *P)
|
||||
{
|
||||
struct rpki_proto *p = (void *) P;
|
||||
struct rpki_config *cf = (void *) P->cf;
|
||||
|
||||
p->cache = rpki_init_cache(p, cf);
|
||||
rpki_start_cache(p->cache);
|
||||
|
||||
return PS_START;
|
||||
}
|
||||
|
||||
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 ---", name);
|
||||
}
|
||||
|
||||
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 (P->proto_state == PS_DOWN)
|
||||
return;
|
||||
|
||||
if (cache)
|
||||
{
|
||||
const char *transport_name = "---";
|
||||
|
||||
switch (cf->tr_config.type)
|
||||
{
|
||||
case RPKI_TR_SSH: transport_name = "SSHv2"; break;
|
||||
case RPKI_TR_TCP: transport_name = "Unprotected over TCP"; break;
|
||||
};
|
||||
|
||||
cli_msg(-1006, " Cache server: %s", rpki_get_cache_ident(cache));
|
||||
cli_msg(-1006, " Status: %s", rpki_cache_state_to_str(cache->state));
|
||||
cli_msg(-1006, " Transport: %s", transport_name);
|
||||
cli_msg(-1006, " Protocol version: %u", cache->version);
|
||||
|
||||
if (cache->request_session_id)
|
||||
cli_msg(-1006, " Session ID: ---");
|
||||
else
|
||||
cli_msg(-1006, " Session ID: %u", cache->session_id);
|
||||
|
||||
if (cache->last_update)
|
||||
{
|
||||
cli_msg(-1006, " Serial number: %u", cache->serial_num);
|
||||
cli_msg(-1006, " Last update: before %us", now - cache->last_update);
|
||||
}
|
||||
else
|
||||
{
|
||||
cli_msg(-1006, " Serial number: ---");
|
||||
cli_msg(-1006, " Last update: ---");
|
||||
}
|
||||
|
||||
rpki_show_proto_info_timer("Refresh interval:", cache->refresh_interval, cache->refresh_timer);
|
||||
rpki_show_proto_info_timer("Retry interval:", cache->retry_interval, cache->retry_timer);
|
||||
rpki_show_proto_info_timer("Expire interval:", cache->expire_interval, cache->expire_timer);
|
||||
|
||||
if (p->roa4_channel)
|
||||
channel_show_info(p->roa4_channel);
|
||||
else
|
||||
cli_msg(-1006, " No roa4 channel");
|
||||
|
||||
if (p->roa6_channel)
|
||||
channel_show_info(p->roa6_channel);
|
||||
else
|
||||
cli_msg(-1006, " No roa6 channel");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* RPKI Protocol Configuration
|
||||
*/
|
||||
|
||||
/**
|
||||
* rpki_check_config - check and complete configuration of RPKI protocol
|
||||
* @cf: RPKI configuration
|
||||
*
|
||||
* This function is called at the end of parsing RPKI protocol configuration.
|
||||
*/
|
||||
void
|
||||
rpki_check_config(struct rpki_config *cf)
|
||||
{
|
||||
/* Do not check templates at all */
|
||||
if (cf->c.class == SYM_TEMPLATE)
|
||||
return;
|
||||
|
||||
if (ipa_zero(cf->ip) && cf->hostname == NULL)
|
||||
cf_error("IP address or hostname of cache server must be set");
|
||||
|
||||
/* Set default transport type */
|
||||
if (cf->tr_config.spec == NULL)
|
||||
{
|
||||
cf->tr_config.spec = cfg_allocz(sizeof(struct rpki_tr_tcp_config));
|
||||
cf->tr_config.type = RPKI_TR_TCP;
|
||||
}
|
||||
|
||||
if (cf->port == 0)
|
||||
{
|
||||
/* Set default port numbers */
|
||||
switch (cf->tr_config.type)
|
||||
{
|
||||
case RPKI_TR_SSH:
|
||||
cf->port = RPKI_SSH_PORT;
|
||||
break;
|
||||
default:
|
||||
cf->port = RPKI_TCP_PORT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
/* Just a shallow copy */
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
166
proto/rpki/rpki.h
Normal file
166
proto/rpki/rpki.h
Normal file
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* BIRD -- The Resource Public Key Infrastructure (RPKI) to Router Protocol
|
||||
*
|
||||
* (c) 2015 CZ.NIC
|
||||
* (c) 2015 Pavel Tvrdik <pawel.tvrdik@gmail.com>
|
||||
*
|
||||
* 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 "transport.h"
|
||||
#include "packets.h"
|
||||
|
||||
#define RPKI_TCP_PORT 323
|
||||
#define RPKI_SSH_PORT 22
|
||||
#define RPKI_RETRY_INTERVAL 600
|
||||
#define RPKI_REFRESH_INTERVAL 3600
|
||||
#define RPKI_EXPIRE_INTERVAL 7200
|
||||
|
||||
#define RPKI_VERSION_0 0
|
||||
#define RPKI_VERSION_1 1
|
||||
#define RPKI_MIN_VERSION RPKI_VERSION_0
|
||||
#define RPKI_MAX_VERSION RPKI_VERSION_1
|
||||
|
||||
|
||||
/*
|
||||
* RPKI Cache
|
||||
*/
|
||||
|
||||
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_START, /* Sending a Serial/Reset Query PDU and expecting a Cache Response PDU */
|
||||
RPKI_CS_SYNC_RUNNING, /* Receiving validation records from the RTR server. A state between Cache Response PDU and End of Data PDU */
|
||||
RPKI_CS_FAST_RECONNECT, /* Reconnect without any waiting period */
|
||||
RPKI_CS_NO_INCR_UPDATE_AVAIL, /* Server is unable to answer the last Serial Query and sent Cache Reset. */
|
||||
RPKI_CS_ERROR_NO_DATA_AVAIL, /* Server is unable to answer either a Serial Query or a Reset Query because it has no useful data available at this time. */
|
||||
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. */
|
||||
};
|
||||
|
||||
struct rpki_cache {
|
||||
pool *pool; /* Pool containing cache objects */
|
||||
struct rpki_proto *p;
|
||||
|
||||
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 received session id */
|
||||
u32 serial_num; /* Serial number denotes the logical version of data from cache server */
|
||||
u8 version; /* Protocol version */
|
||||
bird_clock_t last_update; /* Last successful synchronization with cache server */
|
||||
bird_clock_t last_rx_prefix; /* Last received prefix PDU */
|
||||
|
||||
/* Intervals can be changed by cache server on the fly */
|
||||
u32 refresh_interval; /* Actual refresh interval */
|
||||
u32 retry_interval;
|
||||
u32 expire_interval;
|
||||
timer *retry_timer; /* Retry timer event */
|
||||
timer *refresh_timer; /* Refresh timer event */
|
||||
timer *expire_timer; /* Expire timer event */
|
||||
};
|
||||
|
||||
const char *rpki_get_cache_ident(struct rpki_cache *cache);
|
||||
const char *rpki_cache_state_to_str(enum rpki_cache_state state);
|
||||
|
||||
|
||||
/*
|
||||
* Routes handling
|
||||
*/
|
||||
|
||||
void rpki_table_add_roa(struct rpki_cache *cache, struct channel *channel, const net_addr_union *pfxr);
|
||||
void rpki_table_remove_roa(struct rpki_cache *cache, struct channel *channel, const net_addr_union *pfxr);
|
||||
|
||||
|
||||
/*
|
||||
* RPKI Protocol Logic
|
||||
*/
|
||||
|
||||
void rpki_cache_change_state(struct rpki_cache *cache, const enum rpki_cache_state new_state);
|
||||
|
||||
|
||||
/*
|
||||
* RPKI Timer Events
|
||||
*/
|
||||
|
||||
const char *rpki_check_refresh_interval(uint seconds);
|
||||
const char *rpki_check_retry_interval(uint seconds);
|
||||
const char *rpki_check_expire_interval(uint seconds);
|
||||
|
||||
|
||||
/*
|
||||
* RPKI Protocol Configuration
|
||||
*/
|
||||
|
||||
struct rpki_proto {
|
||||
struct proto p;
|
||||
struct rpki_cache *cache;
|
||||
|
||||
struct channel *roa4_channel;
|
||||
struct channel *roa6_channel;
|
||||
u8 refresh_channels; /* For non-incremental updates using rt_refresh_begin(), rt_refresh_end() */
|
||||
};
|
||||
|
||||
struct rpki_config {
|
||||
struct proto_config c;
|
||||
const char *hostname; /* Full domain name or stringified IP address of cache server */
|
||||
ip_addr ip; /* IP address of cache server or IPA_NONE */
|
||||
u16 port; /* Port number of cache server */
|
||||
struct rpki_tr_config tr_config; /* Specific transport configuration structure */
|
||||
u32 refresh_interval; /* Time interval (in seconds) for periodical downloading data from cache server */
|
||||
u32 retry_interval; /* Time interval (in seconds) for an unreachable server */
|
||||
u32 expire_interval; /* Maximal lifetime (in seconds) of ROAs without any successful refreshment */
|
||||
u8 keep_refresh_interval:1; /* Do not overwrite refresh interval by cache server update */
|
||||
u8 keep_retry_interval:1; /* Do not overwrite retry interval by cache server update */
|
||||
u8 keep_expire_interval:1; /* Do not overwrite expire interval by cache server update */
|
||||
};
|
||||
|
||||
void rpki_check_config(struct rpki_config *cf);
|
||||
|
||||
|
||||
/*
|
||||
* Logger
|
||||
*/
|
||||
|
||||
#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] %s " msg, rpki_get_cache_ident(cache), rpki_cache_state_to_str((cache)->state), __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, msg, ## args); \
|
||||
} while(0)
|
||||
|
||||
#define RPKI_WARN(p, msg, args...) RPKI_LOG(L_WARN, p, msg, ## args);
|
||||
|
||||
#endif /* _BIRD_RPKI_H_ */
|
75
proto/rpki/ssh_transport.c
Normal file
75
proto/rpki/ssh_transport.c
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* BIRD -- An implementation of the SSH protocol for the RPKI transport
|
||||
*
|
||||
* (c) 2015 CZ.NIC
|
||||
* (c) 2015 Pavel Tvrdik <pawel.tvrdik@gmail.com>
|
||||
*
|
||||
* 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"
|
||||
|
||||
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;
|
||||
struct rpki_tr_ssh_config *ssh_cf = (void *) cf->tr_config.spec;
|
||||
sock *sk = tr->sk;
|
||||
|
||||
sk->type = SK_SSH_ACTIVE;
|
||||
sk->ssh = mb_allocz(sk->pool, sizeof(struct ssh_sock));
|
||||
sk->ssh->username = ssh_cf->user;
|
||||
sk->ssh->client_privkey_path = ssh_cf->bird_private_key;
|
||||
sk->ssh->server_hostkey_path = ssh_cf->cache_public_key;
|
||||
sk->ssh->subsystem = "rpki-rtr";
|
||||
sk->ssh->state = SK_SSH_CONNECT;
|
||||
|
||||
if (sk_open(sk) != 0)
|
||||
return RPKI_TR_ERROR;
|
||||
|
||||
return RPKI_TR_SUCCESS;
|
||||
}
|
||||
|
||||
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_config *ssh_cf = (void *) cf->tr_config.spec;
|
||||
|
||||
if (tr->ident != NULL)
|
||||
return tr->ident;
|
||||
|
||||
const char *username = ssh_cf->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);
|
||||
tr->ident = ident;
|
||||
|
||||
return tr->ident;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpki_tr_ssh_init - initializes the RPKI transport structure for a SSH connection
|
||||
* @tr: allocated RPKI transport structure
|
||||
*/
|
||||
void
|
||||
rpki_tr_ssh_init(struct rpki_tr_sock *tr)
|
||||
{
|
||||
tr->open_fp = &rpki_tr_ssh_open;
|
||||
tr->ident_fp = &rpki_tr_ssh_ident;
|
||||
}
|
78
proto/rpki/tcp_transport.c
Normal file
78
proto/rpki/tcp_transport.c
Normal file
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* BIRD -- An implementation of the TCP protocol for the RPKI protocol transport
|
||||
*
|
||||
* (c) 2015 CZ.NIC
|
||||
* (c) 2015 Pavel Tvrdik <pawel.tvrdik@gmail.com>
|
||||
*
|
||||
* 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 "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 RPKI_TR_ERROR;
|
||||
|
||||
return RPKI_TR_SUCCESS;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (tr->ident != NULL)
|
||||
return tr->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);
|
||||
|
||||
tr->ident = ident;
|
||||
return tr->ident;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpki_tr_tcp_init - initializes the RPKI transport structure for a TCP connection
|
||||
* @tr: allocated RPKI transport structure
|
||||
*/
|
||||
void
|
||||
rpki_tr_tcp_init(struct rpki_tr_sock *tr)
|
||||
{
|
||||
tr->open_fp = &rpki_tr_tcp_open;
|
||||
tr->ident_fp = &rpki_tr_tcp_ident;
|
||||
}
|
135
proto/rpki/transport.c
Normal file
135
proto/rpki/transport.c
Normal file
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* BIRD -- The Resource Public Key Infrastructure (RPKI) to Router Protocol
|
||||
*
|
||||
* (c) 2015 CZ.NIC
|
||||
* (c) 2015 Pavel Tvrdik <pawel.tvrdik@gmail.com>
|
||||
*
|
||||
* 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"
|
||||
|
||||
/**
|
||||
* rpki_hostname_autoresolv - auto-resolve an IP address from a hostname
|
||||
* @host: domain name of host, e.g. "rpki-validator.realmv6.org"
|
||||
*
|
||||
* This function resolves an IP address from a hostname.
|
||||
* Returns &ip_addr structure with IP address or |IPA_NONE|.
|
||||
*/
|
||||
static ip_addr
|
||||
rpki_hostname_autoresolv(const char *host)
|
||||
{
|
||||
ip_addr addr = {};
|
||||
struct addrinfo *res;
|
||||
struct addrinfo hints = {
|
||||
.ai_family = AF_UNSPEC,
|
||||
.ai_socktype = SOCK_STREAM,
|
||||
.ai_flags = AI_ADDRCONFIG,
|
||||
};
|
||||
|
||||
if (!host)
|
||||
return IPA_NONE;
|
||||
|
||||
int err_code = getaddrinfo(host, NULL, &hints, &res);
|
||||
if (err_code != 0)
|
||||
{
|
||||
log(L_DEBUG "getaddrinfo failed: %s", gai_strerror(err_code));
|
||||
return IPA_NONE;
|
||||
}
|
||||
|
||||
sockaddr sa = {
|
||||
.sa = *res->ai_addr,
|
||||
};
|
||||
|
||||
uint unused;
|
||||
sockaddr_read(&sa, res->ai_family, &addr, NULL, &unused);
|
||||
|
||||
freeaddrinfo(res);
|
||||
return addr;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpki_tr_open - prepare and open a socket connection
|
||||
* @tr: initialized transport socket
|
||||
*
|
||||
* Prepare and open a socket connection specified by @tr that must be initialized before.
|
||||
* This function ends with a calling the sk_open() function.
|
||||
* Returns RPKI_TR_SUCCESS or RPKI_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->type -1 is invalid value, a correct value MUST be set in the specific transport layer in open_fp() hook */
|
||||
sk->type = -1;
|
||||
|
||||
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;
|
||||
|
||||
if (ipa_zero2(sk->daddr) && sk->host)
|
||||
{
|
||||
sk->daddr = rpki_hostname_autoresolv(sk->host);
|
||||
if (ipa_zero(sk->daddr))
|
||||
{
|
||||
CACHE_TRACE(D_EVENTS, cache, "Cannot resolve the hostname '%s'", sk->host);
|
||||
return RPKI_TR_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return tr->open_fp(tr);
|
||||
}
|
||||
|
||||
/**
|
||||
* rpki_tr_close - close socket and prepare it for possible next open
|
||||
* @tr: successfully opened transport socket
|
||||
*
|
||||
* Close socket and free resources.
|
||||
*/
|
||||
void
|
||||
rpki_tr_close(struct rpki_tr_sock *tr)
|
||||
{
|
||||
if (tr->ident)
|
||||
{
|
||||
mb_free((char *) tr->ident);
|
||||
tr->ident = NULL;
|
||||
}
|
||||
|
||||
if (tr->sk)
|
||||
{
|
||||
rfree(tr->sk);
|
||||
tr->sk = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* rpki_tr_ident - Returns a string identifier for the rpki transport socket
|
||||
* @tr: successfully opened transport socket
|
||||
*
|
||||
* Returns a \0 terminated string identifier for the socket endpoint, e.g. "<host>:<port>".
|
||||
* Memory is allocated inside @tr structure.
|
||||
*/
|
||||
inline const char *
|
||||
rpki_tr_ident(struct rpki_tr_sock *tr)
|
||||
{
|
||||
return tr->ident_fp(tr);
|
||||
}
|
79
proto/rpki/transport.h
Normal file
79
proto/rpki/transport.h
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* BIRD -- The Resource Public Key Infrastructure (RPKI) to Router Protocol
|
||||
*
|
||||
* (c) 2015 CZ.NIC
|
||||
* (c) 2015 Pavel Tvrdik <pawel.tvrdik@gmail.com>
|
||||
*
|
||||
* 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 structure, and forward the remaining arguments.
|
||||
*/
|
||||
|
||||
#ifndef _BIRD_RPKI_TRANSPORT_H_
|
||||
#define _BIRD_RPKI_TRANSPORT_H_
|
||||
|
||||
#include <time.h>
|
||||
|
||||
/* The return values for rpki_tr_ functions */
|
||||
enum rpki_tr_rtvals {
|
||||
RPKI_TR_SUCCESS = 0, /* Operation was successful */
|
||||
RPKI_TR_ERROR = -1, /* Error occurred */
|
||||
RPKI_TR_WOULDBLOCK = -2, /* No data is available on the socket */
|
||||
RPKI_TR_INTR = -3, /* Call was interrupted from a signal */
|
||||
RPKI_TR_CLOSED = -4 /* Connection closed */
|
||||
};
|
||||
|
||||
/* A transport socket structure */
|
||||
struct rpki_tr_sock {
|
||||
sock *sk; /* Standard BIRD socket */
|
||||
struct rpki_cache *cache; /* Cache server */
|
||||
int (*open_fp)(struct rpki_tr_sock *); /* Function that establishes the socket connection */
|
||||
const char *(*ident_fp)(struct rpki_tr_sock *); /* Function that returns an identifier for the socket endpoint */
|
||||
const char *ident; /* Internal. Use ident_fp() hook instead of this pointer */
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
/* Types of supported transports */
|
||||
enum rpki_tr_type {
|
||||
RPKI_TR_TCP, /* Unprotected transport over TCP */
|
||||
RPKI_TR_SSH, /* Protected transport by SSHv2 connection */
|
||||
};
|
||||
|
||||
/* Common configure structure for transports */
|
||||
struct rpki_tr_config {
|
||||
enum rpki_tr_type type; /* RPKI_TR_TCP or RPKI_TR_SSH */
|
||||
const void *spec; /* Specific transport configuration, i.e. rpki_tr_tcp_config or rpki_tr_ssh_config */
|
||||
};
|
||||
|
||||
struct rpki_tr_tcp_config {
|
||||
/* No internal configuration data */
|
||||
};
|
||||
|
||||
struct rpki_tr_ssh_config {
|
||||
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 */
|
||||
};
|
||||
|
||||
/* ssh_transport.c */
|
||||
void rpki_tr_ssh_init(struct rpki_tr_sock *tr);
|
||||
|
||||
/* tcp_transport.c */
|
||||
void rpki_tr_tcp_init(struct rpki_tr_sock *tr);
|
||||
|
||||
#endif /* _BIRD_RPKI_TRANSPORT_H_ */
|
|
@ -44,6 +44,7 @@
|
|||
#undef CONFIG_OSPF
|
||||
#undef CONFIG_PIPE
|
||||
#undef CONFIG_BABEL
|
||||
#undef CONFIG_RPKI
|
||||
|
||||
/* We use multithreading */
|
||||
#undef USE_PTHREADS
|
||||
|
@ -70,4 +71,7 @@
|
|||
/* We have execinfo.h */
|
||||
#undef HAVE_EXECINFO_H
|
||||
|
||||
/* We have LibSSH */
|
||||
#undef HAVE_LIBSSH
|
||||
|
||||
#define CONFIG_PATH ?
|
||||
|
|
382
sysdep/unix/io.c
382
sysdep/unix/io.c
|
@ -1071,26 +1071,63 @@ sk_free_bufs(sock *s)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBSSH
|
||||
static void
|
||||
sk_ssh_free(sock *s)
|
||||
{
|
||||
struct ssh_sock *ssh = s->ssh;
|
||||
|
||||
if (s->ssh == NULL)
|
||||
return;
|
||||
|
||||
s->ssh = NULL;
|
||||
|
||||
if (ssh->channel)
|
||||
{
|
||||
if (ssh_channel_is_open(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;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
sk_free(resource *r)
|
||||
{
|
||||
sock *s = (sock *) r;
|
||||
|
||||
sk_free_bufs(s);
|
||||
if (s->fd >= 0)
|
||||
|
||||
#ifdef HAVE_LIBSSH
|
||||
if (s->type == SK_SSH || s->type == SK_SSH_ACTIVE)
|
||||
sk_ssh_free(s);
|
||||
#endif
|
||||
|
||||
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)
|
||||
current_sock = sk_next(s);
|
||||
if (s == stored_sock)
|
||||
stored_sock = sk_next(s);
|
||||
rem_node(&s->n);
|
||||
}
|
||||
|
||||
if (s->type != SK_SSH && s->type != SK_SSH_ACTIVE)
|
||||
close(s->fd);
|
||||
|
||||
s->fd = -1;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1141,7 +1178,7 @@ static void
|
|||
sk_dump(resource *r)
|
||||
{
|
||||
sock *s = (sock *) r;
|
||||
static char *sk_type_names[] = { "TCP<", "TCP>", "TCP", "UDP", NULL, "IP", NULL, "MAGIC", "UNIX<", "UNIX", "DEL!" };
|
||||
static char *sk_type_names[] = { "TCP<", "TCP>", "TCP", "UDP", NULL, "IP", NULL, "MAGIC", "UNIX<", "UNIX", "SSH>", "SSH", "DEL!" };
|
||||
|
||||
debug("(%s, ud=%p, sa=%I, sp=%d, da=%I, dp=%d, tos=%d, ttl=%d, if=%s)\n",
|
||||
sk_type_names[s->type],
|
||||
|
@ -1192,6 +1229,9 @@ sk_setup(sock *s)
|
|||
int y = 1;
|
||||
int fd = s->fd;
|
||||
|
||||
if (s->type == SK_SSH_ACTIVE)
|
||||
return 0;
|
||||
|
||||
if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
|
||||
ERR("O_NONBLOCK");
|
||||
|
||||
|
@ -1304,6 +1344,14 @@ sk_tcp_connected(sock *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
|
||||
sk_passive_connected(sock *s, int type)
|
||||
{
|
||||
|
@ -1356,6 +1404,201 @@ sk_passive_connected(sock *s, int type)
|
|||
return 1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBSSH
|
||||
/*
|
||||
* 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:
|
||||
/* A quick look into libSSH shows that ssh_get_fd() should return non-(-1)
|
||||
* after SSH_AGAIN is returned by ssh_connect(). This is however nowhere
|
||||
* documented but our code relies on that.
|
||||
*/
|
||||
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));
|
||||
/* TODO: Add SSH_OPTIONS_BINDADDR */
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* sk_open - open a socket
|
||||
* @s: socket
|
||||
|
@ -1421,6 +1664,13 @@ sk_open(sock *s)
|
|||
do_bind = bind_port || ipa_nonzero(bind_addr);
|
||||
break;
|
||||
|
||||
#ifdef HAVE_LIBSSH
|
||||
case SK_SSH_ACTIVE:
|
||||
s->ttx = ""; /* Force s->ttx != s->tpos */
|
||||
fd = sk_open_ssh(s);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case SK_UDP:
|
||||
fd = socket(af, SOCK_DGRAM, IPPROTO_UDP);
|
||||
bind_port = s->sport;
|
||||
|
@ -1501,6 +1751,7 @@ sk_open(sock *s)
|
|||
ERR2("listen");
|
||||
break;
|
||||
|
||||
case SK_SSH_ACTIVE:
|
||||
case SK_MAGIC:
|
||||
break;
|
||||
|
||||
|
@ -1510,6 +1761,7 @@ sk_open(sock *s)
|
|||
|
||||
if (!(s->flags & SKF_THREAD))
|
||||
sk_insert(s);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
|
@ -1692,6 +1944,28 @@ sk_maybe_write(sock *s)
|
|||
reset_tx_buffer(s);
|
||||
return 1;
|
||||
|
||||
#ifdef HAVE_LIBSSH
|
||||
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;
|
||||
#endif
|
||||
|
||||
case SK_UDP:
|
||||
case SK_IP:
|
||||
{
|
||||
|
@ -1716,6 +1990,7 @@ sk_maybe_write(sock *s)
|
|||
reset_tx_buffer(s);
|
||||
return 1;
|
||||
}
|
||||
|
||||
default:
|
||||
bug("sk_maybe_write: unknown socket type %d", s->type);
|
||||
}
|
||||
|
@ -1795,6 +2070,64 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBSSH
|
||||
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 */
|
||||
}
|
||||
#endif
|
||||
|
||||
/* sk_read() and sk_write() are called from BFD's event loop */
|
||||
|
||||
int
|
||||
|
@ -1828,17 +2161,17 @@ sk_read(sock *s, int revents)
|
|||
else
|
||||
{
|
||||
s->rpos += c;
|
||||
if (s->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;
|
||||
}
|
||||
call_rx_hook(s, s->rpos - s->rbuf);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBSSH
|
||||
case SK_SSH:
|
||||
return sk_read_ssh(s);
|
||||
#endif
|
||||
|
||||
case SK_MAGIC:
|
||||
return s->rx_hook(s, 0);
|
||||
|
||||
|
@ -1877,6 +2210,27 @@ sk_write(sock *s)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBSSH
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
if (s->ttx != s->tpos && sk_maybe_write(s) > 0)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue