summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMahdi Varasteh <varasteh@amnesh.ir>2023-09-12 13:39:44 +0200
committerMahdi Varasteh <varasteh@amnesh.ir>2023-09-16 06:08:23 +0200
commitf5011cd5ddfd0eabe359d7013747823c6bd4ed3f (patch)
tree0be666457c571176705bb970582b16fc4d2d688b
parentMerge pull request #14364 from opensourcerouting/event-fix-delete-during-hash... (diff)
downloadfrr-f5011cd5ddfd0eabe359d7013747823c6bd4ed3f.tar.xz
frr-f5011cd5ddfd0eabe359d7013747823c6bd4ed3f.zip
[ospfd]: add support for RFC 5709 HMAC-SHA Auth
This patch includes: * Implementation of RFC 5709 support in OSPF. Using openssl library and FRR key-chain, one can use SHA1, SHA256, SHA384, SHA512 and keyed-MD5( backward compatibility with RFC 2328) HMAC algs. * Updating documentation of OSPF * add topotests for new HMAC algorithms Signed-off-by: Mahdi Varasteh <varasteh@amnesh.ir>
-rw-r--r--doc/user/ospfd.rst32
-rw-r--r--ospfd/ospf_auth.c705
-rw-r--r--ospfd/ospf_auth.h20
-rw-r--r--ospfd/ospf_errors.c6
-rw-r--r--ospfd/ospf_errors.h2
-rw-r--r--ospfd/ospf_interface.c3
-rw-r--r--ospfd/ospf_interface.h7
-rw-r--r--ospfd/ospf_main.c2
-rw-r--r--ospfd/ospf_packet.c363
-rw-r--r--ospfd/ospf_vty.c142
-rw-r--r--ospfd/subdir.am2
-rw-r--r--python/xref2vtysh.py2
-rw-r--r--tests/topotests/lib/ospf.py4
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_authentication.py557
-rw-r--r--vtysh/vtysh.h2
15 files changed, 1419 insertions, 430 deletions
diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst
index b61c9448d..26b2b4397 100644
--- a/doc/user/ospfd.rst
+++ b/doc/user/ospfd.rst
@@ -599,6 +599,38 @@ Interfaces
KEY is the actual message digest key, of up to 16 chars (larger strings will
be truncated), and is associated with the given KEYID.
+.. clicmd:: ip ospf authentication key-chain KEYCHAIN
+
+ Specify that HMAC cryptographic authentication must be used on this interface
+ using a key chain. Overrides any authentication enabled on a per-area basis
+ (:clicmd:`area A.B.C.D authentication message-digest`)
+
+ * ``KEYCHAIN``: Specifies the name of the key chain that contains the authentication
+ key(s) and cryptographic algorithms to be used for OSPF authentication. The key chain
+ is a logical container that holds one or more authentication keys,
+ allowing for key rotation and management.
+
+ Note that OSPF HMAC cryptographic authentication requires that time never go backwards
+ (correct time is NOT important, only that it never goes backwards), even
+ across resets, if ospfd is to be able to promptly reestablish adjacencies
+ with its neighbours after restarts/reboots. The host should have system time
+ be set at boot from an external or non-volatile source (e.g. battery backed
+ clock, NTP, etc.) or else the system clock should be periodically saved to
+ non-volatile storage and restored at boot if HMAC cryptographic authentication is to be
+ expected to work reliably.
+
+ Example:
+
+ .. code:: frr
+
+ r1(config)#key chain temp
+ r1(config-keychain)#key 13
+ r1(config-keychain-key)#key-string ospf
+ r1(config-keychain-key)#cryptographic-algorithm hmac-sha-256
+ r1(config)#int eth0
+ r1(config-if)#ip ospf authentication key-chain temp
+ r1(config-if)#ip ospf area 0
+
.. clicmd:: ip ospf cost (1-65535)
diff --git a/ospfd/ospf_auth.c b/ospfd/ospf_auth.c
new file mode 100644
index 000000000..2d13d4e9a
--- /dev/null
+++ b/ospfd/ospf_auth.c
@@ -0,0 +1,705 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2023 Amnesh Inc.
+ * Mahdi Varasteh
+ */
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "if.h"
+#include "checksum.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_errors.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_auth.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_dump.h"
+#include "ospfd/ospf_packet.h"
+#include "ospfd/ospf_network.h"
+#include "ospfd/ospf_flood.h"
+#include "ospfd/ospf_dump.h"
+#include "ospfd/ospf_bfd.h"
+#include "ospfd/ospf_gr.h"
+#ifdef CRYPTO_INTERNAL
+#include "sha256.h"
+#include "md5.h"
+#endif
+
+const uint8_t ospf_auth_apad[KEYCHAIN_MAX_HASH_SIZE] = {
+ 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
+};
+
+static int ospf_check_sum(struct ospf_header *ospfh)
+{
+ uint32_t ret;
+ uint16_t sum;
+
+ /* clear auth_data for checksum. */
+ memset(ospfh->u.auth_data, 0, OSPF_AUTH_SIMPLE_SIZE);
+
+ /* keep checksum and clear. */
+ sum = ospfh->checksum;
+ memset(&ospfh->checksum, 0, sizeof(uint16_t));
+
+ /* calculate checksum. */
+ ret = in_cksum(ospfh, ntohs(ospfh->length));
+
+ if (ret != sum) {
+ zlog_info("%s: checksum mismatch, my %X, his %X", __func__, ret,
+ sum);
+ return 0;
+ }
+
+ return 1;
+}
+
+#ifdef CRYPTO_OPENSSL
+static const EVP_MD *ospf_auth_get_openssl_evp_md_from_key(struct key *key)
+{
+ if (key->hash_algo == KEYCHAIN_ALGO_HMAC_SHA1)
+ return EVP_get_digestbyname("sha1");
+ else if (key->hash_algo == KEYCHAIN_ALGO_HMAC_SHA256)
+ return EVP_get_digestbyname("sha256");
+ else if (key->hash_algo == KEYCHAIN_ALGO_HMAC_SHA384)
+ return EVP_get_digestbyname("sha384");
+ else if (key->hash_algo == KEYCHAIN_ALGO_HMAC_SHA512)
+ return EVP_get_digestbyname("sha512");
+ return NULL;
+}
+#endif
+
+static int ospf_auth_check_hmac_sha_digest(struct ospf_interface *oi,
+ struct ospf_header *ospfh,
+ struct ip *iph,
+ struct key *key)
+{
+ unsigned char digest[KEYCHAIN_MAX_HASH_SIZE];
+ struct ospf_neighbor *nbr;
+ uint16_t length = ntohs(ospfh->length);
+ uint16_t hash_length = keychain_get_hash_len(key->hash_algo);
+#ifdef CRYPTO_OPENSSL
+ unsigned int openssl_hash_length = hash_length;
+ HMAC_CTX *ctx;
+ const EVP_MD *md_alg = ospf_auth_get_openssl_evp_md_from_key(key);
+
+ if (!md_alg) {
+ flog_warn(EC_OSPF_AUTH,
+ "interface %s: invalid HMAC algorithm, Router-ID: %pI4",
+ IF_NAME(oi), &ospfh->router_id);
+ return 0;
+ }
+#elif CRYPTO_INTERNAL
+ HMAC_SHA256_CTX ctx;
+
+ if (key->hash_algo != KEYCHAIN_ALGO_HMAC_SHA256) {
+ flog_warn(EC_OSPF_AUTH,
+ "interface %s: HMAC algorithm not supported, Router-ID: %pI4",
+ IF_NAME(oi), &ospfh->router_id);
+ return 0;
+ }
+#endif
+ /* check crypto seqnum. */
+ nbr = ospf_nbr_lookup(oi, iph, ospfh);
+
+ if (nbr &&
+ ntohl(nbr->crypt_seqnum) > ntohl(ospfh->u.crypt.crypt_seqnum)) {
+ flog_warn(EC_OSPF_AUTH,
+ "interface %s: ospf_check_hmac_sha bad sequence %u (expect %d), Router-ID: %pI4",
+ IF_NAME(oi), ntohl(ospfh->u.crypt.crypt_seqnum),
+ ntohl(nbr->crypt_seqnum), &ospfh->router_id);
+ return 0;
+ }
+#ifdef CRYPTO_OPENSSL
+ ctx = HMAC_CTX_new();
+ HMAC_Init_ex(ctx, key->string, strlen(key->string), md_alg, NULL);
+ HMAC_Update(ctx, (const unsigned char *)ospfh, length);
+ HMAC_Update(ctx, (const unsigned char *)ospf_auth_apad, hash_length);
+ HMAC_Final(ctx, digest, &openssl_hash_length);
+ HMAC_CTX_free(ctx);
+#elif CRYPTO_INTERNAL
+ memset(&ctx, 0, sizeof(ctx));
+ HMAC__SHA256_Init(&ctx, key->string, strlen(key->string));
+ HMAC__SHA256_Update(&ctx, ospfh, length);
+ HMAC__SHA256_Update(&ctx, ospf_auth_apad, hash_length);
+ HMAC__SHA256_Final(digest, &ctx);
+#endif
+ if (memcmp((caddr_t)ospfh + length, digest, hash_length)) {
+ flog_warn(EC_OSPF_AUTH,
+ "interface %s: ospf_check_hmac_sha checksum mismatch %u, Router-ID: %pI4",
+ IF_NAME(oi), length, &ospfh->router_id);
+ return 0;
+ }
+ if (nbr)
+ nbr->crypt_seqnum = ospfh->u.crypt.crypt_seqnum;
+ return 1;
+}
+
+static int ospf_auth_check_md5_digest(struct ospf_interface *oi,
+ struct ospf_header *ospfh, struct ip *iph, struct key *key)
+{
+#ifdef CRYPTO_OPENSSL
+ EVP_MD_CTX *ctx;
+#elif CRYPTO_INTERNAL
+ MD5_CTX ctx;
+#endif
+ char auth_key[OSPF_AUTH_MD5_SIZE + 1];
+ unsigned char digest[OSPF_AUTH_MD5_SIZE];
+ struct ospf_neighbor *nbr;
+ struct crypt_key *ck = NULL;
+ uint16_t length = ntohs(ospfh->length);
+
+ if (key == NULL) {
+ ck = ospf_crypt_key_lookup(OSPF_IF_PARAM(oi, auth_crypt),
+ ospfh->u.crypt.key_id);
+ if (ck == NULL) {
+ flog_warn(
+ EC_OSPF_AUTH,
+ "interface %s: %s no key %d, Router-ID: %pI4",
+ IF_NAME(oi), __func__, ospfh->u.crypt.key_id, &ospfh->router_id);
+ return 0;
+ }
+ }
+ /* check crypto seqnum. */
+ nbr = ospf_nbr_lookup(oi, iph, ospfh);
+
+ if (nbr &&
+ ntohl(nbr->crypt_seqnum) > ntohl(ospfh->u.crypt.crypt_seqnum)) {
+ flog_warn(EC_OSPF_AUTH,
+ "interface %s: %s bad sequence %d (expect %d), Router-ID: %pI4",
+ IF_NAME(oi), __func__, ntohl(ospfh->u.crypt.crypt_seqnum),
+ ntohl(nbr->crypt_seqnum), &ospfh->router_id);
+ return 0;
+ }
+
+ memset(auth_key, 0, OSPF_AUTH_MD5_SIZE + 1);
+ if (ck == NULL)
+ strlcpy(auth_key, key->string, OSPF_AUTH_MD5_SIZE + 1);
+ else
+ strlcpy(auth_key, (char *)ck->auth_key, OSPF_AUTH_MD5_SIZE + 1);
+ /* Generate a digest for the ospf packet - their digest + our digest. */
+#ifdef CRYPTO_OPENSSL
+ unsigned int md5_size = OSPF_AUTH_MD5_SIZE;
+
+ ctx = EVP_MD_CTX_new();
+ EVP_DigestInit(ctx, EVP_md5());
+ EVP_DigestUpdate(ctx, ospfh, length);
+ EVP_DigestUpdate(ctx, auth_key, OSPF_AUTH_MD5_SIZE);
+ EVP_DigestFinal(ctx, digest, &md5_size);
+ EVP_MD_CTX_free(ctx);
+#elif CRYPTO_INTERNAL
+ memset(&ctx, 0, sizeof(ctx));
+ MD5Init(&ctx);
+ MD5Update(&ctx, ospfh, length);
+ MD5Update(&ctx, auth_key, OSPF_AUTH_MD5_SIZE);
+ MD5Final(digest, &ctx);
+#endif
+
+ /* compare the two */
+ if (memcmp((caddr_t)ospfh + length, digest, OSPF_AUTH_MD5_SIZE)) {
+ flog_warn(EC_OSPF_AUTH,
+ "interface %s: ospf_check_md5 checksum mismatch, Router-ID: %pI4",
+ IF_NAME(oi), &ospfh->router_id);
+ return 0;
+ }
+
+ /* save neighbor's crypt_seqnum */
+ if (nbr)
+ nbr->crypt_seqnum = ospfh->u.crypt.crypt_seqnum;
+ return 1;
+}
+
+static int ospf_auth_make_md5_digest(struct ospf_interface *oi,
+ struct ospf_packet *op, struct key *key)
+{
+ void *ibuf;
+ struct ospf_header *ospfh;
+ unsigned char digest[OSPF_AUTH_MD5_SIZE];
+ uint16_t length;
+#ifdef CRYPTO_OPENSSL
+ EVP_MD_CTX *ctx;
+#elif CRYPTO_INTERNAL
+ MD5_CTX ctx;
+#endif
+ char auth_key[OSPF_AUTH_MD5_SIZE + 1];
+
+ memset(auth_key, 0, OSPF_AUTH_MD5_SIZE + 1);
+ strlcpy(auth_key, key->string, OSPF_AUTH_MD5_SIZE + 1);
+ ibuf = STREAM_DATA(op->s);
+ ospfh = (struct ospf_header *)ibuf;
+ length = ntohs(ospfh->length);
+ /* Generate a digest for the ospf packet - their digest + our digest. */
+#ifdef CRYPTO_OPENSSL
+ unsigned int md5_size = OSPF_AUTH_MD5_SIZE;
+
+ ctx = EVP_MD_CTX_new();
+ EVP_DigestInit(ctx, EVP_md5());
+ EVP_DigestUpdate(ctx, ospfh, length);
+ EVP_DigestUpdate(ctx, auth_key, OSPF_AUTH_MD5_SIZE);
+ EVP_DigestFinal(ctx, digest, &md5_size);
+ EVP_MD_CTX_free(ctx);
+#elif CRYPTO_INTERNAL
+ memset(&ctx, 0, sizeof(ctx));
+ MD5Init(&ctx);
+ MD5Update(&ctx, ospfh, length);
+ MD5Update(&ctx, auth_key, OSPF_AUTH_MD5_SIZE);
+ MD5Final(digest, &ctx);
+#endif
+
+ stream_put(op->s, digest, OSPF_AUTH_MD5_SIZE);
+
+ op->length = ntohs(ospfh->length) + OSPF_AUTH_MD5_SIZE;
+
+ if (stream_get_endp(op->s) != op->length)
+ /* XXX size_t */
+ flog_warn(EC_OSPF_AUTH,
+ "%s: length mismatch stream %lu ospf_packet %u, Router-ID %pI4",
+ __func__, (unsigned long)stream_get_endp(op->s),
+ op->length, &ospfh->router_id);
+
+ return OSPF_AUTH_MD5_SIZE;
+}
+
+static int ospf_auth_make_hmac_sha_digest(struct ospf_interface *oi,
+ struct ospf_packet *op,
+ struct key *key)
+{
+ void *ibuf;
+ struct ospf_header *ospfh;
+ unsigned char digest[KEYCHAIN_MAX_HASH_SIZE] = { 0 };
+ uint16_t hash_length = keychain_get_hash_len(key->hash_algo);
+
+ ibuf = STREAM_DATA(op->s);
+ ospfh = (struct ospf_header *)ibuf;
+#ifdef CRYPTO_OPENSSL
+ unsigned int openssl_hash_length = hash_length;
+ HMAC_CTX *ctx;
+ const EVP_MD *md_alg = ospf_auth_get_openssl_evp_md_from_key(key);
+
+ if (!md_alg) {
+ flog_warn(EC_OSPF_AUTH,
+ "interface %s: invalid HMAC algorithm, Router-ID: %pI4",
+ IF_NAME(oi), &ospfh->router_id);
+ return 0;
+ }
+#elif CRYPTO_INTERNAL
+ HMAC_SHA256_CTX ctx;
+
+ if (key->hash_algo != KEYCHAIN_ALGO_HMAC_SHA256) {
+ flog_warn(EC_OSPF_AUTH,
+ "interface %s: HMAC algorithm not supported, Router-ID: %pI4",
+ IF_NAME(oi), &ospfh->router_id);
+ return 0;
+ }
+#endif
+#ifdef CRYPTO_OPENSSL
+ ctx = HMAC_CTX_new();
+ HMAC_Init_ex(ctx, key->string, strlen(key->string), md_alg, NULL);
+ HMAC_Update(ctx, (const unsigned char *)ospfh, ntohs(ospfh->length));
+ HMAC_Update(ctx, (const unsigned char *)ospf_auth_apad, hash_length);
+ HMAC_Final(ctx, digest, &openssl_hash_length);
+ HMAC_CTX_free(ctx);
+#elif CRYPTO_INTERNAL
+ memset(&ctx, 0, sizeof(ctx));
+ HMAC__SHA256_Init(&ctx, key->string, strlen(key->string));
+ HMAC__SHA256_Update(&ctx, ospfh, ntohs(ospfh->length));
+ HMAC__SHA256_Update(&ctx, ospf_auth_apad, hash_length);
+ HMAC__SHA256_Final(digest, &ctx);
+#endif
+ stream_put(op->s, digest, hash_length);
+
+ op->length = ntohs(ospfh->length) + hash_length;
+
+ if (stream_get_endp(op->s) != op->length)
+ /* XXX size_t */
+ flog_warn(EC_OSPF_AUTH,
+ "%s: length mismatch stream %lu ospf_packet %u, Router-ID %pI4",
+ __func__, (unsigned long)stream_get_endp(op->s),
+ op->length, &ospfh->router_id);
+
+ return hash_length;
+}
+
+int ospf_auth_check_digest(struct ospf_interface *oi, struct ip *iph, struct ospf_header *ospfh)
+{
+ struct keychain *keychain = NULL;
+ struct key *key = NULL;
+ int key_id = ospfh->u.crypt.key_id;
+ uint8_t auth_data_len = ospfh->u.crypt.auth_data_len;
+
+ if (!OSPF_IF_PARAM(oi, keychain_name))
+ return ospf_auth_check_md5_digest(oi, ospfh, iph, NULL);
+
+ keychain = keychain_lookup(OSPF_IF_PARAM(oi, keychain_name));
+ if (!keychain) {
+ if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV))
+ flog_warn(EC_OSPF_AUTH,
+ "interface %s: Keychain %s is not available, Router-ID %pI4",
+ IF_NAME(oi), OSPF_IF_PARAM(oi, keychain_name),
+ &ospfh->router_id);
+ return 0;
+ }
+
+ key = key_lookup_for_accept(keychain, key_id);
+ if (!key) {
+ if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV))
+ flog_warn(EC_OSPF_AUTH,
+ "interface %s: Key ID %d not found in keychain %s, Router-ID %pI4",
+ IF_NAME(oi), key_id, keychain->name,
+ &ospfh->router_id);
+ return 0;
+ }
+
+ if (key->string == NULL || key->hash_algo == KEYCHAIN_ALGO_NULL) {
+ if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV))
+ flog_warn(EC_OSPF_AUTH,
+ "interface %s: Key ID %d in keychain %s is incomplete, Router-ID %pI4",
+ IF_NAME(oi), key_id, keychain->name,
+ &ospfh->router_id);
+ return 0;
+ }
+
+ if (keychain_get_hash_len(key->hash_algo) != auth_data_len) {
+ if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV))
+ flog_warn(EC_OSPF_AUTH,
+ "interface %s: Key ID %d in keychain %s hash length mismatch, Router-ID %pI4",
+ IF_NAME(oi), key_id, keychain->name,
+ &ospfh->router_id);
+ return 0;
+ }
+
+ /* Backward compatibility with RFC 2328 keyed-MD5 authentication */
+ if (key->hash_algo == KEYCHAIN_ALGO_MD5)
+ return ospf_auth_check_md5_digest(oi, ospfh, iph, key);
+
+ return ospf_auth_check_hmac_sha_digest(oi, ospfh, iph, key);
+}
+
+int ospf_auth_make_digest(struct ospf_interface *oi, struct ospf_packet *op)
+{
+ struct ospf_header *ospfh;
+ void *ibuf;
+ struct keychain *keychain = NULL;
+ struct key *key = NULL;
+ int key_id;
+
+ ibuf = STREAM_DATA(op->s);
+ ospfh = (struct ospf_header *)ibuf;
+
+ key_id = ospfh->u.crypt.key_id;
+
+ if (!OSPF_IF_PARAM(oi, keychain_name)) {
+ if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV))
+ flog_warn(EC_OSPF_AUTH,
+ "interface %s: Keychain is not set, Router-ID %pI4",
+ IF_NAME(oi), &ospfh->router_id);
+ return 0;
+ }
+
+ keychain = oi->keychain;
+ if (!keychain) {
+ if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, SEND))
+ flog_warn(EC_OSPF_AUTH,
+ "interface %s: Keychain %s is not available to send, Router-ID %pI4",
+ IF_NAME(oi), OSPF_IF_PARAM(oi, keychain_name),
+ &ospfh->router_id);
+ return 0;
+ }
+
+ key = oi->key;
+ if (!key) {
+ if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, SEND))
+ flog_warn(EC_OSPF_AUTH,
+ "interface %s: Key ID %d not found in keychain %s, Router-ID %pI4",
+ IF_NAME(oi), key_id, keychain->name,
+ &ospfh->router_id);
+ return 0;
+ }
+
+ if (key->string == NULL || key->hash_algo == KEYCHAIN_ALGO_NULL) {
+ if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, SEND))
+ flog_warn(EC_OSPF_AUTH,
+ "interface %s: Key ID %d in keychain %s is incomplete, Router-ID %pI4",
+ IF_NAME(oi), key_id, keychain->name,
+ &ospfh->router_id);
+ return 0;
+ }
+
+ /* Backward compatibility with RFC 2328 keyed-MD5 authentication */
+ if (key->hash_algo == KEYCHAIN_ALGO_MD5)
+ return ospf_auth_make_md5_digest(oi, op, key);
+ else
+ return ospf_auth_make_hmac_sha_digest(oi, op, key);
+}
+
+/* This function is called from ospf_write(), it will detect the
+ * authentication scheme and if it is MD5, it will change the sequence
+ * and update the MD5 digest.
+ */
+
+int ospf_auth_make(struct ospf_interface *oi, struct ospf_packet *op)
+{
+ struct ospf_header *ospfh;
+ unsigned char digest[OSPF_AUTH_MD5_SIZE] = {0};
+#ifdef CRYPTO_OPENSSL
+ EVP_MD_CTX *ctx;
+#elif CRYPTO_INTERNAL
+ MD5_CTX ctx;
+#endif
+ void *ibuf;
+ uint32_t t;
+ struct crypt_key *ck;
+ const uint8_t *auth_key = NULL;
+
+ ibuf = STREAM_DATA(op->s);
+ ospfh = (struct ospf_header *)ibuf;
+
+ if (ntohs(ospfh->auth_type) != OSPF_AUTH_CRYPTOGRAPHIC)
+ return 0;
+
+ /* We do this here so when we dup a packet, we don't have to
+ * waste CPU rewriting other headers.
+
+ Note that frr_time /deliberately/ is not used here.
+ */
+ t = (time(NULL) & 0xFFFFFFFF);
+ if (t > oi->crypt_seqnum)
+ oi->crypt_seqnum = t;
+ else
+ oi->crypt_seqnum++;
+
+ ospfh->u.crypt.crypt_seqnum = htonl(oi->crypt_seqnum);
+
+ /* Get MD5 Authentication key from auth_key list. */
+ if (list_isempty(OSPF_IF_PARAM(oi, auth_crypt)) && OSPF_IF_PARAM(oi, keychain_name) == NULL)
+ auth_key = (const uint8_t *)digest;
+ else if (!list_isempty(OSPF_IF_PARAM(oi, auth_crypt))) {
+ ck = listgetdata(listtail(OSPF_IF_PARAM(oi, auth_crypt)));
+ auth_key = ck->auth_key;
+ }
+
+ if (auth_key) {
+ /* Generate a digest for the entire packet + our secret key. */
+#ifdef CRYPTO_OPENSSL
+ unsigned int md5_size = OSPF_AUTH_MD5_SIZE;
+
+ ctx = EVP_MD_CTX_new();
+ EVP_DigestInit(ctx, EVP_md5());
+ EVP_DigestUpdate(ctx, ibuf, ntohs(ospfh->length));
+ EVP_DigestUpdate(ctx, auth_key, OSPF_AUTH_MD5_SIZE);
+ EVP_DigestFinal(ctx, digest, &md5_size);
+ EVP_MD_CTX_free(ctx);
+#elif CRYPTO_INTERNAL
+ memset(&ctx, 0, sizeof(ctx));
+ MD5Init(&ctx);
+ MD5Update(&ctx, ibuf, ntohs(ospfh->length));
+ MD5Update(&ctx, auth_key, OSPF_AUTH_MD5_SIZE);
+ MD5Final(digest, &ctx);
+#endif
+
+ /* Append md5 digest to the end of the stream. */
+ stream_put(op->s, digest, OSPF_AUTH_MD5_SIZE);
+
+ /* We do *NOT* increment the OSPF header length. */
+ op->length = ntohs(ospfh->length) + OSPF_AUTH_MD5_SIZE;
+
+ if (stream_get_endp(op->s) != op->length)
+ /* XXX size_t */
+ flog_warn(
+ EC_OSPF_AUTH,
+ "%s: length mismatch stream %lu ospf_packet %u, Router-ID %pI4",
+ __func__, (unsigned long)stream_get_endp(op->s),
+ op->length, &ospfh->router_id);
+
+ return OSPF_AUTH_MD5_SIZE;
+ } else
+ return ospf_auth_make_digest(oi, op);
+}
+
+/* Return 1, if the packet is properly authenticated and checksummed,
+ * 0 otherwise. In particular, check that AuType header field is valid and
+ * matches the locally configured AuType, and that D.5 requirements are met.
+ */
+int ospf_auth_check(struct ospf_interface *oi, struct ip *iph,
+ struct ospf_header *ospfh)
+{
+ uint16_t iface_auth_type;
+ uint16_t pkt_auth_type = ntohs(ospfh->auth_type);
+
+ iface_auth_type = ospf_auth_type(oi);
+
+ switch (pkt_auth_type) {
+ case OSPF_AUTH_NULL: /* RFC2328 D.5.1 */
+ if (iface_auth_type != OSPF_AUTH_NULL) {
+ if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV))
+ flog_warn(
+ EC_OSPF_PACKET,
+ "interface %s: auth-type mismatch, local %s, rcvd Null, Router-ID %pI4",
+ IF_NAME(oi),
+ lookup_msg(ospf_auth_type_str,
+ iface_auth_type, NULL),
+ &ospfh->router_id);
+ return 0;
+ }
+ if (!ospf_check_sum(ospfh)) {
+ if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV))
+ flog_warn(
+ EC_OSPF_PACKET,
+ "interface %s: Null auth OK, but checksum error, Router-ID %pI4",
+ IF_NAME(oi),
+ &ospfh->router_id);
+ return 0;
+ }
+ return 1;
+ case OSPF_AUTH_SIMPLE: /* RFC2328 D.5.2 */
+ if (iface_auth_type != OSPF_AUTH_SIMPLE) {
+ if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV))
+ flog_warn(
+ EC_OSPF_PACKET,
+ "interface %s: auth-type mismatch, local %s, rcvd Simple, Router-ID %pI4",
+ IF_NAME(oi),
+ lookup_msg(ospf_auth_type_str,
+ iface_auth_type, NULL),
+ &ospfh->router_id);
+ return 0;
+ }
+ if (memcmp(OSPF_IF_PARAM(oi, auth_simple), ospfh->u.auth_data,
+ OSPF_AUTH_SIMPLE_SIZE)) {
+ if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV))
+ flog_warn(
+ EC_OSPF_PACKET,
+ "interface %s: Simple auth failed, Router-ID %pI4",
+ IF_NAME(oi), &ospfh->router_id);
+ return 0;
+ }
+ if (!ospf_check_sum(ospfh)) {
+ if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV))
+ flog_warn(
+ EC_OSPF_PACKET,
+ "interface %s: Simple auth OK, checksum error, Router-ID %pI4",
+ IF_NAME(oi),
+ &ospfh->router_id);
+ return 0;
+ }
+ return 1;
+ case OSPF_AUTH_CRYPTOGRAPHIC: /* RFC2328 D.5.3 */
+ if (iface_auth_type != OSPF_AUTH_CRYPTOGRAPHIC) {
+ if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV))
+ flog_warn(
+ EC_OSPF_PACKET,
+ "interface %s: auth-type mismatch, local %s, rcvd Cryptographic, Router-ID %pI4",
+ IF_NAME(oi),
+ lookup_msg(ospf_auth_type_str,
+ iface_auth_type, NULL),
+ &ospfh->router_id);
+ return 0;
+ }
+ if (ospfh->checksum) {
+ if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV))
+ flog_warn(
+ EC_OSPF_PACKET,
+ "interface %s: OSPF header checksum is not 0, Router-ID %pI4",
+ IF_NAME(oi), &ospfh->router_id);
+ return 0;
+ }
+ /* If `authentication message-digest` key is not set, we try keychain crypto */
+ if (OSPF_IF_PARAM(oi, keychain_name) || !list_isempty(OSPF_IF_PARAM(oi, auth_crypt)))
+ return ospf_auth_check_digest(oi, iph, ospfh);
+ if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV))
+ flog_warn(
+ EC_OSPF_AUTH,
+ "interface %s: MD5 auth failed, Router-ID %pI4",
+ IF_NAME(oi), &ospfh->router_id);
+ return 0;
+ default:
+ if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV))
+ flog_warn(
+ EC_OSPF_PACKET,
+ "interface %s: invalid packet auth-type (%02x), Router-ID %pI4",
+ IF_NAME(oi), pkt_auth_type, &ospfh->router_id);
+ return 0;
+ }
+}
+
+/* OSPF authentication checking function */
+int ospf_auth_type(struct ospf_interface *oi)
+{
+ int auth_type;
+
+ if (OSPF_IF_PARAM(oi, auth_type) == OSPF_AUTH_NOTSET)
+ auth_type = oi->area->auth_type;
+ else
+ auth_type = OSPF_IF_PARAM(oi, auth_type);
+
+ /* Handle case where MD5 key list, or a key-chain, is not configured aka Cisco */
+ if (auth_type == OSPF_AUTH_CRYPTOGRAPHIC
+ && (list_isempty(OSPF_IF_PARAM(oi, auth_crypt))
+ && OSPF_IF_PARAM(oi, keychain_name) == NULL))
+ return OSPF_AUTH_NULL;
+
+ return auth_type;
+}
+
+/* Make Authentication Data. */
+int ospf_auth_make_data(struct ospf_interface *oi, struct ospf_header *ospfh)
+{
+ struct crypt_key *ck;
+
+ switch (ospf_auth_type(oi)) {
+ case OSPF_AUTH_NULL:
+ /* memset (ospfh->u.auth_data, 0, sizeof(ospfh->u.auth_data));
+ */
+ break;
+ case OSPF_AUTH_SIMPLE:
+ memcpy(ospfh->u.auth_data, OSPF_IF_PARAM(oi, auth_simple),
+ OSPF_AUTH_SIMPLE_SIZE);
+ break;
+ case OSPF_AUTH_CRYPTOGRAPHIC:
+ if (OSPF_IF_PARAM(oi, keychain_name)) {
+ oi->keychain = keychain_lookup(OSPF_IF_PARAM(oi, keychain_name));
+ if (oi->keychain)
+ oi->key = key_lookup_for_send(oi->keychain);
+ if (oi->key) {
+ ospfh->u.crypt.zero = 0;
+ ospfh->u.crypt.key_id = oi->key->index;
+ ospfh->u.crypt.auth_data_len = keychain_get_hash_len(oi->key->hash_algo);
+ } else {
+ /* If key is not set, then set 0. */
+ ospfh->u.crypt.zero = 0;
+ ospfh->u.crypt.key_id = 0;
+ ospfh->u.crypt.auth_data_len = OSPF_AUTH_MD5_SIZE;
+ }
+ } else {
+ /* If key is not set, then set 0. */
+ if (list_isempty(OSPF_IF_PARAM(oi, auth_crypt))) {
+ ospfh->u.crypt.zero = 0;
+ ospfh->u.crypt.key_id = 0;
+ ospfh->u.crypt.auth_data_len = OSPF_AUTH_MD5_SIZE;
+ } else {
+ ck = listgetdata(
+ listtail(OSPF_IF_PARAM(oi, auth_crypt)));
+ ospfh->u.crypt.zero = 0;
+ ospfh->u.crypt.key_id = ck->key_id;
+ ospfh->u.crypt.auth_data_len = OSPF_AUTH_MD5_SIZE;
+ }
+ }
+ /* note: the seq is done in ospf_auth_make() */
+ break;
+ default:
+ /* memset (ospfh->u.auth_data, 0, sizeof(ospfh->u.auth_data));
+ */
+ break;
+ }
+
+ return 0;
+}
diff --git a/ospfd/ospf_auth.h b/ospfd/ospf_auth.h
new file mode 100644
index 000000000..6f6d3db58
--- /dev/null
+++ b/ospfd/ospf_auth.h
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2023 Amnesh Inc.
+ * Mahdi Varasteh
+ */
+
+#ifndef _ZEBRA_OSPF_AUTH_H
+#define _ZEBRA_OSPF_AUTH_H
+
+#include <ospfd/ospf_gr.h>
+#include <ospfd/ospf_packet.h>
+
+int ospf_auth_check(struct ospf_interface *oi, struct ip *iph, struct ospf_header *ospfh);
+int ospf_auth_check_digest(struct ospf_interface *oi, struct ip *iph, struct ospf_header *ospfh);
+int ospf_auth_make(struct ospf_interface *oi, struct ospf_packet *op);
+int ospf_auth_make_digest(struct ospf_interface *oi, struct ospf_packet *op);
+int ospf_auth_type(struct ospf_interface *oi);
+int ospf_auth_make_data(struct ospf_interface *oi, struct ospf_header *ospfh);
+
+#endif /* _ZEBRA_OSPF_AUTH_H */
diff --git a/ospfd/ospf_errors.c b/ospfd/ospf_errors.c
index 16aa3ab52..cf58c6ad8 100644
--- a/ospfd/ospf_errors.c
+++ b/ospfd/ospf_errors.c
@@ -19,9 +19,9 @@ static struct log_ref ferr_ospf_warn[] = {
.suggestion = "Do not use this particular set command for an ospf route-map",
},
{
- .code = EC_OSPF_MD5,
- .title = "OSPF has noticed a MD5 issue",
- .description = "Something has gone wrong with the calculation of the MD5 data",
+ .code = EC_OSPF_AUTH,
+ .title = "OSPF has noticed an authentication issue",
+ .description = "Something has gone wrong with the calculation of the authentication data",
.suggestion = "Ensure your key is correct, gather log data from this side as well as peer and open an Issue",
},
{
diff --git a/ospfd/ospf_errors.h b/ospfd/ospf_errors.h
index dcf9a65ce..e63cb7409 100644
--- a/ospfd/ospf_errors.h
+++ b/ospfd/ospf_errors.h
@@ -22,7 +22,7 @@ enum ospf_log_refs {
EC_OSPF_INVALID_ALGORITHM,
EC_OSPF_FSM_INVALID_STATE,
EC_OSPF_SET_METRIC_PLUS,
- EC_OSPF_MD5,
+ EC_OSPF_AUTH,
EC_OSPF_PACKET,
EC_OSPF_LARGE_LSA,
EC_OSPF_LSA_UNEXPECTED,
diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c
index bdab672b4..760141932 100644
--- a/ospfd/ospf_interface.c
+++ b/ospfd/ospf_interface.c
@@ -549,6 +549,7 @@ static struct ospf_if_params *ospf_new_if_params(void)
UNSET_IF_PARAM(oip, auth_type);
UNSET_IF_PARAM(oip, if_area);
UNSET_IF_PARAM(oip, opaque_capable);
+ UNSET_IF_PARAM(oip, keychain_name);
oip->auth_crypt = list_new();
@@ -566,6 +567,7 @@ static void ospf_del_if_params(struct interface *ifp,
struct ospf_if_params *oip)
{
list_delete(&oip->auth_crypt);
+ XFREE(MTYPE_OSPF_IF_PARAMS, oip->keychain_name);
ospf_interface_disable_bfd(ifp, oip);
ldp_sync_info_free(&(oip->ldp_sync_info));
XFREE(MTYPE_OSPF_IF_PARAMS, oip);
@@ -601,6 +603,7 @@ void ospf_free_if_params(struct interface *ifp, struct in_addr addr)
!OSPF_IF_PARAM_CONFIGURED(oip, if_area) &&
!OSPF_IF_PARAM_CONFIGURED(oip, opaque_capable) &&
!OSPF_IF_PARAM_CONFIGURED(oip, prefix_suppression) &&
+ !OSPF_IF_PARAM_CONFIGURED(oip, keychain_name) &&
listcount(oip->auth_crypt) == 0) {
ospf_del_if_params(ifp, oip);
rn->info = NULL;
diff --git a/ospfd/ospf_interface.h b/ospfd/ospf_interface.h
index 47b70f803..e2290a881 100644
--- a/ospfd/ospf_interface.h
+++ b/ospfd/ospf_interface.h
@@ -10,6 +10,7 @@
#include "lib/bfd.h"
#include "qobj.h"
#include "hook.h"
+#include "keychain.h"
#include "ospfd/ospf_packet.h"
#include "ospfd/ospf_spf.h"
@@ -92,6 +93,8 @@ struct ospf_if_params {
auth_crypt); /* List of Auth cryptographic data. */
DECLARE_IF_PARAM(int, auth_type); /* OSPF authentication type */
+ DECLARE_IF_PARAM(char*, keychain_name); /* OSPF HMAC Cryptographic Authentication*/
+
/* Other, non-configuration state */
uint32_t network_lsa_seqnum; /* Network LSA seqnum */
@@ -279,6 +282,10 @@ struct ospf_interface {
uint32_t full_nbrs;
+ /* Buffered values for keychain and key */
+ struct keychain *keychain;
+ struct key *key;
+
QOBJ_FIELDS;
};
DECLARE_QOBJ_TYPE(ospf_interface);
diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c
index 536bd592d..dd000241f 100644
--- a/ospfd/ospf_main.c
+++ b/ospfd/ospf_main.c
@@ -27,6 +27,7 @@
#include "vrf.h"
#include "libfrr.h"
#include "routemap.h"
+#include "keychain.h"
#include "ospfd/ospfd.h"
#include "ospfd/ospf_interface.h"
@@ -218,6 +219,7 @@ int main(int argc, char **argv)
access_list_init();
prefix_list_init();
+ keychain_init();
/* Configuration processing callback initialization. */
cmd_init_config_callbacks(ospf_config_start, ospf_config_end);
diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c
index cfa0d5d57..b37efa3ef 100644
--- a/ospfd/ospf_packet.c
+++ b/ospfd/ospf_packet.c
@@ -41,6 +41,7 @@
#include "ospfd/ospf_errors.h"
#include "ospfd/ospf_zebra.h"
#include "ospfd/ospf_gr.h"
+#include "ospfd/ospf_auth.h"
/*
* OSPF Fragmentation / fragmented writes
@@ -99,27 +100,6 @@ static const uint16_t ospf_lsa_minlen[] = {
OSPF_OPAQUE_LSA_MIN_SIZE, /* OSPF_OPAQUE_AS_LSA */
};
-/* for ospf_check_auth() */
-static int ospf_check_sum(struct ospf_header *);
-
-/* OSPF authentication checking function */
-static int ospf_auth_type(struct ospf_interface *oi)
-{
- int auth_type;
-
- if (OSPF_IF_PARAM(oi, auth_type) == OSPF_AUTH_NOTSET)
- auth_type = oi->area->auth_type;
- else
- auth_type = OSPF_IF_PARAM(oi, auth_type);
-
- /* Handle case where MD5 key list is not configured aka Cisco */
- if (auth_type == OSPF_AUTH_CRYPTOGRAPHIC
- && list_isempty(OSPF_IF_PARAM(oi, auth_crypt)))
- return OSPF_AUTH_NULL;
-
- return auth_type;
-}
-
static struct ospf_packet *ospf_packet_new(size_t size)
{
struct ospf_packet *new;
@@ -258,8 +238,8 @@ static struct ospf_packet *ospf_packet_dup(struct ospf_packet *op)
"ospf_packet_dup stream %lu ospf_packet %u size mismatch",
(unsigned long)STREAM_SIZE(op->s), op->length);
- /* Reserve space for MD5 authentication that may be added later. */
- new = ospf_packet_new(stream_get_endp(op->s) + OSPF_AUTH_MD5_SIZE);
+ /* Reserve space for MD5/HMAC SHA authentication that may be added later. */
+ new = ospf_packet_new(stream_get_endp(op->s) + KEYCHAIN_MAX_HASH_SIZE);
stream_copy(new->s, op->s);
new->dst = op->dst;
@@ -274,7 +254,7 @@ static unsigned int ospf_packet_authspace(struct ospf_interface *oi)
int auth = 0;
if (ospf_auth_type(oi) == OSPF_AUTH_CRYPTOGRAPHIC)
- auth = OSPF_AUTH_MD5_SIZE;
+ auth = KEYCHAIN_MAX_HASH_SIZE;
return auth;
}
@@ -290,155 +270,6 @@ static unsigned int ospf_packet_max(struct ospf_interface *oi)
return max;
}
-
-static int ospf_check_md5_digest(struct ospf_interface *oi,
- struct ospf_header *ospfh)
-{
-#ifdef CRYPTO_OPENSSL
- EVP_MD_CTX *ctx;
-#elif CRYPTO_INTERNAL
- MD5_CTX ctx;
-#endif
- unsigned char digest[OSPF_AUTH_MD5_SIZE];
- struct crypt_key *ck;
- struct ospf_neighbor *nbr;
- uint16_t length = ntohs(ospfh->length);
-
- /* Get secret key. */
- ck = ospf_crypt_key_lookup(OSPF_IF_PARAM(oi, auth_crypt),
- ospfh->u.crypt.key_id);
- if (ck == NULL) {
- flog_warn(
- EC_OSPF_MD5,
- "interface %s: ospf_check_md5 no key %d, Router-ID: %pI4",
- IF_NAME(oi), ospfh->u.crypt.key_id, &ospfh->router_id);
- return 0;
- }
-
- /* check crypto seqnum. */
- nbr = ospf_nbr_lookup_by_routerid(oi->nbrs, &ospfh->router_id);
-
- if (nbr
- && ntohl(nbr->crypt_seqnum) > ntohl(ospfh->u.crypt.crypt_seqnum)) {
- flog_warn(
- EC_OSPF_MD5,
- "interface %s: ospf_check_md5 bad sequence %d (expect %d), Router-ID: %pI4",
- IF_NAME(oi), ntohl(ospfh->u.crypt.crypt_seqnum),
- ntohl(nbr->crypt_seqnum), &ospfh->router_id);
- return 0;
- }
-
- /* Generate a digest for the ospf packet - their digest + our digest. */
-#ifdef CRYPTO_OPENSSL
- unsigned int md5_size = OSPF_AUTH_MD5_SIZE;
- ctx = EVP_MD_CTX_new();
- EVP_DigestInit(ctx, EVP_md5());
- EVP_DigestUpdate(ctx, ospfh, length);
- EVP_DigestUpdate(ctx, ck->auth_key, OSPF_AUTH_MD5_SIZE);
- EVP_DigestFinal(ctx, digest, &md5_size);
- EVP_MD_CTX_free(ctx);
-#elif CRYPTO_INTERNAL
- memset(&ctx, 0, sizeof(ctx));
- MD5Init(&ctx);
- MD5Update(&ctx, ospfh, length);
- MD5Update(&ctx, ck->auth_key, OSPF_AUTH_MD5_SIZE);
- MD5Final(digest, &ctx);
-#endif
-
- /* compare the two */
- if (memcmp((caddr_t)ospfh + length, digest, OSPF_AUTH_MD5_SIZE)) {
- flog_warn(
- EC_OSPF_MD5,
- "interface %s: ospf_check_md5 checksum mismatch, Router-ID: %pI4",
- IF_NAME(oi), &ospfh->router_id);
- return 0;
- }
-
- /* save neighbor's crypt_seqnum */
- if (nbr)
- nbr->crypt_seqnum = ospfh->u.crypt.crypt_seqnum;
- return 1;
-}
-
-/* This function is called from ospf_write(), it will detect the
- authentication scheme and if it is MD5, it will change the sequence
- and update the MD5 digest. */
-static int ospf_make_md5_digest(struct ospf_interface *oi,
- struct ospf_packet *op)
-{
- struct ospf_header *ospfh;
- unsigned char digest[OSPF_AUTH_MD5_SIZE] = {0};
-#ifdef CRYPTO_OPENSSL
- EVP_MD_CTX *ctx;
-#elif CRYPTO_INTERNAL
- MD5_CTX ctx;
-#endif
- void *ibuf;
- uint32_t t;
- struct crypt_key *ck;
- const uint8_t *auth_key;
-
- ibuf = STREAM_DATA(op->s);
- ospfh = (struct ospf_header *)ibuf;
-
- if (ntohs(ospfh->auth_type) != OSPF_AUTH_CRYPTOGRAPHIC)
- return 0;
-
- /* We do this here so when we dup a packet, we don't have to
- waste CPU rewriting other headers.
-
- Note that frr_time /deliberately/ is not used here */
- t = (time(NULL) & 0xFFFFFFFF);
- if (t > oi->crypt_seqnum)
- oi->crypt_seqnum = t;
- else
- oi->crypt_seqnum++;
-
- ospfh->u.crypt.crypt_seqnum = htonl(oi->crypt_seqnum);
-
- /* Get MD5 Authentication key from auth_key list. */
- if (list_isempty(OSPF_IF_PARAM(oi, auth_crypt)))
- auth_key = (const uint8_t *)digest;
- else {
- ck = listgetdata(listtail(OSPF_IF_PARAM(oi, auth_crypt)));
- auth_key = ck->auth_key;
- }
-
- /* Generate a digest for the entire packet + our secret key. */
-#ifdef CRYPTO_OPENSSL
- unsigned int md5_size = OSPF_AUTH_MD5_SIZE;
- ctx = EVP_MD_CTX_new();
- EVP_DigestInit(ctx, EVP_md5());
- EVP_DigestUpdate(ctx, ibuf, ntohs(ospfh->length));
- EVP_DigestUpdate(ctx, auth_key, OSPF_AUTH_MD5_SIZE);
- EVP_DigestFinal(ctx, digest, &md5_size);
- EVP_MD_CTX_free(ctx);
-#elif CRYPTO_INTERNAL
- memset(&ctx, 0, sizeof(ctx));
- MD5Init(&ctx);
- MD5Update(&ctx, ibuf, ntohs(ospfh->length));
- MD5Update(&ctx, auth_key, OSPF_AUTH_MD5_SIZE);
- MD5Final(digest, &ctx);
-#endif
-
- /* Append md5 digest to the end of the stream. */
- stream_put(op->s, digest, OSPF_AUTH_MD5_SIZE);
-
- /* We do *NOT* increment the OSPF header length. */
- op->length = ntohs(ospfh->length) + OSPF_AUTH_MD5_SIZE;
-
- if (stream_get_endp(op->s) != op->length)
- /* XXX size_t */
- flog_warn(
- EC_OSPF_MD5,
- "%s: length mismatch stream %lu ospf_packet %u, Router-ID %pI4",
- __func__, (unsigned long)stream_get_endp(op->s),
- op->length, &ospfh->router_id);
-
- return OSPF_AUTH_MD5_SIZE;
-}
-
-
static void ospf_ls_req_timer(struct event *thread)
{
struct ospf_neighbor *nbr;
@@ -677,7 +508,7 @@ static void ospf_write(struct event *thread)
ospf_if_ipmulticast(fd, oi->address, oi->ifp->ifindex);
/* Rewrite the md5 signature & update the seq */
- ospf_make_md5_digest(oi, op);
+ ospf_auth_make(oi, op);
/* Retrieve OSPF packet type. */
stream_set_getp(op->s, 1);
@@ -2476,142 +2307,6 @@ static int ospf_check_network_mask(struct ospf_interface *oi,
return 0;
}
-/* Return 1, if the packet is properly authenticated and checksummed,
- 0 otherwise. In particular, check that AuType header field is valid and
- matches the locally configured AuType, and that D.5 requirements are met. */
-static int ospf_check_auth(struct ospf_interface *oi, struct ospf_header *ospfh)
-{
- struct crypt_key *ck;
- uint16_t iface_auth_type;
- uint16_t pkt_auth_type = ntohs(ospfh->auth_type);
-
- switch (pkt_auth_type) {
- case OSPF_AUTH_NULL: /* RFC2328 D.5.1 */
- if (OSPF_AUTH_NULL != (iface_auth_type = ospf_auth_type(oi))) {
- if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV))
- flog_warn(
- EC_OSPF_PACKET,
- "interface %s: auth-type mismatch, local %s, rcvd Null, Router-ID %pI4",
- IF_NAME(oi),
- lookup_msg(ospf_auth_type_str,
- iface_auth_type, NULL),
- &ospfh->router_id);
- return 0;
- }
- if (!ospf_check_sum(ospfh)) {
- if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV))
- flog_warn(
- EC_OSPF_PACKET,
- "interface %s: Null auth OK, but checksum error, Router-ID %pI4",
- IF_NAME(oi),
- &ospfh->router_id);
- return 0;
- }
- return 1;
- case OSPF_AUTH_SIMPLE: /* RFC2328 D.5.2 */
- if (OSPF_AUTH_SIMPLE
- != (iface_auth_type = ospf_auth_type(oi))) {
- if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV))
- flog_warn(
- EC_OSPF_PACKET,
- "interface %s: auth-type mismatch, local %s, rcvd Simple, Router-ID %pI4",
- IF_NAME(oi),
- lookup_msg(ospf_auth_type_str,
- iface_auth_type, NULL),
- &ospfh->router_id);
- return 0;
- }
- if (memcmp(OSPF_IF_PARAM(oi, auth_simple), ospfh->u.auth_data,
- OSPF_AUTH_SIMPLE_SIZE)) {
- if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV))
- flog_warn(
- EC_OSPF_PACKET,
- "interface %s: Simple auth failed, Router-ID %pI4",
- IF_NAME(oi), &ospfh->router_id);
- return 0;
- }
- if (!ospf_check_sum(ospfh)) {
- if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV))
- flog_warn(
- EC_OSPF_PACKET,
- "interface %s: Simple auth OK, checksum error, Router-ID %pI4",
- IF_NAME(oi),
- &ospfh->router_id);
- return 0;
- }
- return 1;
- case OSPF_AUTH_CRYPTOGRAPHIC: /* RFC2328 D.5.3 */
- if (OSPF_AUTH_CRYPTOGRAPHIC
- != (iface_auth_type = ospf_auth_type(oi))) {
- if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV))
- flog_warn(
- EC_OSPF_PACKET,
- "interface %s: auth-type mismatch, local %s, rcvd Cryptographic, Router-ID %pI4",
- IF_NAME(oi),
- lookup_msg(ospf_auth_type_str,
- iface_auth_type, NULL),
- &ospfh->router_id);
- return 0;
- }
- if (ospfh->checksum) {
- if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV))
- flog_warn(
- EC_OSPF_PACKET,
- "interface %s: OSPF header checksum is not 0, Router-ID %pI4",
- IF_NAME(oi), &ospfh->router_id);
- return 0;
- }
- /* only MD5 crypto method can pass ospf_packet_examin() */
- if (NULL == (ck = listgetdata(
- listtail(OSPF_IF_PARAM(oi, auth_crypt))))
- || ospfh->u.crypt.key_id != ck->key_id ||
- /* Condition above uses the last key ID on the list,
- which is
- different from what ospf_crypt_key_lookup() does. A
- bug? */
- !ospf_check_md5_digest(oi, ospfh)) {
- if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV))
- flog_warn(
- EC_OSPF_MD5,
- "interface %s: MD5 auth failed, Router-ID %pI4",
- IF_NAME(oi), &ospfh->router_id);
- return 0;
- }
- return 1;
- default:
- if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV))
- flog_warn(
- EC_OSPF_PACKET,
- "interface %s: invalid packet auth-type (%02x), Router-ID %pI4",
- IF_NAME(oi), pkt_auth_type, &ospfh->router_id);
- return 0;
- }
-}
-
-static int ospf_check_sum(struct ospf_header *ospfh)
-{
- uint32_t ret;
- uint16_t sum;
-
- /* clear auth_data for checksum. */
- memset(ospfh->u.auth_data, 0, OSPF_AUTH_SIMPLE_SIZE);
-
- /* keep checksum and clear. */
- sum = ospfh->checksum;
- memset(&ospfh->checksum, 0, sizeof(uint16_t));
-
- /* calculate checksum. */
- ret = in_cksum(ospfh, ntohs(ospfh->length));
-
- if (ret != sum) {
- zlog_info("%s: checksum mismatch, my %X, his %X", __func__, ret,
- sum);
- return 0;
- }
-
- return 1;
-}
-
/* Verify, that given link/TOS records are properly sized/aligned and match
Router-LSA "# links" and "# TOS" fields as specified in RFC2328 A.4.2. */
static unsigned ospf_router_lsa_links_examin(struct router_lsa_link *link,
@@ -2835,14 +2530,14 @@ static unsigned ospf_packet_examin(struct ospf_header *oh,
if (ntohs(oh->auth_type) != OSPF_AUTH_CRYPTOGRAPHIC)
bytesauth = 0;
else {
- if (oh->u.crypt.auth_data_len != OSPF_AUTH_MD5_SIZE) {
+ if (oh->u.crypt.auth_data_len > KEYCHAIN_MAX_HASH_SIZE) {
if (IS_DEBUG_OSPF_PACKET(0, RECV))
zlog_debug(
"%s: unsupported crypto auth length (%u B)",
__func__, oh->u.crypt.auth_data_len);
return MSG_NG;
}
- bytesauth = OSPF_AUTH_MD5_SIZE;
+ bytesauth = oh->u.crypt.auth_data_len;
}
if (bytesdeclared + bytesauth > bytesonwire) {
if (IS_DEBUG_OSPF_PACKET(0, RECV))
@@ -2953,7 +2648,7 @@ static int ospf_verify_header(struct stream *ibuf, struct ospf_interface *oi,
/* Check authentication. The function handles logging actions, where
* required. */
- if (!ospf_check_auth(oi, ospfh))
+ if (!ospf_auth_check(oi, iph, ospfh))
return -1;
return 0;
@@ -3261,44 +2956,6 @@ static void ospf_make_header(int type, struct ospf_interface *oi,
stream_forward_endp(s, OSPF_HEADER_SIZE);
}
-/* Make Authentication Data. */
-static int ospf_make_auth(struct ospf_interface *oi, struct ospf_header *ospfh)
-{
- struct crypt_key *ck;
-
- switch (ospf_auth_type(oi)) {
- case OSPF_AUTH_NULL:
- /* memset (ospfh->u.auth_data, 0, sizeof(ospfh->u.auth_data));
- */
- break;
- case OSPF_AUTH_SIMPLE:
- memcpy(ospfh->u.auth_data, OSPF_IF_PARAM(oi, auth_simple),
- OSPF_AUTH_SIMPLE_SIZE);
- break;
- case OSPF_AUTH_CRYPTOGRAPHIC:
- /* If key is not set, then set 0. */
- if (list_isempty(OSPF_IF_PARAM(oi, auth_crypt))) {
- ospfh->u.crypt.zero = 0;
- ospfh->u.crypt.key_id = 0;
- ospfh->u.crypt.auth_data_len = OSPF_AUTH_MD5_SIZE;
- } else {
- ck = listgetdata(
- listtail(OSPF_IF_PARAM(oi, auth_crypt)));
- ospfh->u.crypt.zero = 0;
- ospfh->u.crypt.key_id = ck->key_id;
- ospfh->u.crypt.auth_data_len = OSPF_AUTH_MD5_SIZE;
- }
- /* note: the seq is done in ospf_make_md5_digest() */
- break;
- default:
- /* memset (ospfh->u.auth_data, 0, sizeof(ospfh->u.auth_data));
- */
- break;
- }
-
- return 0;
-}
-
/* Fill rest of OSPF header. */
static void ospf_fill_header(struct ospf_interface *oi, struct stream *s,
uint16_t length)
@@ -3317,7 +2974,9 @@ static void ospf_fill_header(struct ospf_interface *oi, struct stream *s,
ospfh->checksum = 0;
/* Add Authentication Data. */
- ospf_make_auth(oi, ospfh);
+ oi->keychain = NULL;
+ oi->key = NULL;
+ ospf_auth_make_data(oi, ospfh);
}
static int ospf_make_hello(struct ospf_interface *oi, struct stream *s)
diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c
index 158e90df5..8c3ad7f37 100644
--- a/ospfd/ospf_vty.c
+++ b/ospfd/ospf_vty.c
@@ -21,6 +21,7 @@
#include <lib/json.h>
#include "defaults.h"
#include "lib/printfrr.h"
+#include "keychain.h"
#include "ospfd/ospfd.h"
#include "ospfd/ospf_asbr.h"
@@ -40,6 +41,7 @@
#include "ospfd/ospf_bfd.h"
#include "ospfd/ospf_ldp_sync.h"
#include "ospfd/ospf_network.h"
+#include "ospfd/ospf_memory.h"
FRR_CFG_DEFAULT_BOOL(OSPF_LOG_ADJACENCY_CHANGES,
{ .val_bool = true, .match_profile = "datacenter", },
@@ -808,6 +810,8 @@ struct ospf_vl_config_data {
char *auth_key; /* simple password if present */
int crypto_key_id; /* Cryptographic key ID */
char *md5_key; /* MD5 authentication key */
+ char *keychain; /* Cryptographic keychain */
+ int del_keychain;
int hello_interval; /* Obvious what these are... */
int retransmit_interval;
int transmit_delay;
@@ -890,6 +894,10 @@ static int ospf_vl_set_security(struct ospf_vl_data *vl_data,
strlcpy((char *)IF_DEF_PARAMS(ifp)->auth_simple,
vl_config->auth_key,
sizeof(IF_DEF_PARAMS(ifp)->auth_simple));
+ } else if (vl_config->keychain) {
+ SET_IF_PARAM(IF_DEF_PARAMS(ifp), keychain_name);
+ XFREE(MTYPE_OSPF_IF_PARAMS, IF_DEF_PARAMS(ifp)->keychain_name);
+ IF_DEF_PARAMS(ifp)->keychain_name = XSTRDUP(MTYPE_OSPF_IF_PARAMS, vl_config->keychain);
} else if (vl_config->md5_key) {
if (ospf_crypt_key_lookup(IF_DEF_PARAMS(ifp)->auth_crypt,
vl_config->crypto_key_id)
@@ -918,6 +926,9 @@ static int ospf_vl_set_security(struct ospf_vl_data *vl_data,
ospf_crypt_key_delete(IF_DEF_PARAMS(ifp)->auth_crypt,
vl_config->crypto_key_id);
+ } else if (vl_config->del_keychain) {
+ UNSET_IF_PARAM(IF_DEF_PARAMS(ifp), keychain_name);
+ XFREE(MTYPE_OSPF_IF_PARAMS, IF_DEF_PARAMS(ifp)->keychain_name);
}
return CMD_SUCCESS;
@@ -1022,9 +1033,11 @@ static int ospf_vl_set(struct ospf *ospf, struct ospf_vl_config_data *vl_config)
DEFUN (ospf_area_vlink,
ospf_area_vlink_cmd,
- "area <A.B.C.D|(0-4294967295)> virtual-link A.B.C.D [authentication [<message-digest|null>]] [<message-digest-key (1-255) md5 KEY|authentication-key AUTH_KEY>]",
+ "area <A.B.C.D|(0-4294967295)> virtual-link A.B.C.D [authentication [<key-chain KEYCHAIN_NAME|message-digest|null>]] [<message-digest-key (1-255) md5 KEY|authentication-key AUTH_KEY>]",
VLINK_HELPSTR_IPADDR
"Enable authentication on this virtual link\n"
+ "Use a key-chain for cryptographic authentication keys\n"
+ "Key-chain name\n"
"Use message-digest authentication\n"
"Use null authentication\n"
VLINK_HELPSTR_AUTH_MD5
@@ -1067,7 +1080,10 @@ DEFUN (ospf_area_vlink,
vl_config.auth_type = OSPF_AUTH_SIMPLE;
}
- if (argv_find(argv, argc, "message-digest", &idx)) {
+ if (argv_find(argv, argc, "key-chain", &idx)) {
+ vl_config.auth_type = OSPF_AUTH_CRYPTOGRAPHIC;
+ vl_config.keychain = argv[idx+1]->arg;
+ } else if (argv_find(argv, argc, "message-digest", &idx)) {
/* authentication message-digest */
vl_config.auth_type = OSPF_AUTH_CRYPTOGRAPHIC;
} else if (argv_find(argv, argc, "null", &idx)) {
@@ -1097,10 +1113,12 @@ DEFUN (ospf_area_vlink,
DEFUN (no_ospf_area_vlink,
no_ospf_area_vlink_cmd,
- "no area <A.B.C.D|(0-4294967295)> virtual-link A.B.C.D [authentication [<message-digest|null>]] [<message-digest-key (1-255) md5 KEY|authentication-key AUTH_KEY>]",
+ "no area <A.B.C.D|(0-4294967295)> virtual-link A.B.C.D [authentication [<key-chain KEYCHAIN_NAME|message-digest|null>]] [<message-digest-key (1-255) md5 KEY|authentication-key AUTH_KEY>]",
NO_STR
VLINK_HELPSTR_IPADDR
"Enable authentication on this virtual link\n"
+ "Use a key-chain for cryptographic authentication keys\n"
+ "Key-chain name\n"
"Use message-digest authentication\n"
"Use null authentication\n"
VLINK_HELPSTR_AUTH_MD5
@@ -1160,6 +1178,11 @@ DEFUN (no_ospf_area_vlink,
vl_config.auth_type = OSPF_AUTH_NOTSET;
}
+ if (argv_find(argv, argc, "key-chain", &idx)) {
+ vl_config.del_keychain = 1;
+ vl_config.keychain = NULL;
+ }
+
if (argv_find(argv, argc, "message-digest-key", &idx)) {
vl_config.md5_key = NULL;
vl_config.crypto_key_id = strtol(argv[idx + 1]->arg, NULL, 10);
@@ -3597,18 +3620,41 @@ static void ospf_interface_auth_show(struct vty *vty, struct ospf_interface *oi,
case OSPF_AUTH_CRYPTOGRAPHIC: {
struct crypt_key *ckey;
- if (list_isempty(OSPF_IF_PARAM(oi, auth_crypt)))
- return;
-
- ckey = listgetdata(listtail(OSPF_IF_PARAM(oi, auth_crypt)));
- if (ckey) {
+ if (OSPF_IF_PARAM(oi, keychain_name)) {
if (use_json) {
json_object_string_add(json, "authentication",
- "authenticationMessageDigest");
+ "authenticationKeyChain");
+ json_object_string_add(json, "keychain",
+ OSPF_IF_PARAM(oi, keychain_name));
} else {
vty_out(vty,
" Cryptographic authentication enabled\n");
- vty_out(vty, " Algorithm:MD5\n");
+ struct keychain *keychain = keychain_lookup(OSPF_IF_PARAM(oi, keychain_name));
+
+ if (keychain) {
+ struct key *key = key_lookup_for_send(keychain);
+
+ if (key) {
+ vty_out(vty, " Sending SA: Key %u, Algorithm %s - key chain %s\n",
+ key->index, keychain_get_algo_name_by_id(key->hash_algo),
+ OSPF_IF_PARAM(oi, keychain_name));
+ }
+ }
+ }
+ } else {
+ if (list_isempty(OSPF_IF_PARAM(oi, auth_crypt)))
+ return;
+
+ ckey = listgetdata(listtail(OSPF_IF_PARAM(oi, auth_crypt)));
+ if (ckey) {
+ if (use_json) {
+ json_object_string_add(json, "authentication",
+ "authenticationMessageDigest");
+ } else {
+ vty_out(vty,
+ " Cryptographic authentication enabled\n");
+ vty_out(vty, " Algorithm:MD5\n");
+ }
}
}
break;
@@ -7535,24 +7581,26 @@ DEFPY (show_ip_ospf_database,
DEFUN (ip_ospf_authentication_args,
ip_ospf_authentication_args_addr_cmd,
- "ip ospf authentication <null|message-digest> [A.B.C.D]",
+ "ip ospf authentication <null|message-digest|key-chain KEYCHAIN_NAME> [A.B.C.D]",
"IP Information\n"
"OSPF interface commands\n"
"Enable authentication on this interface\n"
"Use null authentication\n"
"Use message-digest authentication\n"
+ "Use a key-chain for cryptographic authentication keys\n"
+ "Key-chain name\n"
"Address of interface\n")
{
VTY_DECLVAR_CONTEXT(interface, ifp);
int idx_encryption = 3;
- int idx_ipv4 = 4;
+ int idx_ipv4 = argc-1;
struct in_addr addr;
int ret;
struct ospf_if_params *params;
params = IF_DEF_PARAMS(ifp);
- if (argc == 5) {
+ if (argv[idx_ipv4]->type == IPV4_TKN) {
ret = inet_aton(argv[idx_ipv4]->arg, &addr);
if (!ret) {
vty_out(vty,
@@ -7575,6 +7623,17 @@ DEFUN (ip_ospf_authentication_args,
if (argv[idx_encryption]->arg[0] == 'm') {
SET_IF_PARAM(params, auth_type);
params->auth_type = OSPF_AUTH_CRYPTOGRAPHIC;
+ UNSET_IF_PARAM(params, keychain_name);
+ XFREE(MTYPE_OSPF_IF_PARAMS, params->keychain_name);
+ return CMD_SUCCESS;
+ }
+
+ if (argv[idx_encryption]->arg[0] == 'k') {
+ SET_IF_PARAM(params, auth_type);
+ params->auth_type = OSPF_AUTH_CRYPTOGRAPHIC;
+ SET_IF_PARAM(params, keychain_name);
+ params->keychain_name = XSTRDUP(MTYPE_OSPF_IF_PARAMS, argv[idx_encryption+1]->arg);
+ UNSET_IF_PARAM(params, auth_crypt);
return CMD_SUCCESS;
}
@@ -7618,18 +7677,20 @@ DEFUN (ip_ospf_authentication,
DEFUN (no_ip_ospf_authentication_args,
no_ip_ospf_authentication_args_addr_cmd,
- "no ip ospf authentication <null|message-digest> [A.B.C.D]",
+ "no ip ospf authentication <null|message-digest|key-chain [KEYCHAIN_NAME]> [A.B.C.D]",
NO_STR
"IP Information\n"
"OSPF interface commands\n"
"Enable authentication on this interface\n"
"Use null authentication\n"
"Use message-digest authentication\n"
+ "Use a key-chain for cryptographic authentication keys\n"
+ "Key-chain name\n"
"Address of interface\n")
{
VTY_DECLVAR_CONTEXT(interface, ifp);
int idx_encryption = 4;
- int idx_ipv4 = 5;
+ int idx_ipv4 = argc-1;
struct in_addr addr;
int ret;
struct ospf_if_params *params;
@@ -7638,7 +7699,7 @@ DEFUN (no_ip_ospf_authentication_args,
params = IF_DEF_PARAMS(ifp);
- if (argc == 6) {
+ if (argv[idx_ipv4]->type == IPV4_TKN) {
ret = inet_aton(argv[idx_ipv4]->arg, &addr);
if (!ret) {
vty_out(vty,
@@ -7653,6 +7714,10 @@ DEFUN (no_ip_ospf_authentication_args,
}
params->auth_type = OSPF_AUTH_NOTSET;
UNSET_IF_PARAM(params, auth_type);
+
+ XFREE(MTYPE_OSPF_IF_PARAMS, params->keychain_name);
+ UNSET_IF_PARAM(params, keychain_name);
+
if (params != IF_DEF_PARAMS(ifp)) {
ospf_free_if_params(ifp, addr);
ospf_if_update_params(ifp, addr);
@@ -7660,7 +7725,8 @@ DEFUN (no_ip_ospf_authentication_args,
} else {
if (argv[idx_encryption]->arg[0] == 'n') {
auth_type = OSPF_AUTH_NULL;
- } else if (argv[idx_encryption]->arg[0] == 'm') {
+ } else if (argv[idx_encryption]->arg[0] == 'm' ||
+ argv[idx_encryption]->arg[0] == 'k') {
auth_type = OSPF_AUTH_CRYPTOGRAPHIC;
} else {
vty_out(vty, "Unexpected input encountered\n");
@@ -7676,6 +7742,8 @@ DEFUN (no_ip_ospf_authentication_args,
if (params->auth_type == auth_type) {
params->auth_type = OSPF_AUTH_NOTSET;
UNSET_IF_PARAM(params, auth_type);
+ XFREE(MTYPE_OSPF_IF_PARAMS, params->keychain_name);
+ UNSET_IF_PARAM(params, keychain_name);
}
for (rn = route_top(IF_OIFS_PARAMS(ifp)); rn;
@@ -7684,6 +7752,8 @@ DEFUN (no_ip_ospf_authentication_args,
if (params->auth_type == auth_type) {
params->auth_type = OSPF_AUTH_NOTSET;
UNSET_IF_PARAM(params, auth_type);
+ XFREE(MTYPE_OSPF_IF_PARAMS, params->keychain_name);
+ UNSET_IF_PARAM(params, keychain_name);
if (params != IF_DEF_PARAMS(ifp)) {
ospf_free_if_params(
ifp, rn->p.u.prefix4);
@@ -12098,11 +12168,11 @@ static const char *const ospf_int_type_str[] = {
"loopback"
};
-static const char *interface_config_auth_str(struct ospf_if_params *params)
+static int interface_config_auth_str(struct ospf_if_params *params, char *buf)
{
if (!OSPF_IF_PARAM_CONFIGURED(params, auth_type)
|| params->auth_type == OSPF_AUTH_NOTSET)
- return NULL;
+ return 0;
/* Translation tables are not that much help
* here due to syntax
@@ -12110,16 +12180,22 @@ static const char *interface_config_auth_str(struct ospf_if_params *params)
switch (params->auth_type) {
case OSPF_AUTH_NULL:
- return " null";
+ snprintf(buf, BUFSIZ, " null");
+ break;
case OSPF_AUTH_SIMPLE:
- return "";
+ snprintf(buf, BUFSIZ, " ");
+ break;
case OSPF_AUTH_CRYPTOGRAPHIC:
- return " message-digest";
+ if (OSPF_IF_PARAM_CONFIGURED(params, keychain_name))
+ snprintf(buf, BUFSIZ, " key-chain %s", params->keychain_name);
+ else
+ snprintf(buf, BUFSIZ, " message-digest");
+ break;
}
- return "";
+ return 1;
}
static int config_write_interface_one(struct vty *vty, struct vrf *vrf)
@@ -12129,7 +12205,8 @@ static int config_write_interface_one(struct vty *vty, struct vrf *vrf)
struct crypt_key *ck;
struct route_node *rn = NULL;
struct ospf_if_params *params;
- const char *auth_str;
+ char buf[BUFSIZ];
+ int ret = 0;
int write = 0;
FOR_ALL_INTERFACES (vrf, ifp) {
@@ -12170,10 +12247,10 @@ static int config_write_interface_one(struct vty *vty, struct vrf *vrf)
}
/* OSPF interface authentication print */
- auth_str = interface_config_auth_str(params);
- if (auth_str) {
+ ret = interface_config_auth_str(params, buf);
+ if (ret) {
vty_out(vty, " ip ospf authentication%s",
- auth_str);
+ buf);
if (params != IF_DEF_PARAMS(ifp) && rn)
vty_out(vty, " %pI4",
&rn->p.u.prefix4);
@@ -12613,8 +12690,9 @@ static int config_write_virtual_link(struct vty *vty, struct ospf *ospf)
{
struct listnode *node;
struct ospf_vl_data *vl_data;
- const char *auth_str;
char buf[INET_ADDRSTRLEN];
+ char buf2[BUFSIZ];
+ int ret = 0;
/* Virtual-Link print */
for (ALL_LIST_ELEMENTS_RO(ospf->vlinks, node, vl_data)) {
@@ -12647,12 +12725,12 @@ static int config_write_virtual_link(struct vty *vty, struct ospf *ospf)
vty_out(vty, " area %s virtual-link %pI4\n", buf,
&vl_data->vl_peer);
/* Auth type */
- auth_str = interface_config_auth_str(
- IF_DEF_PARAMS(oi->ifp));
- if (auth_str)
+ ret = interface_config_auth_str(
+ IF_DEF_PARAMS(oi->ifp), buf2);
+ if (ret)
vty_out(vty,
" area %s virtual-link %pI4 authentication%s\n",
- buf, &vl_data->vl_peer, auth_str);
+ buf, &vl_data->vl_peer, buf2);
/* Auth key */
if (IF_DEF_PARAMS(vl_data->vl_oi->ifp)->auth_simple[0]
!= '\0')
diff --git a/ospfd/subdir.am b/ospfd/subdir.am
index 44ee3b0f1..4803aae68 100644
--- a/ospfd/subdir.am
+++ b/ospfd/subdir.am
@@ -56,6 +56,7 @@ ospfd_libfrrospf_a_SOURCES = \
ospfd/ospf_zebra.c \
ospfd/ospfd.c \
ospfd/ospf_gr_helper.c \
+ ospfd/ospf_auth.c \
# end
if OSPFD
@@ -106,6 +107,7 @@ noinst_HEADERS += \
ospfd/ospf_te.h \
ospfd/ospf_vty.h \
ospfd/ospf_zebra.h \
+ ospfd/ospf_auth.h \
# end
ospfd_ospfd_LDADD = ospfd/libfrrospf.a ospfd/libfrrospfclient.a lib/libfrr.la $(LIBCAP) $(LIBM)
diff --git a/python/xref2vtysh.py b/python/xref2vtysh.py
index 6dd5c8866..0a7e28ec7 100644
--- a/python/xref2vtysh.py
+++ b/python/xref2vtysh.py
@@ -36,7 +36,7 @@ daemon_flags = {
"lib/filter.c": "VTYSH_ACL",
"lib/filter_cli.c": "VTYSH_ACL",
"lib/if.c": "VTYSH_INTERFACE",
- "lib/keychain.c": "VTYSH_RIPD|VTYSH_EIGRPD|VTYSH_OSPF6D",
+ "lib/keychain.c": "VTYSH_KEYS",
"lib/mgmt_be_client.c": "VTYSH_STATICD",
"lib/mgmt_fe_client.c": "VTYSH_MGMTD",
"lib/lib_vty.c": "VTYSH_ALL",
diff --git a/tests/topotests/lib/ospf.py b/tests/topotests/lib/ospf.py
index 5486e904d..5b18f8b67 100644
--- a/tests/topotests/lib/ospf.py
+++ b/tests/topotests/lib/ospf.py
@@ -426,6 +426,10 @@ def config_ospf_interface(
cmd = "ip ospf authentication null"
elif data_ospf_auth == "message-digest":
cmd = "ip ospf authentication message-digest"
+ elif data_ospf_auth == "key-chain":
+ cmd = "ip ospf authentication key-chain {}".format(
+ ospf_data["keychain"]
+ )
else:
cmd = "ip ospf authentication"
diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py b/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py
index 88219b840..8dd103013 100644
--- a/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py
@@ -64,7 +64,9 @@ TOPOOLOGY =
TESTCASES =
1. Verify ospf authentication with Simple password authentication.
2. Verify ospf authentication with MD5 authentication.
-3. Verify ospf authentication with different authentication methods.
+3. Verify ospf authentication with MD5 keychain authentication.
+4. Verify ospf authentication with SHA256 keychain authentication.
+5. Verify ospf authentication with different authentication methods.
"""
@@ -535,7 +537,477 @@ def test_ospf_authentication_md5_tc29_p1(request):
write_test_footer(tc_name)
-def test_ospf_authentication_different_auths_tc30_p1(request):
+def test_ospf_authentication_md5_keychain_tc30_p1(request):
+ """
+ OSPF Authentication - Verify ospf authentication with MD5 authentication.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config.")
+ reset_config_on_routers(tgen)
+ step(
+ "Configure ospf with on R1 and R2, enable ospf on R1 interface "
+ "connected to R2 with message-digest authentication using ip "
+ "ospf authentication key-chain cmd."
+ )
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+
+ router1.vtysh_cmd(
+ """configure terminal
+ key chain auth
+ key 10
+ key-string ospf
+ cryptographic-algorithm md5"""
+ )
+
+ r1_ospf_auth = {
+ "r1": {
+ "links": {
+ "r2": {
+ "ospf": {
+ "authentication": "key-chain",
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r1_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that the neighbour is not FULL between R1 and R2.")
+ # wait for dead time expiry.
+ sleep(6)
+ dut = "r1"
+ ospf_covergence = verify_ospf_neighbor(
+ tgen, topo, dut=dut, expected=False, retry_timeout=6
+ )
+ assert ospf_covergence is not True, "Testcase Failed \n Error {}".format(
+ ospf_covergence
+ )
+
+ step(
+ "On R2 enable ospf on interface with message-digest authentication"
+ " using ip ospf authentication message-digest password cmd."
+ )
+
+ router2.vtysh_cmd(
+ """configure terminal
+ key chain auth
+ key 10
+ key-string ospf
+ cryptographic-algorithm md5"""
+ )
+
+ r2_ospf_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf": {
+ "authentication": "key-chain",
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r2_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 "
+ "using show ip ospf neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
+ ospf_covergence
+ )
+
+ step(
+ "Disable message-digest authentication on R2 using no ip ospf "
+ "authentication key-chain cmd."
+ )
+
+ r2_ospf_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf": {
+ "authentication": "key-chain",
+ "keychain": "auth",
+ "del_action": True,
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r2_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify on R1 ,nbr is deleted for R2 after dead interval expiry")
+ # wait till the dead timer expiry
+ sleep(6)
+ dut = "r2"
+ ospf_covergence = verify_ospf_neighbor(
+ tgen, topo, dut=dut, expected=False, retry_timeout=10
+ )
+ assert ospf_covergence is not True, "Testcase Failed \n Error {}".format(
+ ospf_covergence
+ )
+
+ step("Again On R2 enable ospf on interface with key-chain auth")
+ r2_ospf_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf": {
+ "authentication": "key-chain",
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r2_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 using"
+ " show ip ospf neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
+ ospf_covergence
+ )
+
+ step("Shut no shut interface on R1")
+ dut = "r1"
+ intf = topo["routers"]["r1"]["links"]["r2"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ dut = "r2"
+ step(
+ "Verify that the neighbour is not FULL between R1 and R2 using "
+ "show ip ospf neighbor cmd."
+ )
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False)
+ assert ospf_covergence is not True, "Testcase Failed \n Error {}".format(
+ ospf_covergence
+ )
+
+ dut = "r1"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 using "
+ "show ip ospf neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
+ ospf_covergence
+ )
+
+ step("Change Ip address on R1 and R2")
+
+ topo_modify_change_ip = deepcopy(topo)
+
+ intf_ip = topo_modify_change_ip["routers"]["r1"]["links"]["r2"]["ipv4"]
+
+ topo_modify_change_ip["routers"]["r1"]["links"]["r2"]["ipv4"] = str(
+ IPv4Address(frr_unicode(intf_ip.split("/")[0])) + 3
+ ) + "/{}".format(intf_ip.split("/")[1])
+
+ build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False)
+
+ reset_config_on_routers(tgen, routerName="r1")
+ dut = "r1"
+ intf = topo["routers"]["r1"]["links"]["r2"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+ shutdown_bringup_interface(tgen, dut, intf, True)
+ clear_ospf(tgen, "r1")
+ router1.vtysh_cmd(
+ """configure terminal
+ key chain auth
+ key 10
+ key-string ospf
+ cryptographic-algorithm md5"""
+ )
+ r1_ospf_auth = {
+ "r1": {
+ "links": {
+ "r2": {
+ "ospf": {
+ "authentication": "key-chain",
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r1_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 with new "
+ "ip address using show ip ospf "
+ )
+
+ dut = "r1"
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
+ ospf_covergence
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_authentication_sha256_keychain_tc32_p1(request):
+ """
+ OSPF Authentication - Verify ospf authentication with MD5 authentication.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config.")
+ reset_config_on_routers(tgen)
+ step(
+ "Configure ospf with on R1 and R2, enable ospf on R1 interface "
+ "connected to R2 with message-digest authentication using ip "
+ "ospf authentication key-chain cmd."
+ )
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+
+ router1.vtysh_cmd(
+ """configure terminal
+ key chain auth
+ key 10
+ key-string ospf
+ cryptographic-algorithm hmac-sha-256"""
+ )
+
+ r1_ospf_auth = {
+ "r1": {
+ "links": {
+ "r2": {
+ "ospf": {
+ "authentication": "key-chain",
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r1_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that the neighbour is not FULL between R1 and R2.")
+ # wait for dead time expiry.
+ sleep(6)
+ dut = "r1"
+ ospf_covergence = verify_ospf_neighbor(
+ tgen, topo, dut=dut, expected=False, retry_timeout=6
+ )
+ assert ospf_covergence is not True, "Testcase Failed \n Error {}".format(
+ ospf_covergence
+ )
+
+ step(
+ "On R2 enable ospf on interface with message-digest authentication"
+ " using ip ospf authentication message-digest password cmd."
+ )
+
+ router2.vtysh_cmd(
+ """configure terminal
+ key chain auth
+ key 10
+ key-string ospf
+ cryptographic-algorithm hmac-sha-256"""
+ )
+
+ r2_ospf_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf": {
+ "authentication": "key-chain",
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r2_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 "
+ "using show ip ospf neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
+ ospf_covergence
+ )
+
+ step(
+ "Disable message-digest authentication on R2 using no ip ospf "
+ "authentication key-chain cmd."
+ )
+
+ r2_ospf_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf": {
+ "authentication": "key-chain",
+ "keychain": "auth",
+ "del_action": True,
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r2_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify on R1 ,nbr is deleted for R2 after dead interval expiry")
+ # wait till the dead timer expiry
+ sleep(6)
+ dut = "r2"
+ ospf_covergence = verify_ospf_neighbor(
+ tgen, topo, dut=dut, expected=False, retry_timeout=10
+ )
+ assert ospf_covergence is not True, "Testcase Failed \n Error {}".format(
+ ospf_covergence
+ )
+
+ step("Again On R2 enable ospf on interface with key-chain auth")
+ r2_ospf_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf": {
+ "authentication": "key-chain",
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r2_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 using"
+ " show ip ospf neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
+ ospf_covergence
+ )
+
+ step("Shut no shut interface on R1")
+ dut = "r1"
+ intf = topo["routers"]["r1"]["links"]["r2"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ dut = "r2"
+ step(
+ "Verify that the neighbour is not FULL between R1 and R2 using "
+ "show ip ospf neighbor cmd."
+ )
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False)
+ assert ospf_covergence is not True, "Testcase Failed \n Error {}".format(
+ ospf_covergence
+ )
+
+ dut = "r1"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 using "
+ "show ip ospf neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
+ ospf_covergence
+ )
+
+ step("Change Ip address on R1 and R2")
+
+ topo_modify_change_ip = deepcopy(topo)
+
+ intf_ip = topo_modify_change_ip["routers"]["r1"]["links"]["r2"]["ipv4"]
+
+ topo_modify_change_ip["routers"]["r1"]["links"]["r2"]["ipv4"] = str(
+ IPv4Address(frr_unicode(intf_ip.split("/")[0])) + 3
+ ) + "/{}".format(intf_ip.split("/")[1])
+
+ build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False)
+
+ reset_config_on_routers(tgen, routerName="r1")
+ dut = "r1"
+ intf = topo["routers"]["r1"]["links"]["r2"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+ shutdown_bringup_interface(tgen, dut, intf, True)
+ clear_ospf(tgen, "r1")
+ router1.vtysh_cmd(
+ """configure terminal
+ key chain auth
+ key 10
+ key-string ospf
+ cryptographic-algorithm hmac-sha-256"""
+ )
+ r1_ospf_auth = {
+ "r1": {
+ "links": {
+ "r2": {
+ "ospf": {
+ "authentication": "key-chain",
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r1_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 with new "
+ "ip address using show ip ospf "
+ )
+
+ dut = "r1"
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
+ ospf_covergence
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_authentication_different_auths_tc35_p1(request):
"""
OSPF Authentication - Verify ospf authentication with different
authentication methods.
@@ -553,6 +1025,9 @@ def test_ospf_authentication_different_auths_tc30_p1(request):
"ospf authentication message-digest cmd."
)
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+
r1_ospf_auth = {
"r1": {
"links": {
@@ -769,16 +1244,23 @@ def test_ospf_authentication_different_auths_tc30_p1(request):
ospf_covergence
)
- step("Enable Md5 authentication on the interface")
+ step("Enable SHA-256 authentication on the interface")
+
+ router1.vtysh_cmd(
+ """configure terminal
+ key chain auth
+ key 10
+ key-string ospf
+ cryptographic-algorithm hmac-sha-256"""
+ )
r1_ospf_auth = {
"r1": {
"links": {
"r2": {
"ospf": {
- "authentication": "message-digest",
- "authentication-key": "ospf",
- "message-digest-key": "10",
+ "authentication": "key-chain",
+ "keychain": "auth",
}
}
}
@@ -787,14 +1269,21 @@ def test_ospf_authentication_different_auths_tc30_p1(request):
result = config_ospf_interface(tgen, topo, r1_ospf_auth)
assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+ router2.vtysh_cmd(
+ """configure terminal
+ key chain auth
+ key 10
+ key-string ospf
+ cryptographic-algorithm hmac-sha-256"""
+ )
+
r2_ospf_auth = {
"r2": {
"links": {
"r1": {
"ospf": {
- "authentication": "message-digest",
- "authentication-key": "ospf",
- "message-digest-key": "10",
+ "authentication": "key-chain",
+ "keychain": "auth",
}
}
}
@@ -814,39 +1303,27 @@ def test_ospf_authentication_different_auths_tc30_p1(request):
ospf_covergence
)
- step("Change the MD5 authentication password")
+ step("Change the SHA-256 authentication password")
- r1_ospf_auth = {
- "r1": {
- "links": {
- "r2": {
- "ospf": {
- "authentication": "message-digest",
- "authentication-key": "OSPFv4",
- "message-digest-key": "10",
- }
- }
- }
- }
- }
- result = config_ospf_interface(tgen, topo, r1_ospf_auth)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+ router1.vtysh_cmd(
+ """configure terminal
+ key chain auth
+ key 10
+ key-string OSPFv4
+ cryptographic-algorithm hmac-sha-512"""
+ )
- r2_ospf_auth = {
- "r2": {
- "links": {
- "r1": {
- "ospf": {
- "authentication": "message-digest",
- "authentication-key": "OSPFv4",
- "message-digest-key": "10",
- }
- }
- }
- }
- }
- result = config_ospf_interface(tgen, topo, r2_ospf_auth)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+ router2.vtysh_cmd(
+ """configure terminal
+ key chain auth
+ key 10
+ key-string OSPFv4
+ cryptographic-algorithm hmac-sha-512"""
+ )
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "Testcase Failed \n Error {}".format(
+ ospf_covergence
+ )
write_test_footer(tc_name)
diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h
index 9d67003f3..65733ca61 100644
--- a/vtysh/vtysh.h
+++ b/vtysh/vtysh.h
@@ -59,7 +59,7 @@ extern struct event_loop *master;
VTYSH_VRRPD
#define VTYSH_INTERFACE VTYSH_INTERFACE_SUBSET | VTYSH_BGPD
#define VTYSH_VRF VTYSH_INTERFACE_SUBSET | VTYSH_MGMTD
-#define VTYSH_KEYS VTYSH_RIPD | VTYSH_EIGRPD | VTYSH_OSPF6D
+#define VTYSH_KEYS VTYSH_RIPD | VTYSH_EIGRPD | VTYSH_OSPF6D | VTYSH_OSPFD
/* Daemons who can process nexthop-group configs */
#define VTYSH_NH_GROUP VTYSH_PBRD|VTYSH_SHARPD
#define VTYSH_SR VTYSH_ZEBRA|VTYSH_PATHD