mirror of
https://github.com/FRRouting/frr.git
synced 2025-04-30 13:37:17 +02:00
[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>
This commit is contained in:
parent
edd243280c
commit
f5011cd5dd
|
@ -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)
|
||||
|
||||
|
||||
|
|
705
ospfd/ospf_auth.c
Normal file
705
ospfd/ospf_auth.c
Normal file
|
@ -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;
|
||||
}
|
20
ospfd/ospf_auth.h
Normal file
20
ospfd/ospf_auth.h
Normal file
|
@ -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 */
|
|
@ -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",
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
142
ospfd/ospf_vty.c
142
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')
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue