--- /dev/null
+
+ 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.
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])
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
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
Debugging: $enable_debug
POSIX threads: $enable_pthreads
Routing protocols: $protocols
+ BGPsec enabled: $enable_bgpsec
Client: $enable_client
EOF
rm -f $objdir/.*-stamp
#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)
{
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)
{
memcpy(p, &x, 4);
}
+static inline void
+put_u64(void *p, u64 x)
+{
+ x = htobe64(x);
+ memcpy(p, &x, 8);
+}
+
#endif
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
#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)
* (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-2016
+ *
+ * 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
#include "lib/resource.h"
#include "lib/string.h"
#include "lib/unaligned.h"
+#include "stdio.h"
#include "bgp.h"
+#ifdef CONFIG_BGPSEC
+#include "bgpsec/validate.h"
+#endif
+
/*
* UPDATE message error handling
*
static byte bgp_mandatory_attrs[] = { BA_ORIGIN, BA_AS_PATH
-#ifndef IPV6
+#if !defined(IPV6) && !defined(CONFIG_BGPSEC)
,BA_NEXT_HOP
#endif
};
static int
bgp_check_next_hop(struct bgp_proto *p UNUSED, byte *a, int len)
{
-#ifdef IPV6
+#if defined(IPV6) || defined(CONFIG_BGPSEC)
return IGNORE;
#else
ip_addr addr;
bgp_format_next_hop(eattr *a, byte *buf, int buflen UNUSED)
{
ip_addr *ipp = (ip_addr *) a->u.ptr->data;
-#ifdef IPV6
+#if defined(IPV6) || defined(CONFIG_BGSPEC)
/* in IPv6, we might have two addresses in NEXT HOP */
if ((a->u.ptr->length == NEXT_HOP_LENGTH) && ipa_nonzero(ipp[1]))
{
}
static int
-bgp_check_community(struct bgp_proto *p UNUSED, byte *a UNUSED, int len)
+bgp_check_community(struct bgp_proto *p, byte *buf, int len)
{
return ((len % 4) == 0) ? 0 : WITHDRAW;
}
static int
-bgp_check_cluster_list(struct bgp_proto *p UNUSED, byte *a UNUSED, int len)
+bgp_check_cluster_list(struct bgp_proto *p, byte *buf, int len)
{
return ((len % 4) == 0) ? 0 : 5;
}
int_set_format(a->u.ptr, 0, -1, buf, buflen);
}
+
+/* BGPsec Decode Functions */
+
+#ifdef CONFIG_BGPSEC
+
+/* Creates an as_path from the bgpsec attribute secure_path
+ information and adds it to the rta struct. */
+/* The created as_path is used for local route determination and is
+ removed before sending out bgpsec updates */
+int bgpsec_create_aspath(rta *route, byte *secpath_p, u16 secp_len, struct linpool *pool)
+{
+ ea_list *ea;
+ struct adata *ad;
+ byte *secp = secpath_p;
+ secp_len -= 2;
+
+ /* xxx how to handle memory allocation error? */
+ ea = lp_alloc(pool, sizeof(ea_list) + sizeof(eattr));
+ ea->next = route->eattrs;
+ route->eattrs = ea;
+
+ ea->flags = 0;
+ ea->count = 1;
+ ea->attrs[0].id = EA_CODE(EAP_BGP, BA_AS_PATH);
+ ea->attrs[0].flags = BAF_TRANSITIVE;
+ ea->attrs[0].type = EAF_TYPE_AS_PATH;
+
+ byte aspath_len = secp_len / 6;
+ int pattr_len = 2 + (4*aspath_len);
+
+ ad = lp_alloc(pool, sizeof(struct adata) + pattr_len);
+ ea->attrs[0].u.ptr = ad;
+ ad->length = pattr_len;
+
+ byte *asp = ad->data;
+ *asp++ = AS_PATH_SEQUENCE;
+ *asp++ = aspath_len;
+
+ secp += 2; /* skip flags and pcount */
+ while ( (asp < (ad->data + pattr_len)) && (secp < (secpath_p + secp_len)) )
+ {
+ memcpy(asp, secp, 4);
+ asp += 4;
+ secp += 6;
+ }
+
+ return 0;
+} /* int bgpsec_create_aspath() */
+
+
+/* Marks the route as "Valid" by adding a valid attribute */
+int bgpsec_add_valid_attr(rta *route, struct linpool *pool)
+{
+ ea_list *ea;
+
+ /* xxx how to handle memory allocation error? */
+ ea = lp_alloc(pool, sizeof(ea_list) + sizeof(eattr));
+ ea->next = route->eattrs;
+ route->eattrs = ea;
+
+ ea->flags = 0;
+ ea->count = 1;
+ ea->attrs[0].id = EA_CODE(EAP_BGP, BA_INTERNAL_BGPSEC_VALID);
+ ea->attrs[0].flags = BAF_OPTIONAL;
+ ea->attrs[0].type = EAF_TYPE_INT;
+ /* value 1 is arbitrary, existence of the attribute indicates valid */
+ ea->attrs[0].u.data = 1;
+
+ return 0;
+
+} /* int bgpsec_add_valid_attr() */
+
+
+/* XXX subroutine used for debbugging */
+char *
+hashbuff_to_string(u8 *hb, int len)
+{
+ static char ret[(2*BGPSEC_MAX_SIG_LENGTH)+21], *rp;
+ rp = ret;
+ int i;
+ bzero(ret, (2*BGPSEC_MAX_SIG_LENGTH)+21);
+
+ for(i=0; i<len; i++)
+ {
+ sprintf(rp, "%02X", *(hb+i));
+ rp += 2;
+ }
+ return ret;
+}
+
+
+/* Parse bgpsec attr and make sure that it is encoded at a minimum
+ level of properly. I.e., check that the size and lengths of its
+ parts are in an acceptable range. */
+/* authenticate the bgpsec attribute. Return 1 on succes and 0 on
+ failure */
+static int
+decode_bgpsec_attr(struct bgp_proto *bgp,
+ byte *buf,
+ int bgpSec_len,
+ rta *route_attr,
+ struct linpool *pool)
+{
+ log(L_TRACE "DECODE_BGPSEC_ATTR: %d < %d",
+ bgp->local_as, bgp->remote_as);
+
+ byte *bgpSec_p = buf;
+
+ /* hash length, origination < non-orig
+ (e.g. ~22+ octets < 10+last signature length) */
+ static u8 hashBuff[BGPSEC_SIG_HASH_LENGTH];
+ u8 *hash_p = hashBuff;
+
+ /* clean out any previous data */
+ bzero(hashBuff, BGPSEC_SIG_HASH_LENGTH);
+
+ /* variables used by DO_NLRI macro below, defined in bgp.h:
+ p, start, x, len, len0, af, sub and goto 'done:' */
+ struct bgp_proto *p = bgp;
+ byte *start = 0, *x = 0;
+ int len = 0, len0 = 0;
+ unsigned af = 0, sub = 0;
+ /* variables used by DECODE_PREFIX macro below, defined in bgp.h:
+ p, err, path_id, prefix, pxlen and goto 'done:' */
+ int err = 0;
+ u32 path_id = 0;
+ ip_addr prefix = 0;
+ int pxlen = 0;
+
+ /* Is it long enough to have a minimal valid bgpseg_path_attr */
+ /* 43 = 9 (NLRI/SAFI/AFI/AlgoID) + 8 (sec path min) + 26 (sig block min) */
+ if ( bgpSec_len < 43 ) {
+ log(L_WARN "decode_bgpsec: %d < %d: bad bgpsec attribute length: %d, ignoring",
+ bgp->local_as, bgp->remote_as, bgpSec_len);
+ return IGNORE;
+ }
+
+ /* get secure path pointer */
+ u16 secPath_len = get_u16(bgpSec_p);
+ byte *secPath_p = bgpSec_p + 2;
+ byte *secPathSeg_p = secPath_p;
+
+ /* XXX, only handling a single signature block, should handle 1 or 2 */
+ /* get signature block pointer */
+ byte *sigBlock_p = bgpSec_p + secPath_len;
+ u16 sigBlock_len = get_u16(sigBlock_p);
+ int algoID = *(bgpSec_p + 2);
+ byte *sigSegment_p = sigBlock_p + 3; /* skip length value and algo ID byte */
+
+ /* check algorithm signature ID, we only support one algo. ID
+ currently */
+ if ( BGPSEC_ALGO_ID != algoID )
+ {
+ log(L_ERR "decode_bgpsec: %d < %d: Uknown Algorithm ID: %d, ignoring",
+ bgp->local_as, bgp->remote_as, algoID);
+ /* XXX return err unknown sig algo? only if there is no
+ * other known sig algo (e.g.. two sig blocks); */
+ return IGNORE;
+ }
+
+ /* Check secure path size, each segment is 6 octets long */
+ if ( ( ((secPath_len - 2) % 6) != 0 ) || ((secPath_len + 25) > bgpSec_len) )
+ {
+ log(L_WARN "decode_bgpsec: %d < %d: bad secure path length, ignoring",
+ bgp->local_as, bgp->remote_as);
+ /* xxx */
+ /* return errr bad length */
+ return IGNORE;
+ }
+
+ /* if not expecting peer pcount=0, check to make sure first pcount!=0 */
+ if ( ( bgp->cf->bgpsec_no_pcount0 ) &&
+ ( 0 == *secPathSeg_p ) )
+ {
+ log(L_WARN "decode_bgpsec: %d < %d : pcount = 0 not allowed from this peer, invalid",
+ bgp->local_as, bgp->remote_as);
+ /* xxx */
+ /* return spcefic error? */
+ return IGNORE;
+ }
+
+ /* add target AS to signature hash */
+ put_u32(hash_p, bgp->local_as);
+ hash_p += 4;
+
+ /* get last (first in signature block) signature to check againts later */
+ byte *lastSKI_p = sigSegment_p;
+ sigSegment_p += BGPSEC_SKI_LENGTH;
+ u16 lastSig_len = get_u16(sigSegment_p);
+ sigSegment_p += 2;
+ byte *lastSig_p = sigSegment_p;
+ sigSegment_p += lastSig_len;
+
+ /* sanity check */
+ if ( sigSegment_p > (bgpSec_p + bgpSec_len) )
+ {
+ log(L_WARN "decode_bgpsec: %d < %d: bad first signature length: %d, ignoring",
+ bgp->local_as, bgp->remote_as, lastSig_len);
+ /* xxx */
+ /* return errr bad length */
+ return IGNORE;
+ }
+
+ /* while loop through signature / secure path blocks to load signature hash */
+ while ( ( sigSegment_p < (bgpSec_p + bgpSec_len) ) &&
+ ( secPathSeg_p < (bgpSec_p + secPath_len) ) )
+ {
+ /* put next signature segment in hash */
+ int sigSegment_len = BGPSEC_SKI_LENGTH + 2 + get_u16(sigSegment_p + BGPSEC_SKI_LENGTH);
+
+ /* check hashBuff space for adding signature segment (variable)
+ * and secure path segment (6 bytes) */
+ if ( ( (sigSegment_p + sigSegment_len) > (bgpSec_p + bgpSec_len) ) ||
+ ( (hash_p + sigSegment_len + 6) > (hashBuff + BGPSEC_SIG_HASH_LENGTH) )
+ )
+ {
+ log(L_WARN "decode_bgpsec: %d < %d: bad signature segment length: %d, or not enough space in hash buffer, ignoring",
+ bgp->local_as, bgp->remote_as, sigSegment_len);
+ /* xxx */
+ /* return errr bad length */
+ return IGNORE;
+ }
+ memcpy(hash_p, sigSegment_p, sigSegment_len);
+ sigSegment_p += sigSegment_len;
+ hash_p += sigSegment_len;
+
+ memcpy(hash_p, secPathSeg_p, 6);
+ secPathSeg_p += 6;
+ hash_p += 6;
+ }
+
+ byte *beginLastHash_p = hash_p - 4;
+
+ /* should have one secure path segment left */
+ if ( (secPathSeg_p + 6) != (bgpSec_p + secPath_len) ) {
+ log(L_WARN "decode_bgpsec: %d < %d: bad number of secure path and/or signature segments, ignoring",
+ bgp->local_as, bgp->remote_as);
+ }
+ memcpy(hash_p, secPathSeg_p, 6);
+ secPathSeg_p += 6;
+ hash_p += 6;
+
+
+ /* Get the NLRI, AFI, and SAFI information from the MP_REACH attribute */
+
+ /* Decode the MP_REACH attribute */
+ /* macro DO_NLRI, defined in bgp.h, uses:
+ p, start, x, len, len0, af, sub and goto 'done:'
+ */
+ /* macro DECODE_PREFIX, defined in bgp.h, uses:
+ p, err, path_id, prefix, pxlen and goto 'done:'
+ */
+
+ DO_NLRI(mp_reach)
+ {
+ /* check NEXT_HOP length */
+ if (len < 1 || (*x != 4 && *x != 16 && *x != 32) ||
+ len < *x + 2)
+ {
+ log(L_WARN "decode_bgpsec:o: %d < %d : bad mp_reach next hop length: %d, ignoring",
+ bgp->local_as, bgp->remote_as, *x);
+ return IGNORE;
+ }
+ /* skip next_hop length, next_hop addr, and a reserved byte */
+ len -= *x + 2;
+ x += *x + 2;
+
+ /* Get Prefix,
+ macro DECODE_PREFIX sets: prefix, pxlen. Defined in bgp.h*/
+ DECODE_PREFIX(x, len);
+ /* only one prefix is allowed in a BGPSEC message */
+ if ( len > 0 )
+ {
+ /* XXX handle specific errors for logging */
+ log(L_WARN "decode_bgpsec: %d < %d : bad NLRI length, ignoring",
+ bgp->local_as, bgp->remote_as);
+ return IGNORE;
+ }
+
+ log(L_DEBUG "decode_bgpsec: %d < %d : using NLRI %I/%d\n",
+ bgp->local_as, bgp->remote_as, prefix, pxlen);
+ }
+ else {
+ /* unknown Address Family */
+ return IGNORE;
+ }
+
+ /* load algorithm suite identifier */
+ *hash_p = algoID;
+ hash_p++;
+
+ /* AFI and SAFI */
+ bzero(hash_p, 1); /* zero high order AFI */
+ hash_p++;
+ memcpy(hash_p, &af, 1); /* copy low order AFI */
+ hash_p++;
+ memcpy(hash_p, &sub, 1); /* SAFI */
+ hash_p++;
+
+ /* NLRI */
+ memcpy(hash_p, &pxlen, 1); /* prefix length */
+ hash_p++;
+ int prefix_bytes = (pxlen + 7) / 8;
+ memcpy(hash_p, &prefix, prefix_bytes); /* prefix */
+ hash_p += prefix_bytes;
+
+ /* Check Signatures */
+
+ byte *endHash_p = hash_p;
+ hash_p = hashBuff;
+ u32 signersAS = 0;
+ int valid = 1;
+ /* cycle through signature hashBuffer and check signatures */
+ while ( ( hash_p < beginLastHash_p ) && valid ) {
+ /* signers AS in hash_p at offset = 4 (target AS) + SKI length + 2
+ * (sig length value) + next signature length + 1 (flags) + 1
+ * (pcount)
+ */
+ int nextSig_len = get_u16(hash_p + 4 + BGPSEC_SKI_LENGTH);
+ int asOffset = BGPSEC_SKI_LENGTH + nextSig_len + 8;
+ signersAS = get_u32(hash_p + asOffset);
+
+ if ( BGPSEC_SIGNATURE_MATCH !=
+ bgpsec_verify_signature_with_bin_ski
+ (bgp->cf,
+ hash_p, (endHash_p - hash_p),
+ lastSKI_p, BGPSEC_SKI_LENGTH,
+ signersAS, algoID,
+ lastSig_p, lastSig_len)
+ ) {
+ if ( bgp->cf->bgpsec_no_invalid_routes ) {
+ log(L_WARN "decode_bgpsec: %d < %d : bad signature at AS: %d, invalid routes not allowed, ignoring",
+ bgp->local_as, bgp->remote_as, signersAS);
+ return IGNORE;
+ }
+ else {
+ log(L_WARN "decode_bgpsec: %d < %d : bad signature at AS: %d, not BGPsec valid",
+ bgp->local_as, bgp->remote_as, signersAS);
+ valid = 0;
+ }
+ }
+ else {
+ log(L_DEBUG "decode_bgpsec: %d < %d : good signature AS: %d",
+ bgp->local_as, bgp->remote_as, signersAS);
+ }
+
+ /* adjust pointers, note: hash_p gets move to the next target
+ * AS/next secure path AS */
+ lastSKI_p = hash_p + 4;
+ lastSig_p = hash_p + 6 + BGPSEC_SKI_LENGTH;
+ lastSig_len = nextSig_len;
+ hash_p += asOffset;
+ }
+
+ /* check last, origination, signature */
+ if ( valid ) {
+ signersAS = get_u32(hash_p + 6);
+ if ( BGPSEC_SIGNATURE_MATCH !=
+ bgpsec_verify_signature_with_bin_ski
+ (bgp->cf,
+ hash_p, (endHash_p - hash_p),
+ lastSKI_p, BGPSEC_SKI_LENGTH,
+ signersAS, algoID,
+ lastSig_p, lastSig_len)
+ ) {
+ if ( bgp->cf->bgpsec_no_invalid_routes ) {
+ log(L_WARN "decode_bgpsec: %d < %d : bad last signature AS: %d, invalid routes not allowed, ignoring",
+ bgp->local_as, bgp->remote_as, signersAS);
+ return IGNORE;
+ }
+ else {
+ log(L_WARN "decode_bgpsec: %d < %d : bad last signature AS: %d, not BGPsec valid",
+ bgp->local_as, bgp->remote_as, signersAS);
+ valid = 0;
+ }
+ }
+ else {
+ log(L_DEBUG "decode_bgpsec: %d < %d : good last sig. AS: %d, marked BGPsec valid",
+ bgp->local_as, bgp->remote_as, signersAS);
+ /* mark route as valid */
+ if ( 0 < bgpsec_add_valid_attr(route_attr, pool) ) {
+ /* xxx currently should never happen, get rid of check? */
+ log(L_WARN "decode_bgpsec: %d < %d : unable to add valid attribute, failing",
+ bgp->local_as, bgp->remote_as);
+ return IGNORE;
+ }
+ }
+ }
+
+ /* Create a local as_path to use for route selection */
+ if ( 0 < bgpsec_create_aspath(route_attr, secPath_p, secPath_len, pool) ) {
+ /* xxx currently should never happen, get rid of check? */
+ log(L_WARN "decode_bgpsec: %d < %d : unable to create local as4path, ignoring",
+ bgp->local_as, bgp->remote_as);
+ return IGNORE;
+ }
+
+ return 0;
+
+ /* goto 'done:' Used by the DO_NLRI and DECODE_PREFIX macros used
+ above and defined in bgp.h
+ */
+ done:
+ log(L_WARN "bgpsec_decode: %d < %d : failed decoding NLRI: %d, ignoring",
+ bgp->local_as, bgp->remote_as, err);
+ return IGNORE;
+
+} /* static int decode_bgpsec_attr */
+
+#endif
+/* end BGPsec Decode Functions */
+
+
static int
bgp_check_reach_nlri(struct bgp_proto *p UNUSED, byte *a UNUSED, int len UNUSED)
{
-#ifdef IPV6
p->mp_reach_start = a;
p->mp_reach_len = len;
-#endif
+
return IGNORE;
}
static int
bgp_check_unreach_nlri(struct bgp_proto *p UNUSED, byte *a UNUSED, int len UNUSED)
{
-#ifdef IPV6
+#if defined(IPV6) || defined(CONFIG_BGPSEC)
p->mp_unreach_start = a;
p->mp_unreach_len = len;
#endif
static struct attr_desc bgp_attr_table[] = {
- { NULL, -1, 0, 0, 0, /* Undefined */
+ { NULL, -1, 0, 0, 0, /* 0 Undefined */
NULL, NULL },
- { "origin", 1, BAF_TRANSITIVE, EAF_TYPE_INT, 1, /* BA_ORIGIN */
+ { "origin", 1, BAF_TRANSITIVE, EAF_TYPE_INT, 1, /* 1 BA_ORIGIN */
bgp_check_origin, bgp_format_origin },
- { "as_path", -1, BAF_TRANSITIVE, EAF_TYPE_AS_PATH, 1, /* BA_AS_PATH */
+ { "as_path", -1, BAF_TRANSITIVE, EAF_TYPE_AS_PATH, 1, /* 2 BA_AS_PATH */
NULL, NULL }, /* is checked by validate_as_path() as a special case */
- { "next_hop", 4, BAF_TRANSITIVE, EAF_TYPE_IP_ADDRESS, 1, /* BA_NEXT_HOP */
+ { "next_hop", 4, BAF_TRANSITIVE, EAF_TYPE_IP_ADDRESS, 1, /* 3 BA_NEXT_HOP */
bgp_check_next_hop, bgp_format_next_hop },
- { "med", 4, BAF_OPTIONAL, EAF_TYPE_INT, 1, /* BA_MULTI_EXIT_DISC */
+ { "med", 4, BAF_OPTIONAL, EAF_TYPE_INT, 1, /* 4 BA_MULTI_EXIT_DISC */
NULL, NULL },
- { "local_pref", 4, BAF_TRANSITIVE, EAF_TYPE_INT, 0, /* BA_LOCAL_PREF */
+ { "local_pref", 4, BAF_TRANSITIVE, EAF_TYPE_INT, 0, /* 5 BA_LOCAL_PREF */
NULL, NULL },
- { "atomic_aggr", 0, BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_ATOMIC_AGGR */
+ { "atomic_aggr", 0, BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* 6 BA_ATOMIC_AGGR */
NULL, NULL },
- { "aggregator", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_AGGREGATOR */
+ { "aggregator", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* 7 BA_AGGREGATOR */
bgp_check_aggregator, bgp_format_aggregator },
- { "community", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_INT_SET, 1, /* BA_COMMUNITY */
+ { "community", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_INT_SET, 1, /* 8 BA_COMMUNITY */
bgp_check_community, NULL },
- { "originator_id", 4, BAF_OPTIONAL, EAF_TYPE_ROUTER_ID, 0, /* BA_ORIGINATOR_ID */
+ { "originator_id", 4, BAF_OPTIONAL, EAF_TYPE_ROUTER_ID, 0, /* 9 BA_ORIGINATOR_ID */
NULL, NULL },
- { "cluster_list", -1, BAF_OPTIONAL, EAF_TYPE_INT_SET, 0, /* BA_CLUSTER_LIST */
+ { "cluster_list", -1, BAF_OPTIONAL, EAF_TYPE_INT_SET, 0, /* 10 BA_CLUSTER_LIST */
bgp_check_cluster_list, bgp_format_cluster_list },
- { .name = NULL }, /* BA_DPA */
- { .name = NULL }, /* BA_ADVERTISER */
- { .name = NULL }, /* BA_RCID_PATH */
- { "mp_reach_nlri", -1, BAF_OPTIONAL, EAF_TYPE_OPAQUE, 1, /* BA_MP_REACH_NLRI */
+ { .name = NULL }, /* 11 BA_DPA */
+ { .name = NULL }, /* 12 BA_ADVERTISER */
+ { .name = NULL }, /* 13 BA_RCID_PATH */
+ { "mp_reach_nlri", -1, BAF_OPTIONAL, EAF_TYPE_OPAQUE, 1, /* 14 BA_MP_REACH_NLRI */
bgp_check_reach_nlri, NULL },
- { "mp_unreach_nlri", -1, BAF_OPTIONAL, EAF_TYPE_OPAQUE, 1, /* BA_MP_UNREACH_NLRI */
+ { "mp_unreach_nlri", -1, BAF_OPTIONAL, EAF_TYPE_OPAQUE, 1, /* 15 BA_MP_UNREACH_NLRI */
bgp_check_unreach_nlri, NULL },
- { "ext_community", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_EC_SET, 1, /* BA_EXT_COMMUNITY */
+ { "ext_community", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_EC_SET, 1, /* 16 BA_EXT_COMMUNITY */
bgp_check_ext_community, NULL },
- { "as4_path", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_AS4_PATH */
+ { "as4_path", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* 17 BA_AS4_PATH */
+ NULL, NULL },
+ { "as4_aggregator", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* 18 BA_AS4_AGGREGATOR */
NULL, NULL },
- { "as4_aggregator", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1, /* BA_AS4_PATH */
- NULL, NULL }
+ /* not supported attributes */
+ { .name = NULL }, /* 19 BA_SSA */
+ { .name = NULL }, /* 20 BA_CONNECTOR_ATTR */
+ { .name = NULL }, /* 21 BA_AS_PATHLIMIT */
+ { .name = NULL }, /* 22 BA_PMSI_TUNNEL */
+ { .name = NULL }, /* 23 BA_TUNNEL_ENCAP */
+ { .name = NULL }, /* 24 BA_TUNNEL_ENGINEERING */
+ { .name = NULL }, /* 25 BA_IPV6_EXT_COMMUNITY */
+ { .name = NULL }, /* 26 BA_AIGP */
+ { .name = NULL }, /* 27 BA_PE_DIST_LABELS */
+ { .name = NULL }, /* 28 BA_ENTROPY_LABELS */
+ { .name = NULL }, /* 29 BA_LS_ATTRIBUTE */
+ /* supported */
+#ifdef CONFIG_BGPSEC
+ /* Treated as a special case and checked by decode_bgpsec_attr,
+ bgpsec_authenticate, and encode_bgpsec_attr */
+ { "bgpsec_signature", -1, BAF_OPTIONAL, EAF_TYPE_OPAQUE, 1, /* 30 BA_BGPSEC_SIGNATURE */
+ NULL, NULL },
+#endif
};
/* BA_AS4_PATH is type EAF_TYPE_OPAQUE and not type EAF_TYPE_AS_PATH.
return len;
}
+
#define ADVANCE(w, r, l) do { r -= l; w += l; } while (0)
+#ifdef CONFIG_BGPSEC
+
+/* BGPSEC Encode Function */
+/* For the originating AS, add a bgpsec signature attribute to the update */
+/* Otherwise, add an additional signature to the bgpsec signature attribute */
+/* Returns length of attribute added, 0 if no attribute added, and < 0
+ on error */
+unsigned int
+encode_bgpsec_attr(struct bgp_conn *conn,
+ ea_list *attr_list,
+ byte *w,
+ int remains,
+ byte *nlri)
+{
+ log(L_TRACE "encode_bgpsec_attr: %d > %d",
+ conn->bgp->local_as, conn->bgp->remote_as);
+
+ eattr *asPathAttr = ea_find(attr_list, EA_CODE(EAP_BGP, BA_AS_PATH));
+ eattr *bgpSecAttr = ea_find(attr_list, EA_CODE(EAP_BGP, BA_BGPSEC_SIGNATURE));
+
+ if ( NULL == asPathAttr ) {
+ log(L_ERR "encode_bgpsec_attr: Error: %d > %d : AS_Path dose not exists",
+ conn->bgp->local_as, conn->bgp->remote_as);
+ return -1;
+ }
+
+ u8 *pathPtr = (u8 *)&(asPathAttr->u.ptr->data);
+ int numOfAS = (asPathAttr->u.ptr->length - 2) / 4;
+
+ log(L_DEBUG "encode_bgpsec_attr: %d > %d : #AS: %d",
+ conn->bgp->local_as, conn->bgp->remote_as, numOfAS);
+
+ /* if this route does not have a BGPsec attribute and this is not
+ * the origination, do not add a BGPsec attribute to this update */
+ if ( (NULL == bgpSecAttr ) && ( numOfAS > 1 ) ) {
+ log(L_DEBUG "encode_bgpsec_attr: %d > %d : No BGPsec attribute for this non origination route (#AS %d), BGPsec attribute not added",
+ conn->bgp->local_as, conn->bgp->remote_as, numOfAS);
+ return 0;
+ }
+
+ /* must be as_sequence, as_set not allowed for bgpsec */
+ if ( pathPtr[0] != AS_PATH_SEQUENCE ) {
+ log(L_ERR "encode_bgpsec_attr: Error: %d > %d : AS_Path that is not AS_PATH_SEQUENCE not allowed",
+ conn->bgp->local_as, conn->bgp->remote_as);
+ return -1;
+ }
+
+ byte *start = w;
+
+ static u8 sigBuff[BGPSEC_MAX_SIG_LENGTH];
+ static u8 hashBuff[BGPSEC_SIG_HASH_LENGTH];
+ u8 *hash_p = hashBuff;
+
+ /* clean out any previous data in buffers */
+ bzero(sigBuff, BGPSEC_MAX_SIG_LENGTH);
+ bzero(hashBuff, BGPSEC_SIG_HASH_LENGTH);
+
+ int signature_len = 0;
+ char oMark = 'O';
+
+
+ /* load signature hash buffer */
+
+ /* add target AS */
+ put_u32(hash_p, conn->bgp->remote_as);
+ hash_p += 4;
+
+ /* secure path data */
+ byte *secPath_p = NULL;
+ u16 secPath_len = 2; /* default to local secure path header size */
+ byte *secPathSeg_p = NULL;
+
+ /* signature block data */
+ byte *sigBlock_p = NULL;
+ u16 sigBlock_len = 3; /* default to sig block header size */
+ byte *sigSegment_p = NULL;
+ int sigSegment_len = 0;
+
+ /* get hash data from bgp attribute, if we are not the originator,
+ place first signature segment in hash */
+ if ( NULL != bgpSecAttr ) {
+ oMark = 'N';
+ /* get secure path pointer */
+ secPath_p = (byte *)&(bgpSecAttr->u.ptr->data);
+ secPath_len = get_u16(secPath_p);
+ secPathSeg_p = secPath_p + 2; /* skip past secure path length value */
+
+ /* XXX, only handling a single signature block, should handle 1 or 2 */
+ /* get signature block pointer */
+ sigBlock_p = secPath_p + secPath_len;
+ sigBlock_len = get_u16(sigBlock_p);
+ sigSegment_p = sigBlock_p + 3; /* skip length value and algo ID byte */
+
+ /* put first signature segment in hash */
+ /* length is signature length, plus 2B length val + SKI length */
+ sigSegment_len = get_u16(sigSegment_p + BGPSEC_SKI_LENGTH) + BGPSEC_SKI_LENGTH + 2;
+ /* buffer size check */
+ if ( (hash_p + sigSegment_len) > (hashBuff + BGPSEC_SIG_HASH_LENGTH) ) {
+ log(L_ERR
+ "encode_bgpsec_attr: Error: signature segment larger than hash buffer size");
+ return -1;
+ }
+ memcpy(hash_p, sigSegment_p, sigSegment_len);
+ hash_p += sigSegment_len;
+ sigSegment_p += sigSegment_len;
+ }
+
+ /* Add our own secure path segment */
+ /* pcount = 1, XXX configurable */
+ *hash_p = 1 ;
+ hash_p += 1;
+ /* flags */
+ *hash_p = 0x00;
+ hash_p += 1;
+
+ /* our AS */
+ put_u32(hash_p, conn->bgp->local_as);
+ hash_p += 4;
+
+ /* If we are not origination, put following sequence of signature
+ and secure path segments in hash */
+ if ( NULL != bgpSecAttr ) {
+ while ( (sigSegment_p < (sigBlock_p + sigBlock_len)) &&
+ (secPathSeg_p < (secPath_p + secPath_len)) ) {
+
+ /* put next signature segment in hash */
+ sigSegment_len = get_u16(sigSegment_p + BGPSEC_SKI_LENGTH) + BGPSEC_SKI_LENGTH + 2;
+
+ /* buffer size check, include secure path (6 bytes) */
+ if ( (hash_p + sigSegment_len + 6) > (hashBuff + BGPSEC_SIG_HASH_LENGTH) ) {
+ log(L_ERR
+ "encode_bgpsec_attr: Error: signature/secure path segment larger than hash buffer size");
+ return -1;
+ }
+ memcpy(hash_p, sigSegment_p, sigSegment_len);
+ hash_p += sigSegment_len;
+ sigSegment_p += sigSegment_len;
+
+ /* put next secure path segment in hash */
+ memcpy(hash_p, secPathSeg_p, 6);
+ secPathSeg_p += 6;
+ hash_p += 6;
+ }
+
+ /* add last secure path segment */
+ /* buffer size check, include secure path (6 bytes) */
+ if ( ( (hash_p + 6) > (hashBuff + BGPSEC_SIG_HASH_LENGTH) ) &&
+ ( secPathSeg_p < (secPath_p + secPath_len) ) ) {
+ log(L_ERR
+ "encode_bgpsec_attr: Error: last secure path segment larger than hash buffer size or missing");
+ return -1;
+ }
+
+ /* put last secure path segment in hash */
+ memcpy(hash_p, secPathSeg_p, 6);
+ secPathSeg_p += 6;
+ hash_p += 6;
+ }
+
+ /* get NLRI information */
+ u8 px_len = *nlri++;
+ int pxBytes = (px_len+7) / 8;
+ ip_addr prefix;
+ bzero(&prefix, sizeof(ip_addr));
+ memcpy(&prefix, nlri, pxBytes);
+ ipa_ntoh(prefix);
+
+ log(L_DEBUG "encode_bgpsec_attr: %d > %d, using NLRI %I/%d\n",
+ conn->bgp->local_as, conn->bgp->remote_as, prefix, px_len);
+
+ /* buffer size check */
+ if ( (hash_p + 5 + pxBytes) > (hashBuff + BGPSEC_SIG_HASH_LENGTH) ) {
+ log(L_ERR
+ "encode_bgpsec_attr: Error: not enough hash buffer space for AlgoID/AFI/SAFI/NLRI");
+ return -1;
+ }
+
+ /* algorithm suite identifier */
+ *hash_p = BGPSEC_ALGO_ID;
+ hash_p++;
+
+ /* AFI */
+#ifdef IPV6
+ put_u16(hash_p, BGP_AF_IPV6);
+#else
+ put_u16(hash_p, BGP_AF_IPV4);
+#endif
+ hash_p += 2;
+ /* SAFI */
+ *hash_p = 1; /* SAFI unicast */
+ hash_p++;
+
+ /* NLRI */
+ *hash_p = px_len;
+ hash_p++;
+ memcpy(hash_p, &prefix, pxBytes);
+ hash_p += pxBytes;
+
+ /* sign */
+ signature_len = bgpsec_sign_data_with_ascii_ski(conn->bgp->cf,
+ hashBuff, (hash_p - hashBuff),
+ conn->bgp->cf->bgpsec_ski,
+ strlen(conn->bgp->cf->bgpsec_ski),
+ conn->bgp->local_as, BGPSEC_ALGO_ID,
+ sigBuff, BGPSEC_MAX_SIG_LENGTH);
+
+ if ( 1 >= signature_len ) {
+ log(L_ERR "encode_bgpsec_attr:%c: %d > %d, Signing Failed",
+ oMark, conn->bgp->local_as, conn->bgp->remote_as);
+ return -1;
+ }
+ else {
+ log(L_DEBUG "encode_bgpsec_attr:%c: Signed %d > %d, signature length = %d",
+ oMark, conn->bgp->local_as, conn->bgp->remote_as, signature_len);
+ }
+
+ /* BGPsec Attribute length */
+ /* attribute value length + (old secure path length + new secure
+ path segment length) + (old signature block length + new
+ signature segment length) */
+ int bgpsecAttr_len = (secPath_len + 6) + (sigBlock_len + 2 + signature_len + BGPSEC_SKI_LENGTH);
+
+ /* just single sig block XXX */
+ /* is there enough room for adding a new signature */
+ if ( remains < bgpsecAttr_len ) {
+ log(L_ERR "encode_bgpsec_attr: %d > %d, not enough room for bgpsec attribute: %d",
+ conn->bgp->local_as, conn->bgp->remote_as, bgpsecAttr_len );
+ return -1;
+ }
+
+ /* Create outgoing BGPsec attribute */
+ /* attribute header */
+ /* 4 (attr header) + secure path length + signature block length */
+ int rv = bgp_encode_attr_hdr(w, BAF_OPTIONAL, BA_BGPSEC_SIGNATURE,
+ bgpsecAttr_len);
+ ADVANCE(w, remains, rv);
+
+ /* secure path header (len) */
+ put_u16(w, (secPath_len + 6));
+ ADVANCE(w, remains, 2);
+ /* Add our own secure path segment */
+ /* pcount = 1, XXX configurable */
+ *w = 0x01;
+ ADVANCE(w, remains, 1);
+ /* flags */
+ *w = 0x00;
+ ADVANCE(w, remains, 1);
+ /* our AS */
+ put_u32(w, conn->bgp->local_as);
+ ADVANCE(w, remains, 4);
+
+ /* old secure path, if it exists (not origination) */
+ if ( NULL != secPath_p ) {
+ memcpy(w, (secPath_p + 2), (secPath_len - 2));
+ ADVANCE(w, remains, (secPath_len - 2));
+ }
+
+ /* signature block header (length and algorithm ID) */
+ put_u16(w, (sigBlock_len + 2 + signature_len + BGPSEC_SKI_LENGTH));
+ ADVANCE(w, remains, 2);
+ *w = BGPSEC_ALGO_ID;
+ ADVANCE(w, remains, 1);
+
+ /* new signature segment */
+ memcpy(w, conn->bgp->cf->bgpsec_bski, BGPSEC_SKI_LENGTH);
+ ADVANCE(w, remains, BGPSEC_SKI_LENGTH);
+ put_u16(w, signature_len);
+ ADVANCE(w, remains, 2);
+ memcpy(w, sigBuff, signature_len);
+ ADVANCE(w, remains, signature_len);
+
+ /* old signature segments, if they exists (not origination) */
+ if ( NULL != sigBlock_p ) {
+ memcpy(w, (sigBlock_p + 3), (sigBlock_len - 3));
+ ADVANCE(w, remains, (sigBlock_len - 3));
+ }
+
+ return (w - start);
+
+} /* int encode_bgpsec_attr */
+
+#endif
+/* End BGPsec Sign Function */
+
+
/**
* bgp_encode_attrs - encode BGP attributes
* @p: BGP instance
ASSERT(EA_PROTO(a->id) == EAP_BGP);
code = EA_ID(a->id);
-#ifdef IPV6
+#if defined(IPV6) || defined(CONFIG_BGSPEC)
/* When talking multiprotocol BGP, the NEXT_HOP attributes are used only temporarily. */
if (code == BA_NEXT_HOP)
continue;
#endif
+#ifdef CONFIG_BGPSEC
+ /* Do not send internally used extended attribute.
+ * Do not handle the BPGsec attribute here. */
+ if ( code == BA_INTERNAL_BGPSEC_VALID ||
+ code == BA_BGPSEC_SIGNATURE ) {
+ continue;
+ }
+
+ /* Do not send AS_PATH with the BGPsec attribute. */
+ /* If this is an AS_PATH and the connection is configured for
+ * BPGsec, do not add the AS_PATH attribute if a BGPsec
+ * attribute exists or this is the originatian for the prefix,
+ * ie. AS_Path <= 1 */
+ if ( ( code == BA_AS_PATH ) && ( p->cf->enable_bgpsec ) &&
+ ( ( ea_find(attrs, EA_CODE(EAP_BGP, BA_BGPSEC_SIGNATURE)) ) ||
+ ( 1 >= (a->u.ptr->length - 2) / 4 ) )
+ ) {
+ continue;
+ }
+#endif
+
/* When AS4-aware BGP speaker is talking to non-AS4-aware BGP speaker,
* we have to convert our 4B AS_PATH to 2B AS_PATH and send our AS_PATH
* as optional AS4_PATH attribute.
}
ADVANCE(w, remains, len);
}
+
return w - start;
err_no_buffer:
/* Hash */
hash = ea_hash(new);
for(b=p->bucket_hash[hash & (p->hash_size - 1)]; b; b=b->hash_next)
- if (b->hash == hash && ea_same(b->eattrs, new))
+ if ( (b->hash == hash && ea_same(b->eattrs, new))
+#ifdef CONFIG_BGPSEC
+ /* multiple prefixes not allowed in BGPSEC NLRI*/
+ && (!p->conn->peer_bgpsec_support)
+#endif
+ )
{
DBG("Found bucket.\n");
return b;
if (n < o)
return 0;
+#ifdef CONFIG_BGPSEC
+ if ( new_bgp->cf->bgpsec_prefer || old_bgp->cf->bgpsec_prefer ) {
+ /* Somewhat arbitrary (after local pref before as_path, ordering
+ * placement for bgpsec validity check */
+ x = ea_find(new->attrs->eattrs, EA_CODE(EAP_BGP, BA_INTERNAL_BGPSEC_VALID));
+ y = ea_find(old->attrs->eattrs, EA_CODE(EAP_BGP, BA_INTERNAL_BGPSEC_VALID));
+ n = x ? 1 : 0;
+ o = y ? 1 : 0;
+ if (n > o) return 1;
+ if (n < o) return 0;
+ }
+#endif
+
/* RFC 4271 9.1.2.2. a) Use AS path lengths */
if (new_bgp->cf->compare_path_lengths || old_bgp->cf->compare_path_lengths)
{
* by a &rta.
*/
struct rta *
-bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct linpool *pool, int mandatory)
+bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len,
+ struct linpool *pool, byte *nlri, int nlri_len)
{
struct bgp_proto *bgp = conn->bgp;
rta *a = lp_alloc(pool, sizeof(struct rta));
unsigned int flags, code, l, i, type;
int errcode;
- byte *z, *attr_start;
+ byte *z=0, *attr_start=0;
byte seen[256/8];
ea_list *ea;
struct adata *ad;
int withdraw = 0;
+ int mandatory = nlri_len;
+#ifdef CONFIG_BGPSEC
+ unsigned int bgpsec_len = 0;
+ byte *bgpsec_start = 0;
+#endif
+/* mp_reach attr is required for ipv6 or bgpsec, see mandatory check below */
+#if defined(IPV6) || defined(CONFIG_BGPSEC)
+ mandatory = 0;
+#endif
bzero(a, sizeof(rta));
a->source = RTS_BGP;
{
struct attr_desc *desc = &bgp_attr_table[code];
if (desc->expected_length >= 0 && desc->expected_length != (int) l)
- { errcode = 5; goto err; }
+ { errcode = BGP_UPD_ERROR_ATTR_LENGTH; goto err; }
if ((desc->expected_flags ^ flags) & (BAF_OPTIONAL | BAF_TRANSITIVE))
- { errcode = 4; goto err; }
+ { errcode = BGP_UPD_ERROR_ATTR_FLAG; goto err; }
if (!desc->allow_in_ebgp && !bgp->is_internal)
continue;
if (desc->validate)
{
/* Special case as it might also trim the attribute */
if (validate_as_path(bgp, z, &l) < 0)
- { errcode = 11; goto err; }
+ { errcode = BGP_UPD_ERROR_MALFORMED_ASPATH; goto err; }
}
+#ifdef CONFIG_BGPSEC
+ else if (code == BA_BGPSEC_SIGNATURE)
+ {
+ log(L_DEBUG "UPDATE: message has BA_BGPSEC_SIGNATURE");
+ /* Special case, attribute must be parsed and
+ cryptographically checked. */
+ /* AS_PATH should not be in the same update with a
+ BGPSEC_SIGNATURE attribute, check that a AS_PATH
+ attribute has not already been seen and mark it as
+ seen. */
+ if (seen[BA_AS_PATH/8] & (1 << (BA_AS_PATH%8)))
+ goto malformed;
+ /* Note: It is mandatory for an update to have either a
+ AS_PATH or a BGPSEC_SIGNATURE attribute. AS_PATH is
+ set to 'seen' here to cover both the mandatory and
+ exclusivity requirements. */
+ seen[BA_AS_PATH/8] |= (1 << (BA_AS_PATH%8));
+ /* Only handle BGPsec if connection is configured for
+ * BGPsec and the peer supports BGPsec, otherwise this
+ * fails because there is no AS_PATH */
+ if (!bgp->cf->enable_bgpsec || !bgp->conn->peer_bgpsec_support) {
+ log(L_WARN "UPDATE: malformed: recieved BGPsec attribute, but connection not configured for BGPsec or peer does not support");
+ goto malformed;
+ }
+ /* bgpsec requires mp_reach attribute, so bgpsec
+ * decoding must occur after the attribute parsing
+ * loop, save attr info here */
+ bgpsec_start = z;
+ bgpsec_len = l;
+ }
+#endif
type = desc->type;
}
else /* Unknown attribute */
{
if (!(flags & BAF_OPTIONAL))
- { errcode = 2; goto err; }
+ { errcode = BGP_UPD_ERROR_UNRCGNZD_WK_ATTR; goto err; }
type = EAF_TYPE_OPAQUE;
}
// Only OPTIONAL and TRANSITIVE attributes may have non-zero PARTIAL flag
// if (!((flags & BAF_OPTIONAL) && (flags & BAF_TRANSITIVE)) && (flags & BAF_PARTIAL))
- // { errcode = 4; goto err; }
+ // { errcode = BGP_UPD_ERROR_ATTR_FLAG; goto err; }
seen[code/8] |= (1 << (code%8));
ea = lp_alloc(pool, sizeof(ea_list) + sizeof(eattr));
if (withdraw)
goto withdraw;
-#ifdef IPV6
+#if defined(IPV6) || defined(CONFIG_BGPSEC)
/* If we received MP_REACH_NLRI we should check mandatory attributes */
if (bgp->mp_reach_len != 0)
mandatory = 1;
}
}
+#ifdef CONFIG_BGPSEC
+ if ( bgp->cf->bgpsec_require &&
+ (0 == bgpsec_len || 0 == bgpsec_start) ) {
+ log(L_WARN "UPDATE: malformed: BGPsec attribute required but not in Update");
+ goto malformed;
+ }
+
+ if ( (0 != bgpsec_len) && (0 != bgpsec_start) ) {
+ if ( decode_bgpsec_attr(bgp, bgpsec_start, bgpsec_len, a, pool) < 0 ) {
+ errcode = BGP_UPD_ERROR_MALFORMED_ATTR;
+ goto err;
+ }
+ }
+#endif
+
/* When receiving attributes from non-AS4-aware BGP speaker,
* we have to reconstruct 4B AS_PATH and AGGREGATOR attributes
*/
return NULL;
malformed:
- bgp_error(conn, 3, 1, NULL, 0);
+ bgp_error(conn, 3, BGP_UPD_ERROR_MALFORMED_ATTR, NULL, 0);
return NULL;
err:
* (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.
*/
/**
#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 */
if (c->c.class == SYM_TEMPLATE)
return;
-
/* EBGP direct by default, IBGP multihop by default */
if (c->multihop < 0)
c->multihop = internal ? 64 : 0;
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");
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
* (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_
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;
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 */
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] */
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 */
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 */
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);
#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 */
#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_ADVERTISER 0x0c /* [RFC1863] */
-#define BA_RCID_PATH 0x0d
-#define BA_MP_REACH_NLRI 0x0e /* [RFC2283] */
-#define BA_MP_UNREACH_NLRI 0x0f
-#define BA_EXT_COMMUNITY 0x10 /* [RFC4360] */
-#define BA_AS4_PATH 0x11 /* [RFC4893] */
-#define BA_AS4_AGGREGATOR 0x12
+#define BA_DPA 0x0b /* DPA deprecated */
+#define BA_ADVERTISER 0x0c /* [RFC1863] */
+#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 /* [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 */
#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. */
#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
--- /dev/null
+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)
--- /dev/null
+#!/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
--- /dev/null
+/*
+ * 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
--- /dev/null
+#!/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())
--- /dev/null
+#
+# 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)
--- /dev/null
+#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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
* (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
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
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;
| 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); }
;
* (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
#include "nest/cli.h"
#include "bgp.h"
+#undef sk_new /* remove the bird-specific compatability wrapper */
#define BGP_RR_REQUEST 0
[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:
}
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);
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)
{
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);
}
}
-#ifndef IPV6 /* IPv4 version */
+
+
+#if !defined(IPV6) && !defined(CONFIG_BGPSEC) /* IPv4 version */
+
static byte *
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)
{
return buf+4;
}
-#else /* IPv6 version */
+
+#else /* IPv6 or BGPSEC version */
+
static inline int
same_iface(struct bgp_proto *p, ip_addr *ip)
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);
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);
ASSERT(size >= 0);
w += size;
remains -= size;
- }
+ }
if (remains >= 3072)
{
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);
}
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;
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;
}
}
void
bgp_parse_capabilities(struct bgp_conn *conn, byte *opt, int len)
{
- // struct bgp_proto *p = conn->bgp;
- int i, cl;
+ struct bgp_proto *p = conn->bgp; /* used in BGP_TRACE */
+ int i,cl;
+#ifdef CONFIG_BGPSEC
+ u16 afi;
+ u8 safi;
+#endif
while (len > 0)
{
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;
}
-#define DECODE_PREFIX(pp, ll) do { \
+/* 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) \
{ \
if (ll < 5) { err=1; goto done; } \
prefix = ipa_and(prefix, ipa_mkmask(b)); \
pxlen = b; \
} while (0)
+*/
static inline void
return 1;
}
-#ifndef IPV6 /* IPv4 version */
+
+#if !defined(IPV6) && !defined(CONFIG_BGPSEC) /* IPv4 version */
static void
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;
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; \
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)
{
}
else
nh[1] = IPA_NONE;
+#endif
}
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;
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);
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;
}
{ 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" },
#undef CONFIG_BGP
#undef CONFIG_OSPF
#undef CONFIG_PIPE
+#undef CONFIG_BGPSEC
/* We use multithreading */
#undef USE_PTHREADS