[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:
Mahdi Varasteh 2023-09-12 15:09:44 +03:30
parent edd243280c
commit f5011cd5dd
15 changed files with 1419 additions and 430 deletions

View file

@ -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
View 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
View 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 */

View file

@ -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",
},
{

View file

@ -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,

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -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)

View file

@ -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,6 +3620,28 @@ static void ospf_interface_auth_show(struct vty *vty, struct ospf_interface *oi,
case OSPF_AUTH_CRYPTOGRAPHIC: {
struct crypt_key *ckey;
if (OSPF_IF_PARAM(oi, keychain_name)) {
if (use_json) {
json_object_string_add(json, "authentication",
"authenticationKeyChain");
json_object_string_add(json, "keychain",
OSPF_IF_PARAM(oi, keychain_name));
} else {
vty_out(vty,
" Cryptographic authentication enabled\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;
@ -3611,6 +3656,7 @@ static void ospf_interface_auth_show(struct vty *vty, struct ospf_interface *oi,
vty_out(vty, " Algorithm:MD5\n");
}
}
}
break;
}
default:
@ -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')

View file

@ -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)

View file

@ -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",

View file

@ -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"

View file

@ -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)

View file

@ -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