mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2025-01-03 07:31:54 +00:00
The Michael Baer's patch for BGPsec Support
Imported from: https://securerouting.net/download/bird-1.5.0-bgpsec-0.7.tar.bz2
This commit is contained in:
parent
deec752ef9
commit
e728484249
275
README.bgpsec
Normal file
275
README.bgpsec
Normal file
@ -0,0 +1,275 @@
|
||||
|
||||
1. BGPSEC tar ball
|
||||
2. Installation Instructions:
|
||||
3. BIRD run time configuration
|
||||
4. Getting RPKI-RTR data (ROA's and Router Keys)
|
||||
5. License(s)
|
||||
|
||||
|
||||
1. BGPSEC patch
|
||||
|
||||
This code adds BGPSEC capability to the BIRD BGP implementation.
|
||||
|
||||
This has only been tested on Linux machines. It is in an Alpha release
|
||||
and ***should not be considered for production systems***. The basic
|
||||
BGPSEC protocol is supported with a several notable exceptions: more
|
||||
than one signature block (for algorithm rollover), confederations, and
|
||||
bugs we have not seen yet.
|
||||
|
||||
For information on BGPSEC see the Internet Engineering Task Force
|
||||
(IETF) Secure Inter-Domain Routing (SIDR) working group page and
|
||||
specifically the draft describing the BGPSEC protocol:
|
||||
|
||||
https://datatracker.ietf.org/wg/sidr/
|
||||
https://datatracker.ietf.org/doc/draft-ietf-sidr-bgpsec-protocol/
|
||||
|
||||
This code is based on the v1.5.0 of the BIRD software. Information
|
||||
about BIRD including download instructions can be found at:
|
||||
|
||||
http://bird.network.cz/
|
||||
|
||||
|
||||
2. Installation Instructions:
|
||||
|
||||
General Instructions
|
||||
|
||||
Building BGPSEC enabled bird
|
||||
|
||||
This describes building bird with BGPSEC support turned on, which
|
||||
requires a few steps. Contents
|
||||
|
||||
2.1 Dependencies
|
||||
2.1.1 Use An OpenSSL version that supports ECDSA (Elliptic
|
||||
Curve Digital Signature Algorithm)
|
||||
2.2 Building Bird
|
||||
2.2.1 Configuring and Compiling
|
||||
2.3 Testing
|
||||
2.4 Using It
|
||||
2.5 Coding For It
|
||||
|
||||
|
||||
2.1 Dependencies
|
||||
|
||||
On Fedora, you'll want flex, bison, and readline-devel packages.
|
||||
|
||||
2.1.1 Use an OpenSSL version that supports ECDSA (Elliptic Curve
|
||||
Digital Signature Algorithm)
|
||||
|
||||
The default OpenSSL distributed on some Linux vendors does not include
|
||||
elliptic curve support. If yours distribution does not support
|
||||
elliptic curve in the OpenSSL libraries, you'll need to grab a fresh
|
||||
copy and compile it by hand. You may want to install it in a location
|
||||
separate from the normally installed package. Use the --prefix option
|
||||
to do this:
|
||||
|
||||
# ./config --prefix=/usr/local/openssl-ecdsa
|
||||
|
||||
Then make and make install
|
||||
|
||||
|
||||
2.2 Building Bird
|
||||
|
||||
Configuring and Compiling
|
||||
|
||||
If you are using the patch, download BIRD bird-1.4.5.tar.gz from
|
||||
http://bird.network.cz/
|
||||
|
||||
# tar xvjpf bird-1.5.0.-bgpsec-0.7.tar.bz2
|
||||
# cd bird-1.5.0-bgpsec-0.7/
|
||||
|
||||
Build it.
|
||||
First rebuild configure (configure.in was changed by the patch):
|
||||
|
||||
# autoconf
|
||||
|
||||
Then Use configure flags that look something like the following. if a
|
||||
version of OpenSSL that supported ecdsa had to be installed in a
|
||||
non-standard location on your platform, it will be necessary to add
|
||||
something like '-I/path/to//openssl-ecdsa/include' and
|
||||
'-L/path/to/openssl-ecdsa/lib' options to the configure command.
|
||||
|
||||
# ./configure '--enable-bgpsec'
|
||||
|
||||
Then make and you should be good to go.
|
||||
|
||||
|
||||
2.3 Using It
|
||||
|
||||
You can create key pairs using the proto/bgp/bgpsec/keytool.py
|
||||
script. For Example:
|
||||
|
||||
# proto/bgp/bgpsec/keytool.py --printski --public-key-dir /usr/share/bird/bgpsec-keys --private-key-dir /usr/share/bird/bgpsec-private-keys generate 'ASN'
|
||||
40C70252FE48D29401E9156ADBECF3EF42296AE4
|
||||
|
||||
Where ASN is the AS number for the key you are generating.
|
||||
|
||||
The generated public key is stored in '--public-key-dir' (default
|
||||
/usr/share/bird/bgpsec-keys) and the private key is stored in
|
||||
'--private-key-dir' (default /usr/share/bird/bgpsec-private-keys).
|
||||
The file names are based on the AS number and the SKI value associated
|
||||
with the keys, 'ASN.SKI#', e.g. for an ASN of 12345,
|
||||
12345.40C70252FE48D29401E9156ADBECF3EF42296AE4.
|
||||
|
||||
The public key can be copied to other machines and placed in the same
|
||||
public key directory without the private key. Likewise, keys from
|
||||
other routers can be placed into the public key directory with their
|
||||
ASN/SKI identifying the file names in order for the validation
|
||||
routines to look them up.
|
||||
|
||||
NOTE: in the future, the rpki-rtr protocol could be used instead to
|
||||
pull router keys. For example, BGPSEC-BIRD-Client is a tool that can
|
||||
pull router keys from a rpki cache using the rpki-rtr protocol.
|
||||
|
||||
|
||||
2.4 Coding For It
|
||||
|
||||
The API for use in validating stuff can be found in
|
||||
proto/bgp/bgpsec/validate.h. But most importantly, these two functions
|
||||
will be of the most use:
|
||||
|
||||
int bgpsec_sign_data_with_ski(...);
|
||||
int bgpsec_verify_signature_with_ski(...);
|
||||
|
||||
As they sign and verify data simply by passing the data along with a
|
||||
SKI in ascii/hex form and a ASN integer (in reality, it's just the
|
||||
filename from above so as long as it can be stored in a file name it's
|
||||
usable).
|
||||
|
||||
The algorithm option should be set to
|
||||
BGPSEC_ALGORITHM_SHA256_ECDSA_P_256 or BGPSEC_DEFAULT_CURVE.
|
||||
|
||||
|
||||
3. BIRD run time configuration
|
||||
|
||||
The BGPSEC implementation currently has several additional
|
||||
configuration options for the configuration file. The following is an
|
||||
example bgp section from a BIRD configuration file supporting BGPSEC:
|
||||
|
||||
protocol bgp {
|
||||
# BGPsec configuration
|
||||
|
||||
# AS4 is required for BGPSEC, this must be enabled
|
||||
enable as4;
|
||||
|
||||
# enable bgpsec for this connection
|
||||
bgpsec on;
|
||||
|
||||
# The local BIRD router subject key identifier (SKI) for this
|
||||
# connection. 'bgpsec_ski' identifies the (private) key that
|
||||
# the local BIRD router should use to sign BGPSEC packets on
|
||||
# this connection.
|
||||
bgpsec_ski "8CA56CF0A4D943ACCEB9CB67967561CA8A773B73" ;
|
||||
|
||||
# The local directory paths for the public router key and private
|
||||
# key storage. The defaults are below:
|
||||
|
||||
bgpsec_key_repo_path "/usr/share/bird/bgpsec-keys/" ;
|
||||
bgpsec_priv_key_path "/usr/share/bird/bgpsec-private-keys" ;
|
||||
|
||||
# bgpsec_no_pcount0 indicates whether a peer is allowed to
|
||||
# set its pcount to 0. Default is true. Set this value to
|
||||
# false/0 if you want to allow your peer to not have their AS
|
||||
# included in the effective AS_PATH of a route (e.g. Route
|
||||
# Servers).
|
||||
bgpsec_no_pcount0 1;
|
||||
|
||||
# bgpsec_prefer indicates whether validly signed bgpsec
|
||||
# routes are preferred to non-valid and/or non-signed
|
||||
# routes. Default is true. This decision is made after the
|
||||
# local pref and before the as_path comparison in the best
|
||||
# route selection algorithm.
|
||||
bgpsec_prefer 1;
|
||||
|
||||
# bgpsec_require indicates whether bgpsec signed routes are
|
||||
# required on this connection. If true, Non-signed routes
|
||||
# will not be accepted. Default is false.
|
||||
bgpsec_require 0;
|
||||
|
||||
# bgpsec_no_invalid_routes indicates if invalid routes are
|
||||
# accepted. If true, routes that fail the BGPsec validity
|
||||
# check are not accepted. Default is false.
|
||||
bgpsec_no_invalid_routes 0;
|
||||
|
||||
|
||||
# Non BGPsec configuration
|
||||
|
||||
description "BGP Link";
|
||||
local as 64521;
|
||||
|
||||
neighbor 172.16.1.2 as 64522;
|
||||
gateway direct;
|
||||
|
||||
path metric 1; # prefer shorter paths
|
||||
default bgp_med 0; # when none is available
|
||||
|
||||
password "demonet";
|
||||
}
|
||||
|
||||
|
||||
4. Getting RPKI-RTR data (ROA's and Router Keys)
|
||||
|
||||
BGPSEC-BIRD-client is a separate application that is provided in order
|
||||
to pull data from a rpki-rtr using rtrLib. It can garner Router
|
||||
Origin Authorizations (ROAs) from a rpki-rtr and populate BIRD's ROA
|
||||
tables in order to filter for Origin Authentication. It can get
|
||||
router public keys and place them in the local file system for use by
|
||||
the BGPsec code. Please see the README with that software for
|
||||
instructions on how to use it.
|
||||
|
||||
|
||||
5. License(s)
|
||||
|
||||
This BGPSEC code created by Parsons, Inc.
|
||||
|
||||
(c) 2013-2016 Parsons, Inc.
|
||||
All Rights Reserved
|
||||
|
||||
Code within this patch is dual copyrighted under both the GPLv2+ and
|
||||
the BSD license. It can be used under either license below:
|
||||
|
||||
|
||||
GPLv2+
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
USA
|
||||
|
||||
|
||||
BSD
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of Parsons, Inc nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
|
||||
IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
18
configure.in
18
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(ipv6, [ --enable-ipv6 enable building of IPv6 version (default: disabled)],,enable_ipv6=no)
|
||||
AC_ARG_ENABLE(bgpsec,[ --enable-bgpsec enable building of bgp with security (default: disabled)],,enable_bgpsec=no)
|
||||
AC_ARG_ENABLE(pthreads, [ --enable-pthreads enable POSIX threads support (default: detect)],,enable_pthreads=try)
|
||||
AC_ARG_WITH(suffix, [ --with-suffix=STRING use specified suffix for BIRD files (default: 6 for IPv6 version)],[given_suffix="yes"])
|
||||
AC_ARG_WITH(sysconfig, [ --with-sysconfig=FILE use specified BIRD system configuration file])
|
||||
@ -21,7 +22,6 @@ AC_ARG_VAR([FLEX], [location of the Flex program])
|
||||
AC_ARG_VAR([BISON], [location of the Bison program])
|
||||
AC_ARG_VAR([M4], [location of the M4 program])
|
||||
|
||||
|
||||
if test "$srcdir" = . ; then
|
||||
# Building in current directory => create obj directory holding all objects
|
||||
objdir=obj
|
||||
@ -262,6 +262,21 @@ if test "$enable_debug" = yes ; then
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([BGPsec enabled])
|
||||
if test "$enable_bgpsec" = yes ; then
|
||||
AC_MSG_RESULT(yes)
|
||||
protocols="$protocols bgp/bgpsec"
|
||||
AC_CHECK_LIB(dl, dlopen)
|
||||
AC_CHECK_LIB(crypto, PEM_read_X509)
|
||||
AC_CHECK_LIB(crypto, EC_KEY_set_asn1_flag)
|
||||
if test $ac_cv_lib_crypto_EC_KEY_set_asn1_flag != yes ; then
|
||||
AC_MSG_ERROR([openssl: libcrypt does not support elliptical curves. EC support is required for BGPsec])
|
||||
fi
|
||||
AC_DEFINE(CONFIG_BGPSEC)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
CLIENT=
|
||||
CLIENT_LIBS=
|
||||
if test "$enable_client" = yes ; then
|
||||
@ -304,6 +319,7 @@ BIRD was configured with the following options:
|
||||
Debugging: $enable_debug
|
||||
POSIX threads: $enable_pthreads
|
||||
Routing protocols: $protocols
|
||||
BGPsec enabled: $enable_bgpsec
|
||||
Client: $enable_client
|
||||
EOF
|
||||
rm -f $objdir/.*-stamp
|
||||
|
@ -19,6 +19,9 @@
|
||||
|
||||
#include "lib/string.h"
|
||||
|
||||
/* XXX need ifdef to intsead use bsd's #include <sys/endian.h> */
|
||||
#include <endian.h>
|
||||
|
||||
static inline u16
|
||||
get_u16(void *p)
|
||||
{
|
||||
@ -35,6 +38,14 @@ get_u32(void *p)
|
||||
return ntohl(x);
|
||||
}
|
||||
|
||||
static inline u64
|
||||
get_u64(void *p)
|
||||
{
|
||||
u64 x;
|
||||
memcpy(&x, p, 8);
|
||||
return be64toh(x);
|
||||
}
|
||||
|
||||
static inline void
|
||||
put_u16(void *p, u16 x)
|
||||
{
|
||||
@ -49,4 +60,11 @@ put_u32(void *p, u32 x)
|
||||
memcpy(p, &x, 4);
|
||||
}
|
||||
|
||||
static inline void
|
||||
put_u64(void *p, u64 x)
|
||||
{
|
||||
x = htobe64(x);
|
||||
memcpy(p, &x, 8);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -76,7 +76,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_ospf, proto_pipe, proto_bgp, proto_bgpsec, proto_bfd;
|
||||
|
||||
/*
|
||||
* Routing Protocol Instance
|
||||
|
@ -407,7 +407,8 @@ typedef struct eattr {
|
||||
#define EAP_RIP 2 /* RIP */
|
||||
#define EAP_OSPF 3 /* OSPF */
|
||||
#define EAP_KRT 4 /* Kernel route attributes */
|
||||
#define EAP_MAX 5
|
||||
#define EAP_BGPSEC 5 /* BGPSEC attributes */
|
||||
#define EAP_MAX 6
|
||||
|
||||
#define EA_CODE(proto,id) (((proto) << 8) | (id))
|
||||
#define EA_PROTO(ea) ((ea) >> 8)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -4,6 +4,14 @@
|
||||
* (c) 2000 Martin Mares <mj@ucw.cz>
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*
|
||||
*
|
||||
* Code added from Parsons, Inc. (BGPSEC additions)
|
||||
* (c) 2013-2013
|
||||
*
|
||||
* Can be used under either license:
|
||||
* - Freely distributed and used under the terms of the GNU GPLv2.
|
||||
* - Freely distributed and used under a BSD license, See README.bgpsec.
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -78,6 +86,10 @@
|
||||
|
||||
#include "bgp.h"
|
||||
|
||||
#ifdef CONFIG_BGPSEC
|
||||
/* sscanf parsing of SKI configuration value */
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
struct linpool *bgp_linpool; /* Global temporary pool */
|
||||
static sock *bgp_listen_sk; /* Global listening socket */
|
||||
@ -1266,7 +1278,6 @@ bgp_check_config(struct bgp_config *c)
|
||||
if (c->c.class == SYM_TEMPLATE)
|
||||
return;
|
||||
|
||||
|
||||
/* EBGP direct by default, IBGP multihop by default */
|
||||
if (c->multihop < 0)
|
||||
c->multihop = internal ? 64 : 0;
|
||||
@ -1283,7 +1294,6 @@ bgp_check_config(struct bgp_config *c)
|
||||
if (c->c.in_limit && (c->c.in_limit->action == PLA_RESTART) && c->disable_after_error)
|
||||
c->c.in_limit->action = PLA_DISABLE;
|
||||
|
||||
|
||||
if (!c->local_as)
|
||||
cf_error("Local AS number must be set");
|
||||
|
||||
@ -1329,6 +1339,61 @@ bgp_check_config(struct bgp_config *c)
|
||||
|
||||
if (c->secondary && !c->c.table->sorted)
|
||||
cf_error("BGP with secondary option requires sorted table");
|
||||
|
||||
#ifdef CONFIG_BGPSEC
|
||||
/* create a binary SKI from config */
|
||||
if ( c->enable_bgpsec ) {
|
||||
if ( strnlen(c->bgpsec_ski, (2 * BGPSEC_SKI_LENGTH))
|
||||
!= (BGPSEC_SKI_LENGTH * 2) ) {
|
||||
cf_error("BGPSEC: bad length of the configured SKI value");
|
||||
}
|
||||
|
||||
if ( BGPSEC_SKI_LENGTH !=
|
||||
sscanf(c->bgpsec_ski, "%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
|
||||
(unsigned char *)c->bgpsec_bski,
|
||||
(unsigned char *)(c->bgpsec_bski+1),
|
||||
(unsigned char *)(c->bgpsec_bski+2),
|
||||
(unsigned char *)(c->bgpsec_bski+3),
|
||||
(unsigned char *)(c->bgpsec_bski+4),
|
||||
(unsigned char *)(c->bgpsec_bski+5),
|
||||
(unsigned char *)(c->bgpsec_bski+6),
|
||||
(unsigned char *)(c->bgpsec_bski+7),
|
||||
(unsigned char *)(c->bgpsec_bski+8),
|
||||
(unsigned char *)(c->bgpsec_bski+9),
|
||||
(unsigned char *)(c->bgpsec_bski+10),
|
||||
(unsigned char *)(c->bgpsec_bski+11),
|
||||
(unsigned char *)(c->bgpsec_bski+12),
|
||||
(unsigned char *)(c->bgpsec_bski+13),
|
||||
(unsigned char *)(c->bgpsec_bski+14),
|
||||
(unsigned char *)(c->bgpsec_bski+15),
|
||||
(unsigned char *)(c->bgpsec_bski+16),
|
||||
(unsigned char *)(c->bgpsec_bski+17),
|
||||
(unsigned char *)(c->bgpsec_bski+18),
|
||||
(unsigned char *)(c->bgpsec_bski+19)) ) {
|
||||
cf_error("BGPSEC: unable to parse the configured SKI value");
|
||||
}
|
||||
|
||||
|
||||
log(L_WARN "BPGPSEC: bgpsec_key_repo_path is %s", c->bgpsec_key_repo_path);
|
||||
if (c->bgpsec_key_repo_path) {
|
||||
int krp = strnlen(c->bgpsec_key_repo_path, 10);
|
||||
if ( 0 < krp && krp < 2 ) {
|
||||
log(L_WARN "BPGPSEC: unable to parse bpgsec_key_repo_path: %d", krp);
|
||||
cf_error("BGPSEC:: unable to parse bpgsec_key_repo_path");
|
||||
}
|
||||
}
|
||||
log(L_WARN "BPGPSEC: bgpsec_key_repo_path is %s", c->bgpsec_priv_key_path);
|
||||
if (c->bgpsec_priv_key_path) {
|
||||
int pkp = strnlen(c->bgpsec_priv_key_path, 10);
|
||||
if ( 0 < pkp && pkp < 2 ) {
|
||||
log(L_WARN "BPGPSEC: unable to parse bpgsec_key_repo_path: %d", pkp);
|
||||
cf_error("BGPSEC:: unable to parse bpgsec_key_repo_path");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
static int
|
||||
|
144
proto/bgp/bgp.h
144
proto/bgp/bgp.h
@ -4,6 +4,14 @@
|
||||
* (c) 2000 Martin Mares <mj@ucw.cz>
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*
|
||||
*
|
||||
* Code added from Parsons, Inc. (BGPSEC additions)
|
||||
* (c) 2013-2013
|
||||
*
|
||||
* Can be used under either license:
|
||||
* - Freely distributed and used under the terms of the GNU GPLv2.
|
||||
* - Freely distributed and used under a BSD license, See README.bgpsec.
|
||||
*/
|
||||
|
||||
#ifndef _BIRD_BGP_H_
|
||||
@ -17,6 +25,24 @@
|
||||
struct linpool;
|
||||
struct eattr;
|
||||
|
||||
#ifdef CONFIG_BGPSEC
|
||||
/* BGPSec constants */
|
||||
#define BGPSEC_VERSION 0
|
||||
/* currently capability is arbitrary number from private use */
|
||||
#define BGPSEC_CAPABILITY 212
|
||||
#define BGPSEC_SKI_LENGTH 20
|
||||
#define BGPSEC_ALGO_ID 1 /* XXX this needs to be changed */
|
||||
#define BGPSEC_MAX_SIG_LENGTH 80
|
||||
/* sig hash length is somewhat arbitrary,
|
||||
= 20 + MaxASPathLength*(28 + max_sig_length).
|
||||
As of 2016, max unique AS Path length found is 14.
|
||||
This value will allowy for for a hash buffer that can handle an AS
|
||||
path length ~47 long
|
||||
*/
|
||||
#define BGPSEC_SIG_HASH_LENGTH 5120
|
||||
#define BGPSEC_MAX_INFO_ATTR_LENGTH 0 /* XXX this needs to be checked */
|
||||
#endif
|
||||
|
||||
struct bgp_config {
|
||||
struct proto_config c;
|
||||
u32 local_as, remote_as;
|
||||
@ -40,6 +66,20 @@ struct bgp_config {
|
||||
int capabilities; /* Enable capability handshake [RFC3392] */
|
||||
int enable_refresh; /* Enable local support for route refresh [RFC2918] */
|
||||
int enable_as4; /* Enable local support for 4B AS numbers [RFC4893] */
|
||||
|
||||
/* BGPSec */
|
||||
/* cannot be ifdef'd out due to config.Y compatibility */
|
||||
int enable_bgpsec; /* Whether neighbor should be a BGPSec peer */
|
||||
int bgpsec_prefer; /* Whether validly signed BGPsec routes are prefered during route selection */
|
||||
int bgpsec_require; /* Whether neighbor should be a BGPSec peer */
|
||||
char *bgpsec_ski; /* local subject key id */
|
||||
u8 bgpsec_bski[BGPSEC_SKI_LENGTH]; /* binary local SKI */
|
||||
char *bgpsec_key_repo_path; /* Path to the public key repository */
|
||||
char *bgpsec_priv_key_path; /* Path to the private key location */
|
||||
int bgpsec_save_binary_keys; /* Save a copy of the binary key */
|
||||
int bgpsec_no_pcount0; /* allow peer to have pcount 0, xxx current default allows */
|
||||
int bgpsec_no_invalid_routes; /* should invalid routes be dropped */
|
||||
|
||||
u32 rr_cluster_id; /* Route reflector cluster ID, if different from local ID */
|
||||
int rr_client; /* Whether neighbor is RR client of me */
|
||||
int rs_client; /* Whether neighbor is RS client of me */
|
||||
@ -100,6 +140,12 @@ struct bgp_conn {
|
||||
byte *notify_data;
|
||||
u32 advertised_as; /* Temporary value for AS number received */
|
||||
int start_state; /* protocol start_state snapshot when connection established */
|
||||
|
||||
#ifdef CONFIG_BGPSEC
|
||||
/* BGPsec */
|
||||
u8 peer_bgpsec_support; /* Peer supports BGPSec */
|
||||
#endif
|
||||
|
||||
u8 peer_refresh_support; /* Peer supports route refresh [RFC2918] */
|
||||
u8 peer_as4_support; /* Peer supports 4B AS numbers [RFC4893] */
|
||||
u8 peer_add_path; /* Peer supports ADD-PATH [draft] */
|
||||
@ -117,6 +163,15 @@ struct bgp_proto {
|
||||
struct bgp_config *cf; /* Shortcut to BGP configuration */
|
||||
u32 local_as, remote_as;
|
||||
int start_state; /* Substates that partitions BS_START */
|
||||
|
||||
#ifdef CONFIG_BGPSEC
|
||||
/* BGPsec */
|
||||
u8 bgpsec_send; /* Sender can send BGPSec messages */
|
||||
u8 bgpsec_receive; /* Sender can receive BGPSec messages */
|
||||
u8 bgpsec_ipv4; /* Sender uses BGPSec over iPv4 */
|
||||
u8 bgpsec_ipv6; /* Sender uses BGPSec over iPv6 */
|
||||
#endif
|
||||
|
||||
u8 is_internal; /* Internal BGP connection (local_as == remote_as) */
|
||||
u8 as4_session; /* Session uses 4B AS numbers in AS_PATH (both sides support it) */
|
||||
u8 add_path_rx; /* Session expects receive of ADD-PATH extended NLRI */
|
||||
@ -152,7 +207,7 @@ struct bgp_proto {
|
||||
u8 last_error_class; /* Error class of last error */
|
||||
u32 last_error_code; /* Error code of last error. BGP protocol errors
|
||||
are encoded as (bgp_err_code << 16 | bgp_err_subcode) */
|
||||
#ifdef IPV6
|
||||
#if defined(IPV6) || defined CONFIG_BGPSEC
|
||||
byte *mp_reach_start, *mp_unreach_start; /* Multiprotocol BGP attribute notes */
|
||||
unsigned mp_reach_len, mp_unreach_len;
|
||||
ip_addr local_link; /* Link-level version of source_addr */
|
||||
@ -235,7 +290,7 @@ static inline void set_next_hop(byte *b, ip_addr addr) { ((ip_addr *) b)[0] = ad
|
||||
|
||||
void bgp_attach_attr(struct ea_list **to, struct linpool *pool, unsigned attr, uintptr_t val);
|
||||
byte *bgp_attach_attr_wa(struct ea_list **to, struct linpool *pool, unsigned attr, unsigned len);
|
||||
struct rta *bgp_decode_attrs(struct bgp_conn *conn, byte *a, unsigned int len, struct linpool *pool, int mandatory);
|
||||
struct rta *bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct linpool *pool, byte * nlri, int nlri_len);
|
||||
int bgp_get_attr(struct eattr *e, byte *buf, int buflen);
|
||||
int bgp_rte_better(struct rte *, struct rte *);
|
||||
int bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best);
|
||||
@ -278,6 +333,8 @@ void bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsi
|
||||
#define BAF_PARTIAL 0x20
|
||||
#define BAF_EXT_LEN 0x10
|
||||
|
||||
/* Note: these must match location in the bgp_attr_table */
|
||||
|
||||
#define BA_ORIGIN 0x01 /* [RFC1771] */ /* WM */
|
||||
#define BA_AS_PATH 0x02 /* WM */
|
||||
#define BA_NEXT_HOP 0x03 /* WM */
|
||||
@ -287,16 +344,34 @@ void bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsi
|
||||
#define BA_AGGREGATOR 0x07 /* OT */
|
||||
#define BA_COMMUNITY 0x08 /* [RFC1997] */ /* OT */
|
||||
#define BA_ORIGINATOR_ID 0x09 /* [RFC1966] */ /* ON */
|
||||
#define BA_CLUSTER_LIST 0x0a /* ON */
|
||||
#define BA_CLUSTER_LIST 0x0a /* [RFC4456] */
|
||||
/* We don't support these: */
|
||||
#define BA_DPA 0x0b /* ??? */
|
||||
#define BA_DPA 0x0b /* DPA deprecated */
|
||||
#define BA_ADVERTISER 0x0c /* [RFC1863] */
|
||||
#define BA_RCID_PATH 0x0d
|
||||
#define BA_MP_REACH_NLRI 0x0e /* [RFC2283] */
|
||||
#define BA_MP_UNREACH_NLRI 0x0f
|
||||
#define BA_RCID_PATH 0x0d /* [RFC1863] */
|
||||
/* supported? */
|
||||
#define BA_MP_REACH_NLRI 0x0e /* [RFC4760] */
|
||||
#define BA_MP_UNREACH_NLRI 0x0f /* [RFC4760] */
|
||||
#define BA_EXT_COMMUNITY 0x10 /* [RFC4360] */
|
||||
#define BA_AS4_PATH 0x11 /* [RFC4893] */
|
||||
#define BA_AS4_AGGREGATOR 0x12
|
||||
#define BA_AS4_PATH 0x11 /* [RFC6793] */
|
||||
#define BA_AS4_AGGREGATOR 0x12 /* [RFC6793] */
|
||||
/* not supported */
|
||||
#define BA_SSA 0x13 /* SAFI Specific Attribute (SSA) (deprecated) */
|
||||
#define BA_CONNECTOR_ATTR 0x14 /* (deprecated) [RFC6037] */
|
||||
#define BA_AS_PATHLIMIT 0x15 /* (deprecated) [draft-ietf-idr-as-pathlimit] */
|
||||
#define BA_PMSI_TUNNEL 0x16 /* [RFC6514] */
|
||||
#define BA_TUNNEL_ENCAP 0x17 /* Tunnel Encapsulation [RFC5512] */
|
||||
#define BA_TUNNEL_ENGINEERING 0x18 /* Traffic Engineering [RFC5543] */
|
||||
#define BA_IPV6_EXT_COMMUNITY 0x19 /* IPv6 Address Specific Extended Community [RFC5701] */
|
||||
#define BA_AIGP 0x1a /* AIGP (TEMPORARY, expired 2013-04-25) [draft-ietf-idr-aigp][Rex_Fernando][Pradosh_Mohapatra][Eric_Rosen][James_Uttaro] */
|
||||
#define BA_PE_DIST_LABELS 0x1b /* PE Distinguisher Labels [RFC6514] */
|
||||
#define BA_ENTROPY_LABELS 0x1c /* BGP Entropy Label Capability Attribute [RFC6790] */
|
||||
#define BA_LS_ATTRIBUTE 0x1d /* BGP-LS Attribute (TEMPORARY, expired 2014-03-11) [draft-ietf-idr-ls-distribution] */
|
||||
|
||||
/* Supported */
|
||||
#define BA_BGPSEC_SIGNATURE 0x1E /* XXX 30 is best guess, draft-ietf-sidr-bgpsec-protocol */
|
||||
/* internal use only */
|
||||
#define BA_INTERNAL_BGPSEC_VALID 0xdd
|
||||
|
||||
/* BGP connection states */
|
||||
|
||||
@ -376,6 +451,18 @@ void bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsi
|
||||
|
||||
#define BEA_ROUTE_LIMIT_EXCEEDED 1
|
||||
|
||||
/* BGP Update Error codes */
|
||||
#define BGP_UPD_ERROR_MALFORMED_ATTR 1
|
||||
#define BGP_UPD_ERROR_UNRCGNZD_WK_ATTR 2
|
||||
#define BGP_UPD_ERROR_MISSING_WK_ATTR 3
|
||||
#define BGP_UPD_ERROR_ATTR_FLAG 4
|
||||
#define BGP_UPD_ERROR_ATTR_LENGTH 5
|
||||
#define BGP_UPD_ERROR_INVALID_ORGIN 6
|
||||
#define BGP_UPD_ERROR_INVALID_HOP 8
|
||||
#define BGP_UPD_ERROR_OPT_ATTR 9
|
||||
#define BGP_UPD_ERROR_INVALID_NETWORK 10
|
||||
#define BGP_UPD_ERROR_MALFORMED_ASPATH 11
|
||||
|
||||
/* Well-known communities */
|
||||
|
||||
#define BGP_COMM_NO_EXPORT 0xffffff01 /* Don't export outside local AS / confed. */
|
||||
@ -399,4 +486,43 @@ void bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsi
|
||||
#define BGP_AF BGP_AF_IPV4
|
||||
#endif
|
||||
|
||||
#define DO_NLRI(name) \
|
||||
start = x = p->name##_start; \
|
||||
len = len0 = p->name##_len; \
|
||||
if (len) \
|
||||
{ \
|
||||
if (len < 3) { err=9; goto done; } \
|
||||
af = get_u16(x); \
|
||||
sub = x[2]; \
|
||||
x += 3; \
|
||||
len -= 3; \
|
||||
DBG("\tNLRI AF=%d sub=%d len=%d\n", af, sub, len);\
|
||||
} \
|
||||
else \
|
||||
af = 0; \
|
||||
if ((af == BGP_AF_IPV6) || (af == BGP_AF_IPV4))
|
||||
|
||||
|
||||
#define DECODE_PREFIX(pp, ll) do { \
|
||||
if (p->add_path_rx) \
|
||||
{ \
|
||||
if (ll < 5) { err=1; goto done; } \
|
||||
path_id = get_u32(pp); \
|
||||
pp += 4; \
|
||||
ll -= 4; \
|
||||
} \
|
||||
int b = *pp++; \
|
||||
int q; \
|
||||
ll--; \
|
||||
if (b > BITS_PER_IP_ADDRESS) { err=10; goto done; } \
|
||||
q = (b+7) / 8; \
|
||||
if (ll < q) { err=1; goto done; } \
|
||||
memcpy(&prefix, pp, q); \
|
||||
pp += q; \
|
||||
ll -= q; \
|
||||
ipa_ntoh(prefix); \
|
||||
prefix = ipa_and(prefix, ipa_mkmask(b)); \
|
||||
pxlen = b; \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
11
proto/bgp/bgpsec/Makefile
Normal file
11
proto/bgp/bgpsec/Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
source=validate.c
|
||||
root-rel=../../../
|
||||
dir-name=proto/bgp/bgpsec
|
||||
|
||||
include ../../../Rules
|
||||
|
||||
validate_tmp.o: ../../../../proto/bgp/bgpsec/validate.c
|
||||
gcc -c $(CFLAGS) -DLOG_TO_STDERR -o $@ $<
|
||||
|
||||
tests: tests.o validate_tmp.o
|
||||
gcc -o $@ $^ bgpsec.o $(LDFLAGS) $(LIBS)
|
190
proto/bgp/bgpsec/bgpsec-create-hash-dir
Executable file
190
proto/bgp/bgpsec/bgpsec-create-hash-dir
Executable file
@ -0,0 +1,190 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
use File::Find;
|
||||
use strict;
|
||||
my %opts =
|
||||
(
|
||||
'openssl' => "/usr/local/openssl-ecdsa/bin",
|
||||
'f' => "\.cer\$",
|
||||
'i' => "DER",
|
||||
);
|
||||
|
||||
LocalGetOptions(\%opts,
|
||||
['GUI:otherargs', '[input-directory] [output-directory]'],
|
||||
["GUI:separator", "Processing Configuration"],
|
||||
["f|file-regexp=s", "Regexp of filenames to select from"],
|
||||
["s|suffix=s", "Use STRING as the new extension suffix"],
|
||||
["i|input-format=s", "Input format type of files found"],
|
||||
|
||||
["GUI:separator", "Basic Configuration"],
|
||||
["openssl|openssl-path=s", "Path to OpenSSL install directory with EC support"],
|
||||
|
||||
["GUI:separator", "Output Options"],
|
||||
["v|verbose", "Be verbose about what is being done"],
|
||||
);
|
||||
|
||||
my $inputDirectory = $ARGV[0];
|
||||
my $outputDirectory = $ARGV[1];
|
||||
|
||||
my $opensslPath = $opts{'openssl'};
|
||||
my $fileRegexp = $opts{'f'};
|
||||
my $inform = $opts{'i'};
|
||||
|
||||
# ensure we have input and output
|
||||
if (!defined($inputDirectory) || !defined($outputDirectory)) {
|
||||
print STDERR "Both an input and output directory are required\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
# add in a separate openssl path
|
||||
if ($opensslPath) {
|
||||
$ENV{'PATH'} = $opensslPath . ":" . $ENV{'PATH'};
|
||||
}
|
||||
|
||||
if (! -d $outputDirectory) {
|
||||
Verbose("creating $outputDirectory\n");
|
||||
mkdir($outputDirectory);
|
||||
}
|
||||
|
||||
# find and process every file of certain types
|
||||
Verbose("Searching directory $inputDirectory\n");
|
||||
find({no_chdir => 1, wanted => \&convert_file}, $inputDirectory);
|
||||
|
||||
sub convert_file {
|
||||
if ($File::Find::name =~ /$fileRegexp/io) {
|
||||
my $ski;
|
||||
# process it...
|
||||
Verbose("Found $File::Find::name\n");
|
||||
open(SKI, "openssl x509 -inform $inform -in $File::Find::name -text|");
|
||||
while(<SKI>) {
|
||||
if (/X509v3 Subject Key Identifier/) {
|
||||
$ski = <SKI>;
|
||||
$ski =~ s/\s//g;
|
||||
$ski =~s/://g;
|
||||
last;
|
||||
}
|
||||
}
|
||||
close(SKI);
|
||||
|
||||
if (defined($ski)) {
|
||||
my $suffix;
|
||||
if ($opts{'s'}) {
|
||||
$suffix = $opts{'s'};
|
||||
} else {
|
||||
$suffix = $File::Find::name;
|
||||
$suffix =~ s/.*\.//;
|
||||
}
|
||||
|
||||
Verbose(" linking to $ski.$suffix\n");
|
||||
symlink($File::Find::name, "$outputDirectory/$ski.$suffix");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
######################################################################
|
||||
# support functions
|
||||
#
|
||||
|
||||
sub Verbose {
|
||||
print STDERR @_ if ($opts{'v'});
|
||||
}
|
||||
|
||||
######################################################################
|
||||
# Getopt bootstrapping
|
||||
#
|
||||
sub LocalGetOptions {
|
||||
if (eval {require Getopt::GUI::Long;}) {
|
||||
import Getopt::GUI::Long;
|
||||
# optional configure call
|
||||
Getopt::GUI::Long::Configure(qw(display_help no_gui no_ignore_case allow_zero));
|
||||
return GetOptions(@_);
|
||||
}
|
||||
require Getopt::Long;
|
||||
import Getopt::Long;
|
||||
# optional configure call
|
||||
Getopt::Long::Configure(qw(auto_help no_ignore_case));
|
||||
GetOptions(LocalOptionsMap(@_));
|
||||
}
|
||||
|
||||
sub LocalOptionsMap {
|
||||
my ($st, $cb, @opts) = ((ref($_[0]) eq 'HASH')
|
||||
? (1, 1, $_[0]) : (0, 2));
|
||||
for (my $i = $st; $i <= $#_; $i += $cb) {
|
||||
if ($_[$i]) {
|
||||
next if (ref($_[$i]) eq 'ARRAY' && $_[$i][0] =~ /^GUI:/);
|
||||
push @opts, ((ref($_[$i]) eq 'ARRAY') ? $_[$i][0] : $_[$i]);
|
||||
push @opts, $_[$i+1] if ($cb == 2);
|
||||
}
|
||||
}
|
||||
return @opts;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
bgpsec-create-hash-dir - creates a SKI-based hash directory
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
bgpsec-create-hash-dir /path/to/rpki-authenticated-dir /path/to/hash-dir
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
=head2 Processing Configuration
|
||||
|
||||
=over 4
|
||||
|
||||
=item -f STRING
|
||||
|
||||
|
||||
|
||||
=item --file-regexp=STRING
|
||||
|
||||
Regexp of filenames to select from
|
||||
|
||||
=item -s STRING
|
||||
|
||||
|
||||
|
||||
=item --suffix=STRING
|
||||
|
||||
Use STRING as the new extension suffix
|
||||
|
||||
=item -i STRING
|
||||
|
||||
=item --input-format=STRING
|
||||
|
||||
Input format of files found. Defaults to DER.
|
||||
|
||||
=back
|
||||
|
||||
=head2 Basic Configuration
|
||||
|
||||
=over 4
|
||||
|
||||
=item --openssl=STRING
|
||||
|
||||
|
||||
|
||||
=item --openssl-path=STRING
|
||||
|
||||
Path to OpenSSL install directory with EC support
|
||||
|
||||
=back
|
||||
|
||||
=head2 Output Options
|
||||
|
||||
=over 4
|
||||
|
||||
=item -v
|
||||
|
||||
|
||||
|
||||
=item --verbose
|
||||
|
||||
Be verbose about what is being done
|
||||
|
||||
=back
|
27
proto/bgp/bgpsec/config.Y
Normal file
27
proto/bgp/bgpsec/config.Y
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* BIRD -- The Border Gateway Protocol
|
||||
*
|
||||
*
|
||||
* Parsons, Inc.
|
||||
* (c) 2013-2013
|
||||
*
|
||||
* Code can be used under either license:
|
||||
* - Freely distributed and used under the terms of the GNU GPLv2.
|
||||
* - Freely distributed and used under a BSD license, See README.bgpsec.
|
||||
*/
|
||||
|
||||
CF_HDR
|
||||
|
||||
/* #include "proto/bgp/bgpsec/bgpsec.h" */
|
||||
|
||||
CF_DEFINES
|
||||
|
||||
CF_DECLS
|
||||
|
||||
CF_KEYWORDS(BGPSEC_CONFIG)
|
||||
|
||||
CF_GRAMMAR
|
||||
|
||||
CF_CODE
|
||||
|
||||
CF_END
|
225
proto/bgp/bgpsec/keytool.py
Executable file
225
proto/bgp/bgpsec/keytool.py
Executable file
@ -0,0 +1,225 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (C) 2014 Dragon Research Labs ("DRL")
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for any
|
||||
# purpose with or without fee is hereby granted, provided that the above
|
||||
# copyright notice and this permission notice appear in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND DRL DISCLAIMS ALL WARRANTIES WITH
|
||||
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
# AND FITNESS. IN NO EVENT SHALL DRL BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
# PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
"""
|
||||
Key management tool for revised version of Sparta's BGPSEC implementation.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import subprocess
|
||||
|
||||
|
||||
default_openssl_binary = os.getenv("BGPSEC_OPENSSL_BINARY", "openssl")
|
||||
default_public_key_dir = os.getenv("BGPSEC_PUBLIC_KEY_DIR", "/usr/share/bird/bgpsec-keys")
|
||||
default_private_key_dir = os.getenv("BGPSEC_PRIVATE_KEY_DIR", "/usr/share/bird/bgpsec-private-keys")
|
||||
|
||||
|
||||
class OpenSSLPipeline(object):
|
||||
"""
|
||||
String together one or more OpenSSL commands in a pipeline, return
|
||||
stdout of the final command. Callable object rather than function
|
||||
so we can instantiate it as a closure over the program arguments.
|
||||
"""
|
||||
|
||||
allowed_keywords = set(["input"])
|
||||
|
||||
def __init__(self, args):
|
||||
self.args = args
|
||||
|
||||
def __call__(self, *argses, **kwargs):
|
||||
assert all(kw in self.allowed_keywords for kw in kwargs)
|
||||
procs = []
|
||||
for args in argses:
|
||||
procs.append(subprocess.Popen((self.args.openssl_binary,) + args,
|
||||
stdout = subprocess.PIPE,
|
||||
stdin = procs[-1].stdout if procs else subprocess.PIPE))
|
||||
if "input" in kwargs:
|
||||
procs[0].stdin.write(kwargs["input"])
|
||||
procs[0].stdin.close()
|
||||
output = procs[-1].stdout.read()
|
||||
for i, proc in enumerate(procs):
|
||||
if proc.wait() != 0:
|
||||
raise subprocess.CalledProcessError(proc.returncode, argses[i][0])
|
||||
return output
|
||||
# class OpenSSLPipeline(object):
|
||||
|
||||
|
||||
def public_filename(args, asn, skihex):
|
||||
"""
|
||||
Figure out what the filename for a key should be, and create the
|
||||
containing directory if it doesn't already exist.
|
||||
"""
|
||||
|
||||
for n in xrange(args.max_ski_collisions):
|
||||
fn = "%s/%s.%s.%s.key" % (args.public_key_dir, asn, skihex, n)
|
||||
if args.skip_collision_check or not os.path.exists(fn):
|
||||
break
|
||||
else:
|
||||
sys.exit("Too many SKI collisions for ASN %s SKI %s" % (asn, skihex))
|
||||
dn = os.path.dirname(fn)
|
||||
if not os.path.isdir(dn):
|
||||
if args.verbose:
|
||||
print "Creating directory", dn
|
||||
os.makedirs(dn)
|
||||
return fn
|
||||
# def public_filename(args, asn, skihex):
|
||||
|
||||
|
||||
def generate(args):
|
||||
"""
|
||||
Generate an EC keypair, store in .key files named using the key's
|
||||
SKI value to generate the filenames.
|
||||
"""
|
||||
|
||||
# We go through some silly gymnastics using the old OpenSSL ecparam
|
||||
# command instead of using the newer OpenSSL genpkey command,
|
||||
# because we have to force the key into the required namedCurve form
|
||||
# instead of explicitCurve. OpenSSL itself doesn't much care, but
|
||||
# since the SKI is defined as the SHA1 hash of the binary key value,
|
||||
# using the wrong key encoding yields the wrong SKI value.
|
||||
|
||||
openssl = OpenSSLPipeline(args)
|
||||
pemkey = openssl(("ecparam", "-name", "prime256v1"),
|
||||
("ecparam", "-param_enc", "named_curve", "-genkey"))
|
||||
pemkey = pemkey.splitlines(True)
|
||||
pemkey = "".join(pemkey[pemkey.index("-----BEGIN EC PRIVATE KEY-----\n"):])
|
||||
skihex = openssl(("pkey", "-outform", "DER", "-pubout"),
|
||||
("dgst", "-sha1", "-hex"),
|
||||
input = pemkey)
|
||||
skihex = skihex.split()[-1].upper()
|
||||
if args.printski:
|
||||
print skihex
|
||||
fn = public_filename(args, args.asns[0], skihex)
|
||||
if args.verbose:
|
||||
print "Writing", fn
|
||||
openssl(("pkey", "-outform", "DER", "-out", fn, "-pubout"), input = pemkey)
|
||||
for asn in args.asns[1:]:
|
||||
ln = public_filename(args, asn, skihex)
|
||||
if args.verbose:
|
||||
print "Linking", ln
|
||||
os.link(fn, ln)
|
||||
os.umask(077)
|
||||
fn = "%s/%s.%s.key" % (args.private_key_dir, args.asns[0], skihex)
|
||||
if args.verbose:
|
||||
print "Writing", fn
|
||||
openssl(("pkey", "-outform", "DER", "-out", fn), input = pemkey)
|
||||
for asn in args.asns[1:]:
|
||||
ln = "%s/%s.%s.key" % (args.private_key_dir, asn, skihex)
|
||||
if args.verbose:
|
||||
print "Linking", ln
|
||||
os.link(fn, ln)
|
||||
# def generate(args):
|
||||
|
||||
|
||||
def hashdir(args):
|
||||
"""
|
||||
Extract router keys from certificates in an RPKI certificate tree,
|
||||
store as .key files using each key's SKI value to generate the
|
||||
corresponding filename.
|
||||
"""
|
||||
|
||||
openssl = OpenSSLPipeline(args)
|
||||
for root, dirs, files in os.walk(args.cert_dir):
|
||||
for fn in files:
|
||||
if fn.endswith(".cer"):
|
||||
fn = os.path.join(root, fn)
|
||||
text = openssl(("x509", "-inform", "DER", "-noout", "-text", "-in", fn))
|
||||
if "Public Key Algorithm: id-ecPublicKey" not in text or "ASN1 OID: prime256v1" not in text:
|
||||
continue
|
||||
if args.verbose:
|
||||
print "Examining", fn
|
||||
skihex = text[text.index("X509v3 Subject Key Identifier:"):].splitlines()[1].strip().replace(":", "").upper()
|
||||
if args.paranoia:
|
||||
checkski = openssl(("x509", "-inform", "DER", "-noout", "-pubkey", "-in", fn),
|
||||
("pkey", "-pubin", "-outform", "DER"),
|
||||
("dgst", "-sha1", "-hex"))
|
||||
checkski = checkski.split()[-1].upper()
|
||||
if skihex != checkski:
|
||||
sys.stderr.write("SKI %s in certificate %s does not match calculated SKI %s\n" % (skihex, fn, checkski))
|
||||
asns = []
|
||||
b = text.index("Autonomous System Numbers:")
|
||||
e = text.index("\n\n", b)
|
||||
for line in text[b:e].splitlines()[1:]:
|
||||
b, _, e = line.strip().partition("-")
|
||||
if e == "":
|
||||
asns.append(int(b))
|
||||
else:
|
||||
asns.extend(xrange(int(b), int(e) + 1))
|
||||
outfn = public_filename(args, asns[0], skihex)
|
||||
if args.verbose:
|
||||
print "Writing", outfn
|
||||
openssl(("x509", "-inform", "DER", "-noout", "-pubkey", "-in", fn),
|
||||
("pkey", "-pubin", "-outform", "DER", "-out", outfn))
|
||||
for asn in asns[1:]:
|
||||
ln = public_filename(args, asn, skihex)
|
||||
if args.verbose:
|
||||
print "Linking", ln
|
||||
os.link(outfn, ln)
|
||||
# def hashdir(args):
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description = __doc__)
|
||||
parser.add_argument("--openssl-binary",
|
||||
default = default_openssl_binary,
|
||||
help = "Path to EC-capable OpenSSL binary")
|
||||
parser.add_argument("--public-key-dir",
|
||||
default = default_public_key_dir,
|
||||
help = "directory to which we save parsed router keys")
|
||||
parser.add_argument("--private-key-dir",
|
||||
default = default_private_key_dir,
|
||||
help = "directory to which we save generated private keys")
|
||||
parser.add_argument("--verbose",
|
||||
action = "store_true",
|
||||
help = "whistle while you work")
|
||||
parser.add_argument("--printski",
|
||||
action = "store_true",
|
||||
help = "print out the SKI value")
|
||||
parser.add_argument("--paranoia",
|
||||
action = "store_true",
|
||||
help = "perform paranoid checks")
|
||||
parser.add_argument("--max-ski-collisions",
|
||||
type = int,
|
||||
default = 3,
|
||||
help = "maximum number of SKI collisions to allow when writing public keys")
|
||||
parser.add_argument("--skip-collision-check",
|
||||
action = "store_true",
|
||||
help = "don't check for SKI collisions")
|
||||
subparsers = parser.add_subparsers(title = "Commands",
|
||||
metavar = "")
|
||||
subparser = subparsers.add_parser("generate",
|
||||
description = generate.__doc__,
|
||||
help = "generate new keypair")
|
||||
subparser.set_defaults(func = generate)
|
||||
subparser.add_argument("--router-id",
|
||||
type = int)
|
||||
subparser.add_argument("asns",
|
||||
nargs = "+",
|
||||
type = int)
|
||||
subparser = subparsers.add_parser("hashdir",
|
||||
description = hashdir.__doc__,
|
||||
help = "hash directory of certs")
|
||||
subparser.set_defaults(func = hashdir)
|
||||
subparser.add_argument("cert_dir")
|
||||
args = parser.parse_args()
|
||||
return args.func(args)
|
||||
# def main():
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
350
proto/bgp/bgpsec/router-key.cnf
Normal file
350
proto/bgp/bgpsec/router-key.cnf
Normal file
@ -0,0 +1,350 @@
|
||||
#
|
||||
# OpenSSL example configuration file.
|
||||
# This is mostly being used for generation of certificate requests.
|
||||
#
|
||||
|
||||
# This definition stops the following lines choking if HOME isn't
|
||||
# defined.
|
||||
HOME = .
|
||||
RANDFILE = $ENV::HOME/.rnd
|
||||
|
||||
# To use this configuration file with the "-extfile" option of the
|
||||
# "openssl x509" utility, name here the section containing the
|
||||
# X.509v3 extensions to use:
|
||||
extensions = bgpsec_router_ext
|
||||
|
||||
oid_section = new_oids
|
||||
|
||||
[openssl_init]
|
||||
|
||||
# Extra OBJECT IDENTIFIER info:
|
||||
oid_section = new_oids
|
||||
|
||||
[ x509 ]
|
||||
extensions = bgpsec_router_ext
|
||||
|
||||
[ new_oids ]
|
||||
|
||||
# XXX: replace with the real assignments at some point
|
||||
# (from draft-ietf-sidr-bgpsec-pki-profiles-00.txt)
|
||||
id_kp_bgpsec_router = 1.3.6.1.5.5.7.99.99
|
||||
|
||||
#extKeyUsage = 2.5.29.37
|
||||
|
||||
####################################################################
|
||||
####################################################################
|
||||
#
|
||||
# CA definition
|
||||
#
|
||||
[ ca ]
|
||||
default_ca = bgpsec_CA # The default ca section
|
||||
|
||||
[ bgpsec_CA ]
|
||||
|
||||
dir = . # Where everything is kept
|
||||
certs = $dir/certs # Where the issued certs are kept
|
||||
crl_dir = $dir/crl # Where the issued crl are kept
|
||||
database = $dir/index.txt # database index file.
|
||||
#unique_subject = no # Set to 'no' to allow creation of
|
||||
# several ctificates with same
|
||||
# subject.
|
||||
new_certs_dir = $dir/newcerts # default place for new certs.
|
||||
|
||||
certificate = $dir/cacert.pem # The CA certificate
|
||||
serial = $dir/serial # The current serial number
|
||||
crlnumber = $dir/crlnumber # the current crl number
|
||||
# must be commented out to
|
||||
# leave a V1 CRL
|
||||
crl = $dir/crl.pem # The current CRL
|
||||
private_key = $dir/private/cakey.pem# The private key
|
||||
RANDFILE = $dir/private/.rand # private random number file
|
||||
|
||||
x509_extensions = usr_cert # The extentions to add to the cert
|
||||
|
||||
# Comment out the following two lines for the "traditional"
|
||||
# (and highly broken) format.
|
||||
name_opt = ca_default # Subject Name options
|
||||
cert_opt = ca_default # Certificate field options
|
||||
|
||||
# Extension copying option: use with caution.
|
||||
# copy_extensions = copy
|
||||
|
||||
# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
|
||||
# so this is commented out by default to leave a V1 CRL.
|
||||
# crlnumber must also be commented out to leave a V1 CRL.
|
||||
# crl_extensions = crl_ext
|
||||
|
||||
default_days = 365 # how long to certify for
|
||||
default_crl_days= 30 # how long before next CRL
|
||||
default_md = default # use public key default MD
|
||||
preserve = no # keep passed DN ordering
|
||||
|
||||
# A few difference way of specifying how similar the request should look
|
||||
# For type CA, the listed attributes must be the same, and the optional
|
||||
# and supplied fields are just that :-)
|
||||
policy = policy_match
|
||||
|
||||
# For the CA policy
|
||||
[ policy_match ]
|
||||
countryName = match
|
||||
stateOrProvinceName = match
|
||||
organizationName = match
|
||||
organizationalUnitName = optional
|
||||
commonName = supplied
|
||||
emailAddress = optional
|
||||
|
||||
# For the 'anything' policy
|
||||
# At this point in time, you must list all acceptable 'object'
|
||||
# types.
|
||||
[ policy_anything ]
|
||||
countryName = optional
|
||||
stateOrProvinceName = optional
|
||||
localityName = optional
|
||||
organizationName = optional
|
||||
organizationalUnitName = optional
|
||||
commonName = supplied
|
||||
emailAddress = optional
|
||||
|
||||
####################################################################
|
||||
#
|
||||
# CSR (req)uest generation
|
||||
#
|
||||
[ req ]
|
||||
default_bits = 2048
|
||||
default_md = sha256
|
||||
default_keyfile = privkey.pem
|
||||
distinguished_name = req_distinguished_name
|
||||
attributes = req_attributes
|
||||
encrypt_key = no
|
||||
x509_extensions = bgpsec_router_ext # The extentions to add to the self signed cert
|
||||
|
||||
# This sets a mask for permitted string types. There are several options.
|
||||
# default: PrintableString, T61String, BMPString.
|
||||
# pkix : PrintableString, BMPString (PKIX recommendation before 2004)
|
||||
# utf8only: only UTF8Strings (PKIX recommendation after 2004).
|
||||
# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
|
||||
# MASK:XXXX a literal mask value.
|
||||
# WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings.
|
||||
string_mask = utf8only
|
||||
|
||||
req_extensions = bgpsec_router_csr # The extensions to add to a certificate request
|
||||
|
||||
[ req_distinguished_name ]
|
||||
commonName = special RPKI field... need to gen it.
|
||||
commonName_max = 64
|
||||
commonName_default = finishMeBecauseThisIsWrong
|
||||
|
||||
# SET-ex3 = SET extension number 3
|
||||
|
||||
[ req_attributes ]
|
||||
|
||||
[ usr_cert ]
|
||||
|
||||
# These extensions are added when 'ca' signs a request.
|
||||
|
||||
# This goes against PKIX guidelines but some CAs do it and some software
|
||||
# requires this to avoid interpreting an end user certificate as a CA.
|
||||
|
||||
basicConstraints=CA:FALSE
|
||||
|
||||
# Here are some examples of the usage of nsCertType. If it is omitted
|
||||
# the certificate can be used for anything *except* object signing.
|
||||
|
||||
# This is OK for an SSL server.
|
||||
# nsCertType = server
|
||||
|
||||
# For an object signing certificate this would be used.
|
||||
# nsCertType = objsign
|
||||
|
||||
# For normal client use this is typical
|
||||
# nsCertType = client, email
|
||||
|
||||
# and for everything including object signing:
|
||||
# nsCertType = client, email, objsign
|
||||
|
||||
# This is typical in keyUsage for a client certificate.
|
||||
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
|
||||
|
||||
# This will be displayed in Netscape's comment listbox.
|
||||
nsComment = "OpenSSL Generated Certificate"
|
||||
|
||||
# PKIX recommendations harmless if included in all certificates.
|
||||
subjectKeyIdentifier=hash
|
||||
authorityKeyIdentifier=keyid,issuer
|
||||
|
||||
# This stuff is for subjectAltName and issuerAltname.
|
||||
# Import the email address.
|
||||
# subjectAltName=email:copy
|
||||
# An alternative to produce certificates that aren't
|
||||
# deprecated according to PKIX.
|
||||
# subjectAltName=email:move
|
||||
|
||||
# Copy subject details
|
||||
# issuerAltName=issuer:copy
|
||||
|
||||
#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
|
||||
#nsBaseUrl
|
||||
#nsRevocationUrl
|
||||
#nsRenewalUrl
|
||||
#nsCaPolicyUrl
|
||||
#nsSslServerName
|
||||
|
||||
# This is required for TSA certificates.
|
||||
# extendedKeyUsage = critical,timeStamping
|
||||
|
||||
[ v3_req ]
|
||||
|
||||
# Extensions to add to a certificate request
|
||||
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
|
||||
|
||||
[bgpsec_router_ext]
|
||||
# 3.1.3.1. Extended Key Usage
|
||||
#extKeyUsage = id_kp_bgpsec_router
|
||||
keyUsage = digitalSignature
|
||||
basicConstraints=CA:FALSE
|
||||
subjectKeyIdentifier=hash
|
||||
#extendedKeyUsage = id_kp_bgpsec_router
|
||||
extendedKeyUsage = 1.3.6.1.5.5.7.99.99
|
||||
|
||||
#extKeyUsage = id_kp_bgpsec_router
|
||||
|
||||
# 3.1.3.4. AS Resources
|
||||
# as specified in section 4.8.11 of [ID.sidr-res-cert-profile]
|
||||
#asNumber = AS number for this router (digits only)
|
||||
#asNumber_default = XXXX
|
||||
|
||||
[bgpsec_router_csr]
|
||||
# defined in 3.2. BGPSEC Router Certificate Request Profile
|
||||
# 3.1.3.1. Extended Key Usage
|
||||
extendedKeyUsage = id_kp_bgpsec_router
|
||||
subjectKeyIdentifier=hash
|
||||
|
||||
[ v3_ca ]
|
||||
|
||||
|
||||
# Extensions for a typical CA
|
||||
|
||||
|
||||
# PKIX recommendation.
|
||||
|
||||
subjectKeyIdentifier=hash
|
||||
|
||||
authorityKeyIdentifier=keyid:always,issuer
|
||||
|
||||
# This is what PKIX recommends but some broken software chokes on critical
|
||||
# extensions.
|
||||
#basicConstraints = critical,CA:true
|
||||
# So we do this instead.
|
||||
basicConstraints = CA:true
|
||||
|
||||
# Key usage: this is typical for a CA certificate. However since it will
|
||||
# prevent it being used as an test self-signed certificate it is best
|
||||
# left out by default.
|
||||
# keyUsage = cRLSign, keyCertSign
|
||||
|
||||
# Some might want this also
|
||||
# nsCertType = sslCA, emailCA
|
||||
|
||||
# Include email address in subject alt name: another PKIX recommendation
|
||||
# subjectAltName=email:copy
|
||||
# Copy issuer details
|
||||
# issuerAltName=issuer:copy
|
||||
|
||||
# DER hex encoding of an extension: beware experts only!
|
||||
# obj=DER:02:03
|
||||
# Where 'obj' is a standard or added object
|
||||
# You can even override a supported extension:
|
||||
# basicConstraints= critical, DER:30:03:01:01:FF
|
||||
|
||||
[ crl_ext ]
|
||||
|
||||
# CRL extensions.
|
||||
# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
|
||||
|
||||
# issuerAltName=issuer:copy
|
||||
authorityKeyIdentifier=keyid:always
|
||||
|
||||
[ proxy_cert_ext ]
|
||||
# These extensions should be added when creating a proxy certificate
|
||||
|
||||
# This goes against PKIX guidelines but some CAs do it and some software
|
||||
# requires this to avoid interpreting an end user certificate as a CA.
|
||||
|
||||
basicConstraints=CA:FALSE
|
||||
|
||||
# Here are some examples of the usage of nsCertType. If it is omitted
|
||||
# the certificate can be used for anything *except* object signing.
|
||||
|
||||
# This is OK for an SSL server.
|
||||
# nsCertType = server
|
||||
|
||||
# For an object signing certificate this would be used.
|
||||
# nsCertType = objsign
|
||||
|
||||
# For normal client use this is typical
|
||||
# nsCertType = client, email
|
||||
|
||||
# and for everything including object signing:
|
||||
# nsCertType = client, email, objsign
|
||||
|
||||
# This is typical in keyUsage for a client certificate.
|
||||
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
|
||||
|
||||
# This will be displayed in Netscape's comment listbox.
|
||||
nsComment = "OpenSSL Generated Certificate"
|
||||
|
||||
# PKIX recommendations harmless if included in all certificates.
|
||||
subjectKeyIdentifier=hash
|
||||
authorityKeyIdentifier=keyid,issuer
|
||||
|
||||
# This stuff is for subjectAltName and issuerAltname.
|
||||
# Import the email address.
|
||||
# subjectAltName=email:copy
|
||||
# An alternative to produce certificates that aren't
|
||||
# deprecated according to PKIX.
|
||||
# subjectAltName=email:move
|
||||
|
||||
# Copy subject details
|
||||
# issuerAltName=issuer:copy
|
||||
|
||||
#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
|
||||
#nsBaseUrl
|
||||
#nsRevocationUrl
|
||||
#nsRenewalUrl
|
||||
#nsCaPolicyUrl
|
||||
#nsSslServerName
|
||||
|
||||
# This really needs to be in place for it to be a proxy certificate.
|
||||
proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo
|
||||
|
||||
####################################################################
|
||||
[ tsa ]
|
||||
|
||||
default_tsa = tsa_config1 # the default TSA section
|
||||
|
||||
[ tsa_config1 ]
|
||||
|
||||
# These are used by the TSA reply generation only.
|
||||
dir = ./demoCA # TSA root directory
|
||||
serial = $dir/tsaserial # The current serial number (mandatory)
|
||||
crypto_device = builtin # OpenSSL engine to use for signing
|
||||
signer_cert = $dir/tsacert.pem # The TSA signing certificate
|
||||
# (optional)
|
||||
certs = $dir/cacert.pem # Certificate chain to include in reply
|
||||
# (optional)
|
||||
signer_key = $dir/private/tsakey.pem # The TSA private key (optional)
|
||||
|
||||
default_policy = tsa_policy1 # Policy if request did not specify it
|
||||
# (optional)
|
||||
other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional)
|
||||
digests = md5, sha1 # Acceptable message digests (mandatory)
|
||||
accuracy = secs:1, millisecs:500, microsecs:100 # (optional)
|
||||
clock_precision_digits = 0 # number of digits after dot. (optional)
|
||||
ordering = yes # Is ordering defined for timestamps?
|
||||
# (optional, default: no)
|
||||
tsa_name = yes # Must the TSA name be included in the reply?
|
||||
# (optional, default: no)
|
||||
ess_cert_id_chain = no # Must the ESS cert id chain be included?
|
||||
# (optional, default: no)
|
287
proto/bgp/bgpsec/tests.c
Normal file
287
proto/bgp/bgpsec/tests.c
Normal file
@ -0,0 +1,287 @@
|
||||
#include "validate.h"
|
||||
#include <sys/param.h>
|
||||
#include <openssl/ec.h>
|
||||
|
||||
#define HEADER(msg) printf("--------------- " msg "\n");
|
||||
#define HEADER1(msg, arg) printf("--------------- " msg "\n", arg);
|
||||
|
||||
#define RESULT(test, is_success) { \
|
||||
printf("%7.7s: ", ((is_success) ? "ok" : "not ok")); \
|
||||
printf("%4d: ", __LINE__); \
|
||||
printf test; \
|
||||
printf("\n"); \
|
||||
if (is_success) good++; \
|
||||
else bad++; \
|
||||
}
|
||||
|
||||
#define DIE(msg) do { fprintf(stderr, "CRITICAL FAIL: %s\n", msg); exit(1); } while(1);
|
||||
|
||||
#define DUMMYCERTFILE "router-key.15708"
|
||||
|
||||
#define TEST_KEY_REPO_PATH "/tmp/bgpsec-keys-testrepo"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
byte signature[1024];
|
||||
int signature_len = sizeof(signature);
|
||||
char strBuffer[1024];
|
||||
bgpsec_key_data key_data;
|
||||
char ski[1024];
|
||||
char filePrefix[MAXPATHLEN];
|
||||
char fileName[MAXPATHLEN];
|
||||
int signature_algorithms[] = { BGPSEC_ALGORITHM_SHA256_ECDSA_P_256, -1 };
|
||||
byte data_to_sign[] = { 1,2,3,4,5,6,7,8 };
|
||||
|
||||
struct bgp_config bgpconfig;
|
||||
bgpconfig.bgpsec_key_repo_path = TEST_KEY_REPO_PATH;
|
||||
bgpconfig.bgpsec_save_binary_keys = 1;
|
||||
|
||||
BIGNUM newbignum;
|
||||
EC_POINT *new_point;
|
||||
|
||||
FILE *fp;
|
||||
|
||||
printf("Testing:\n");
|
||||
|
||||
int good = 0, bad = 0;
|
||||
|
||||
/* test whether we can sign a block of text */
|
||||
int ret;
|
||||
int curveId;
|
||||
int bin_save;
|
||||
|
||||
for(bin_save = 0; bin_save < 2; bin_save++) {
|
||||
HEADER1("starting test with binary_keys = %d", bin_save);
|
||||
|
||||
int algorithm_count = 0;
|
||||
bgpconfig.bgpsec_save_binary_keys = bin_save;
|
||||
|
||||
|
||||
/* create a dummy certificate to use */
|
||||
system("../proto/bgp/bgpsec/gen-router-key -d " TEST_KEY_REPO_PATH " -c ../proto/bgp/bgpsec/router-key.cnf -p > ski.txt");
|
||||
fp = fopen("ski.txt", "r");
|
||||
if (NULL == fp)
|
||||
DIE("failed to open the ski.txt file that should have been created");
|
||||
ski[sizeof(ski)-1] = '\0';
|
||||
if (NULL == fgets(ski, sizeof(ski)-1, fp))
|
||||
DIE("Couldn't read the SKI from the ski.txt file");
|
||||
ski[strlen(ski)-1] = '\0'; /* chomp the LF off */
|
||||
fclose(fp);
|
||||
|
||||
/* now define all the file names based on it the new cert */
|
||||
filePrefix[sizeof(filePrefix)-1] = '\0';
|
||||
snprintf(filePrefix, sizeof(filePrefix)-1, "%s/%s", TEST_KEY_REPO_PATH, ski);
|
||||
generate_ski_filename(filePrefix, sizeof(filePrefix), TEST_KEY_REPO_PATH,
|
||||
ski, strlen(ski));
|
||||
|
||||
while(signature_algorithms[algorithm_count] > 0) {
|
||||
HEADER1("-- starting test with sig alg = %d",
|
||||
signature_algorithms[algorithm_count]);
|
||||
bgpsec_key_data key_data;
|
||||
curveId = signature_algorithms[algorithm_count];
|
||||
|
||||
ret = bgpsec_load_key(&bgpconfig, filePrefix, &key_data,
|
||||
curveId, 1);
|
||||
RESULT(("cert sign: loaded the router key from tmp file: %s",
|
||||
filePrefix),
|
||||
ret == BGPSEC_SUCCESS);
|
||||
|
||||
|
||||
/* generate a signature using a certificate */
|
||||
signature_len =
|
||||
bgpsec_sign_data_with_key(&bgpconfig,
|
||||
data_to_sign, sizeof(data_to_sign),
|
||||
key_data,
|
||||
signature_algorithms[algorithm_count],
|
||||
signature, sizeof(signature));
|
||||
|
||||
RESULT(("cert sign: algorithm %d, signature length (%d) is not negative",
|
||||
signature_algorithms[algorithm_count], signature_len),
|
||||
signature_len > -1);
|
||||
RESULT(("cert sign: algorithm %d, signature length (%d) has at least a byte", signature_algorithms[algorithm_count], signature_len), signature_len > 0);
|
||||
|
||||
/* modify the private key so it can't be part of the verification */
|
||||
BN_init(&newbignum);
|
||||
EC_KEY_set_private_key(key_data.ecdsa_key, &newbignum);
|
||||
|
||||
/* verify that the signature matches */
|
||||
ret = bgpsec_verify_signature_with_key(&bgpconfig, data_to_sign,
|
||||
sizeof(data_to_sign),
|
||||
key_data,
|
||||
signature_algorithms[algorithm_count],
|
||||
signature, signature_len);
|
||||
RESULT(("cert sign: verify signature result: %d (should be %d)",
|
||||
ret, BGPSEC_SIGNATURE_MATCH),
|
||||
ret == BGPSEC_SIGNATURE_MATCH);
|
||||
|
||||
/* verify that the signature matches */
|
||||
ret = bgpsec_verify_signature_with_key(&bgpconfig, data_to_sign,
|
||||
sizeof(data_to_sign),
|
||||
key_data,
|
||||
signature_algorithms[algorithm_count],
|
||||
signature, signature_len);
|
||||
RESULT(("cert sign: verify signature result2: %d (should be %d)",
|
||||
ret, BGPSEC_SIGNATURE_MATCH),
|
||||
ret == BGPSEC_SIGNATURE_MATCH);
|
||||
|
||||
/* modify the public key so it can't be part of the verification */
|
||||
/* (which should make the verification fail now) */
|
||||
new_point = EC_POINT_new(EC_GROUP_new_by_curve_name(curveId));
|
||||
EC_KEY_set_public_key(key_data.ecdsa_key, new_point);
|
||||
EC_POINT_free(new_point);
|
||||
|
||||
/* verify that the signature no longer matches */
|
||||
ret = bgpsec_verify_signature_with_key(&bgpconfig,
|
||||
data_to_sign,
|
||||
sizeof(data_to_sign),
|
||||
key_data,
|
||||
signature_algorithms[algorithm_count],
|
||||
signature, signature_len);
|
||||
RESULT(("cert sign: verify signature fail result: %d (should be %d)",
|
||||
ret, BGPSEC_SIGNATURE_MISMATCH),
|
||||
ret == BGPSEC_SIGNATURE_MISMATCH);
|
||||
|
||||
/* completely get rid of the current key */
|
||||
EC_KEY_free(key_data.ecdsa_key);
|
||||
key_data.ecdsa_key = NULL;
|
||||
|
||||
/* now reload the key from the files and use them to verify it */
|
||||
/* NOTE: this should reload the previously saved binary key */
|
||||
ret = bgpsec_load_key(&bgpconfig, filePrefix, &key_data, curveId, 1);
|
||||
RESULT(("cert sign: loading key function returned: %d (should be %d)",
|
||||
ret, BGPSEC_SUCCESS), ret == BGPSEC_SUCCESS);
|
||||
|
||||
/* verify that the signature matches again with the loaded key */
|
||||
ret = bgpsec_verify_signature_with_key(&bgpconfig,
|
||||
data_to_sign,
|
||||
sizeof(data_to_sign),
|
||||
key_data,
|
||||
signature_algorithms[algorithm_count],
|
||||
signature, signature_len);
|
||||
RESULT(("cert sign: verify signature result of generated bin key: %d (should be %d)",
|
||||
ret, BGPSEC_SIGNATURE_MATCH),
|
||||
ret == BGPSEC_SIGNATURE_MATCH);
|
||||
|
||||
|
||||
/* Nuke the binary version of the key, and make sure the x.509
|
||||
gets reloaded again */
|
||||
snprintf(fileName, sizeof(fileName), "%s.bin_pub", filePrefix);
|
||||
unlink(fileName);
|
||||
|
||||
ret = bgpsec_load_key(&bgpconfig, filePrefix, &key_data, curveId, 1);
|
||||
RESULT(("cert sign: loading key function returned: %d (should be %d)",
|
||||
ret, BGPSEC_SUCCESS), ret == BGPSEC_SUCCESS);
|
||||
|
||||
/* verify that the signature matches again with the loaded key */
|
||||
ret = bgpsec_verify_signature_with_key(&bgpconfig,
|
||||
data_to_sign,
|
||||
sizeof(data_to_sign),
|
||||
key_data,
|
||||
signature_algorithms[algorithm_count],
|
||||
signature, signature_len);
|
||||
RESULT(("cert sign: verify signature result of non-bin: %d (should be %d)",
|
||||
ret, BGPSEC_SIGNATURE_MATCH),
|
||||
ret == BGPSEC_SIGNATURE_MATCH);
|
||||
|
||||
|
||||
|
||||
/* completely get rid of the current key */
|
||||
EC_KEY_free(key_data.ecdsa_key);
|
||||
key_data.ecdsa_key = NULL;
|
||||
|
||||
/* now reload just the public part of the key and test just it */
|
||||
ret = bgpsec_load_key(&bgpconfig, filePrefix, &key_data, curveId, 0);
|
||||
RESULT(("cert sign: loading public key function returned: %d (should be %d)",
|
||||
ret, BGPSEC_SUCCESS), ret == BGPSEC_SUCCESS);
|
||||
|
||||
/* verify that the signature matches again with the public key */
|
||||
ret = bgpsec_verify_signature_with_key(&bgpconfig,
|
||||
data_to_sign,
|
||||
sizeof(data_to_sign),
|
||||
key_data,
|
||||
signature_algorithms[algorithm_count],
|
||||
signature, signature_len);
|
||||
RESULT(("cert sign: verify (pub) signature result: %d (should be %d)",
|
||||
ret, BGPSEC_SIGNATURE_MATCH),
|
||||
ret == BGPSEC_SIGNATURE_MATCH);
|
||||
|
||||
/* generate a signature using a fingerprint */
|
||||
/* XXX: set test directory to search for matching ski->certs */
|
||||
signature_len =
|
||||
bgpsec_sign_data_with_ascii_ski(&bgpconfig,
|
||||
data_to_sign,
|
||||
sizeof(data_to_sign),
|
||||
ski, strlen(ski)+1,
|
||||
signature_algorithms[algorithm_count],
|
||||
signature, sizeof(signature));
|
||||
|
||||
RESULT(("ski sign: algorithm %d, signature length (%d) is not negative",
|
||||
signature_algorithms[algorithm_count], signature_len),
|
||||
signature_len > -1);
|
||||
RESULT(("ski sign: algorithm %d, signature length (%d) has at least a byte", signature_algorithms[algorithm_count], signature_len), signature_len > 0);
|
||||
|
||||
|
||||
/* verify that the signature matches */
|
||||
ret = bgpsec_verify_signature_with_ascii_ski(&bgpconfig,
|
||||
data_to_sign,
|
||||
sizeof(data_to_sign),
|
||||
ski, strlen(ski)+1,
|
||||
signature_algorithms[algorithm_count],
|
||||
signature, sizeof(signature));
|
||||
RESULT(("ski sign: verify signature result: %d (should be %d)",
|
||||
ret, BGPSEC_SIGNATURE_MATCH),
|
||||
ret == BGPSEC_SIGNATURE_MATCH);
|
||||
|
||||
if (bin_save) {
|
||||
/* Nuke the x.509 certificate version of the key, and make
|
||||
sure the binary version can be loaded by itself. */
|
||||
snprintf(fileName, sizeof(fileName), "%s.pub", filePrefix);
|
||||
unlink(fileName);
|
||||
snprintf(fileName, sizeof(fileName), "%s.private", filePrefix);
|
||||
unlink(fileName);
|
||||
|
||||
|
||||
/* verify that the signature matches with an ski */
|
||||
ret = bgpsec_verify_signature_with_ascii_ski(&bgpconfig,
|
||||
data_to_sign,
|
||||
sizeof(data_to_sign),
|
||||
ski, strlen(ski)+1,
|
||||
signature_algorithms[algorithm_count],
|
||||
signature, sizeof(signature));
|
||||
|
||||
RESULT(("ski sign: verify signature result of binary only: %d (should be %d)",
|
||||
ret, BGPSEC_SIGNATURE_MATCH),
|
||||
ret == BGPSEC_SIGNATURE_MATCH);
|
||||
|
||||
|
||||
/* verify that the signature matches with the key */
|
||||
|
||||
ret = bgpsec_load_key(&bgpconfig, filePrefix, &key_data, curveId, 1);
|
||||
RESULT(("cert sign: loading binary-only key function returned: %d (should be %d)",
|
||||
ret, BGPSEC_SUCCESS), ret == BGPSEC_SUCCESS);
|
||||
|
||||
/* verify that the signature matches again with the loaded key */
|
||||
ret = bgpsec_verify_signature_with_key(&bgpconfig,
|
||||
data_to_sign,
|
||||
sizeof(data_to_sign),
|
||||
key_data,
|
||||
signature_algorithms[algorithm_count],
|
||||
signature, signature_len);
|
||||
RESULT(("cert sign: verify signature result of binary-only: %d (should be %d)",
|
||||
ret, BGPSEC_SIGNATURE_MATCH),
|
||||
ret == BGPSEC_SIGNATURE_MATCH);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* move on to the next algorithm */
|
||||
algorithm_count++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
printf("\nResults:\n");
|
||||
printf(" Good: %d\n", good);
|
||||
printf(" Bad: %d\n", bad);
|
||||
|
||||
return 0;
|
||||
}
|
253
proto/bgp/bgpsec/validate.c
Normal file
253
proto/bgp/bgpsec/validate.c
Normal file
@ -0,0 +1,253 @@
|
||||
/*
|
||||
* bgpsec: validation functions
|
||||
*
|
||||
*
|
||||
* Parsons, Inc.
|
||||
* (c) 2013-2013
|
||||
*
|
||||
* Code can be used under either license:
|
||||
* - Freely distributed and used under the terms of the GNU GPLv2.
|
||||
* - Freely distributed and used under a BSD license, See README.bgpsec.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <openssl/ec.h>
|
||||
#include <openssl/x509.h>
|
||||
#include "validate.h"
|
||||
|
||||
static int
|
||||
convert_ski_to_ascii(const byte *ski, const size_t ski_len,
|
||||
char *ascii_ski, size_t ascii_ski_len) {
|
||||
|
||||
int i;
|
||||
|
||||
if (ski_len * 2 + 1 >= ascii_ski_len) {
|
||||
log(L_ERR "validate: buffer to small for SKI length: %d", ski_len);
|
||||
return BGPSEC_FAILURE;
|
||||
}
|
||||
|
||||
memset(ascii_ski, 0, ascii_ski_len);
|
||||
|
||||
for (i = 0; i < ski_len; i++)
|
||||
sprintf(ascii_ski + 2 * i, "%02X", ski[i]);
|
||||
|
||||
return BGPSEC_SUCCESS;
|
||||
}
|
||||
|
||||
static int
|
||||
bgpsec_load_key_internal(const struct bgp_config *conf,
|
||||
const char *filename,
|
||||
bgpsec_key_data *key_data,
|
||||
EVP_PKEY *(*d2i_bio_method)(BIO *, EVP_PKEY **)) {
|
||||
int ret = BGPSEC_FAILURE;
|
||||
BIO *bio = NULL;
|
||||
|
||||
if ((bio = BIO_new_file(filename, "rb")) != NULL &&
|
||||
d2i_bio_method(bio, &key_data->pkey) != NULL &&
|
||||
EVP_PKEY_id(key_data->pkey) == EVP_PKEY_EC &&
|
||||
BIO_free(bio))
|
||||
{
|
||||
EC_KEY_set_asn1_flag(EVP_PKEY_get0(key_data->pkey), OPENSSL_EC_NAMED_CURVE);
|
||||
ret = BGPSEC_SUCCESS;
|
||||
bio = NULL;
|
||||
}
|
||||
|
||||
BIO_free(bio);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
bgpsec_load_private_key(const struct bgp_config *conf,
|
||||
const char *filename,
|
||||
bgpsec_key_data *key_data) {
|
||||
return bgpsec_load_key_internal(conf, filename, key_data, d2i_PrivateKey_bio);
|
||||
}
|
||||
|
||||
int
|
||||
bgpsec_load_public_key(const struct bgp_config *conf,
|
||||
const char *filename,
|
||||
bgpsec_key_data *key_data) {
|
||||
return bgpsec_load_key_internal(conf, filename, key_data, d2i_PUBKEY_bio);
|
||||
}
|
||||
|
||||
/* Might need to call OpenSSL_add_all_digests() somewhere */
|
||||
|
||||
int bgpsec_sign_data_with_key(const struct bgp_config *conf,
|
||||
const byte *octets, const size_t octets_len,
|
||||
const bgpsec_key_data key,
|
||||
const int signature_algorithm,
|
||||
byte *signature, size_t signature_len) {
|
||||
|
||||
EVP_PKEY_CTX *ctx = NULL;
|
||||
unsigned char md_value[EVP_MAX_MD_SIZE];
|
||||
size_t md_len;
|
||||
size_t sig_len = signature_len;
|
||||
int result = -1;
|
||||
|
||||
switch (signature_algorithm) {
|
||||
case BGPSEC_ALGORITHM_SHA256_ECDSA_P_256:
|
||||
|
||||
if (EVP_Digest(octets, octets_len, md_value, (unsigned int *)&md_len,
|
||||
EVP_sha256(), NULL) &&
|
||||
(ctx = EVP_PKEY_CTX_new(key.pkey, NULL)) != NULL &&
|
||||
EVP_PKEY_sign_init(ctx) > 0 &&
|
||||
EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha256()) > 0 &&
|
||||
EVP_PKEY_sign(ctx, signature, &sig_len, md_value, md_len) > 0)
|
||||
{
|
||||
result = sig_len;
|
||||
}
|
||||
else
|
||||
{
|
||||
log(L_ERR "validate: Failed to create digest/sign");
|
||||
return BGPSEC_FAILURE;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
return result;
|
||||
}
|
||||
|
||||
int bgpsec_sign_data_with_ascii_ski(const struct bgp_config *conf,
|
||||
const byte *octets, const size_t octets_len,
|
||||
const char *ski, const size_t ski_len,
|
||||
const int asn,
|
||||
const int signature_algorithm,
|
||||
byte *signature, size_t signature_len) {
|
||||
const char *rootPath = (conf && conf->bgpsec_priv_key_path) ? conf->bgpsec_priv_key_path : DEFAULT_PRIV_KEY_PATH;
|
||||
bgpsec_key_data key = { NULL };
|
||||
char filename[MAXPATHLEN];
|
||||
|
||||
if (snprintf(filename, sizeof(filename), "%s/%d.%s.key", rootPath, asn, ski) >= sizeof(filename) ||
|
||||
bgpsec_load_private_key(conf, filename, &key) != BGPSEC_SUCCESS)
|
||||
{
|
||||
return BGPSEC_FAILURE;
|
||||
}
|
||||
|
||||
return bgpsec_sign_data_with_key(conf, octets, octets_len, key,
|
||||
signature_algorithm, signature, signature_len);
|
||||
}
|
||||
|
||||
int bgpsec_sign_data_with_bin_ski(const struct bgp_config *conf,
|
||||
const byte *octets, const size_t octets_len,
|
||||
const byte *ski, const size_t ski_len,
|
||||
const int asn,
|
||||
const int signature_algorithm,
|
||||
byte *signature, size_t signature_len) {
|
||||
char ascii_ski[MAXPATHLEN];
|
||||
|
||||
if (convert_ski_to_ascii(ski, ski_len, ascii_ski, sizeof(ascii_ski)) == BGPSEC_FAILURE)
|
||||
return BGPSEC_FAILURE;
|
||||
|
||||
return bgpsec_sign_data_with_ascii_ski(conf, octets, octets_len, ascii_ski, sizeof(ascii_ski),
|
||||
asn, signature_algorithm, signature, signature_len);
|
||||
}
|
||||
|
||||
int bgpsec_verify_signature_with_key(const struct bgp_config *conf,
|
||||
const byte *octets, const size_t octets_len,
|
||||
const bgpsec_key_data key,
|
||||
const int signature_algorithm,
|
||||
const byte *signature, const size_t signature_len) {
|
||||
EVP_PKEY_CTX *ctx = NULL;
|
||||
unsigned char md_value[EVP_MAX_MD_SIZE];
|
||||
size_t md_len;
|
||||
int result = BGPSEC_SIGNATURE_ERROR;
|
||||
|
||||
switch (signature_algorithm) {
|
||||
case BGPSEC_ALGORITHM_SHA256_ECDSA_P_256:
|
||||
|
||||
if (EVP_Digest(octets, octets_len, md_value, (unsigned int *)&md_len,
|
||||
EVP_sha256(), NULL) &&
|
||||
(ctx = EVP_PKEY_CTX_new(key.pkey, NULL)) != NULL &&
|
||||
EVP_PKEY_verify_init(ctx) > 0 &&
|
||||
EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha256()) > 0 &&
|
||||
EVP_PKEY_verify(ctx, signature, signature_len, md_value, md_len) > 0)
|
||||
{
|
||||
result = BGPSEC_SIGNATURE_MATCH;
|
||||
}
|
||||
|
||||
#if 0 && defined(LOG_OPENSSL_ERRORS) && defined(LOG_TO_STDERR)
|
||||
else
|
||||
ERR_print_errors_fp(stderr);
|
||||
#endif
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
return result;
|
||||
}
|
||||
|
||||
int bgpsec_verify_signature_with_ascii_ski(const struct bgp_config *conf,
|
||||
const byte *octets, const size_t octets_len,
|
||||
const char *ski, const size_t ski_len,
|
||||
const int asn,
|
||||
const int signature_algorithm,
|
||||
const byte *signature, const size_t signature_len) {
|
||||
const char *rootPath = (conf && conf->bgpsec_key_repo_path) ? conf->bgpsec_key_repo_path : DEFAULT_KEY_REPO_PATH;
|
||||
bgpsec_key_data key = { NULL };
|
||||
char filename[MAXPATHLEN];
|
||||
int n;
|
||||
|
||||
for (n = 0; n < BGPSEC_MAX_SKI_COLLISIONS; n++) {
|
||||
|
||||
if (snprintf(filename, sizeof(filename), "%s/%d.%s.%d.key", rootPath, asn, ski, n) >= sizeof(filename))
|
||||
break;
|
||||
|
||||
if (bgpsec_load_public_key(conf, filename, &key) != BGPSEC_SUCCESS)
|
||||
break;
|
||||
|
||||
if (bgpsec_verify_signature_with_key(conf, octets, octets_len, key, signature_algorithm,
|
||||
signature, signature_len) == BGPSEC_SIGNATURE_MATCH)
|
||||
return BGPSEC_SIGNATURE_MATCH;
|
||||
|
||||
EVP_PKEY_free(key.pkey);
|
||||
key.pkey = NULL;
|
||||
}
|
||||
|
||||
return BGPSEC_SIGNATURE_ERROR;
|
||||
}
|
||||
|
||||
int bgpsec_verify_signature_with_bin_ski(const struct bgp_config *conf,
|
||||
const byte *octets, const size_t octets_len,
|
||||
const byte *ski, const size_t ski_len,
|
||||
const int asn,
|
||||
const int signature_algorithm,
|
||||
const byte *signature, const size_t signature_len) {
|
||||
char ascii_ski[MAXPATHLEN];
|
||||
|
||||
if (convert_ski_to_ascii(ski, ski_len, ascii_ski, sizeof(ascii_ski)) == BGPSEC_FAILURE)
|
||||
return BGPSEC_SIGNATURE_ERROR;
|
||||
|
||||
return bgpsec_verify_signature_with_ascii_ski(conf, octets, octets_len, ascii_ski, sizeof(ascii_ski),
|
||||
asn, signature_algorithm, signature, signature_len);
|
||||
}
|
||||
|
||||
int bgpsec_calculate_ski(const bgpsec_key_data key,
|
||||
byte *ski, const size_t ski_len) {
|
||||
X509_PUBKEY *pubkey = NULL;
|
||||
byte digest[EVP_MAX_MD_SIZE];
|
||||
int result = BGPSEC_FAILURE;
|
||||
unsigned digest_len;
|
||||
|
||||
if (X509_PUBKEY_set(&pubkey, key.pkey) &&
|
||||
EVP_Digest(pubkey->public_key->data, pubkey->public_key->length,
|
||||
digest, &digest_len, EVP_sha1(), NULL) &&
|
||||
digest_len <= ski_len)
|
||||
{
|
||||
memcpy(ski, digest, digest_len);
|
||||
result = digest_len;
|
||||
}
|
||||
|
||||
X509_PUBKEY_free(pubkey);
|
||||
return result;
|
||||
}
|
159
proto/bgp/bgpsec/validate.h
Normal file
159
proto/bgp/bgpsec/validate.h
Normal file
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* bgpsec: validation functions
|
||||
*
|
||||
*
|
||||
* Parsons, Inc.
|
||||
* (c) 2013-2013
|
||||
*
|
||||
* Code can be used under either license:
|
||||
* - Freely distributed and used under the terms of the GNU GPLv2.
|
||||
* - Freely distributed and used under a BSD license, See README.bgpsec.
|
||||
*/
|
||||
|
||||
#ifndef _BIRD_VALIDATE_H_
|
||||
#define _BIRD_VALIDATE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include "nest/route.h"
|
||||
#include "../bgp.h"
|
||||
|
||||
/* XXX: these need to be configurable in the bird config file instead */
|
||||
#define DEFAULT_KEY_REPO_PATH "/usr/share/bird/bgpsec-keys"
|
||||
#define DEFAULT_PRIV_KEY_PATH "/usr/share/bird/bgpsec-private-keys"
|
||||
#define BGPSEC_MAX_SKI_COLLISIONS 3
|
||||
|
||||
/*
|
||||
* Structure to store keying data in. This used to be a union, but
|
||||
* since we should be using EVP_PKEY everywhere it's now just a wrapper.
|
||||
*/
|
||||
typedef struct {
|
||||
EVP_PKEY *pkey;
|
||||
} bgpsec_key_data;
|
||||
|
||||
/* Generic error codes */
|
||||
#define BGPSEC_SUCCESS 0
|
||||
#define BGPSEC_FAILURE -1
|
||||
|
||||
|
||||
/* These match the defined algorithm bytes from the protocol definition */
|
||||
|
||||
/* Algorithm #: 1
|
||||
* Digest algorithm: SHA-256
|
||||
* Signature algorithm: ECDSA P-256
|
||||
*/
|
||||
/* XXX: IANA has yet to assign this number; 1 is a logical guess */
|
||||
/* XXX: Definiton in draft-turner-sidr-bgpsec-algs-00.txt */
|
||||
#define BGPSEC_ALGORITHM_SHA256_ECDSA_P_256 1
|
||||
|
||||
#define BGPSEC_DEFAULT_CURVE BGPSEC_ALGORITHM_SHA256_ECDSA_P_256
|
||||
|
||||
/*
|
||||
* Signs a blob of octets in 'octets' with the certificate found using
|
||||
* the 'subject_key_ident' using the algorithm indicated by
|
||||
* 'signature_algorithm'. The resulting signature is placed in the
|
||||
* pre-allocated 'signature' block, whose pre-allocated length must be
|
||||
* stored in 'signature_len'.
|
||||
*
|
||||
* Internally this looks up the certificate and then calls
|
||||
* bgpsec_sign_data_with_key(), defined below.
|
||||
*
|
||||
* Returns: The length of the signature actually created, or -1 on error.
|
||||
*/
|
||||
int bgpsec_sign_data_with_ascii_ski(const struct bgp_config *conf,
|
||||
const byte *octets, const size_t octets_len,
|
||||
const char *ski, const size_t ski_len,
|
||||
const int asn,
|
||||
const int signature_algorithm,
|
||||
byte *signature, size_t signature_len);
|
||||
|
||||
int bgpsec_sign_data_with_bin_ski(const struct bgp_config *conf,
|
||||
const byte *octets, const size_t octets_len,
|
||||
const byte *ski, const size_t ski_len,
|
||||
const int asn,
|
||||
const int signature_algorithm,
|
||||
byte *signature, size_t signature_len);
|
||||
|
||||
/*
|
||||
* Signs a blob of octets in 'octets' with the private key 'key' using
|
||||
* the algorithm indicated by 'signature_algorithm'. The resulting signature
|
||||
* is placed in the pre-allocated 'signature' block, who's
|
||||
* pre-allocated length bust be stored in 'signature_len'.
|
||||
*
|
||||
* Returns: The length of the signature actually created, or -1 on error.
|
||||
*/
|
||||
int bgpsec_sign_data_with_key(const struct bgp_config *conf,
|
||||
const byte *octets, const size_t octets_len,
|
||||
const bgpsec_key_data key,
|
||||
const int signature_algorithm,
|
||||
byte *signature, size_t signature_len);
|
||||
|
||||
|
||||
#define BGPSEC_SIGNATURE_MATCH 0
|
||||
#define BGPSEC_SIGNATURE_ERROR 1
|
||||
/*
|
||||
* Validates a signature on a block and returns an error code if the
|
||||
* signature dosen't match. The data to check the signature for
|
||||
* should be in 'octets' with length 'octets_len', and the public key
|
||||
* to check with should be in 'key' using algorithm
|
||||
* 'signature_algorithm'. The signature from the bgp packet should
|
||||
* should be in 'signature' with length 'signature_len'.
|
||||
*
|
||||
* Returns:
|
||||
* Success: BGPSEC_SIGNATURE_MATCH
|
||||
* Failure: BGPSEC_SIGNATURE_ERROR
|
||||
*/
|
||||
int bgpsec_verify_signature_with_key(const struct bgp_config *conf,
|
||||
const byte *octets, const size_t octets_len,
|
||||
const bgpsec_key_data key,
|
||||
const int signature_algorithm,
|
||||
const byte *signature, const size_t signature_len);
|
||||
|
||||
/* verifies a signature when passed an ascii SKI */
|
||||
int bgpsec_verify_signature_with_ascii_ski(const struct bgp_config *conf,
|
||||
const byte *octets, const size_t octets_len,
|
||||
const char *ski, const size_t ski_len,
|
||||
const int asn,
|
||||
const int signature_algorithm,
|
||||
const byte *signature, const size_t signature_len);
|
||||
|
||||
/* verifies a signature when passed a binary SKI
|
||||
(internally, this is a wrapper around the above function and merely
|
||||
prints the binary to an hex-encoded ascii first) */
|
||||
int bgpsec_verify_signature_with_bin_ski(const struct bgp_config *conf,
|
||||
const byte *octets, const size_t octets_len,
|
||||
const byte *ski, const size_t ski_len,
|
||||
const int asn,
|
||||
const int signature_algorithm,
|
||||
const byte *signature, const size_t signature_len);
|
||||
|
||||
|
||||
/*
|
||||
* Load private and public keys from files.
|
||||
*
|
||||
* Returns:
|
||||
* Success: BGPSEC_SUCCESS
|
||||
* Failure: BGPSEC_FAILURE
|
||||
*/
|
||||
|
||||
int bgpsec_load_private_key(const struct bgp_config *conf,
|
||||
const char *filename,
|
||||
bgpsec_key_data *key_data);
|
||||
|
||||
int bgpsec_load_public_key(const struct bgp_config *conf,
|
||||
const char *filename,
|
||||
bgpsec_key_data *key_data);
|
||||
|
||||
/*
|
||||
* Calculate the SKI of a key.
|
||||
*
|
||||
* Returns:
|
||||
* Success: length of calculated SKI
|
||||
* Failure: BGPSEC_FAILURE
|
||||
*/
|
||||
|
||||
int bgpsec_calculate_ski(const bgpsec_key_data key,
|
||||
byte *ski, const size_t ski_len);
|
||||
|
||||
#endif
|
@ -4,6 +4,14 @@
|
||||
* (c) 2000 Martin Mares <mj@ucw.cz>
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*
|
||||
*
|
||||
* Code added from Parsons, Inc. (BGPSEC additions)
|
||||
* (c) 2013-2013
|
||||
*
|
||||
* Can be used under either license:
|
||||
* - Freely distributed and used under the terms of the GNU GPLv2.
|
||||
* - Freely distributed and used under a BSD license, See README.bgpsec.
|
||||
*/
|
||||
|
||||
CF_HDR
|
||||
@ -27,7 +35,10 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY,
|
||||
INTERPRET, COMMUNITIES, BGP_ORIGINATOR_ID, BGP_CLUSTER_LIST, IGP,
|
||||
TABLE, GATEWAY, DIRECT, RECURSIVE, MED, TTL, SECURITY, DETERMINISTIC,
|
||||
SECONDARY, ALLOW, BFD, ADD, PATHS, RX, TX, GRACEFUL, RESTART, AWARE,
|
||||
CHECK, LINK, PORT)
|
||||
CHECK, LINK, PORT,
|
||||
BGPSEC, BGPSEC_SKI, BGPSEC_KEY_REPO_PATH, BGPSEC_PRIV_KEY_PATH,
|
||||
BGPSEC_SAVE_BINARY_KEYS, BGPSEC_PREFER, BGPSEC_NO_PCOUNT0,
|
||||
BGPSEC_REQUIRE, BGPSEC_NO_INVALID_ROUTES)
|
||||
|
||||
CF_GRAMMAR
|
||||
|
||||
@ -48,6 +59,15 @@ bgp_proto_start: proto_start BGP {
|
||||
BGP_CFG->error_delay_time_max = 300;
|
||||
BGP_CFG->enable_refresh = 1;
|
||||
BGP_CFG->enable_as4 = 1;
|
||||
BGP_CFG->enable_bgpsec = 1;
|
||||
BGP_CFG->bgpsec_ski = 0;
|
||||
BGP_CFG->bgpsec_priv_key_path = 0;
|
||||
BGP_CFG->bgpsec_key_repo_path = 0;
|
||||
BGP_CFG->bgpsec_prefer = 1;
|
||||
BGP_CFG->bgpsec_no_pcount0 = 1;
|
||||
BGP_CFG->bgpsec_no_invalid_routes = 0;
|
||||
BGP_CFG->bgpsec_require = 0;
|
||||
BGP_CFG->bgpsec_save_binary_keys = 0;
|
||||
BGP_CFG->capabilities = 2;
|
||||
BGP_CFG->advertise_ipv4 = 1;
|
||||
BGP_CFG->interpret_communities = 1;
|
||||
@ -130,6 +150,14 @@ bgp_proto:
|
||||
| bgp_proto GRACEFUL RESTART TIME expr ';' { BGP_CFG->gr_time = $5; }
|
||||
| bgp_proto IGP TABLE rtable ';' { BGP_CFG->igp_table = $4; }
|
||||
| bgp_proto TTL SECURITY bool ';' { BGP_CFG->ttl_security = $4; }
|
||||
| bgp_proto BGPSEC bool ';' { BGP_CFG->enable_bgpsec = $3; }
|
||||
| bgp_proto BGPSEC_PREFER bool ';' { BGP_CFG->bgpsec_prefer = $3; }
|
||||
| bgp_proto BGPSEC_NO_INVALID_ROUTES bool ';' { BGP_CFG->bgpsec_no_invalid_routes = $3; }
|
||||
| bgp_proto BGPSEC_SKI text ';' { BGP_CFG->bgpsec_ski = $3; }
|
||||
| bgp_proto BGPSEC_KEY_REPO_PATH text ';' { BGP_CFG->bgpsec_key_repo_path = $3; }
|
||||
| bgp_proto BGPSEC_PRIV_KEY_PATH text ';' { BGP_CFG->bgpsec_priv_key_path = $3; }
|
||||
| bgp_proto BGPSEC_SAVE_BINARY_KEYS bool ';' { BGP_CFG->bgpsec_save_binary_keys = $3; }
|
||||
| bgp_proto BGPSEC_NO_PCOUNT0 bool ';' { BGP_CFG->bgpsec_no_pcount0 = $3; }
|
||||
| bgp_proto CHECK LINK bool ';' { BGP_CFG->check_link = $4; }
|
||||
| bgp_proto BFD bool ';' { BGP_CFG->bfd = $3; cf_check_bfd($3); }
|
||||
;
|
||||
|
@ -4,7 +4,14 @@
|
||||
* (c) 2000 Martin Mares <mj@ucw.cz>
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
*
|
||||
*
|
||||
* Code added from Parsons, Inc. (BGPSEC additions)
|
||||
* (c) 2013-2013
|
||||
*
|
||||
* Can be used under either license:
|
||||
* - Freely distributed and used under the terms of the GNU GPLv2.
|
||||
* - Freely distributed and used under a BSD license, See README.bgpsec. */
|
||||
|
||||
#undef LOCAL_DEBUG
|
||||
|
||||
@ -21,6 +28,7 @@
|
||||
#include "nest/cli.h"
|
||||
|
||||
#include "bgp.h"
|
||||
#undef sk_new /* remove the bird-specific compatability wrapper */
|
||||
|
||||
|
||||
#define BGP_RR_REQUEST 0
|
||||
@ -38,6 +46,13 @@ static byte fsm_err_subcode[BS_MAX] = {
|
||||
[BS_ESTABLISHED] = 3
|
||||
};
|
||||
|
||||
/* convert from AF_INET address family to BGP AF AF */
|
||||
u16 af_to_bgp_af(int af) {
|
||||
if (af == AF_INET) { return BGP_AF_IPV4; }
|
||||
else if (af == AF_INET6) { return BGP_AF_IPV6; }
|
||||
else { return 0; }
|
||||
}
|
||||
|
||||
/*
|
||||
* MRT Dump format is not semantically specified.
|
||||
* We will use these values in appropriate fields:
|
||||
@ -73,7 +88,7 @@ mrt_put_bgp4_hdr(byte *buf, struct bgp_conn *conn, int as4)
|
||||
}
|
||||
|
||||
put_u16(buf+0, (p->neigh && p->neigh->iface) ? p->neigh->iface->index : 0);
|
||||
put_u16(buf+2, BGP_AF);
|
||||
put_u16(buf+2, af_to_bgp_af(conn->sk->af) );
|
||||
buf+=4;
|
||||
buf = put_ipa(buf, conn->sk ? conn->sk->daddr : IPA_NONE);
|
||||
buf = put_ipa(buf, conn->sk ? conn->sk->saddr : IPA_NONE);
|
||||
@ -200,6 +215,35 @@ bgp_put_cap_as4(struct bgp_proto *p, byte *buf)
|
||||
return buf + 4;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BGPSEC
|
||||
/* Note: this adds 2 capabilities to bgp capabilitiies.
|
||||
* One indicates this router can send BGPSEC messages and
|
||||
* the other indicating it can recieve BGPSEC messages
|
||||
*
|
||||
* Currently, this code doesn't have differentiate in its
|
||||
* configuration. It either supports sending/receiving BGPSEC
|
||||
* messages or doesn't support BGPSEC */
|
||||
static byte *
|
||||
bgp_put_cap_bgpsec(struct bgp_conn *conn UNUSED, byte *buf)
|
||||
{
|
||||
/* can send bgpsec capability */
|
||||
*buf++ = BGPSEC_CAPABILITY; /* XXX Capability 72: best guess, BGPSEC */
|
||||
*buf++ = 3; /* BGPSEC Capability length */
|
||||
/* bgpsec version and capable of sending */
|
||||
*buf++ = ( (BGPSEC_VERSION << 4) | 0x08 );
|
||||
put_u16(buf, BGP_AF); /* address family */
|
||||
buf = buf + 2;
|
||||
|
||||
/* can receive bgpsec capability */
|
||||
*buf++ = BGPSEC_CAPABILITY; /* XXX Capability 72: best guess, BGPSEC */
|
||||
*buf++ = 3; /* BGPSEC Capability length */
|
||||
/* bgpsec version and capable of receiving bgpsec */
|
||||
*buf++ = ( (BGPSEC_VERSION << 4) | 0x00 );
|
||||
put_u16(buf, BGP_AF); /* address family */
|
||||
return buf + 2;
|
||||
}
|
||||
#endif
|
||||
|
||||
static byte *
|
||||
bgp_put_cap_add_path(struct bgp_proto *p, byte *buf)
|
||||
{
|
||||
@ -271,6 +315,14 @@ bgp_create_open(struct bgp_conn *conn, byte *buf)
|
||||
if (p->cf->add_path)
|
||||
cap = bgp_put_cap_add_path(p, cap);
|
||||
|
||||
#ifdef CONFIG_BGPSEC
|
||||
/* xxx */
|
||||
BGP_TRACE(D_PACKETS, "Add BGPSec capability? \'%d\', v%d, as4:%d",
|
||||
p->cf->enable_bgpsec, BGPSEC_VERSION, p->cf->enable_as4);
|
||||
if (p->cf->enable_bgpsec)
|
||||
cap = bgp_put_cap_bgpsec(conn, cap);
|
||||
#endif
|
||||
|
||||
if (p->cf->enable_refresh)
|
||||
cap = bgp_put_cap_err(p, cap);
|
||||
|
||||
@ -335,7 +387,10 @@ bgp_flush_prefixes(struct bgp_proto *p, struct bgp_bucket *buck)
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef IPV6 /* IPv4 version */
|
||||
|
||||
|
||||
#if !defined(IPV6) && !defined(CONFIG_BGPSEC) /* IPv4 version */
|
||||
|
||||
|
||||
static byte *
|
||||
bgp_create_update(struct bgp_conn *conn, byte *buf)
|
||||
@ -371,7 +426,7 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
|
||||
}
|
||||
|
||||
DBG("Processing bucket %p\n", buck);
|
||||
a_size = bgp_encode_attrs(p, w+2, buck->eattrs, 2048);
|
||||
a_size = bgp_encode_attrs(p, w+2, buck->eattrs, 2048, buck);
|
||||
|
||||
if (a_size < 0)
|
||||
{
|
||||
@ -413,7 +468,9 @@ bgp_create_end_mark(struct bgp_conn *conn, byte *buf)
|
||||
return buf+4;
|
||||
}
|
||||
|
||||
#else /* IPv6 version */
|
||||
|
||||
#else /* IPv6 or BGPSEC version */
|
||||
|
||||
|
||||
static inline int
|
||||
same_iface(struct bgp_proto *p, ip_addr *ip)
|
||||
@ -431,7 +488,7 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
|
||||
int remains = BGP_MAX_PACKET_LENGTH - BGP_HEADER_LENGTH - 4;
|
||||
byte *w, *w_stored, *tmp, *tstart;
|
||||
ip_addr *ipp, ip, ip_ll;
|
||||
ea_list *ea;
|
||||
ea_list *ea = NULL;
|
||||
eattr *nh;
|
||||
|
||||
put_u16(buf, 0);
|
||||
@ -442,7 +499,11 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
|
||||
DBG("Withdrawn routes:\n");
|
||||
tmp = bgp_attach_attr_wa(&ea, bgp_linpool, BA_MP_UNREACH_NLRI, remains-8);
|
||||
*tmp++ = 0;
|
||||
#ifdef IPV6
|
||||
*tmp++ = BGP_AF_IPV6;
|
||||
#else
|
||||
*tmp++ = BGP_AF_IPV4;
|
||||
#endif
|
||||
*tmp++ = 1;
|
||||
ea->attrs[0].u.ptr->length = 3 + bgp_encode_prefixes(p, tmp, buck, remains-11);
|
||||
size = bgp_encode_attrs(p, w, ea, remains);
|
||||
@ -468,6 +529,7 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
|
||||
w_stored = w;
|
||||
|
||||
size = bgp_encode_attrs(p, w, buck->eattrs, 2048);
|
||||
|
||||
if (size < 0)
|
||||
{
|
||||
log(L_ERR "%s: Attribute list too long, skipping corresponding routes", p->p.name);
|
||||
@ -536,9 +598,12 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
|
||||
}
|
||||
|
||||
tstart = tmp = bgp_attach_attr_wa(&ea, bgp_linpool, BA_MP_REACH_NLRI, remains-8);
|
||||
*tmp++ = 0;
|
||||
*tmp++ = BGP_AF_IPV6;
|
||||
*tmp++ = 1;
|
||||
*tmp++ = 0; /* high order byte of AFI */
|
||||
|
||||
#ifdef IPV6
|
||||
*tmp++ = BGP_AF_IPV6; /* AFI */
|
||||
*tmp++ = 1; /* SAFI */
|
||||
|
||||
|
||||
if (ipa_is_link_local(ip))
|
||||
ip = IPA_NONE;
|
||||
@ -559,13 +624,39 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
|
||||
memcpy(tmp, &ip, 16);
|
||||
tmp += 16;
|
||||
}
|
||||
#else
|
||||
*tmp++ = BGP_AF_IPV4; /* AFI */
|
||||
*tmp++ = 1; /* SAFI */
|
||||
*tmp++ = 4; /* next hop length */
|
||||
ipa_hton(ip); /* next hop */
|
||||
memcpy(tmp, &ip, 4);
|
||||
tmp += 4;
|
||||
#endif
|
||||
|
||||
*tmp++ = 0; /* No SNPA information */
|
||||
*tmp++ = 0; /* reserved byte (No SNPA information) */
|
||||
byte *nlri = tmp;
|
||||
tmp += bgp_encode_prefixes(p, tmp, buck, remains - (8+3+32+1));
|
||||
ea->attrs[0].u.ptr->length = tmp - tstart;
|
||||
|
||||
size = bgp_encode_attrs(p, w, ea, remains);
|
||||
|
||||
ASSERT(size >= 0);
|
||||
w += size;
|
||||
remains -= size;
|
||||
|
||||
#ifdef CONFIG_BGPSEC
|
||||
if (p->conn->peer_bgpsec_support) {
|
||||
int bgpsec_len = encode_bgpsec_attr(p->conn, buck->eattrs, w, remains, nlri);
|
||||
|
||||
if ( bgpsec_len < 0 ) {
|
||||
log(L_ERR "encode_bgpsec_attrs: bgpsec signing failed");
|
||||
return NULL;
|
||||
}
|
||||
w += bgpsec_len;
|
||||
remains -= bgpsec_len;
|
||||
}
|
||||
#endif
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -798,8 +889,12 @@ bgp_tx(sock *sk)
|
||||
void
|
||||
bgp_parse_capabilities(struct bgp_conn *conn, byte *opt, int len)
|
||||
{
|
||||
// struct bgp_proto *p = conn->bgp;
|
||||
struct bgp_proto *p = conn->bgp; /* used in BGP_TRACE */
|
||||
int i,cl;
|
||||
#ifdef CONFIG_BGPSEC
|
||||
u16 afi;
|
||||
u8 safi;
|
||||
#endif
|
||||
|
||||
while (len > 0)
|
||||
{
|
||||
@ -840,6 +935,53 @@ bgp_parse_capabilities(struct bgp_conn *conn, byte *opt, int len)
|
||||
conn->advertised_as = get_u32(opt + 2);
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_BGPSEC
|
||||
case BGPSEC_CAPABILITY: /* BGPSEC_CAPABILITY value currently arbitrary */
|
||||
if (cl != 3) /* data length must be 3 */
|
||||
goto err;
|
||||
|
||||
if ( ! conn->bgp->cf->enable_bgpsec ) {
|
||||
BGP_TRACE(D_PACKETS, "Error: bgp_parse_capabilities: BGPSEC NOT enabled locally");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if ( BGPSEC_VERSION == (opt[2] & 0xF0) ) {
|
||||
BGP_TRACE(D_PACKETS, "bgp_parse_capabilities: sender BGPSEC_VERSION matches : %d", (opt[2] & 0x0F));
|
||||
conn->peer_bgpsec_support = 1;
|
||||
}
|
||||
else {
|
||||
BGP_TRACE(D_PACKETS, "Error: bgp_parse_capabilities: BGPSEC_VERSION does not match, loc : %d, rem : $d", BGPSEC_VERSION, (opt[2] & 0x0F));
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (opt[2] & 0x08) {
|
||||
BGP_TRACE(D_PACKETS, "bgp_parse_capabilities: sender can send BGPSEC messages : %d", opt[2]);
|
||||
conn->bgp->bgpsec_send = 1;
|
||||
}
|
||||
|
||||
if (0 == (opt[2] & 0x08)) {
|
||||
BGP_TRACE(D_PACKETS, "bgp_parse_capabilities: sender can receive BGPSEC messages : %d", opt[2]);
|
||||
conn->bgp->bgpsec_receive = 1;
|
||||
}
|
||||
|
||||
afi = get_u16(opt + 3);
|
||||
|
||||
if (BGP_AF_IPV4 == afi) {
|
||||
BGP_TRACE(D_PACKETS, "bgp_parse_capabilities: sender using bgpsec IPV4 Address Family : %d", afi);
|
||||
conn->bgp->bgpsec_ipv4 = 1;
|
||||
}
|
||||
else if (BGP_AF_IPV6 == afi) {
|
||||
BGP_TRACE(D_PACKETS, "bgp_parse_capabilities: sender using IPV6 Address Family : %d", afi);
|
||||
conn->bgp->bgpsec_ipv6 = 1;
|
||||
}
|
||||
else {
|
||||
BGP_TRACE(D_PACKETS, "Error: bgp_parse_capabilities: unknown AFI: %d", afi);
|
||||
goto err;
|
||||
}
|
||||
|
||||
break;
|
||||
#endif
|
||||
|
||||
case 69: /* ADD-PATH capability, draft */
|
||||
if (cl % 4)
|
||||
goto err;
|
||||
@ -1047,6 +1189,9 @@ bgp_rx_end_mark(struct bgp_proto *p)
|
||||
}
|
||||
|
||||
|
||||
/* DECODE_PREFIX definition moved to bgp.h, so that it can also be
|
||||
* used by attrs.c : decode_bgpsec_attr() */
|
||||
/*
|
||||
#define DECODE_PREFIX(pp, ll) do { \
|
||||
if (p->add_path_rx) \
|
||||
{ \
|
||||
@ -1068,6 +1213,7 @@ bgp_rx_end_mark(struct bgp_proto *p)
|
||||
prefix = ipa_and(prefix, ipa_mkmask(b)); \
|
||||
pxlen = b; \
|
||||
} while (0)
|
||||
*/
|
||||
|
||||
|
||||
static inline void
|
||||
@ -1169,7 +1315,8 @@ bgp_set_next_hop(struct bgp_proto *p, rta *a)
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifndef IPV6 /* IPv4 version */
|
||||
|
||||
#if !defined(IPV6) && !defined(CONFIG_BGPSEC) /* IPv4 version */
|
||||
|
||||
static void
|
||||
bgp_do_rx_update(struct bgp_conn *conn,
|
||||
@ -1204,7 +1351,8 @@ bgp_do_rx_update(struct bgp_conn *conn,
|
||||
if (!attr_len && !nlri_len) /* shortcut */
|
||||
return;
|
||||
|
||||
a0 = bgp_decode_attrs(conn, attrs, attr_len, bgp_linpool, nlri_len);
|
||||
/* Note: bgp_linpool, nlri, nlri_len needed for bgpsec decoding */
|
||||
a0 = bgp_decode_attrs(conn, attrs, attr_len, bgp_linpool, nlri, nlri_len);
|
||||
|
||||
if (conn->state != BS_ESTABLISHED) /* fatal error during decoding */
|
||||
return;
|
||||
@ -1236,8 +1384,12 @@ bgp_do_rx_update(struct bgp_conn *conn,
|
||||
return;
|
||||
}
|
||||
|
||||
#else /* IPv6 version */
|
||||
|
||||
#else /* IPv6 || BGPSEC version */
|
||||
|
||||
/* DO_NLRI definition moved to bgp.h, so that it can also be
|
||||
* used by attrs.c : decode_bgpsec_attr() */
|
||||
/*
|
||||
#define DO_NLRI(name) \
|
||||
start = x = p->name##_start; \
|
||||
len = len0 = p->name##_len; \
|
||||
@ -1253,14 +1405,16 @@ bgp_do_rx_update(struct bgp_conn *conn,
|
||||
else \
|
||||
af = 0; \
|
||||
if (af == BGP_AF_IPV6)
|
||||
*/
|
||||
|
||||
static void
|
||||
bgp_attach_next_hop(rta *a0, byte *x)
|
||||
{
|
||||
ip_addr *nh = (ip_addr *) bgp_attach_attr_wa(&a0->eattrs, bgp_linpool, BA_NEXT_HOP, NEXT_HOP_LENGTH);
|
||||
memcpy(nh, x+1, 16);
|
||||
memcpy(nh, x+1, (*x <= sizeof(ip_addr) ? *x : sizeof(ip_addr)) );
|
||||
ipa_ntoh(nh[0]);
|
||||
|
||||
#ifdef IPv6
|
||||
/* We store received link local address in the other part of BA_NEXT_HOP eattr. */
|
||||
if (*x == 32)
|
||||
{
|
||||
@ -1269,6 +1423,7 @@ bgp_attach_next_hop(rta *a0, byte *x)
|
||||
}
|
||||
else
|
||||
nh[1] = IPA_NONE;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -1291,7 +1446,7 @@ bgp_do_rx_update(struct bgp_conn *conn,
|
||||
|
||||
p->mp_reach_len = 0;
|
||||
p->mp_unreach_len = 0;
|
||||
a0 = bgp_decode_attrs(conn, attrs, attr_len, bgp_linpool, 0);
|
||||
a0 = bgp_decode_attrs(conn, attrs, attr_len, bgp_linpool, nlri, nlri_len);
|
||||
|
||||
if (conn->state != BS_ESTABLISHED) /* fatal error during decoding */
|
||||
return;
|
||||
@ -1316,9 +1471,9 @@ bgp_do_rx_update(struct bgp_conn *conn,
|
||||
|
||||
DO_NLRI(mp_reach)
|
||||
{
|
||||
/* Create fake NEXT_HOP attribute */
|
||||
if (len < 1 || (*x != 16 && *x != 32) || len < *x + 2)
|
||||
{ err = 9; goto done; }
|
||||
/* Create fake NEXT_HOP attribute, check IP addr length */
|
||||
if (len < 1 || (*x != 4 && *x != 16 && *x != 32) || len < *x + 2)
|
||||
{ err = BGP_UPD_ERROR_OPT_ATTR; goto done; }
|
||||
|
||||
if (a0)
|
||||
bgp_attach_next_hop(a0, x);
|
||||
@ -1350,7 +1505,7 @@ bgp_do_rx_update(struct bgp_conn *conn,
|
||||
rta_free(a);
|
||||
|
||||
if (err) /* Use subcode 9, not err */
|
||||
bgp_error(conn, 3, 9, NULL, 0);
|
||||
bgp_error(conn, 3, BGP_UPD_ERROR_OPT_ATTR, NULL, 0);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -1432,6 +1587,7 @@ static struct {
|
||||
{ 3, 9, "Optional attribute error" },
|
||||
{ 3, 10, "Invalid network field" },
|
||||
{ 3, 11, "Malformed AS_PATH" },
|
||||
{ 3, 12, "Bad BGPSEC Signature"},
|
||||
{ 4, 0, "Hold timer expired" },
|
||||
{ 5, 0, "Finite state machine error" }, /* Subcodes are according to [RFC6608] */
|
||||
{ 5, 1, "Unexpected message in OpenSent state" },
|
||||
|
@ -43,6 +43,7 @@
|
||||
#undef CONFIG_BGP
|
||||
#undef CONFIG_OSPF
|
||||
#undef CONFIG_PIPE
|
||||
#undef CONFIG_BGPSEC
|
||||
|
||||
/* We use multithreading */
|
||||
#undef USE_PTHREADS
|
||||
|
Loading…
Reference in New Issue
Block a user