]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
The Michael Baer's patch for BGPsec Support
authorMichael Baer <baerm@tislabs.com>
Thu, 26 May 2016 12:09:08 +0000 (14:09 +0200)
committerPavel Tvrdik <pawel.tvrdik@gmail.com>
Thu, 26 May 2016 12:09:08 +0000 (14:09 +0200)
Imported from:
  https://securerouting.net/download/bird-1.5.0-bgpsec-0.7.tar.bz2

19 files changed:
README.bgpsec [new file with mode: 0644]
configure.in
lib/unaligned.h
nest/protocol.h
nest/route.h
proto/bgp/attrs.c
proto/bgp/bgp.c
proto/bgp/bgp.h
proto/bgp/bgpsec/Makefile [new file with mode: 0644]
proto/bgp/bgpsec/bgpsec-create-hash-dir [new file with mode: 0755]
proto/bgp/bgpsec/config.Y [new file with mode: 0644]
proto/bgp/bgpsec/keytool.py [new file with mode: 0755]
proto/bgp/bgpsec/router-key.cnf [new file with mode: 0644]
proto/bgp/bgpsec/tests.c [new file with mode: 0644]
proto/bgp/bgpsec/validate.c [new file with mode: 0644]
proto/bgp/bgpsec/validate.h [new file with mode: 0644]
proto/bgp/config.Y
proto/bgp/packets.c
sysdep/autoconf.h.in

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