diff -upwbNr quagga-0.99.21/configure quagga-rfc6506/configure --- quagga-0.99.21/configure 2012-05-02 02:18:03.000000000 +0530 +++ quagga-rfc6506/configure 2013-07-11 15:04:54.000000000 +0530 @@ -642,6 +642,10 @@ PILDFLAGS PICFLAGS CONFDATE LIBCAP +HAVE_LIBGCRYPT +LIBGCRYPT_LIBS +LIBGCRYPT_CFLAGS +LIBGCRYPT_CONFIG SNMP_INCLUDES LIB_REGEX HAVE_LIBPCREPOSIX @@ -857,6 +861,7 @@ enable_broken_aliases with_crypto enable_snmp with_libpam +with_libgcrypt enable_tcp_zebra enable_opaque_lsa enable_ospfapi @@ -878,6 +883,7 @@ enable_gcc_rdynamic enable_time_check enable_pcreposix enable_largefile +with_libgcrypt_prefix enable_pie ' ac_precious_vars='build_alias @@ -1572,6 +1578,9 @@ Optional Packages: (or the compiler's sysroot if not specified). --without-crypto do not use libcrypto in SNMP --with-libpam use libpam for PAM support in vtysh + --with-libgcrypt use gcrypt library for crypto functions + --with-libgcrypt-prefix=PFX + prefix where LIBGCRYPT is installed (optional) Some influential environment variables: GAWK GNU AWK @@ -3936,6 +3945,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ #include #include struct stat; +#include +#include /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); @@ -12370,6 +12381,10 @@ if test "${with_libpam+set}" = set; then withval=$with_libpam; fi +# Check whether --with-libgcrypt was given. +if test "${with_libgcrypt+set}" = set; then : + withval=$with_libgcrypt; +fi # Check whether --enable-tcp-zebra was given. if test "${enable_tcp_zebra+set}" = set; then : enableval=$enable_tcp_zebra; @@ -18052,6 +18067,220 @@ fi fi +if test "$with_libgcrypt" = "yes"; then +# Check whether --with-libgcrypt-prefix was given. +if test "${with_libgcrypt_prefix+set}" = set; then : + withval=$with_libgcrypt_prefix; libgcrypt_config_prefix="$withval" +else + libgcrypt_config_prefix="" +fi + + if test x$libgcrypt_config_prefix != x ; then + if test x${LIBGCRYPT_CONFIG+set} != xset ; then + LIBGCRYPT_CONFIG=$libgcrypt_config_prefix/bin/libgcrypt-config + fi + fi + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}libgcrypt-config", so it can be a program name with args. +set dummy ${ac_tool_prefix}libgcrypt-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_LIBGCRYPT_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $LIBGCRYPT_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_LIBGCRYPT_CONFIG="$LIBGCRYPT_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_path_LIBGCRYPT_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +LIBGCRYPT_CONFIG=$ac_cv_path_LIBGCRYPT_CONFIG +if test -n "$LIBGCRYPT_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBGCRYPT_CONFIG" >&5 +$as_echo "$LIBGCRYPT_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +fi +if test -z "$ac_cv_path_LIBGCRYPT_CONFIG"; then + ac_pt_LIBGCRYPT_CONFIG=$LIBGCRYPT_CONFIG + # Extract the first word of "libgcrypt-config", so it can be a program name with args. +set dummy libgcrypt-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_ac_pt_LIBGCRYPT_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $ac_pt_LIBGCRYPT_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_ac_pt_LIBGCRYPT_CONFIG="$ac_pt_LIBGCRYPT_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_path_ac_pt_LIBGCRYPT_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + ;; +esac +fi +ac_pt_LIBGCRYPT_CONFIG=$ac_cv_path_ac_pt_LIBGCRYPT_CONFIG +if test -n "$ac_pt_LIBGCRYPT_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_LIBGCRYPT_CONFIG" >&5 +$as_echo "$ac_pt_LIBGCRYPT_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_pt_LIBGCRYPT_CONFIG" = x; then + LIBGCRYPT_CONFIG="no" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + LIBGCRYPT_CONFIG=$ac_pt_LIBGCRYPT_CONFIG + fi +else + LIBGCRYPT_CONFIG="$ac_cv_path_LIBGCRYPT_CONFIG" +fi + tmp=1.2.0 + if echo "$tmp" | grep ':' >/dev/null 2>/dev/null ; then + req_libgcrypt_api=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\1/'` + min_libgcrypt_version=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\2/'` + else + req_libgcrypt_api=0 + min_libgcrypt_version="$tmp" + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBGCRYPT - version >= $min_libgcrypt_version" >&5 +$as_echo_n "checking for LIBGCRYPT - version >= $min_libgcrypt_version... " >&6; } + ok=no + if test "$LIBGCRYPT_CONFIG" != "no" ; then + req_major=`echo $min_libgcrypt_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\1/'` + req_minor=`echo $min_libgcrypt_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\2/'` + req_micro=`echo $min_libgcrypt_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\3/'` + libgcrypt_config_version=`$LIBGCRYPT_CONFIG --version` + major=`echo $libgcrypt_config_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\).*/\1/'` + minor=`echo $libgcrypt_config_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\).*/\2/'` + micro=`echo $libgcrypt_config_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\).*/\3/'` + if test "$major" -gt "$req_major"; then + ok=yes + else + if test "$major" -eq "$req_major"; then + if test "$minor" -gt "$req_minor"; then + ok=yes + else + if test "$minor" -eq "$req_minor"; then + if test "$micro" -ge "$req_micro"; then + ok=yes + fi + fi + fi + fi + fi + fi + if test $ok = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes ($libgcrypt_config_version)" >&5 +$as_echo "yes ($libgcrypt_config_version)" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + if test $ok = yes; then + # If we have a recent libgcrypt, we should also check that the + # API is compatible + if test "$req_libgcrypt_api" -gt 0 ; then + tmp=`$LIBGCRYPT_CONFIG --api-version 2>/dev/null || echo 0` + if test "$tmp" -gt 0 ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking LIBGCRYPT API version" >&5 +$as_echo_n "checking LIBGCRYPT API version... " >&6; } + if test "$req_libgcrypt_api" -eq "$tmp" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: okay" >&5 +$as_echo "okay" >&6; } + else + ok=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: does not match. want=$req_libgcrypt_api got=$tmp" >&5 +$as_echo "does not match. want=$req_libgcrypt_api got=$tmp" >&6; } + fi + fi + fi + fi + if test $ok = yes; then + LIBGCRYPT_CFLAGS=`$LIBGCRYPT_CONFIG --cflags` + LIBGCRYPT_LIBS=`$LIBGCRYPT_CONFIG --libs` + : + if test x"$host" != x ; then + libgcrypt_config_host=`$LIBGCRYPT_CONFIG --host 2>/dev/null || echo none` + if test x"$libgcrypt_config_host" != xnone ; then + if test x"$libgcrypt_config_host" != x"$host" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: +*** +*** The config script $LIBGCRYPT_CONFIG was +*** built for $libgcrypt_config_host and thus may not match the +*** used host $host. +*** You may want to use the configure option --with-libgcrypt-prefix +*** to specify a matching config script. +***" >&5 +$as_echo "$as_me: WARNING: +*** +*** The config script $LIBGCRYPT_CONFIG was +*** built for $libgcrypt_config_host and thus may not match the +*** used host $host. +*** You may want to use the configure option --with-libgcrypt-prefix +*** to specify a matching config script. +***" >&2;} + fi + fi + fi + else + LIBGCRYPT_CFLAGS="" + LIBGCRYPT_LIBS="" + as_fn_error $? "'NOT FOUND: libgcrypt'" "$LINENO" 5 + fi + LIBS="${LIBS} ${LIBGCRYPT_LIBS}" + CFLAGS="${CFLAGS} ${LIBGCRYPT_CFLAGS}" + +$as_echo "#define HAVE_LIBGCRYPT /**/" >>confdefs.h + + +fi ac_fn_c_check_type "$LINENO" "struct sockaddr" "ac_cv_type_struct_sockaddr" "#ifdef SUNOS_5 #define _XPG4_2 #define __EXTENSIONS__ @@ -23434,7 +23663,8 @@ compiler : ${CC} compiler flags : ${CFLAGS} make : ${MAKE-make} includes : ${INCLUDES} ${SNMP_INCLUDES} -linker flags : ${LDFLAGS} ${LIBS} ${LIBCAP} ${LIBREADLINE} ${LIBM} +linker flags : ${LDFLAGS} ${LIBS} +linker conditional libs : ${LIBCAP} ${LIBREADLINE} ${LIBM} state file directory : ${quagga_statedir} config file directory : `eval echo \`echo ${sysconfdir}\`` example directory : `eval echo \`echo ${exampledir}\`` diff -upwbNr quagga-0.99.21/configure.ac quagga-rfc6506/configure.ac --- quagga-0.99.21/configure.ac 2012-05-02 01:12:00.000000000 +0530 +++ quagga-rfc6506/configure.ac 2013-07-11 14:41:23.000000000 +0530 @@ -227,6 +227,8 @@ AC_ARG_ENABLE(snmp, [ --enable-snmp enable SNMP support]) AC_ARG_WITH(libpam, [ --with-libpam use libpam for PAM support in vtysh]) +AC_ARG_WITH(libgcrypt, +[ --with-libgcrypt use gcrypt library for crypto functions]) AC_ARG_ENABLE(tcp-zebra, [ --enable-tcp-zebra enable TCP/IP socket connection between zebra and protocol daemon]) AC_ARG_ENABLE(opaque-lsa, @@ -1382,6 +1384,20 @@ if test "${enable_snmp}" = "yes"; then AC_SUBST(SNMP_INCLUDES) fi +dnl ----------------------------------------------------------------------- +dnl A "HAVE_LIBGCRYPT" macro will be defined, if --with-libgcrypt configure +dnl option is given and the library is available (along with Quagga functions +dnl directly dependent on libgcrypt). Each protocol process using libgcrypt +dnl must call hash_library_init() once before calling any other libgcrypt +dnl functions. +dnl ----------------------------------------------------------------------- +if test "$with_libgcrypt" = "yes"; then + AM_PATH_LIBGCRYPT([1.2.0], [], AC_MSG_ERROR('NOT FOUND: libgcrypt')) + LIBS="${LIBS} ${LIBGCRYPT_LIBS}" + CFLAGS="${CFLAGS} ${LIBGCRYPT_CFLAGS}" + AC_DEFINE(HAVE_LIBGCRYPT,,[libgcrypt]) + AC_SUBST(HAVE_LIBGCRYPT) +fi dnl --------------------------- dnl sockaddr and netinet checks dnl --------------------------- @@ -1626,7 +1642,8 @@ compiler : ${CC} compiler flags : ${CFLAGS} make : ${MAKE-make} includes : ${INCLUDES} ${SNMP_INCLUDES} -linker flags : ${LDFLAGS} ${LIBS} ${LIBCAP} ${LIBREADLINE} ${LIBM} +linker flags : ${LDFLAGS} ${LIBS} +linker conditional libs : ${LIBCAP} ${LIBREADLINE} ${LIBM} state file directory : ${quagga_statedir} config file directory : `eval echo \`echo ${sysconfdir}\`` example directory : `eval echo \`echo ${exampledir}\`` diff -upwbNr quagga-0.99.21/lib/cryptohash.c quagga-rfc6506/lib/cryptohash.c --- quagga-0.99.21/lib/cryptohash.c 1970-01-01 05:30:00.000000000 +0530 +++ quagga-rfc6506/lib/cryptohash.c 2013-07-10 10:11:00.000000000 +0530 @@ -0,0 +1,272 @@ +/* + * This file, a part of Quagga, implements an interface to crypto hashes. + * + * + * Quagga 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, or (at your option) any + * later version. + * + * Quagga 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 Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include +#include "cryptohash.h" +#include "md5.h" +#include "command.h" + +#ifdef HAVE_LIBGCRYPT +#define GCRYPT_NO_DEPRECATED +#include +#endif /* HAVE_LIBGCRYPT */ + +const struct message hash_algo_str[] = +{ + { HASH_KEYED_MD5, "Keyed-MD5" }, + { HASH_HMAC_SHA1, "HMAC-SHA-1" }, + { HASH_HMAC_SHA256, "HMAC-SHA-256" }, + { HASH_HMAC_SHA384, "HMAC-SHA-384" }, + { HASH_HMAC_SHA512, "HMAC-SHA-512" }, + { HASH_HMAC_RMD160, "HMAC-RIPEMD-160" }, + { HASH_HMAC_WHIRLPOOL, "HMAC-Whirlpool" }, +}; +const size_t hash_algo_str_max = sizeof (hash_algo_str) / sizeof (struct message); + +/* hash_algo_byname() assumes this array to be exactly HASH_ALGO_MAX items big */ +const struct message hash_algo_cli_str[] = +{ + { HASH_KEYED_MD5, "md5" }, + { HASH_HMAC_SHA1, "sha1" }, + { HASH_HMAC_SHA256, "sha256" }, + { HASH_HMAC_SHA384, "sha384" }, + { HASH_HMAC_SHA512, "sha512" }, + { HASH_HMAC_RMD160, "rmd160" }, + { HASH_HMAC_WHIRLPOOL, "whirlpool" }, +}; +const size_t hash_algo_cli_str_max = sizeof (hash_algo_cli_str) / sizeof (struct message); + +/* hash digest size map */ +const u_int8_t hash_digest_length[] = +{ + [HASH_KEYED_MD5] = HASH_SIZE_MD5, + [HASH_HMAC_SHA1] = HASH_SIZE_SHA1, + [HASH_HMAC_SHA256] = HASH_SIZE_SHA256, + [HASH_HMAC_SHA384] = HASH_SIZE_SHA384, + [HASH_HMAC_SHA512] = HASH_SIZE_SHA512, + [HASH_HMAC_RMD160] = HASH_SIZE_RMD160, + [HASH_HMAC_WHIRLPOOL] = HASH_SIZE_WHIRLPOOL, +}; + +/* RFC4822 2.5: Apad is the hexadecimal value 0x878FE1F3 repeated (L/4) times. */ +const u_int8_t hash_apad_sha512[HASH_SIZE_SHA512] = +{ + 0x87, 0x8f, 0xe1, 0xf3, 0x87, 0x8f, 0xe1, 0xf3, + 0x87, 0x8f, 0xe1, 0xf3, 0x87, 0x8f, 0xe1, 0xf3, + 0x87, 0x8f, 0xe1, 0xf3, 0x87, 0x8f, 0xe1, 0xf3, + 0x87, 0x8f, 0xe1, 0xf3, 0x87, 0x8f, 0xe1, 0xf3, + 0x87, 0x8f, 0xe1, 0xf3, 0x87, 0x8f, 0xe1, 0xf3, + 0x87, 0x8f, 0xe1, 0xf3, 0x87, 0x8f, 0xe1, 0xf3, + 0x87, 0x8f, 0xe1, 0xf3, 0x87, 0x8f, 0xe1, 0xf3, + 0x87, 0x8f, 0xe1, 0xf3, 0x87, 0x8f, 0xe1, 0xf3, +}; + +#ifdef HAVE_LIBGCRYPT +/* ripd to gcrypto hash algorithm code map */ +static const int hash_gcrypt_algo_map[] = +{ + [HASH_HMAC_SHA1] = GCRY_MD_SHA1, + [HASH_HMAC_SHA256] = GCRY_MD_SHA256, + [HASH_HMAC_SHA384] = GCRY_MD_SHA384, + [HASH_HMAC_SHA512] = GCRY_MD_SHA512, + [HASH_HMAC_RMD160] = GCRY_MD_RMD160, + [HASH_HMAC_WHIRLPOOL] = GCRY_MD_WHIRLPOOL, +}; +#endif /* HAVE_LIBGCRYPT */ + +/* available only in processes with a call to hash_library_init() */ +DEFUN (show_crypto, + show_crypto_cmd, + "show crypto", + SHOW_STR + "Crypto module information\n") +{ +#ifdef HAVE_LIBGCRYPT + unsigned i; + vty_out (vty, "Compiled with libgcrypt version %s%s", GCRYPT_VERSION, VTY_NEWLINE); + vty_out (vty, "Running with libgcrypt version %s%s", gcry_check_version (NULL), VTY_NEWLINE); + for (i = 0; i < sizeof (hash_gcrypt_algo_map) / sizeof (hash_gcrypt_algo_map[0]); i++) + if (hash_gcrypt_algo_map[i]) + vty_out (vty, "%-17s: %s%s", LOOKUP (hash_algo_str, i), + hash_algo_enabled (i) ? "enabled" : "disabled", VTY_NEWLINE); +#else + vty_out (vty, "Compiled without libgcrypt%s", VTY_NEWLINE); +#endif /* HAVE_LIBGCRYPT */ + return CMD_SUCCESS; +} + +extern unsigned +hash_library_init (void) +{ +#ifdef HAVE_LIBGCRYPT + if (! gcry_check_version (GCRYPT_VERSION)) + { + zlog_err ("libgcrypt initialization failed"); + return 1; + } + gcry_control (GCRYCTL_DISABLE_SECMEM, 0); + gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); + install_element (VIEW_NODE, &show_crypto_cmd); + install_element (ENABLE_NODE, &show_crypto_cmd); +#endif /* HAVE_LIBGCRYPT */ + return 0; +} + +/* Map a string name of a listed hash algorithm into Quagga internal code. */ +unsigned +hash_algo_byname (const char *algo) +{ + unsigned i; + + for (i = 0; i < HASH_ALGO_MAX; i++) + if (! strcmp (algo, hash_algo_cli_str[i].str)) + return hash_algo_cli_str[i].key; + return 0; +} + +/* Test whether a hash algorithm with the given Quagga code is available in the + * current runtime. Using this function requires neither libgcrypt presence nor + * knowing libgcrypt internal code for the hash algorithm. */ +unsigned char +hash_algo_enabled (const unsigned hash_algo) +{ + switch (hash_algo) + { + case HASH_KEYED_MD5: + return 1; +#ifdef HAVE_LIBGCRYPT + case HASH_HMAC_SHA1: + case HASH_HMAC_SHA256: + case HASH_HMAC_SHA384: + case HASH_HMAC_SHA512: + case HASH_HMAC_RMD160: + case HASH_HMAC_WHIRLPOOL: + return 0 == gcry_md_test_algo (hash_gcrypt_algo_map[hash_algo]); +#endif /* HAVE_LIBGCRYPT */ + default: + return 0; + } +} + +/* Process input data with Keyed-MD5 algorithm and store digest as output. */ +unsigned +hash_make_keyed_md5 +( + const void *input, + const size_t inputlen, + const void *auth_str, + const size_t auth_str_len, + void *output +) +{ + char auth_str_padded[HASH_SIZE_MD5] = { 0 }; + MD5_CTX ctx; + + memcpy (auth_str_padded, auth_str, MIN (HASH_SIZE_MD5, auth_str_len)); + memset (&ctx, 0, sizeof (ctx)); + MD5Init (&ctx); + MD5Update (&ctx, input, inputlen); + MD5Update (&ctx, auth_str_padded, HASH_SIZE_MD5); + MD5Final (output, &ctx); + return 0; +} + +#ifdef HAVE_LIBGCRYPT +/* Process input data with a HMAC algorithm using the given hash function and + * store digest as output. It is safe for output digest buffer to be within + * input buffer. */ +unsigned +hash_make_hmac +( + const unsigned hash_algo, + const void *input, + const size_t inputlen, + const void *auth_str, + const size_t authlen, + void *output +) +{ + gcry_md_hd_t ctx; + + if (gcry_md_open (&ctx, hash_gcrypt_algo_map[hash_algo], GCRY_MD_FLAG_HMAC)) + return 1; + /* gcrypt handles preparing the key, Ipad and Opad */ + if (gcry_md_setkey (ctx, auth_str, authlen)) + { + gcry_md_close (ctx); + return 2; + } + gcry_md_write (ctx, input, inputlen); + gcry_md_final (ctx); + memcpy (output, gcry_md_read (ctx, 0), hash_digest_length[hash_algo]); + gcry_md_close (ctx); + return 0; +} + +/* The construct defined in RFC4822 and reused in RFC5709, RFC6506 and probably + * other works is similar to HMAC (RFC2104) but is not HMAC, to be precise. The + * principal difference is in the key preparation step. The original RFC2104 + * construct defines Ko to be B octets long and derives Ko from K respectively, + * whereas RFC4822 construct defines Ko to be L octets long (L <= B). Since + * L < B for most modern hash functions, these two constructs produce different + * digests for the same Text and K, when length of K is greater than L but not + * greater than B. + * + * In practice this means, that an implementation of RFC4822 construct (e. g. + * ripd) can reuse an existing implementation of HMAC (e. g. libgcrypt) as long + * as the authentication key is pre-processed with the function below. At the + * same time, this processing must not be performed by an implementation of the + * original HMAC construct (e. g. babeld). + */ +void +hash_key_compress_rfc4822 +( + const unsigned hash_algo, + const void *orig_key_bytes, + const size_t orig_key_len, + void *compr_key_bytes, /* size must be >= hash_algo digest length */ + size_t *compr_key_len +) +{ + switch (hash_algo) + { + case HASH_HMAC_SHA1: + case HASH_HMAC_SHA256: + case HASH_HMAC_SHA384: + case HASH_HMAC_SHA512: + case HASH_HMAC_RMD160: + case HASH_HMAC_WHIRLPOOL: + if (orig_key_len > hash_digest_length[hash_algo] ) /* > L, Ko := H(K) */ + { + gcry_md_hash_buffer (hash_gcrypt_algo_map[hash_algo], compr_key_bytes, orig_key_bytes, orig_key_len); + *compr_key_len = hash_digest_length[hash_algo]; + } + else /* <= L */ + { + memset (compr_key_bytes, 0, hash_digest_length[hash_algo]); + memcpy (compr_key_bytes, orig_key_bytes, orig_key_len); + *compr_key_len = orig_key_len; + } + break; + default: + assert (0); + } +} +#endif /* HAVE_LIBGCRYPT */ diff -upwbNr quagga-0.99.21/lib/cryptohash.h quagga-rfc6506/lib/cryptohash.h --- quagga-0.99.21/lib/cryptohash.h 1970-01-01 05:30:00.000000000 +0530 +++ quagga-rfc6506/lib/cryptohash.h 2013-07-10 10:14:00.000000000 +0530 @@ -0,0 +1,60 @@ +/* + * This file, a part of Quagga, implements an interface to crypto hashes. + * + * + * Quagga 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, or (at your option) any + * later version. + * + * Quagga 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 Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef QUAGGA_CRYPTOHASH_H +#define QUAGGA_CRYPTOHASH_H + +#include "log.h" + +#define HASH_KEYED_MD5 1 +#define HASH_HMAC_SHA1 2 +#define HASH_HMAC_SHA256 3 +#define HASH_HMAC_SHA384 4 +#define HASH_HMAC_SHA512 5 +#define HASH_HMAC_RMD160 6 +#define HASH_HMAC_WHIRLPOOL 7 +#define HASH_ALGO_MAX 7 + +#define HASH_SIZE_MD5 16U +#define HASH_SIZE_SHA1 20U +#define HASH_SIZE_SHA256 32U +#define HASH_SIZE_SHA384 48U +#define HASH_SIZE_SHA512 64U +#define HASH_SIZE_RMD160 20U +#define HASH_SIZE_WHIRLPOOL 64U +#define HASH_SIZE_MAX 64U + +extern const struct message hash_algo_str[]; +extern const size_t hash_algo_str_max; +extern const struct message hash_algo_cli_str[]; +extern const size_t hash_algo_cli_str_max; +extern const u_int8_t hash_digest_length[]; +extern const u_int8_t hash_apad_sha512[]; + +extern unsigned hash_library_init (void); +extern unsigned hash_algo_byname (const char *); +extern unsigned char hash_algo_enabled (const unsigned); +extern unsigned hash_make_keyed_md5 (const void *, const size_t, const void *, const size_t, void *); +#ifdef HAVE_LIBGCRYPT +extern unsigned hash_make_hmac (const unsigned, const void *, const size_t, const void *, const size_t, void *); +extern void hash_key_compress_rfc4822 (const unsigned, const void *, const size_t, void *, size_t *); +#endif /* HAVE_LIBGCRYPT */ + +#endif /* QUAGGA_CRYPTOHASH_H */ diff -upwbNr quagga-0.99.21/lib/Makefile.am quagga-rfc6506/lib/Makefile.am --- quagga-0.99.21/lib/Makefile.am 2012-05-02 01:10:13.000000000 +0530 +++ quagga-rfc6506/lib/Makefile.am 2013-07-10 10:14:00.000000000 +0530 @@ -12,7 +12,7 @@ libzebra_la_SOURCES = \ sockunion.c prefix.c thread.c if.c memory.c buffer.c table.c hash.c \ filter.c routemap.c distribute.c stream.c str.c log.c plist.c \ zclient.c sockopt.c smux.c md5.c if_rmap.c keychain.c privs.c \ - sigevent.c pqueue.c jhash.c memtypes.c workqueue.c + sigevent.c pqueue.c jhash.c memtypes.c workqueue.c cryptohash.c BUILT_SOURCES = memtypes.h route_types.h @@ -27,7 +27,7 @@ pkginclude_HEADERS = \ str.h stream.h table.h thread.h vector.h version.h vty.h zebra.h \ plist.h zclient.h sockopt.h smux.h md5.h if_rmap.h keychain.h \ privs.h sigevent.h pqueue.h jhash.h zassert.h memtypes.h \ - workqueue.h route_types.h + workqueue.h route_types.h cryptohash.h EXTRA_DIST = regex.c regex-gnu.h memtypes.awk route_types.pl route_types.txt diff -upwbNr quagga-0.99.21/lib/memtypes.c quagga-rfc6506/lib/memtypes.c --- quagga-0.99.21/lib/memtypes.c 2012-05-01 21:40:27.000000000 +0530 +++ quagga-rfc6506/lib/memtypes.c 2013-07-10 10:14:00.000000000 +0530 @@ -216,6 +216,7 @@ struct memory_list memory_list_ospf6[] = { { MTYPE_OSPF6_TOP, "OSPF6 top" }, { MTYPE_OSPF6_AREA, "OSPF6 area" }, + { MTYPE_OSPF6_CRYPT_KEY, "OSPF6 crypt key" }, { MTYPE_OSPF6_IF, "OSPF6 interface" }, { MTYPE_OSPF6_NEIGHBOR, "OSPF6 neighbor" }, { MTYPE_OSPF6_ROUTE, "OSPF6 route" }, diff -upwbNr quagga-0.99.21/lib/memtypes.h quagga-rfc6506/lib/memtypes.h --- quagga-0.99.21/lib/memtypes.h 2012-05-01 22:15:24.000000000 +0530 +++ quagga-rfc6506/lib/memtypes.h 2013-07-10 10:12:00.000000000 +0530 @@ -166,6 +166,7 @@ enum MTYPE_OSPF_MESSAGE, MTYPE_OSPF6_TOP, MTYPE_OSPF6_AREA, + MTYPE_OSPF6_CRYPT_KEY, MTYPE_OSPF6_IF, MTYPE_OSPF6_NEIGHBOR, MTYPE_OSPF6_ROUTE, diff -upwbNr quagga-0.99.21/ospf6d/ospf6_interface.c quagga-rfc6506/ospf6d/ospf6_interface.c --- quagga-0.99.21/ospf6d/ospf6_interface.c 2012-03-24 01:13:19.000000000 +0530 +++ quagga-rfc6506/ospf6d/ospf6_interface.c 2013-07-29 15:31:01.172733514 +0530 @@ -29,6 +29,18 @@ #include "prefix.h" #include "plist.h" +#include "sockunion.h" +#include "network.h" +#include "table.h" +#include "stream.h" +#include "zclient.h" +#include "filter.h" +#include "sockopt.h" +#include "privs.h" +#include "cryptohash.h" + +#include "zebra/connected.h" + #include "ospf6_lsa.h" #include "ospf6_lsdb.h" #include "ospf6_network.h" @@ -41,6 +53,7 @@ #include "ospf6_intra.h" #include "ospf6_spf.h" #include "ospf6d.h" +#include "memtypes.h" unsigned char conf_debug_ospf6_interface = 0; @@ -141,6 +154,13 @@ ospf6_interface_create (struct interface oi->route_connected = OSPF6_ROUTE_TABLE_CREATE (INTERFACE, CONNECTED_ROUTES); oi->route_connected->scope = oi; + oi->auth_crypt = list_new (); + + oi->low_order_seqnum = time (NULL); + oi->high_order_seqnum = 0; + oi->auth_type = 0; + oi->hash_algo = 0; + /* link both */ oi->interface = ifp; ifp->info = oi; @@ -840,6 +860,50 @@ ospf6_interface_show (struct vty *vty, s return 0; } +struct ospf6_crypt_key * +ospf6_crypt_key_new () +{ + return XCALLOC (MTYPE_OSPF6_CRYPT_KEY, sizeof (struct ospf6_crypt_key)); +} + +void +ospf6_crypt_key_add (struct list *crypt, struct ospf6_crypt_key *ck) +{ + listnode_add (crypt, ck); +} + +struct ospf6_crypt_key * +ospf6_crypt_key_lookup (struct list *auth_crypt, u_char key_id) +{ + struct listnode *node; + struct ospf6_crypt_key *ck; + + for (ALL_LIST_ELEMENTS_RO (auth_crypt, node, ck)) + if (ck->key_id == key_id) + return ck; + + return NULL; +} + +int +ospf6_crypt_key_delete (struct list *auth_crypt, u_char key_id) +{ + struct listnode *node, *nnode; + struct ospf6_crypt_key *ck; + + for (ALL_LIST_ELEMENTS (auth_crypt, node, nnode, ck)) + { + if (ck->key_id == key_id) + { + listnode_delete (auth_crypt, ck); + XFREE (MTYPE_OSPF6_CRYPT_KEY, ck); + return 1; + } + } + + return 0; +} + /* show interface */ DEFUN (show_ipv6_ospf6_interface, show_ipv6_ospf6_interface_ifname_cmd, @@ -1504,10 +1568,187 @@ DEFUN (no_ipv6_ospf6_advertise_prefix_li return CMD_SUCCESS; } +DEFUN (ipv6_ospf6_sha_authentication_mode, + ipv6_ospf6_sha_authentication_mode_cmd, + "ipv6 ospf6 authentication-mode (sha1|sha256|sha384|sha512|default)", + IP_STR + "IPv6 Information\n" + "OSPF6 interface commands\n" + "Authentication mode\n" + "HMAC-SHA-1 authentication\n" + "HMAC-SHA-256 authentication\n" + "HMAC-SHA-384 authentication\n" + "HMAC-SHA-512 authentication\n" + "Enable SHA algorithm for authentication\n" + ) +{ + struct ospf6_interface *oi; + struct interface *ifp; + unsigned hash_algo; + struct ospf6_crypt_key *ck; + struct listnode *node, *nnode; + struct list *auth_crypt; + u_char key_id; + + ifp = (struct interface *)vty->index; + assert (ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create (ifp); + assert (oi); + + /* As per RFC-6506, default sha algorithm is sha-256 */ + if(strcmp(argv[0],"default")==0) + hash_algo = hash_algo_byname ("sha256"); + else + hash_algo = hash_algo_byname (argv[0]); + + if (! hash_algo_enabled (hash_algo)) + { + vty_out (vty, "Algorithm '%s' is not enabled in this build%s", argv[0], VTY_NEWLINE); + return CMD_ERR_NO_MATCH; + } + + /*If hash algorithm is changed, the key attached with previous algorithm is deleted and the user will have to re-enter the key*/ + if(oi->auth_crypt!=NULL) + { + for (ALL_LIST_ELEMENTS (oi->auth_crypt, node, nnode, ck)) + { + listnode_delete (oi->auth_crypt, ck); + XFREE (MTYPE_OSPF6_CRYPT_KEY, ck); + } + } + + oi->auth_type = OSPF6_AUTH_CRYPTOGRAPHIC; + oi->hash_algo = hash_algo; + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_ospf6_sha_authentication_mode, + no_ipv6_ospf6_sha_authentication_mode_cmd, + "no ipv6 ospf6 authentication-mode", + NO_STR + "IPv6 Information\n" + "OSPF6 interface commands\n" + "Disable SHA algorithm for authentication\n" + ) +{ + struct ospf6_interface *oi; + struct interface *ifp; + + ifp = (struct interface *) vty->index; + assert (ifp); + + oi = (struct ospf6_interface *) ifp->info; + if (oi == NULL) + oi = ospf6_interface_create (ifp); + assert (oi); + + oi->auth_type = OSPF6_AUTH_NULL ; + return CMD_SUCCESS; +} + +DEFUN (ipv6_ospf6_sha_key, + ipv6_ospf6_sha_key_cmd, + "ipv6 ospf6 sha-key <1-255> sha KEY", + "IPv6 Information\n" + "OSPF6 interface commands\n" + "SHA-algorithm authentication key-ID\n" + "Key ID\n" + "Enable SHA algorithm\n" + "The authentication password (key)" + ) +{ + struct ospf6_interface *oi; + struct interface *ifp; + struct ospf6_crypt_key *ck; + u_char key_id; + + ifp = (struct interface *)vty->index; + assert (ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create (ifp); + assert (oi); + + if (oi->hash_algo==0) + { + vty_out (vty, "Please specify authentication mode first%s", VTY_NEWLINE); + return CMD_WARNING; + } + + key_id = strtol (argv[0], NULL, 10); + if (ospf6_crypt_key_lookup (oi->auth_crypt, key_id)!= NULL) + { + vty_out (vty, "OSPF6: Key %d already exists%s", key_id, VTY_NEWLINE); + return CMD_WARNING; + } + + ck = ospf6_crypt_key_new (); + ck->key_id = (u_char) key_id; + memset (ck->auth_key, 0, hash_digest_length[oi->hash_algo]); + strncpy ((char *) ck->auth_key, argv[1], hash_digest_length[oi->hash_algo]); + + ospf6_crypt_key_add (oi->auth_crypt, ck); + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_ospf6_sha_key, + no_ipv6_ospf6_sha_key_cmd, + "no ipv6 ospf6 sha-key <1-255>", + NO_STR + "IPv6 Information\n" + "OSPF6 interface commands\n" + "Disable SHA authentication password (key)\n" + "Key ID\n" + ) + +{ + struct ospf6_interface *oi; + struct interface *ifp; + u_char key_id; + + ifp = (struct interface *) vty->index; + assert (ifp); + + oi = (struct ospf6_interface *) ifp->info; + if (oi == NULL) + oi = ospf6_interface_create (ifp); + assert (oi); + + key_id = strtol (argv[0], NULL, 10); + if (ospf6_crypt_key_lookup (oi->auth_crypt, key_id)== NULL) + { + vty_out (vty, "OSPF6: Key %d does not exist%s", key_id, VTY_NEWLINE); + return CMD_WARNING; + } + + ospf6_crypt_key_delete (oi->auth_crypt, key_id); + + return CMD_SUCCESS; +} + +ALIAS ( no_ipv6_ospf6_sha_authentication_mode, + no_ipv6_ospf6_sha_authentication_mode_type_cmd, + "no ipv6 ospf6 authentication-mode (sha1|sha256|sha384|sha512)", + NO_STR + IP_STR + "IPv6 Information\n" + "OSPF6 interface commands\n" + "Disable SHA algorithm for authentication\n" + "HMAC-SHA-1 authentication\n" + "HMAC-SHA-256 authentication\n" + "HMAC-SHA-384 authentication\n" + "HMAC-SHA-512 authentication\n" ) + static int config_write_ospf6_interface (struct vty *vty) { - struct listnode *i; + struct listnode *i,*n1; + struct ospf6_crypt_key *ck; struct ospf6_interface *oi; struct interface *ifp; @@ -1563,6 +1804,16 @@ config_write_ospf6_interface (struct vty if (oi->mtu_ignore) vty_out (vty, " ipv6 ospf6 mtu-ignore%s", VNL); + for (ALL_LIST_ELEMENTS_RO (oi->auth_crypt, n1, ck)) + { + vty_out (vty, " ipv6 ospf6 sha-key %d sha %s%s", + ck->key_id, ck->auth_key, VNL); + } + + if (oi->auth_type) + vty_out (vty, " ipv6 ospf6 authentication-mode %s %s", LOOKUP (hash_algo_cli_str, oi->hash_algo), VNL); + + vty_out (vty, "!%s", VNL); } return 0; @@ -1620,6 +1871,13 @@ ospf6_interface_init (void) install_element (INTERFACE_NODE, &ipv6_ospf6_advertise_prefix_list_cmd); install_element (INTERFACE_NODE, &no_ipv6_ospf6_advertise_prefix_list_cmd); + + install_element (INTERFACE_NODE, &ipv6_ospf6_sha_key_cmd); + install_element (INTERFACE_NODE, &no_ipv6_ospf6_sha_key_cmd); + + install_element (INTERFACE_NODE, &ipv6_ospf6_sha_authentication_mode_cmd); + install_element (INTERFACE_NODE, &no_ipv6_ospf6_sha_authentication_mode_cmd); + install_element (INTERFACE_NODE, &no_ipv6_ospf6_sha_authentication_mode_type_cmd); } DEFUN (debug_ospf6_interface, diff -upwbNr quagga-0.99.21/ospf6d/ospf6_interface.h quagga-rfc6506/ospf6d/ospf6_interface.h --- quagga-0.99.21/ospf6d/ospf6_interface.h 2012-03-24 01:13:19.000000000 +0530 +++ quagga-rfc6506/ospf6d/ospf6_interface.h 2013-07-29 12:54:08.000000000 +0530 @@ -33,6 +33,13 @@ extern unsigned char conf_debug_ospf6_in #define IS_OSPF6_DEBUG_INTERFACE \ (conf_debug_ospf6_interface) +#define OSPF6_AUTH_DATA_SIZE 64U +struct ospf6_crypt_key +{ + u_char key_id; + u_char auth_key[OSPF6_AUTH_DATA_SIZE]; +}; + /* Interface structure */ struct ospf6_interface { @@ -105,6 +112,12 @@ struct ospf6_interface /* prefix-list name to filter connected prefix */ char *plist_name; + + struct list * auth_crypt ; /* list of crypt data */ + u_int8_t auth_type ; /* auth type for interface */ + u_int32_t high_order_seqnum; /* higher order Cryptographic Sequence Number */ + u_int32_t low_order_seqnum; /* lower order Cryptographic Sequence Number */ + int hash_algo; /*sha algorithm type*/ }; /* interface state */ diff -upwbNr quagga-0.99.21/ospf6d/ospf6_message.c quagga-rfc6506/ospf6d/ospf6_message.c --- quagga-0.99.21/ospf6d/ospf6_message.c 2012-04-17 19:26:26.000000000 +0530 +++ quagga-rfc6506/ospf6d/ospf6_message.c 2013-07-29 15:21:33.000000000 +0530 @@ -27,6 +27,7 @@ #include "command.h" #include "thread.h" #include "linklist.h" +#include "cryptohash.h" #include "ospf6_proto.h" #include "ospf6_lsa.h" @@ -49,6 +50,23 @@ #include +/*Initialization of Apad last octets (to be appended after source address)*/ +const u_int8_t Apad_LastOctets[HASH_SIZE_SHA512] = +{ + 0x87, 0x8f, 0xe1, 0xf3, 0x87, 0x8f, 0xe1, 0xf3, + 0x87, 0x8f, 0xe1, 0xf3, 0x87, 0x8f, 0xe1, 0xf3, + 0x87, 0x8f, 0xe1, 0xf3, 0x87, 0x8f, 0xe1, 0xf3, + 0x87, 0x8f, 0xe1, 0xf3, 0x87, 0x8f, 0xe1, 0xf3, + 0x87, 0x8f, 0xe1, 0xf3, 0x87, 0x8f, 0xe1, 0xf3, + 0x87, 0x8f, 0xe1, 0xf3, 0x87, 0x8f, 0xe1, 0xf3, + 0x87, 0x8f, 0xe1, 0xf3, 0x87, 0x8f, 0xe1, 0xf3, + 0x87, 0x8f, 0xe1, 0xf3, 0x87, 0x8f, 0xe1, 0xf3, +}; + +/*Initialization of CPID(Cryptographic Protocol ID)*/ +static const uint16_t CPID =1; + + unsigned char conf_debug_ospf6_message[6] = {0x03, 0, 0, 0, 0, 0}; static const struct message ospf6_message_type_str [] = { @@ -58,6 +76,18 @@ static const struct message ospf6_messag { OSPF6_MESSAGE_TYPE_LSUPDATE, "LSUpdate" }, { OSPF6_MESSAGE_TYPE_LSACK, "LSAck" }, }; + +/* Authentication Types */ +static +const struct message ospf6_auth_type_string[] = +{ + { OSPF6_AUTH_NULL, "Null" }, + { OSPF6_AUTH_CRYPTOGRAPHIC, "Cryptographic" }, +}; + +static const size_t ospf6_auth_type_string_max = + sizeof (ospf6_auth_type_string) / sizeof (ospf6_auth_type_string[0]); + static const size_t ospf6_message_type_str_max = sizeof (ospf6_message_type_str) / sizeof (ospf6_message_type_str[0]); @@ -105,10 +135,10 @@ ospf6_header_print (struct ospf6_header } void -ospf6_hello_print (struct ospf6_header *oh) +ospf6_hello_print (struct ospf6_header *oh, struct ospfv3_crypt *ospf6_at) { struct ospf6_hello *hello; - char options[16]; + char options[36]; char drouter[16], bdrouter[16], neighbor[16]; char *p; @@ -122,6 +152,25 @@ ospf6_hello_print (struct ospf6_header * inet_ntop (AF_INET, &hello->bdrouter, bdrouter, sizeof (bdrouter)); ospf6_options_printbuf (hello->options, options, sizeof (options)); + switch (ospf6_at->auth_type) + { + case OSPF6_AUTH_NULL: + zlog_debug (" Autype : Null Authentication"); + break; + case OSPF6_AUTH_CRYPTOGRAPHIC: + zlog_debug (" Autype : SHA Cryptographic Authentication"); + zlog_debug (" Key ID : %d", ospf6_at->sa_id); + zlog_debug (" Auth Data Len : %d", ospf6_at->auth_data_length); + zlog_debug (" Higher Sequence number : %ld", + (u_long)ntohl (ospf6_at->high_order_seqnum)); + zlog_debug (" Lower Sequence number : %ld", + (u_long)ntohl (ospf6_at->low_order_seqnum)); + break; + default: + zlog_debug ("* This is not supported authentication type"); + break; + } + zlog_debug (" I/F-Id:%ld Priority:%d Option:%s", (u_long) ntohl (hello->interface_id), hello->priority, options); zlog_debug (" HelloInterval:%hu DeadInterval:%hu", @@ -140,10 +189,10 @@ ospf6_hello_print (struct ospf6_header * } void -ospf6_dbdesc_print (struct ospf6_header *oh) +ospf6_dbdesc_print (struct ospf6_header *oh, struct ospfv3_crypt *ospf6_at) { struct ospf6_dbdesc *dbdesc; - char options[16]; + char options[36]; char *p; ospf6_header_print (oh); @@ -154,6 +203,25 @@ ospf6_dbdesc_print (struct ospf6_header ospf6_options_printbuf (dbdesc->options, options, sizeof (options)); + switch (ospf6_at->auth_type) + { + case OSPF6_AUTH_NULL: + zlog_debug (" Autype : Null Authentication"); + break; + case OSPF6_AUTH_CRYPTOGRAPHIC: + zlog_debug (" Autype : SHA Cryptographic Authentication"); + zlog_debug (" Key ID : %d", ospf6_at->sa_id); + zlog_debug (" Auth Data Len : %d", ospf6_at->auth_data_length); + zlog_debug (" Higher Sequence number : %ld", + (u_long)ntohl (ospf6_at->high_order_seqnum)); + zlog_debug (" Lower Sequence number : %ld", + (u_long)ntohl (ospf6_at->low_order_seqnum)); + break; + default: + zlog_debug ("* This is not supported authentication type"); + break; + } + zlog_debug (" MBZ: %#x Option: %s IfMTU: %hu", dbdesc->reserved1, options, ntohs (dbdesc->ifmtu)); zlog_debug (" MBZ: %#x Bits: %s%s%s SeqNum: %#lx", @@ -170,7 +238,6 @@ ospf6_dbdesc_print (struct ospf6_header assert (p == OSPF6_MESSAGE_END (oh)); } - void ospf6_lsreq_print (struct ospf6_header *oh) { @@ -247,10 +314,13 @@ ospf6_hello_recv (struct in6_addr *src, int twoway = 0; int neighborchange = 0; int backupseen = 0; + struct ospfv3_crypt *ospf6_at ; hello = (struct ospf6_hello *) ((caddr_t) oh + sizeof (struct ospf6_header)); + ospf6_at = (struct ospfv3_crypt *)((caddr_t) oh + ntohs (oh->length)); + /* HelloInterval check */ if (ntohs (hello->hello_interval) != oi->hello_interval) { @@ -280,7 +350,7 @@ ospf6_hello_recv (struct in6_addr *src, on = ospf6_neighbor_lookup (oh->router_id, oi); if (on == NULL) { - on = ospf6_neighbor_create (oh->router_id, oi); + on = ospf6_neighbor_create (oh->router_id, oi , ospf6_at); on->prev_drouter = on->drouter = hello->drouter; on->prev_bdrouter = on->bdrouter = hello->bdrouter; on->priority = hello->priority; @@ -1163,12 +1233,31 @@ ospf6_lsaseq_examin return MSG_OK; } +/* OSPF6 authentication type & AT Bit checking function */ +static int +ospf6_auth_type (struct ospf6_interface *oi) +{ + int auth_type = OSPF6_AUTH_NULL; + if(oi->auth_type) + auth_type = OSPF6_AUTH_CRYPTOGRAPHIC; + + /* Handle case where MD5 key list is not configured aka Cisco */ + if (auth_type == OSPF6_AUTH_CRYPTOGRAPHIC && + (list_isempty (oi->auth_crypt))) + return OSPF6_AUTH_NULL; + + return auth_type; + +} + /* Verify a complete OSPF packet for proper sizing/alignment. */ static unsigned -ospf6_packet_examin (struct ospf6_header *oh, const unsigned bytesonwire) +ospf6_packet_examin (struct ospf6_interface *oi, struct ospf6_header *oh, const unsigned bytesonwire) { struct ospf6_lsupdate *lsupd; unsigned test; + u_int16_t bytesdeclared; + struct ospfv3_crypt *ospf6_at; /* length, 1st approximation */ if (bytesonwire < OSPF6_HEADER_SIZE) @@ -1178,13 +1267,29 @@ ospf6_packet_examin (struct ospf6_header return MSG_NG; } /* Now it is safe to access header fields. */ - if (bytesonwire != ntohs (oh->length)) + bytesdeclared = ntohs (oh->length); + ospf6_at = (struct ospfv3_crypt *)((caddr_t) oh + ntohs (oh->length)); + if (ospf6_at->auth_type == OSPF6_AUTH_CRYPTOGRAPHIC) //when authentication is cryptographic + { + if (bytesonwire > ntohs (oh->length) + AUTH_TRAILER_HEADER + OSPF6_AUTH_DATA_SIZE) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + zlog_debug ("%s: packet length error (%u real, %u declared)", + __func__, bytesonwire, ntohs (oh->length) + AUTH_TRAILER_HEADER + OSPF6_AUTH_DATA_SIZE); + return MSG_NG; + } + } + else //when authentication is NULL + { + if (bytesonwire > ntohs (oh->length)) { if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) zlog_debug ("%s: packet length error (%u real, %u declared)", __func__, bytesonwire, ntohs (oh->length)); return MSG_NG; } + } + /* version check */ if (oh->version != OSPFV3_VERSION) { @@ -1192,17 +1297,19 @@ ospf6_packet_examin (struct ospf6_header zlog_debug ("%s: invalid (%u) protocol version", __func__, oh->version); return MSG_NG; } - /* length, 2nd approximation */ + + /* Length, 2nd approximation. The type-specific constraint is checked + against declared length, not amount of bytes on wire. As done in ospfv2 */ if ( oh->type < OSPF6_MESSAGE_TYPE_ALL && ospf6_packet_minlen[oh->type] && - bytesonwire < OSPF6_HEADER_SIZE + ospf6_packet_minlen[oh->type] + bytesdeclared < OSPF6_HEADER_SIZE + ospf6_packet_minlen[oh->type] ) { if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) zlog_debug ("%s: undersized (%u B) %s packet", __func__, - bytesonwire, LOOKUP (ospf6_message_type_str, oh->type)); + bytesdeclared, LOOKUP (ospf6_message_type_str, oh->type)); return MSG_NG; } /* type-specific deeper validation */ @@ -1211,26 +1318,28 @@ ospf6_packet_examin (struct ospf6_header case OSPF6_MESSAGE_TYPE_HELLO: /* RFC5340 A.3.2, packet header + OSPF6_HELLO_MIN_SIZE bytes followed by N>=0 router-IDs. */ - if (0 == (bytesonwire - OSPF6_HEADER_SIZE - OSPF6_HELLO_MIN_SIZE) % 4) + if (0 == (bytesdeclared - OSPF6_HEADER_SIZE - OSPF6_HELLO_MIN_SIZE ) % 4) return MSG_OK; + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) zlog_debug ("%s: alignment error in %s packet", __func__, LOOKUP (ospf6_message_type_str, oh->type)); return MSG_NG; + case OSPF6_MESSAGE_TYPE_DBDESC: /* RFC5340 A.3.3, packet header + OSPF6_DB_DESC_MIN_SIZE bytes followed by N>=0 header-only LSAs. */ test = ospf6_lsaseq_examin ( (struct ospf6_lsa_header *) ((caddr_t) oh + OSPF6_HEADER_SIZE + OSPF6_DB_DESC_MIN_SIZE), - bytesonwire - OSPF6_HEADER_SIZE - OSPF6_DB_DESC_MIN_SIZE, + bytesdeclared - OSPF6_HEADER_SIZE - OSPF6_DB_DESC_MIN_SIZE, 1, 0 ); break; case OSPF6_MESSAGE_TYPE_LSREQ: /* RFC5340 A.3.4, packet header + N>=0 LS description blocks. */ - if (0 == (bytesonwire - OSPF6_HEADER_SIZE - OSPF6_LS_REQ_MIN_SIZE) % OSPF6_LSREQ_LSDESC_FIX_SIZE) + if (0 == (bytesdeclared - OSPF6_HEADER_SIZE - OSPF6_LS_REQ_MIN_SIZE) % OSPF6_LSREQ_LSDESC_FIX_SIZE) return MSG_OK; if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) zlog_debug ("%s: alignment error in %s packet", @@ -1243,7 +1352,7 @@ ospf6_packet_examin (struct ospf6_header test = ospf6_lsaseq_examin ( (struct ospf6_lsa_header *) ((caddr_t) lsupd + OSPF6_LS_UPD_MIN_SIZE), - bytesonwire - OSPF6_HEADER_SIZE - OSPF6_LS_UPD_MIN_SIZE, + bytesdeclared - OSPF6_HEADER_SIZE - OSPF6_LS_UPD_MIN_SIZE, 0, ntohl (lsupd->lsa_number) /* 32 bits */ ); @@ -1253,7 +1362,7 @@ ospf6_packet_examin (struct ospf6_header test = ospf6_lsaseq_examin ( (struct ospf6_lsa_header *) ((caddr_t) oh + OSPF6_HEADER_SIZE + OSPF6_LS_ACK_MIN_SIZE), - bytesonwire - OSPF6_HEADER_SIZE - OSPF6_LS_ACK_MIN_SIZE, + bytesdeclared - OSPF6_HEADER_SIZE - OSPF6_LS_ACK_MIN_SIZE, 1, 0 ); @@ -1268,6 +1377,7 @@ ospf6_packet_examin (struct ospf6_header return test; } + /* Verify particular fields of otherwise correct received OSPF packet to meet the requirements of RFC. */ static int @@ -1275,7 +1385,7 @@ ospf6_rxpacket_examin (struct ospf6_inte { char buf[2][INET_ADDRSTRLEN]; - if (MSG_OK != ospf6_packet_examin (oh, bytesonwire)) + if (MSG_OK != ospf6_packet_examin (oi, oh, bytesonwire)) return MSG_NG; /* Area-ID check */ @@ -1313,6 +1423,167 @@ ospf6_rxpacket_examin (struct ospf6_inte return MSG_OK; } + +static int +ospf6_check_sha_digest(struct ospf6_interface *oi, struct ospf6_header *oh, struct ospfv3_crypt *ospf6_at,struct in6_addr *src) +{ + const u_int8_t *auth_key; + struct ospf6_crypt_key *ck; + int i; + uint8_t local_dlen=hash_digest_length[oi->hash_algo]; + uint8_t Apad[local_dlen]; + uint8_t *ks; + uint8_t key_len; + unsigned hash_algo; + uint8_t auth_key_len; + u_char received_auth_data[local_dlen]; + u_int8_t compr_auth_str[OSPF6_AUTH_DATA_SIZE]; + size_t compr_authlen; + u_char output[local_dlen]; + unsigned hash_result; + struct ospf6_neighbor *on; + oh->checksum=0; + + /* Get sha Authentication key from auth_crypt list. */ + ck = ospf6_crypt_key_lookup (oi->auth_crypt, ospf6_at->sa_id); + if (ck == NULL) + { + zlog_warn ("interface %s: ospf6_check_sha no key %d", + oi->interface->name, ospf6_at->sa_id); + return 0; + } + + auth_key = ck->auth_key; + auth_key_len = strlen(ck->auth_key); + + /*As per RFC-6506 Sec 4.5, Apad is a value that is the same length as the hash output or message digest. + The first 16 octets contain the IPv6 source address followed by the hexadecimal value 0x878FE1F3 repeated (L-16)/4 times. + This implies that hash output is always a length of at least 16 octets.*/ + + memset(Apad,0, sizeof(Apad)); + memcpy(Apad,src, sizeof (struct in6_addr)); + + /*Append the value (0x87, 0x8f, 0xe1, 0xf3) repeated (L-16) times + (that is same as appending hexadecimal value 0x878FE1F3 repeated (L-16)/4 times) */ + memcpy(Apad + sizeof (struct in6_addr), Apad_LastOctets, local_dlen-16); + + /*Method to (create ks) Function for appending CPID to Authentication key K to form Ks*/ + ks = malloc(auth_key_len+sizeof(CPID)); + memset(ks, 0, sizeof(ks)); + memcpy(ks,auth_key,auth_key_len); + memcpy(ks + auth_key_len,&CPID, sizeof(CPID)); + + key_len = auth_key_len + sizeof(CPID); + + memcpy( received_auth_data, ospf6_at->auth_data, local_dlen); + + /* OSPFv3 Authentication Process Starts*/ + hash_key_compress_rfc4822 (oi->hash_algo, ks, key_len, compr_auth_str, &compr_authlen); + memset(ospf6_at->auth_data, 0, sizeof(Apad)); + memcpy( ospf6_at->auth_data, Apad, sizeof(Apad)); + hash_result = hash_make_hmac (oi->hash_algo, oh, sizeof(oh), compr_auth_str, compr_authlen, output); + memcpy( ospf6_at->auth_data, output, hash_digest_length[oi->hash_algo] ); + + /* Compare the local and received digests */ + if (memcmp (ospf6_at->auth_data , received_auth_data, hash_digest_length[oi->hash_algo])) + { + zlog_warn ("interface %s: ospf6_check_sha digest mismatch", oi->interface->name); + return 0; + } + + /* Check crypto seqnum. As done in ospfv2*/ + on = ospf6_neighbor_lookup (oh->router_id, oi); + + if (on && ntohl(on->high_order_seqnum) > ntohl(ospf6_at->high_order_seqnum)) + { + zlog_warn ("interface %s: ospf6_check_sha bad High-order-sequence %d (expect %d)", + oi->interface->name, + ntohl(ospf6_at->high_order_seqnum), + ntohl(on->ospf6_if->high_order_seqnum)); + return 0; + } + + if (on && ntohl(on->high_order_seqnum) == ntohl(ospf6_at->high_order_seqnum) && ntohl(on->low_order_seqnum) >= ntohl(ospf6_at->low_order_seqnum)) + { + zlog_warn ("interface %s: ospf6_check_sha bad Low-order-sequence %d (expect %d)", + oi->interface->name, + ntohl(ospf6_at->low_order_seqnum), + ntohl(on->ospf6_if->low_order_seqnum)); + return 0; + } + + /* save neighbor's crypt_seqnum */ + if (on) + { + on->low_order_seqnum = ospf6_at->low_order_seqnum; + on->high_order_seqnum = ospf6_at->high_order_seqnum; + } + free(ks); + return 1; +} + +static int +ospf6_check_auth (struct ospf6_interface *oi, struct ospf6_header *oh, struct ospfv3_crypt *ospf6_at, struct in6_addr *src) +{ + struct ospf6_crypt_key *ck_recv; + struct ospf6_hello *hello; + struct ospf6_dbdesc *dbdesc; + u_int16_t iface_auth_type; //local auth type + u_int16_t pkt_auth_type = OSPF6_AUTH_NULL; //received auth type + + hello = (struct ospf6_hello *) + ((caddr_t) oh + sizeof (struct ospf6_header)); + + dbdesc = (struct ospf6_dbdesc *) + ((caddr_t) oh + sizeof (struct ospf6_header)); + + /* case when AT Bit is set at sender but key is not specified, auth_type is considered to be NULL */ + if((OSPF6_OPT_ISSET_AT(hello->options,OSPF6_OPT_AT) || OSPF6_OPT_ISSET_AT(dbdesc->options ,OSPF6_OPT_AT)) && (ospf6_at->auth_type == OSPF6_AUTH_CRYPTOGRAPHIC)) + pkt_auth_type = OSPF6_AUTH_CRYPTOGRAPHIC; + + + switch (pkt_auth_type) + { + case OSPF6_AUTH_NULL: + if (OSPF6_AUTH_NULL != (iface_auth_type = ospf6_auth_type (oi))) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_warn ("interface %s: auth-type mismatch, local %s, rcvd Null", + oi->interface->name, LOOKUP(ospf6_auth_type_string, iface_auth_type)); + return MSG_NG; + } + return MSG_OK; + + case OSPF6_AUTH_CRYPTOGRAPHIC: + if (OSPF6_AUTH_CRYPTOGRAPHIC != (iface_auth_type = ospf6_auth_type (oi))) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_warn ("interface %s: auth-type mismatch, local %s, rcvd Cryptographic", + oi->interface->name, LOOKUP (ospf6_auth_type_string, iface_auth_type)); + return MSG_NG; + } + + /* As per RFC 6506 sec 4.2, Checksum verification should be omitted , if it does not include + a non-zero checksum, it will not be modified by the receiver and simply be included in + calculation of Authentication Trailer message digest */ + + /* SHA crypto method can pass ospf6_packet_examin() */ + + if (! ospf6_check_sha_digest (oi, oh,ospf6_at,src)) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_warn ("interface %s: SHA auth failed", oi->interface->name); + return MSG_NG; + } + return MSG_OK; + default: + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_warn ("interface %s: invalid packet auth-type (%02x)", + oi->interface->name, pkt_auth_type); + return MSG_NG; + } +} + static void ospf6_lsupdate_recv (struct in6_addr *src, struct in6_addr *dst, struct ospf6_interface *oi, struct ospf6_header *oh) @@ -1533,6 +1804,7 @@ ospf6_receive (struct thread *thread) struct iovec iovector[2]; struct ospf6_interface *oi; struct ospf6_header *oh; + struct ospfv3_crypt *ospf6_at; /* add next read thread */ sockfd = THREAD_FD (thread); @@ -1574,9 +1846,19 @@ ospf6_receive (struct thread *thread) if (ospf6_rxpacket_examin (oi, oh, len) != MSG_OK) return 0; + /* Check Authentication of data */ + + /* ospf6_at can create problem when AT bit is not set */ + ospf6_at = (struct ospfv3_crypt *)((caddr_t) oh + ntohs (oh->length)); // Value of ospf6_at for received packet + + if (ospf6_check_auth (oi, oh,ospf6_at,&src)!= MSG_OK) + return 0; + + /* Being here means, that no sizing/alignment issues were detected in - the input packet. This renders the additional checks performed below - and also in the type-specific dispatching functions a dead code, + the input packet.It also means that if Authentication is set for the + interface, it has been passed. This renders the additional checks performed + below and also in the type-specific dispatching functions a dead code, which can be dismissed in a cleanup-focused review round later. */ /* Log */ @@ -1592,10 +1874,10 @@ ospf6_receive (struct thread *thread) switch (oh->type) { case OSPF6_MESSAGE_TYPE_HELLO: - ospf6_hello_print (oh); + ospf6_hello_print (oh, ospf6_at); break; case OSPF6_MESSAGE_TYPE_DBDESC: - ospf6_dbdesc_print (oh); + ospf6_dbdesc_print (oh, ospf6_at); break; case OSPF6_MESSAGE_TYPE_LSREQ: ospf6_lsreq_print (oh); @@ -1641,6 +1923,111 @@ ospf6_receive (struct thread *thread) } static void +ospf6_make_sha_digest (struct in6_addr *src,struct ospf6_interface *oi, struct ospf6_header *oh, struct ospfv3_crypt *ospf6_at) +{ + const u_int8_t *auth_key; + struct ospf6_crypt_key *ck; + int i; + unsigned hash_algo; + uint8_t dlen=hash_digest_length[oi->hash_algo]; + uint8_t Apad[dlen]; + uint8_t *ks; + uint8_t auth_key_len; + uint8_t key_len; + struct in6_addr src_temp; + u_int8_t compr_auth_str[OSPF6_AUTH_DATA_SIZE]; + size_t compr_authlen; + u_char output[dlen]; + unsigned hash_result; + + /* Get sha Authentication key from auth_crypt list. */ + ck = listgetdata (listtail(oi->auth_crypt)); + auth_key = ck->auth_key; + auth_key_len = strlen(ck->auth_key); + + /*As per RFC-6506, Apad is a value that is the same length as the hash output or message digest. + The first 16 octets contain the IPv6 source address followed by the hexadecimal value 0x878FE1F3 repeated (L-16)/4 times. + This implies that hash output is always a length of at least 16 octets.*/ + memset(Apad,0, sizeof(Apad)); + memset(&src_temp,0,sizeof(struct in6_addr)); + + if(src) + memcpy(Apad,src, sizeof (struct in6_addr)); + else + memcpy(Apad,&src_temp, sizeof (struct in6_addr)); + + memcpy(Apad + sizeof (struct in6_addr), Apad_LastOctets, dlen-16); /*Append the value (0x87, 0x8f, 0xe1, 0xf3) repeated (L-16) times (that is same as appending hexadecimal value 0x878FE1F3 repeated (L-16)/4 times) */ + + /*Method to (create ks) Function for appending CPID to Authentication key K to form Ks*/ + ks = malloc(auth_key_len+sizeof(CPID)); + memset(ks, 0, sizeof(ks)); + memcpy(ks,auth_key, auth_key_len); + memcpy(ks + auth_key_len,&CPID, sizeof(CPID)); + key_len = auth_key_len + sizeof(CPID); + + /* OSPFv3 Authentication Process Starts*/ + hash_key_compress_rfc4822 (oi->hash_algo, ks, key_len, compr_auth_str, &compr_authlen); + memcpy( ospf6_at->auth_data, Apad, sizeof(Apad)); + hash_result = hash_make_hmac (oi->hash_algo, oh, sizeof(oh), compr_auth_str, compr_authlen, output); + memcpy( ospf6_at->auth_data, output, hash_digest_length[oi->hash_algo] ); + +} + + +static void +ospf6_make_auth(struct in6_addr *src,struct ospf6_interface *oi, struct ospf6_header *oh, struct ospfv3_crypt *ospf6_at) +{ + struct ospf6_crypt_key *ck; + u_int32_t t; + + ospf6_at->auth_type = OSPF6_AUTH_CRYPTOGRAPHIC ; /* Setting of auth type */ + ospf6_at->reserved =0; /* Setting of reserved field to 0 */ + + /* Get sha Authentication key-ID from auth_crypt list.*/ + /* If key is not set, then set 0. */ + if (list_isempty (oi->auth_crypt)) + ospf6_at->sa_id = 0 ; + else + { + ck = listgetdata (listtail(oi->auth_crypt)); + ospf6_at->sa_id = ck->key_id; + } + + /* Logic taken from ospfv2 sequence number code */ + t = (time(NULL) & 0xFFFFFFFF); + if (t > oi->low_order_seqnum) + oi->low_order_seqnum = t; + else + oi->low_order_seqnum++; + + /* As per RFC-6506 section 4.1.1 "Sequence Number Wrap" */ + //for lower order sequence wrap + if(oi->low_order_seqnum == 0xFFFFFFFF) + { + oi->high_order_seqnum++; + oi->low_order_seqnum=0; + } + //for higher order sequence wrap...if higher order is filled reset higher order and lower order and reset keys to 0 + if(oi->high_order_seqnum == 0xFFFFFFFF && oi->low_order_seqnum == 0xFFFFFFFF) + { + oi->high_order_seqnum = 0; + oi->low_order_seqnum = 0; + ck = listgetdata (listtail(oi->auth_crypt)); + while(ck != NULL){ + ospf6_crypt_key_delete (oi->auth_crypt,ck->key_id); + } + } + + ospf6_at->low_order_seqnum = htonl (oi->low_order_seqnum); + ospf6_at->high_order_seqnum = htonl (oi->high_order_seqnum); + ospf6_at->auth_data_length = AUTH_TRAILER_HEADER + hash_digest_length[oi->hash_algo]; /* Authentication data length = Authentication trailer (16 octet) + Digest (Algorithm specific digest length) */ + + ospf6_make_sha_digest (src,oi,oh,ospf6_at); + +} + + +static void ospf6_send (struct in6_addr *src, struct in6_addr *dst, struct ospf6_interface *oi, struct ospf6_header *oh) { @@ -1648,11 +2035,11 @@ ospf6_send (struct in6_addr *src, struct char srcname[64], dstname[64]; struct iovec iovector[2]; - /* initialize */ - iovector[0].iov_base = (caddr_t) oh; - iovector[0].iov_len = ntohs (oh->length); - iovector[1].iov_base = NULL; - iovector[1].iov_len = 0; + struct ospfv3_crypt *ospf6_at ; + + /* Append the Authentication Trailer to OSPFv3 packet */ + + ospf6_at = (struct ospfv3_crypt *)((caddr_t) oh + ntohs (oh->length)); /* fill OSPF header */ oh->version = OSPFV3_VERSION; @@ -1664,6 +2051,32 @@ ospf6_send (struct in6_addr *src, struct oh->instance_id = oi->instance_id; oh->reserved = 0; + + if(ospf6_auth_type(oi)) + { + /* As per RFC 6506 sec 4.2, Checksum should be set to 0 prior to + calculation of Authentication Trailer message digest */ + + oh->checksum = 0; + + ospf6_make_auth(src,oi, oh,ospf6_at); + + /* initialize */ + iovector[0].iov_base = (caddr_t) oh; + iovector[0].iov_len = ntohs(oh->length) + ospf6_at->auth_data_length; + iovector[1].iov_base = NULL; + iovector[1].iov_len = 0; + } + else + { + ospf6_at->auth_type = OSPF6_AUTH_NULL; + /* initialize */ + iovector[0].iov_base = (caddr_t) oh; + iovector[0].iov_len = ntohs (oh->length); + iovector[1].iov_base = NULL; + iovector[1].iov_len = 0; + + } /* Log */ if (IS_OSPF6_DEBUG_MESSAGE (oh->type, SEND)) { @@ -1680,10 +2093,10 @@ ospf6_send (struct in6_addr *src, struct switch (oh->type) { case OSPF6_MESSAGE_TYPE_HELLO: - ospf6_hello_print (oh); + ospf6_hello_print (oh,ospf6_at); break; case OSPF6_MESSAGE_TYPE_DBDESC: - ospf6_dbdesc_print (oh); + ospf6_dbdesc_print (oh,ospf6_at); break; case OSPF6_MESSAGE_TYPE_LSREQ: ospf6_lsreq_print (oh); @@ -1703,9 +2116,16 @@ ospf6_send (struct in6_addr *src, struct /* send message */ len = ospf6_sendmsg (src, dst, &oi->interface->ifindex, iovector); - if (len != ntohs (oh->length)) + + if(ospf6_auth_type(oi)){ + if (len != (ntohs (oh->length)+ ospf6_at->auth_data_length)) + zlog_err ("Could not send entire message"); + } + else + { if (len != ntohs (oh->length)) zlog_err ("Could not send entire message"); } +} static uint32_t ospf6_packet_max(struct ospf6_interface *oi) @@ -1714,6 +2134,7 @@ ospf6_packet_max(struct ospf6_interface return oi->ifmtu - (sizeof (struct ip6_hdr)); } + int ospf6_hello_send (struct thread *thread) { @@ -1748,6 +2169,12 @@ ospf6_hello_send (struct thread *thread) hello->options[0] = oi->area->options[0]; hello->options[1] = oi->area->options[1]; hello->options[2] = oi->area->options[2]; + + if(oi->auth_type) + { + OSPF6_OPT_SET_AT(hello->options, OSPF6_OPT_AT); + } + hello->hello_interval = htons (oi->hello_interval); hello->dead_interval = htons (oi->dead_interval); hello->drouter = oi->drouter; @@ -1794,7 +2221,7 @@ ospf6_dbdesc_send (struct thread *thread { if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_DBDESC, SEND)) zlog_debug ("Quit to send DbDesc to neighbor %s state %s", - on->name, ospf6_neighbor_state_str[on->state]); + on->name, LOOKUP (ospf6_neighbor_state_str, on->state)); return 0; } @@ -1821,6 +2248,12 @@ ospf6_dbdesc_send (struct thread *thread dbdesc->options[0] = on->ospf6_if->area->options[0]; dbdesc->options[1] = on->ospf6_if->area->options[1]; dbdesc->options[2] = on->ospf6_if->area->options[2]; + + if(on->ospf6_if->auth_type) + { + OSPF6_OPT_SET_AT(dbdesc->options, OSPF6_OPT_AT); + } + dbdesc->ifmtu = htons (on->ospf6_if->ifmtu); dbdesc->bits = on->dbdesc_bits; dbdesc->seqnum = htonl (on->dbdesc_seqnum); @@ -1853,7 +2286,6 @@ ospf6_dbdesc_send (struct thread *thread on->ospf6_if, oh); return 0; } - int ospf6_dbdesc_send_newone (struct thread *thread) { diff -upwbNr quagga-0.99.21/ospf6d/ospf6_message.h quagga-rfc6506/ospf6d/ospf6_message.h --- quagga-0.99.21/ospf6d/ospf6_message.h 2012-03-24 01:13:20.000000000 +0530 +++ quagga-rfc6506/ospf6d/ospf6_message.h 2013-07-10 10:15:00.000000000 +0530 @@ -60,6 +60,26 @@ struct ospf6_header #define OSPF6_MESSAGE_END(H) ((caddr_t) (H) + ntohs ((H)->length)) +#define OSPF6_AUTH_DATA_SIZE 64U +#define BLOCK_SIZE 128U +#define LENGTH_OF_HASH 64U +#define AUTH_TRAILER_HEADER 16U +#define OSPF6_AUTH_NULL 0 +#define OSPF6_AUTH_CRYPTOGRAPHIC 1 + +/* Authentication Trailer Structure */ +struct ospfv3_crypt +{ + u_int16_t auth_type; + u_int16_t auth_data_length; + u_int16_t reserved; + u_int16_t sa_id; + u_int32_t high_order_seqnum; + u_int32_t low_order_seqnum; + u_char auth_data[OSPF6_AUTH_DATA_SIZE]; + +}; + /* Hello */ #define OSPF6_HELLO_MIN_SIZE 20U struct ospf6_hello @@ -116,8 +136,8 @@ struct ospf6_lsupdate /* It is just a sequence of LSA Headers */ /* Function definition */ -extern void ospf6_hello_print (struct ospf6_header *); -extern void ospf6_dbdesc_print (struct ospf6_header *); +extern void ospf6_hello_print (struct ospf6_header * , struct ospfv3_crypt *); +extern void ospf6_dbdesc_print (struct ospf6_header * , struct ospfv3_crypt *); extern void ospf6_lsreq_print (struct ospf6_header *); extern void ospf6_lsupdate_print (struct ospf6_header *); extern void ospf6_lsack_print (struct ospf6_header *); diff -upwbNr quagga-0.99.21/ospf6d/ospf6_neighbor.c quagga-rfc6506/ospf6d/ospf6_neighbor.c --- quagga-0.99.21/ospf6d/ospf6_neighbor.c 2010-08-12 18:29:32.000000000 +0530 +++ quagga-rfc6506/ospf6d/ospf6_neighbor.c 2013-07-10 10:15:00.000000000 +0530 @@ -46,6 +46,9 @@ const char *ospf6_neighbor_state_str[] = { "None", "Down", "Attempt", "Init", "Twoway", "ExStart", "ExChange", "Loading", "Full", NULL }; +const size_t ospf6_neighbor_state_str_max = sizeof (ospf6_neighbor_state_str) / + sizeof (ospf6_neighbor_state_str[0]); + int ospf6_neighbor_cmp (void *va, void *vb) { @@ -69,8 +72,11 @@ ospf6_neighbor_lookup (u_int32_t router_ } /* create ospf6_neighbor */ + +/* ospf6_at added as parameter in order to update sequence no. */ + struct ospf6_neighbor * -ospf6_neighbor_create (u_int32_t router_id, struct ospf6_interface *oi) +ospf6_neighbor_create (u_int32_t router_id, struct ospf6_interface *oi, struct ospfv3_crypt *ospf6_at) { struct ospf6_neighbor *on; char buf[16]; @@ -100,6 +106,12 @@ ospf6_neighbor_create (u_int32_t router_ on->lsreq_list = ospf6_lsdb_create (on); on->lsupdate_list = ospf6_lsdb_create (on); on->lsack_list = ospf6_lsdb_create (on); + /* if Authentication is enabled, auth_type will be non-zero value, it will update the higher and lower cryt seq no. */ + if(ospf6_at->auth_type) + { + on->low_order_seqnum = ospf6_at->low_order_seqnum; + on->high_order_seqnum = ospf6_at->high_order_seqnum; + } listnode_add_sort (oi->neighbor_list, on); return on; diff -upwbNr quagga-0.99.21/ospf6d/ospf6_neighbor.h quagga-rfc6506/ospf6d/ospf6_neighbor.h --- quagga-0.99.21/ospf6d/ospf6_neighbor.h 2010-08-12 18:29:32.000000000 +0530 +++ quagga-rfc6506/ospf6d/ospf6_neighbor.h 2013-07-10 10:16:00.000000000 +0530 @@ -93,6 +93,9 @@ struct ospf6_neighbor struct thread *thread_send_lsreq; struct thread *thread_send_lsupdate; struct thread *thread_send_lsack; + + u_int32_t high_order_seqnum; /* higher order Cryptographic Sequence Number */ + u_int32_t low_order_seqnum; /* lower order Cryptographic Sequence Number */ }; /* Neighbor state */ @@ -106,7 +109,7 @@ struct ospf6_neighbor #define OSPF6_NEIGHBOR_FULL 8 extern const char *ospf6_neighbor_state_str[]; - +extern const size_t ospf6_neighbor_state_str_max; /* Function Prototypes */ int ospf6_neighbor_cmp (void *va, void *vb); @@ -115,7 +118,7 @@ void ospf6_neighbor_dbex_init (struct os struct ospf6_neighbor *ospf6_neighbor_lookup (u_int32_t, struct ospf6_interface *); struct ospf6_neighbor *ospf6_neighbor_create (u_int32_t, - struct ospf6_interface *); + struct ospf6_interface *, struct ospfv3_crypt *); void ospf6_neighbor_delete (struct ospf6_neighbor *); /* Neighbor event */ diff -upwbNr quagga-0.99.21/ospf6d/ospf6_proto.c quagga-rfc6506/ospf6d/ospf6_proto.c --- quagga-0.99.21/ospf6d/ospf6_proto.c 2012-03-24 01:13:20.000000000 +0530 +++ quagga-rfc6506/ospf6d/ospf6_proto.c 2013-07-10 10:16:00.000000000 +0530 @@ -71,14 +71,17 @@ ospf6_capability_printbuf (char capabili void ospf6_options_printbuf (u_char *options, char *buf, int size) { - const char *dc, *r, *n, *mc, *e, *v6; + const char *dc, *r, *n, *mc, *e, *v6, *at; + dc = (OSPF6_OPT_ISSET (options, OSPF6_OPT_DC) ? "DC" : "--"); r = (OSPF6_OPT_ISSET (options, OSPF6_OPT_R) ? "R" : "-" ); n = (OSPF6_OPT_ISSET (options, OSPF6_OPT_N) ? "N" : "-" ); mc = (OSPF6_OPT_ISSET (options, OSPF6_OPT_MC) ? "MC" : "--"); e = (OSPF6_OPT_ISSET (options, OSPF6_OPT_E) ? "E" : "-" ); v6 = (OSPF6_OPT_ISSET (options, OSPF6_OPT_V6) ? "V6" : "--"); - snprintf (buf, size, "%s|%s|%s|%s|%s|%s", dc, r, n, mc, e, v6); -} + at = (OSPF6_OPT_ISSET_AT (options, OSPF6_OPT_AT) ? "AT" : "--"); + snprintf (buf, size, "|%s|-|--|--|--|%s|%s|%s|%s|%s|%s", at, dc, r, n, mc, e, v6); + +} diff -upwbNr quagga-0.99.21/ospf6d/ospf6_proto.h quagga-rfc6506/ospf6d/ospf6_proto.h --- quagga-0.99.21/ospf6d/ospf6_proto.h 2012-03-24 01:13:20.000000000 +0530 +++ quagga-rfc6506/ospf6d/ospf6_proto.h 2013-07-10 10:16:00.000000000 +0530 @@ -60,6 +60,12 @@ /* OSPF options */ /* present in HELLO, DD, LSA */ +#define OSPF6_OPT_SET_AT(x,opt) ((x)[1] |= (opt)) +#define OSPF6_OPT_ISSET_AT(x,opt) ((x)[1] & (opt)) +#define OSPF6_OPT_CLEAR_AT(x,opt) ((x)[1] &= ~(opt)) + +/* OSPF options */ +/* present in HELLO, DD, LSA */ #define OSPF6_OPT_SET(x,opt) ((x)[2] |= (opt)) #define OSPF6_OPT_ISSET(x,opt) ((x)[2] & (opt)) #define OSPF6_OPT_CLEAR(x,opt) ((x)[2] &= ~(opt)) @@ -72,6 +78,8 @@ #define OSPF6_OPT_E (1 << 1) /* AS External Capability */ #define OSPF6_OPT_V6 (1 << 0) /* IPv6 forwarding Capability */ +#define OSPF6_OPT_AT (1 << 2) /* Authentication Capability */ + /* OSPF6 Prefix */ #define OSPF6_PREFIX_MIN_SIZE 4U /* .length == 0 */ struct ospf6_prefix