diff options
31 files changed, 1863 insertions, 499 deletions
diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c index da70f67c18..6ee5b5dc5c 100644 --- a/bgpd/bgp_open.c +++ b/bgpd/bgp_open.c @@ -576,18 +576,6 @@ static int bgp_capability_restart(struct peer *peer, static int bgp_capability_llgr(struct peer *peer, struct capability_header *caphdr) { -/* - * +--------------------------------------------------+ - * | Address Family Identifier (16 bits) | - * +--------------------------------------------------+ - * | Subsequent Address Family Identifier (8 bits) | - * +--------------------------------------------------+ - * | Flags for Address Family (8 bits) | - * +--------------------------------------------------+ - * | Long-lived Stale Time (24 bits) | - * +--------------------------------------------------+ - */ -#define BGP_CAP_LLGR_MIN_PACKET_LEN 7 struct stream *s = BGP_INPUT(peer); size_t end = stream_get_getp(s) + caphdr->length; diff --git a/bgpd/bgp_open.h b/bgpd/bgp_open.h index 20f5fdb22b..a92c56d1b5 100644 --- a/bgpd/bgp_open.h +++ b/bgpd/bgp_open.h @@ -20,11 +20,24 @@ struct capability_mp_data { }; struct graceful_restart_af { - afi_t afi; - safi_t safi; + uint16_t afi; + uint8_t safi; uint8_t flag; }; +/* + * +--------------------------------------------------+ + * | Address Family Identifier (16 bits) | + * +--------------------------------------------------+ + * | Subsequent Address Family Identifier (8 bits) | + * +--------------------------------------------------+ + * | Flags for Address Family (8 bits) | + * +--------------------------------------------------+ + * | Long-lived Stale Time (24 bits) | + * +--------------------------------------------------+ + */ +#define BGP_CAP_LLGR_MIN_PACKET_LEN 7 + /* Capability Code */ #define CAPABILITY_CODE_MP 1 /* Multiprotocol Extensions */ #define CAPABILITY_CODE_REFRESH 2 /* Route Refresh Capability */ diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 6ae418b98e..851432d562 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -1211,6 +1211,8 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi, unsigned long cap_len; uint16_t len; uint32_t gr_restart_time; + const char *capability = lookup_msg(capcode_str, capability_code, + "Unknown"); if (!peer_established(peer->connection)) return; @@ -1254,12 +1256,12 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi, stream_putc_at(s, cap_len, len); if (bgp_debug_neighbor_events(peer)) - zlog_debug("%pBP sending CAPABILITY has %s Software Version for afi/safi: %s/%s", + zlog_debug("%pBP sending CAPABILITY has %s %s for afi/safi: %s/%s", peer, action == CAPABILITY_ACTION_SET ? "Advertising" : "Removing", - iana_afi2str(pkt_afi), + capability, iana_afi2str(pkt_afi), iana_safi2str(pkt_safi)); break; case CAPABILITY_CODE_MP: @@ -1271,12 +1273,13 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi, stream_putc(s, pkt_safi); if (bgp_debug_neighbor_events(peer)) - zlog_debug( - "%pBP sending CAPABILITY has %s MP_EXT CAP for afi/safi: %s/%s", - peer, - action == CAPABILITY_ACTION_SET ? "Advertising" - : "Removing", - iana_afi2str(pkt_afi), iana_safi2str(pkt_safi)); + zlog_debug("%pBP sending CAPABILITY has %s %s for afi/safi: %s/%s", + peer, + action == CAPABILITY_ACTION_SET + ? "Advertising" + : "Removing", + capability, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); break; case CAPABILITY_CODE_RESTART: if (!CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART) && @@ -1320,27 +1323,63 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi, } } - /* Software Version capability Len. */ len = stream_get_endp(s) - cap_len - 1; stream_putc_at(s, cap_len, len); if (bgp_debug_neighbor_events(peer)) - zlog_debug("%pBP sending CAPABILITY has %s Graceful-Restart CAP for afi/safi: %s/%s", + zlog_debug("%pBP sending CAPABILITY has %s %s for afi/safi: %s/%s", peer, action == CAPABILITY_ACTION_SET ? "Advertising" : "Removing", - iana_afi2str(pkt_afi), + capability, iana_afi2str(pkt_afi), iana_safi2str(pkt_safi)); break; + case CAPABILITY_CODE_LLGR: + if (!CHECK_FLAG(peer->cap, PEER_CAP_RESTART_ADV)) + return; + + SET_FLAG(peer->cap, PEER_CAP_LLGR_ADV); + + stream_putc(s, action); + stream_putc(s, CAPABILITY_CODE_LLGR); + cap_len = stream_get_endp(s); + stream_putc(s, 0); + + FOREACH_AFI_SAFI (afi, safi) { + if (!peer->afc[afi][safi]) + continue; + + bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, + &pkt_safi); + + stream_putw(s, pkt_afi); + stream_putc(s, pkt_safi); + stream_putc(s, LLGR_F_BIT); + stream_put3(s, peer->bgp->llgr_stale_time); + + SET_FLAG(peer->af_cap[afi][safi], PEER_CAP_LLGR_AF_ADV); + } + + len = stream_get_endp(s) - cap_len - 1; + stream_putc_at(s, cap_len, len); + + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%pBP sending CAPABILITY has %s %s for afi/safi: %s/%s", + peer, + action == CAPABILITY_ACTION_SET + ? "Advertising" + : "Removing", + capability, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); + break; case CAPABILITY_CODE_REFRESH: case CAPABILITY_CODE_ORF: case CAPABILITY_CODE_AS4: case CAPABILITY_CODE_DYNAMIC: case CAPABILITY_CODE_ADDPATH: case CAPABILITY_CODE_ENHANCED_RR: - case CAPABILITY_CODE_LLGR: case CAPABILITY_CODE_FQDN: case CAPABILITY_CODE_ENHE: case CAPABILITY_CODE_EXT_MESSAGE: @@ -2832,6 +2871,83 @@ static int bgp_route_refresh_receive(struct peer_connection *connection, return BGP_PACKET_NOOP; } +static void bgp_dynamic_capability_llgr(uint8_t *pnt, int action, + struct capability_header *hdr, + struct peer *peer) +{ + uint8_t *data = pnt + 3; + uint8_t *end = data + hdr->length; + size_t len = end - data; + + if (action == CAPABILITY_ACTION_SET) { + if (len < BGP_CAP_LLGR_MIN_PACKET_LEN) { + zlog_err("%pBP: Received invalid Long-Lived Graceful-Restart capability length %zu", + peer, len); + return; + } + + SET_FLAG(peer->cap, PEER_CAP_LLGR_RCV); + + while (data + BGP_CAP_LLGR_MIN_PACKET_LEN <= end) { + afi_t afi; + safi_t safi; + iana_afi_t pkt_afi; + iana_safi_t pkt_safi; + struct graceful_restart_af graf; + + memcpy(&graf, data, sizeof(graf)); + pkt_afi = ntohs(graf.afi); + pkt_safi = safi_int2iana(graf.safi); + + /* Stale time is after AFI/SAFI/flags. + * It's encoded as 24 bits (= 3 bytes), so we need to + * put it into 32 bits. + */ + uint32_t stale_time; + uint8_t *stale_time_ptr = data + 4; + + stale_time = stale_time_ptr[0] << 16; + stale_time |= stale_time_ptr[1] << 8; + stale_time |= stale_time_ptr[2]; + + if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, + &safi)) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s Addr-family %s/%s(afi/safi) not supported. Ignore the Long-lived Graceful Restart capability for this AFI/SAFI", + peer->host, + iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); + } else if (!peer->afc[afi][safi] || + !CHECK_FLAG(peer->af_cap[afi][safi], + PEER_CAP_RESTART_AF_RCV)) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s Addr-family %s/%s(afi/safi) not enabled. Ignore the Long-lived Graceful Restart capability", + peer->host, + iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); + } else { + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s Addr-family %s/%s(afi/safi) Long-lived Graceful Restart capability stale time %u sec", + peer->host, + iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi), + stale_time); + + peer->llgr[afi][safi].flags = graf.flag; + peer->llgr[afi][safi].stale_time = + MIN(stale_time, + peer->bgp->llgr_stale_time); + SET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_LLGR_AF_RCV); + } + + data += BGP_CAP_LLGR_MIN_PACKET_LEN; + } + } else { + UNSET_FLAG(peer->cap, PEER_CAP_LLGR_RCV); + } +} + static void bgp_dynamic_capability_graceful_restart(uint8_t *pnt, int action, struct capability_header *hdr, struct peer *peer) @@ -3065,13 +3181,15 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt, bgp_dynamic_capability_graceful_restart(pnt, action, hdr, peer); break; + case CAPABILITY_CODE_LLGR: + bgp_dynamic_capability_llgr(pnt, action, hdr, peer); + break; case CAPABILITY_CODE_REFRESH: case CAPABILITY_CODE_ORF: case CAPABILITY_CODE_AS4: case CAPABILITY_CODE_DYNAMIC: case CAPABILITY_CODE_ADDPATH: case CAPABILITY_CODE_ENHANCED_RR: - case CAPABILITY_CODE_LLGR: case CAPABILITY_CODE_FQDN: case CAPABILITY_CODE_ENHE: case CAPABILITY_CODE_EXT_MESSAGE: diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 227a68ee55..df3397af99 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -2378,9 +2378,6 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, bgp_otc_egress(peer, attr)) return false; - bgp_peer_remove_private_as(bgp, afi, safi, peer, attr); - bgp_peer_as_override(bgp, afi, safi, peer, attr); - if (filter->advmap.update_type == UPDATE_TYPE_WITHDRAW && filter->advmap.aname && route_map_lookup_by_name(filter->advmap.aname)) { @@ -2453,6 +2450,9 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, } } + bgp_peer_remove_private_as(bgp, afi, safi, peer, attr); + bgp_peer_as_override(bgp, afi, safi, peer, attr); + /* RFC 8212 to prevent route leaks. * This specification intends to improve this situation by requiring the * explicit configuration of both BGP Import and Export Policies for any diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 9949ce8187..bbe74ef5a7 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -3619,10 +3619,16 @@ DEFUN(bgp_llgr_stalepath_time, bgp_llgr_stalepath_time_cmd, VTY_DECLVAR_CONTEXT(bgp, bgp); uint32_t llgr_stale_time; + struct listnode *node, *nnode; + struct peer *peer; llgr_stale_time = strtoul(argv[3]->arg, NULL, 10); bgp->llgr_stale_time = llgr_stale_time; + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) + bgp_capability_send(peer, AFI_IP, SAFI_UNICAST, + CAPABILITY_CODE_LLGR, CAPABILITY_ACTION_SET); + return CMD_SUCCESS; } @@ -3634,9 +3640,16 @@ DEFUN(no_bgp_llgr_stalepath_time, no_bgp_llgr_stalepath_time_cmd, "Stale time value (seconds)\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); + struct listnode *node, *nnode; + struct peer *peer; bgp->llgr_stale_time = BGP_DEFAULT_LLGR_STALE_TIME; + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) + bgp_capability_send(peer, AFI_IP, SAFI_UNICAST, + CAPABILITY_CODE_LLGR, + CAPABILITY_ACTION_UNSET); + return CMD_SUCCESS; } @@ -12622,6 +12635,8 @@ static void bgp_show_neighbor_graceful_restart_capability_per_afi_safi( if (json) { json_object_int_add(json_timer, "stalePathTimer", peer->bgp->stalepath_time); + json_object_int_add(json_timer, "llgrStaleTime", + peer->llgr[afi][safi].stale_time); if (peer->connection->t_gr_stale != NULL) { json_object_int_add(json_timer, @@ -12672,6 +12687,9 @@ static void bgp_show_neighbor_graceful_restart_capability_per_afi_safi( " Configured Selection Deferral Time(sec): %u\n", peer->bgp->select_defer_time); + vty_out(vty, " LLGR Stale Path Time(sec): %u\n", + peer->llgr[afi][safi].stale_time); + if (peer->bgp->gr_info[afi][safi].t_select_deferral != NULL) vty_out(vty, @@ -12703,6 +12721,8 @@ static void bgp_show_neighbor_graceful_restart_time(struct vty *vty, json_object_int_add(json_timer, "configuredRestartTimer", p->bgp->restart_time); + json_object_int_add(json_timer, "configuredLlgrStaleTime", + p->bgp->llgr_stale_time); json_object_int_add(json_timer, "receivedRestartTimer", p->v_gr_restart); @@ -12721,6 +12741,8 @@ static void bgp_show_neighbor_graceful_restart_time(struct vty *vty, vty_out(vty, " Received Restart Time(sec): %u\n", p->v_gr_restart); + vty_out(vty, " Configured LLGR Stale Path Time(sec): %u\n", + p->bgp->llgr_stale_time); if (p->connection->t_gr_restart != NULL) vty_out(vty, " Restart Time Remaining(sec): %ld\n", event_timer_remain_second( diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst index b61c9448db..26b2b43971 100644 --- a/doc/user/ospfd.rst +++ b/doc/user/ospfd.rst @@ -599,6 +599,38 @@ Interfaces KEY is the actual message digest key, of up to 16 chars (larger strings will be truncated), and is associated with the given KEYID. +.. clicmd:: ip ospf authentication key-chain KEYCHAIN + + Specify that HMAC cryptographic authentication must be used on this interface + using a key chain. Overrides any authentication enabled on a per-area basis + (:clicmd:`area A.B.C.D authentication message-digest`) + + * ``KEYCHAIN``: Specifies the name of the key chain that contains the authentication + key(s) and cryptographic algorithms to be used for OSPF authentication. The key chain + is a logical container that holds one or more authentication keys, + allowing for key rotation and management. + + Note that OSPF HMAC cryptographic authentication requires that time never go backwards + (correct time is NOT important, only that it never goes backwards), even + across resets, if ospfd is to be able to promptly reestablish adjacencies + with its neighbours after restarts/reboots. The host should have system time + be set at boot from an external or non-volatile source (e.g. battery backed + clock, NTP, etc.) or else the system clock should be periodically saved to + non-volatile storage and restored at boot if HMAC cryptographic authentication is to be + expected to work reliably. + + Example: + + .. code:: frr + + r1(config)#key chain temp + r1(config-keychain)#key 13 + r1(config-keychain-key)#key-string ospf + r1(config-keychain-key)#cryptographic-algorithm hmac-sha-256 + r1(config)#int eth0 + r1(config-if)#ip ospf authentication key-chain temp + r1(config-if)#ip ospf area 0 + .. clicmd:: ip ospf cost (1-65535) diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c index cd7b3b8842..ecf43faa70 100644 --- a/isisd/isis_tlvs.c +++ b/isisd/isis_tlvs.c @@ -2751,7 +2751,7 @@ static int unpack_item_srv6_end_sid(uint16_t mtid, uint8_t len, void *dest, int indent) { struct isis_subtlvs *subtlvs = dest; - struct isis_srv6_end_sid_subtlv *sid; + struct isis_srv6_end_sid_subtlv *sid = NULL; size_t consume; uint8_t subsubtlv_len; @@ -2763,7 +2763,7 @@ static int unpack_item_srv6_end_sid(uint16_t mtid, uint8_t len, log, indent, "Not enough data left. (expected 19 or more bytes, got %hhu)\n", len); - return 1; + goto out; } sid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*sid)); diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c index ada8f1ad29..788618ef8b 100644 --- a/isisd/isis_zebra.c +++ b/isisd/isis_zebra.c @@ -1017,12 +1017,15 @@ void isis_zebra_srv6_adj_sid_install(struct srv6_adjacency *sra) struct seg6local_context ctx = {}; uint16_t prefixlen = IPV6_MAX_BITLEN; struct interface *ifp; - struct isis_circuit *circuit = sra->adj->circuit; - struct isis_area *area = circuit->area; + struct isis_circuit *circuit; + struct isis_area *area; if (!sra) return; + circuit = sra->adj->circuit; + area = circuit->area; + sr_debug("ISIS-SRv6 (%s): setting adjacency SID %pI6", area->area_tag, &sra->sid); @@ -1071,12 +1074,15 @@ void isis_zebra_srv6_adj_sid_uninstall(struct srv6_adjacency *sra) enum seg6local_action_t action = ZEBRA_SEG6_LOCAL_ACTION_UNSPEC; struct interface *ifp; uint16_t prefixlen = IPV6_MAX_BITLEN; - struct isis_circuit *circuit = sra->adj->circuit; - struct isis_area *area = circuit->area; + struct isis_circuit *circuit; + struct isis_area *area; if (!sra) return; + circuit = sra->adj->circuit; + area = circuit->area; + switch (sra->behavior) { case SRV6_ENDPOINT_BEHAVIOR_END_X: prefixlen = IPV6_MAX_BITLEN; @@ -1130,6 +1136,9 @@ static int isis_zebra_process_srv6_locator_chunk(ZAPI_CALLBACK_ARGS) enum srv6_endpoint_behavior_codepoint behavior; bool allocated = false; + if (!isis) + return -1; + /* Decode the received zebra message */ s = zclient->ibuf; if (zapi_srv6_locator_chunk_decode(s, chunk) < 0) diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index 301fccecd7..eb5514adfb 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -87,7 +87,7 @@ static int ospf6_router_lsa_show(struct vty *vty, struct ospf6_lsa *lsa, char buf[32], name[32], bits[16], options[32]; struct ospf6_router_lsa *router_lsa; struct ospf6_router_lsdesc *lsdesc; - json_object *json_arr; + json_object *json_arr = NULL; json_object *json_loop; router_lsa = @@ -460,7 +460,7 @@ static int ospf6_network_lsa_show(struct vty *vty, struct ospf6_lsa *lsa, struct ospf6_network_lsa *network_lsa; struct ospf6_network_lsdesc *lsdesc; char buf[128], options[32]; - json_object *json_arr; + json_object *json_arr = NULL; network_lsa = (struct ospf6_network_lsa *)((caddr_t)lsa->header diff --git a/ospfd/ospf_auth.c b/ospfd/ospf_auth.c new file mode 100644 index 0000000000..2d13d4e9ad --- /dev/null +++ b/ospfd/ospf_auth.c @@ -0,0 +1,705 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2023 Amnesh Inc. + * Mahdi Varasteh + */ + +#include <zebra.h> + +#include "linklist.h" +#include "if.h" +#include "checksum.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_errors.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_auth.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_network.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_bfd.h" +#include "ospfd/ospf_gr.h" +#ifdef CRYPTO_INTERNAL +#include "sha256.h" +#include "md5.h" +#endif + +const uint8_t ospf_auth_apad[KEYCHAIN_MAX_HASH_SIZE] = { + 0x87, 0x8F, 0xE1, 0xF3, 0x87, 0x8F, 0xE1, 0xF3, 0x87, 0x8F, 0xE1, + 0xF3, 0x87, 0x8F, 0xE1, 0xF3, 0x87, 0x8F, 0xE1, 0xF3, 0x87, 0x8F, + 0xE1, 0xF3, 0x87, 0x8F, 0xE1, 0xF3, 0x87, 0x8F, 0xE1, 0xF3, 0x87, + 0x8F, 0xE1, 0xF3, 0x87, 0x8F, 0xE1, 0xF3, 0x87, 0x8F, 0xE1, 0xF3, + 0x87, 0x8F, 0xE1, 0xF3, 0x87, 0x8F, 0xE1, 0xF3, 0x87, 0x8F, 0xE1, + 0xF3, 0x87, 0x8F, 0xE1, 0xF3, 0x87, 0x8F, 0xE1, 0xF3 +}; + +static int ospf_check_sum(struct ospf_header *ospfh) +{ + uint32_t ret; + uint16_t sum; + + /* clear auth_data for checksum. */ + memset(ospfh->u.auth_data, 0, OSPF_AUTH_SIMPLE_SIZE); + + /* keep checksum and clear. */ + sum = ospfh->checksum; + memset(&ospfh->checksum, 0, sizeof(uint16_t)); + + /* calculate checksum. */ + ret = in_cksum(ospfh, ntohs(ospfh->length)); + + if (ret != sum) { + zlog_info("%s: checksum mismatch, my %X, his %X", __func__, ret, + sum); + return 0; + } + + return 1; +} + +#ifdef CRYPTO_OPENSSL +static const EVP_MD *ospf_auth_get_openssl_evp_md_from_key(struct key *key) +{ + if (key->hash_algo == KEYCHAIN_ALGO_HMAC_SHA1) + return EVP_get_digestbyname("sha1"); + else if (key->hash_algo == KEYCHAIN_ALGO_HMAC_SHA256) + return EVP_get_digestbyname("sha256"); + else if (key->hash_algo == KEYCHAIN_ALGO_HMAC_SHA384) + return EVP_get_digestbyname("sha384"); + else if (key->hash_algo == KEYCHAIN_ALGO_HMAC_SHA512) + return EVP_get_digestbyname("sha512"); + return NULL; +} +#endif + +static int ospf_auth_check_hmac_sha_digest(struct ospf_interface *oi, + struct ospf_header *ospfh, + struct ip *iph, + struct key *key) +{ + unsigned char digest[KEYCHAIN_MAX_HASH_SIZE]; + struct ospf_neighbor *nbr; + uint16_t length = ntohs(ospfh->length); + uint16_t hash_length = keychain_get_hash_len(key->hash_algo); +#ifdef CRYPTO_OPENSSL + unsigned int openssl_hash_length = hash_length; + HMAC_CTX *ctx; + const EVP_MD *md_alg = ospf_auth_get_openssl_evp_md_from_key(key); + + if (!md_alg) { + flog_warn(EC_OSPF_AUTH, + "interface %s: invalid HMAC algorithm, Router-ID: %pI4", + IF_NAME(oi), &ospfh->router_id); + return 0; + } +#elif CRYPTO_INTERNAL + HMAC_SHA256_CTX ctx; + + if (key->hash_algo != KEYCHAIN_ALGO_HMAC_SHA256) { + flog_warn(EC_OSPF_AUTH, + "interface %s: HMAC algorithm not supported, Router-ID: %pI4", + IF_NAME(oi), &ospfh->router_id); + return 0; + } +#endif + /* check crypto seqnum. */ + nbr = ospf_nbr_lookup(oi, iph, ospfh); + + if (nbr && + ntohl(nbr->crypt_seqnum) > ntohl(ospfh->u.crypt.crypt_seqnum)) { + flog_warn(EC_OSPF_AUTH, + "interface %s: ospf_check_hmac_sha bad sequence %u (expect %d), Router-ID: %pI4", + IF_NAME(oi), ntohl(ospfh->u.crypt.crypt_seqnum), + ntohl(nbr->crypt_seqnum), &ospfh->router_id); + return 0; + } +#ifdef CRYPTO_OPENSSL + ctx = HMAC_CTX_new(); + HMAC_Init_ex(ctx, key->string, strlen(key->string), md_alg, NULL); + HMAC_Update(ctx, (const unsigned char *)ospfh, length); + HMAC_Update(ctx, (const unsigned char *)ospf_auth_apad, hash_length); + HMAC_Final(ctx, digest, &openssl_hash_length); + HMAC_CTX_free(ctx); +#elif CRYPTO_INTERNAL + memset(&ctx, 0, sizeof(ctx)); + HMAC__SHA256_Init(&ctx, key->string, strlen(key->string)); + HMAC__SHA256_Update(&ctx, ospfh, length); + HMAC__SHA256_Update(&ctx, ospf_auth_apad, hash_length); + HMAC__SHA256_Final(digest, &ctx); +#endif + if (memcmp((caddr_t)ospfh + length, digest, hash_length)) { + flog_warn(EC_OSPF_AUTH, + "interface %s: ospf_check_hmac_sha checksum mismatch %u, Router-ID: %pI4", + IF_NAME(oi), length, &ospfh->router_id); + return 0; + } + if (nbr) + nbr->crypt_seqnum = ospfh->u.crypt.crypt_seqnum; + return 1; +} + +static int ospf_auth_check_md5_digest(struct ospf_interface *oi, + struct ospf_header *ospfh, struct ip *iph, struct key *key) +{ +#ifdef CRYPTO_OPENSSL + EVP_MD_CTX *ctx; +#elif CRYPTO_INTERNAL + MD5_CTX ctx; +#endif + char auth_key[OSPF_AUTH_MD5_SIZE + 1]; + unsigned char digest[OSPF_AUTH_MD5_SIZE]; + struct ospf_neighbor *nbr; + struct crypt_key *ck = NULL; + uint16_t length = ntohs(ospfh->length); + + if (key == NULL) { + ck = ospf_crypt_key_lookup(OSPF_IF_PARAM(oi, auth_crypt), + ospfh->u.crypt.key_id); + if (ck == NULL) { + flog_warn( + EC_OSPF_AUTH, + "interface %s: %s no key %d, Router-ID: %pI4", + IF_NAME(oi), __func__, ospfh->u.crypt.key_id, &ospfh->router_id); + return 0; + } + } + /* check crypto seqnum. */ + nbr = ospf_nbr_lookup(oi, iph, ospfh); + + if (nbr && + ntohl(nbr->crypt_seqnum) > ntohl(ospfh->u.crypt.crypt_seqnum)) { + flog_warn(EC_OSPF_AUTH, + "interface %s: %s bad sequence %d (expect %d), Router-ID: %pI4", + IF_NAME(oi), __func__, ntohl(ospfh->u.crypt.crypt_seqnum), + ntohl(nbr->crypt_seqnum), &ospfh->router_id); + return 0; + } + + memset(auth_key, 0, OSPF_AUTH_MD5_SIZE + 1); + if (ck == NULL) + strlcpy(auth_key, key->string, OSPF_AUTH_MD5_SIZE + 1); + else + strlcpy(auth_key, (char *)ck->auth_key, OSPF_AUTH_MD5_SIZE + 1); + /* Generate a digest for the ospf packet - their digest + our digest. */ +#ifdef CRYPTO_OPENSSL + unsigned int md5_size = OSPF_AUTH_MD5_SIZE; + + ctx = EVP_MD_CTX_new(); + EVP_DigestInit(ctx, EVP_md5()); + EVP_DigestUpdate(ctx, ospfh, length); + EVP_DigestUpdate(ctx, auth_key, OSPF_AUTH_MD5_SIZE); + EVP_DigestFinal(ctx, digest, &md5_size); + EVP_MD_CTX_free(ctx); +#elif CRYPTO_INTERNAL + memset(&ctx, 0, sizeof(ctx)); + MD5Init(&ctx); + MD5Update(&ctx, ospfh, length); + MD5Update(&ctx, auth_key, OSPF_AUTH_MD5_SIZE); + MD5Final(digest, &ctx); +#endif + + /* compare the two */ + if (memcmp((caddr_t)ospfh + length, digest, OSPF_AUTH_MD5_SIZE)) { + flog_warn(EC_OSPF_AUTH, + "interface %s: ospf_check_md5 checksum mismatch, Router-ID: %pI4", + IF_NAME(oi), &ospfh->router_id); + return 0; + } + + /* save neighbor's crypt_seqnum */ + if (nbr) + nbr->crypt_seqnum = ospfh->u.crypt.crypt_seqnum; + return 1; +} + +static int ospf_auth_make_md5_digest(struct ospf_interface *oi, + struct ospf_packet *op, struct key *key) +{ + void *ibuf; + struct ospf_header *ospfh; + unsigned char digest[OSPF_AUTH_MD5_SIZE]; + uint16_t length; +#ifdef CRYPTO_OPENSSL + EVP_MD_CTX *ctx; +#elif CRYPTO_INTERNAL + MD5_CTX ctx; +#endif + char auth_key[OSPF_AUTH_MD5_SIZE + 1]; + + memset(auth_key, 0, OSPF_AUTH_MD5_SIZE + 1); + strlcpy(auth_key, key->string, OSPF_AUTH_MD5_SIZE + 1); + ibuf = STREAM_DATA(op->s); + ospfh = (struct ospf_header *)ibuf; + length = ntohs(ospfh->length); + /* Generate a digest for the ospf packet - their digest + our digest. */ +#ifdef CRYPTO_OPENSSL + unsigned int md5_size = OSPF_AUTH_MD5_SIZE; + + ctx = EVP_MD_CTX_new(); + EVP_DigestInit(ctx, EVP_md5()); + EVP_DigestUpdate(ctx, ospfh, length); + EVP_DigestUpdate(ctx, auth_key, OSPF_AUTH_MD5_SIZE); + EVP_DigestFinal(ctx, digest, &md5_size); + EVP_MD_CTX_free(ctx); +#elif CRYPTO_INTERNAL + memset(&ctx, 0, sizeof(ctx)); + MD5Init(&ctx); + MD5Update(&ctx, ospfh, length); + MD5Update(&ctx, auth_key, OSPF_AUTH_MD5_SIZE); + MD5Final(digest, &ctx); +#endif + + stream_put(op->s, digest, OSPF_AUTH_MD5_SIZE); + + op->length = ntohs(ospfh->length) + OSPF_AUTH_MD5_SIZE; + + if (stream_get_endp(op->s) != op->length) + /* XXX size_t */ + flog_warn(EC_OSPF_AUTH, + "%s: length mismatch stream %lu ospf_packet %u, Router-ID %pI4", + __func__, (unsigned long)stream_get_endp(op->s), + op->length, &ospfh->router_id); + + return OSPF_AUTH_MD5_SIZE; +} + +static int ospf_auth_make_hmac_sha_digest(struct ospf_interface *oi, + struct ospf_packet *op, + struct key *key) +{ + void *ibuf; + struct ospf_header *ospfh; + unsigned char digest[KEYCHAIN_MAX_HASH_SIZE] = { 0 }; + uint16_t hash_length = keychain_get_hash_len(key->hash_algo); + + ibuf = STREAM_DATA(op->s); + ospfh = (struct ospf_header *)ibuf; +#ifdef CRYPTO_OPENSSL + unsigned int openssl_hash_length = hash_length; + HMAC_CTX *ctx; + const EVP_MD *md_alg = ospf_auth_get_openssl_evp_md_from_key(key); + + if (!md_alg) { + flog_warn(EC_OSPF_AUTH, + "interface %s: invalid HMAC algorithm, Router-ID: %pI4", + IF_NAME(oi), &ospfh->router_id); + return 0; + } +#elif CRYPTO_INTERNAL + HMAC_SHA256_CTX ctx; + + if (key->hash_algo != KEYCHAIN_ALGO_HMAC_SHA256) { + flog_warn(EC_OSPF_AUTH, + "interface %s: HMAC algorithm not supported, Router-ID: %pI4", + IF_NAME(oi), &ospfh->router_id); + return 0; + } +#endif +#ifdef CRYPTO_OPENSSL + ctx = HMAC_CTX_new(); + HMAC_Init_ex(ctx, key->string, strlen(key->string), md_alg, NULL); + HMAC_Update(ctx, (const unsigned char *)ospfh, ntohs(ospfh->length)); + HMAC_Update(ctx, (const unsigned char *)ospf_auth_apad, hash_length); + HMAC_Final(ctx, digest, &openssl_hash_length); + HMAC_CTX_free(ctx); +#elif CRYPTO_INTERNAL + memset(&ctx, 0, sizeof(ctx)); + HMAC__SHA256_Init(&ctx, key->string, strlen(key->string)); + HMAC__SHA256_Update(&ctx, ospfh, ntohs(ospfh->length)); + HMAC__SHA256_Update(&ctx, ospf_auth_apad, hash_length); + HMAC__SHA256_Final(digest, &ctx); +#endif + stream_put(op->s, digest, hash_length); + + op->length = ntohs(ospfh->length) + hash_length; + + if (stream_get_endp(op->s) != op->length) + /* XXX size_t */ + flog_warn(EC_OSPF_AUTH, + "%s: length mismatch stream %lu ospf_packet %u, Router-ID %pI4", + __func__, (unsigned long)stream_get_endp(op->s), + op->length, &ospfh->router_id); + + return hash_length; +} + +int ospf_auth_check_digest(struct ospf_interface *oi, struct ip *iph, struct ospf_header *ospfh) +{ + struct keychain *keychain = NULL; + struct key *key = NULL; + int key_id = ospfh->u.crypt.key_id; + uint8_t auth_data_len = ospfh->u.crypt.auth_data_len; + + if (!OSPF_IF_PARAM(oi, keychain_name)) + return ospf_auth_check_md5_digest(oi, ospfh, iph, NULL); + + keychain = keychain_lookup(OSPF_IF_PARAM(oi, keychain_name)); + if (!keychain) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) + flog_warn(EC_OSPF_AUTH, + "interface %s: Keychain %s is not available, Router-ID %pI4", + IF_NAME(oi), OSPF_IF_PARAM(oi, keychain_name), + &ospfh->router_id); + return 0; + } + + key = key_lookup_for_accept(keychain, key_id); + if (!key) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) + flog_warn(EC_OSPF_AUTH, + "interface %s: Key ID %d not found in keychain %s, Router-ID %pI4", + IF_NAME(oi), key_id, keychain->name, + &ospfh->router_id); + return 0; + } + + if (key->string == NULL || key->hash_algo == KEYCHAIN_ALGO_NULL) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) + flog_warn(EC_OSPF_AUTH, + "interface %s: Key ID %d in keychain %s is incomplete, Router-ID %pI4", + IF_NAME(oi), key_id, keychain->name, + &ospfh->router_id); + return 0; + } + + if (keychain_get_hash_len(key->hash_algo) != auth_data_len) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) + flog_warn(EC_OSPF_AUTH, + "interface %s: Key ID %d in keychain %s hash length mismatch, Router-ID %pI4", + IF_NAME(oi), key_id, keychain->name, + &ospfh->router_id); + return 0; + } + + /* Backward compatibility with RFC 2328 keyed-MD5 authentication */ + if (key->hash_algo == KEYCHAIN_ALGO_MD5) + return ospf_auth_check_md5_digest(oi, ospfh, iph, key); + + return ospf_auth_check_hmac_sha_digest(oi, ospfh, iph, key); +} + +int ospf_auth_make_digest(struct ospf_interface *oi, struct ospf_packet *op) +{ + struct ospf_header *ospfh; + void *ibuf; + struct keychain *keychain = NULL; + struct key *key = NULL; + int key_id; + + ibuf = STREAM_DATA(op->s); + ospfh = (struct ospf_header *)ibuf; + + key_id = ospfh->u.crypt.key_id; + + if (!OSPF_IF_PARAM(oi, keychain_name)) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) + flog_warn(EC_OSPF_AUTH, + "interface %s: Keychain is not set, Router-ID %pI4", + IF_NAME(oi), &ospfh->router_id); + return 0; + } + + keychain = oi->keychain; + if (!keychain) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, SEND)) + flog_warn(EC_OSPF_AUTH, + "interface %s: Keychain %s is not available to send, Router-ID %pI4", + IF_NAME(oi), OSPF_IF_PARAM(oi, keychain_name), + &ospfh->router_id); + return 0; + } + + key = oi->key; + if (!key) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, SEND)) + flog_warn(EC_OSPF_AUTH, + "interface %s: Key ID %d not found in keychain %s, Router-ID %pI4", + IF_NAME(oi), key_id, keychain->name, + &ospfh->router_id); + return 0; + } + + if (key->string == NULL || key->hash_algo == KEYCHAIN_ALGO_NULL) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, SEND)) + flog_warn(EC_OSPF_AUTH, + "interface %s: Key ID %d in keychain %s is incomplete, Router-ID %pI4", + IF_NAME(oi), key_id, keychain->name, + &ospfh->router_id); + return 0; + } + + /* Backward compatibility with RFC 2328 keyed-MD5 authentication */ + if (key->hash_algo == KEYCHAIN_ALGO_MD5) + return ospf_auth_make_md5_digest(oi, op, key); + else + return ospf_auth_make_hmac_sha_digest(oi, op, key); +} + +/* This function is called from ospf_write(), it will detect the + * authentication scheme and if it is MD5, it will change the sequence + * and update the MD5 digest. + */ + +int ospf_auth_make(struct ospf_interface *oi, struct ospf_packet *op) +{ + struct ospf_header *ospfh; + unsigned char digest[OSPF_AUTH_MD5_SIZE] = {0}; +#ifdef CRYPTO_OPENSSL + EVP_MD_CTX *ctx; +#elif CRYPTO_INTERNAL + MD5_CTX ctx; +#endif + void *ibuf; + uint32_t t; + struct crypt_key *ck; + const uint8_t *auth_key = NULL; + + ibuf = STREAM_DATA(op->s); + ospfh = (struct ospf_header *)ibuf; + + if (ntohs(ospfh->auth_type) != OSPF_AUTH_CRYPTOGRAPHIC) + return 0; + + /* We do this here so when we dup a packet, we don't have to + * waste CPU rewriting other headers. + + Note that frr_time /deliberately/ is not used here. + */ + t = (time(NULL) & 0xFFFFFFFF); + if (t > oi->crypt_seqnum) + oi->crypt_seqnum = t; + else + oi->crypt_seqnum++; + + ospfh->u.crypt.crypt_seqnum = htonl(oi->crypt_seqnum); + + /* Get MD5 Authentication key from auth_key list. */ + if (list_isempty(OSPF_IF_PARAM(oi, auth_crypt)) && OSPF_IF_PARAM(oi, keychain_name) == NULL) + auth_key = (const uint8_t *)digest; + else if (!list_isempty(OSPF_IF_PARAM(oi, auth_crypt))) { + ck = listgetdata(listtail(OSPF_IF_PARAM(oi, auth_crypt))); + auth_key = ck->auth_key; + } + + if (auth_key) { + /* Generate a digest for the entire packet + our secret key. */ +#ifdef CRYPTO_OPENSSL + unsigned int md5_size = OSPF_AUTH_MD5_SIZE; + + ctx = EVP_MD_CTX_new(); + EVP_DigestInit(ctx, EVP_md5()); + EVP_DigestUpdate(ctx, ibuf, ntohs(ospfh->length)); + EVP_DigestUpdate(ctx, auth_key, OSPF_AUTH_MD5_SIZE); + EVP_DigestFinal(ctx, digest, &md5_size); + EVP_MD_CTX_free(ctx); +#elif CRYPTO_INTERNAL + memset(&ctx, 0, sizeof(ctx)); + MD5Init(&ctx); + MD5Update(&ctx, ibuf, ntohs(ospfh->length)); + MD5Update(&ctx, auth_key, OSPF_AUTH_MD5_SIZE); + MD5Final(digest, &ctx); +#endif + + /* Append md5 digest to the end of the stream. */ + stream_put(op->s, digest, OSPF_AUTH_MD5_SIZE); + + /* We do *NOT* increment the OSPF header length. */ + op->length = ntohs(ospfh->length) + OSPF_AUTH_MD5_SIZE; + + if (stream_get_endp(op->s) != op->length) + /* XXX size_t */ + flog_warn( + EC_OSPF_AUTH, + "%s: length mismatch stream %lu ospf_packet %u, Router-ID %pI4", + __func__, (unsigned long)stream_get_endp(op->s), + op->length, &ospfh->router_id); + + return OSPF_AUTH_MD5_SIZE; + } else + return ospf_auth_make_digest(oi, op); +} + +/* Return 1, if the packet is properly authenticated and checksummed, + * 0 otherwise. In particular, check that AuType header field is valid and + * matches the locally configured AuType, and that D.5 requirements are met. + */ +int ospf_auth_check(struct ospf_interface *oi, struct ip *iph, + struct ospf_header *ospfh) +{ + uint16_t iface_auth_type; + uint16_t pkt_auth_type = ntohs(ospfh->auth_type); + + iface_auth_type = ospf_auth_type(oi); + + switch (pkt_auth_type) { + case OSPF_AUTH_NULL: /* RFC2328 D.5.1 */ + if (iface_auth_type != OSPF_AUTH_NULL) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) + flog_warn( + EC_OSPF_PACKET, + "interface %s: auth-type mismatch, local %s, rcvd Null, Router-ID %pI4", + IF_NAME(oi), + lookup_msg(ospf_auth_type_str, + iface_auth_type, NULL), + &ospfh->router_id); + return 0; + } + if (!ospf_check_sum(ospfh)) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) + flog_warn( + EC_OSPF_PACKET, + "interface %s: Null auth OK, but checksum error, Router-ID %pI4", + IF_NAME(oi), + &ospfh->router_id); + return 0; + } + return 1; + case OSPF_AUTH_SIMPLE: /* RFC2328 D.5.2 */ + if (iface_auth_type != OSPF_AUTH_SIMPLE) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) + flog_warn( + EC_OSPF_PACKET, + "interface %s: auth-type mismatch, local %s, rcvd Simple, Router-ID %pI4", + IF_NAME(oi), + lookup_msg(ospf_auth_type_str, + iface_auth_type, NULL), + &ospfh->router_id); + return 0; + } + if (memcmp(OSPF_IF_PARAM(oi, auth_simple), ospfh->u.auth_data, + OSPF_AUTH_SIMPLE_SIZE)) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) + flog_warn( + EC_OSPF_PACKET, + "interface %s: Simple auth failed, Router-ID %pI4", + IF_NAME(oi), &ospfh->router_id); + return 0; + } + if (!ospf_check_sum(ospfh)) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) + flog_warn( + EC_OSPF_PACKET, + "interface %s: Simple auth OK, checksum error, Router-ID %pI4", + IF_NAME(oi), + &ospfh->router_id); + return 0; + } + return 1; + case OSPF_AUTH_CRYPTOGRAPHIC: /* RFC2328 D.5.3 */ + if (iface_auth_type != OSPF_AUTH_CRYPTOGRAPHIC) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) + flog_warn( + EC_OSPF_PACKET, + "interface %s: auth-type mismatch, local %s, rcvd Cryptographic, Router-ID %pI4", + IF_NAME(oi), + lookup_msg(ospf_auth_type_str, + iface_auth_type, NULL), + &ospfh->router_id); + return 0; + } + if (ospfh->checksum) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) + flog_warn( + EC_OSPF_PACKET, + "interface %s: OSPF header checksum is not 0, Router-ID %pI4", + IF_NAME(oi), &ospfh->router_id); + return 0; + } + /* If `authentication message-digest` key is not set, we try keychain crypto */ + if (OSPF_IF_PARAM(oi, keychain_name) || !list_isempty(OSPF_IF_PARAM(oi, auth_crypt))) + return ospf_auth_check_digest(oi, iph, ospfh); + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) + flog_warn( + EC_OSPF_AUTH, + "interface %s: MD5 auth failed, Router-ID %pI4", + IF_NAME(oi), &ospfh->router_id); + return 0; + default: + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) + flog_warn( + EC_OSPF_PACKET, + "interface %s: invalid packet auth-type (%02x), Router-ID %pI4", + IF_NAME(oi), pkt_auth_type, &ospfh->router_id); + return 0; + } +} + +/* OSPF authentication checking function */ +int ospf_auth_type(struct ospf_interface *oi) +{ + int auth_type; + + if (OSPF_IF_PARAM(oi, auth_type) == OSPF_AUTH_NOTSET) + auth_type = oi->area->auth_type; + else + auth_type = OSPF_IF_PARAM(oi, auth_type); + + /* Handle case where MD5 key list, or a key-chain, is not configured aka Cisco */ + if (auth_type == OSPF_AUTH_CRYPTOGRAPHIC + && (list_isempty(OSPF_IF_PARAM(oi, auth_crypt)) + && OSPF_IF_PARAM(oi, keychain_name) == NULL)) + return OSPF_AUTH_NULL; + + return auth_type; +} + +/* Make Authentication Data. */ +int ospf_auth_make_data(struct ospf_interface *oi, struct ospf_header *ospfh) +{ + struct crypt_key *ck; + + switch (ospf_auth_type(oi)) { + case OSPF_AUTH_NULL: + /* memset (ospfh->u.auth_data, 0, sizeof(ospfh->u.auth_data)); + */ + break; + case OSPF_AUTH_SIMPLE: + memcpy(ospfh->u.auth_data, OSPF_IF_PARAM(oi, auth_simple), + OSPF_AUTH_SIMPLE_SIZE); + break; + case OSPF_AUTH_CRYPTOGRAPHIC: + if (OSPF_IF_PARAM(oi, keychain_name)) { + oi->keychain = keychain_lookup(OSPF_IF_PARAM(oi, keychain_name)); + if (oi->keychain) + oi->key = key_lookup_for_send(oi->keychain); + if (oi->key) { + ospfh->u.crypt.zero = 0; + ospfh->u.crypt.key_id = oi->key->index; + ospfh->u.crypt.auth_data_len = keychain_get_hash_len(oi->key->hash_algo); + } else { + /* If key is not set, then set 0. */ + ospfh->u.crypt.zero = 0; + ospfh->u.crypt.key_id = 0; + ospfh->u.crypt.auth_data_len = OSPF_AUTH_MD5_SIZE; + } + } else { + /* If key is not set, then set 0. */ + if (list_isempty(OSPF_IF_PARAM(oi, auth_crypt))) { + ospfh->u.crypt.zero = 0; + ospfh->u.crypt.key_id = 0; + ospfh->u.crypt.auth_data_len = OSPF_AUTH_MD5_SIZE; + } else { + ck = listgetdata( + listtail(OSPF_IF_PARAM(oi, auth_crypt))); + ospfh->u.crypt.zero = 0; + ospfh->u.crypt.key_id = ck->key_id; + ospfh->u.crypt.auth_data_len = OSPF_AUTH_MD5_SIZE; + } + } + /* note: the seq is done in ospf_auth_make() */ + break; + default: + /* memset (ospfh->u.auth_data, 0, sizeof(ospfh->u.auth_data)); + */ + break; + } + + return 0; +} diff --git a/ospfd/ospf_auth.h b/ospfd/ospf_auth.h new file mode 100644 index 0000000000..6f6d3db588 --- /dev/null +++ b/ospfd/ospf_auth.h @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2023 Amnesh Inc. + * Mahdi Varasteh + */ + +#ifndef _ZEBRA_OSPF_AUTH_H +#define _ZEBRA_OSPF_AUTH_H + +#include <ospfd/ospf_gr.h> +#include <ospfd/ospf_packet.h> + +int ospf_auth_check(struct ospf_interface *oi, struct ip *iph, struct ospf_header *ospfh); +int ospf_auth_check_digest(struct ospf_interface *oi, struct ip *iph, struct ospf_header *ospfh); +int ospf_auth_make(struct ospf_interface *oi, struct ospf_packet *op); +int ospf_auth_make_digest(struct ospf_interface *oi, struct ospf_packet *op); +int ospf_auth_type(struct ospf_interface *oi); +int ospf_auth_make_data(struct ospf_interface *oi, struct ospf_header *ospfh); + +#endif /* _ZEBRA_OSPF_AUTH_H */ diff --git a/ospfd/ospf_errors.c b/ospfd/ospf_errors.c index 16aa3ab526..cf58c6ad8b 100644 --- a/ospfd/ospf_errors.c +++ b/ospfd/ospf_errors.c @@ -19,9 +19,9 @@ static struct log_ref ferr_ospf_warn[] = { .suggestion = "Do not use this particular set command for an ospf route-map", }, { - .code = EC_OSPF_MD5, - .title = "OSPF has noticed a MD5 issue", - .description = "Something has gone wrong with the calculation of the MD5 data", + .code = EC_OSPF_AUTH, + .title = "OSPF has noticed an authentication issue", + .description = "Something has gone wrong with the calculation of the authentication data", .suggestion = "Ensure your key is correct, gather log data from this side as well as peer and open an Issue", }, { diff --git a/ospfd/ospf_errors.h b/ospfd/ospf_errors.h index dcf9a65ce9..e63cb7409c 100644 --- a/ospfd/ospf_errors.h +++ b/ospfd/ospf_errors.h @@ -22,7 +22,7 @@ enum ospf_log_refs { EC_OSPF_INVALID_ALGORITHM, EC_OSPF_FSM_INVALID_STATE, EC_OSPF_SET_METRIC_PLUS, - EC_OSPF_MD5, + EC_OSPF_AUTH, EC_OSPF_PACKET, EC_OSPF_LARGE_LSA, EC_OSPF_LSA_UNEXPECTED, diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c index bdab672b47..7601419325 100644 --- a/ospfd/ospf_interface.c +++ b/ospfd/ospf_interface.c @@ -549,6 +549,7 @@ static struct ospf_if_params *ospf_new_if_params(void) UNSET_IF_PARAM(oip, auth_type); UNSET_IF_PARAM(oip, if_area); UNSET_IF_PARAM(oip, opaque_capable); + UNSET_IF_PARAM(oip, keychain_name); oip->auth_crypt = list_new(); @@ -566,6 +567,7 @@ static void ospf_del_if_params(struct interface *ifp, struct ospf_if_params *oip) { list_delete(&oip->auth_crypt); + XFREE(MTYPE_OSPF_IF_PARAMS, oip->keychain_name); ospf_interface_disable_bfd(ifp, oip); ldp_sync_info_free(&(oip->ldp_sync_info)); XFREE(MTYPE_OSPF_IF_PARAMS, oip); @@ -601,6 +603,7 @@ void ospf_free_if_params(struct interface *ifp, struct in_addr addr) !OSPF_IF_PARAM_CONFIGURED(oip, if_area) && !OSPF_IF_PARAM_CONFIGURED(oip, opaque_capable) && !OSPF_IF_PARAM_CONFIGURED(oip, prefix_suppression) && + !OSPF_IF_PARAM_CONFIGURED(oip, keychain_name) && listcount(oip->auth_crypt) == 0) { ospf_del_if_params(ifp, oip); rn->info = NULL; diff --git a/ospfd/ospf_interface.h b/ospfd/ospf_interface.h index 47b70f8039..e2290a881c 100644 --- a/ospfd/ospf_interface.h +++ b/ospfd/ospf_interface.h @@ -10,6 +10,7 @@ #include "lib/bfd.h" #include "qobj.h" #include "hook.h" +#include "keychain.h" #include "ospfd/ospf_packet.h" #include "ospfd/ospf_spf.h" @@ -92,6 +93,8 @@ struct ospf_if_params { auth_crypt); /* List of Auth cryptographic data. */ DECLARE_IF_PARAM(int, auth_type); /* OSPF authentication type */ + DECLARE_IF_PARAM(char*, keychain_name); /* OSPF HMAC Cryptographic Authentication*/ + /* Other, non-configuration state */ uint32_t network_lsa_seqnum; /* Network LSA seqnum */ @@ -279,6 +282,10 @@ struct ospf_interface { uint32_t full_nbrs; + /* Buffered values for keychain and key */ + struct keychain *keychain; + struct key *key; + QOBJ_FIELDS; }; DECLARE_QOBJ_TYPE(ospf_interface); diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c index 536bd592d2..dd000241f4 100644 --- a/ospfd/ospf_main.c +++ b/ospfd/ospf_main.c @@ -27,6 +27,7 @@ #include "vrf.h" #include "libfrr.h" #include "routemap.h" +#include "keychain.h" #include "ospfd/ospfd.h" #include "ospfd/ospf_interface.h" @@ -218,6 +219,7 @@ int main(int argc, char **argv) access_list_init(); prefix_list_init(); + keychain_init(); /* Configuration processing callback initialization. */ cmd_init_config_callbacks(ospf_config_start, ospf_config_end); diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index cfa0d5d574..b37efa3efa 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -41,6 +41,7 @@ #include "ospfd/ospf_errors.h" #include "ospfd/ospf_zebra.h" #include "ospfd/ospf_gr.h" +#include "ospfd/ospf_auth.h" /* * OSPF Fragmentation / fragmented writes @@ -99,27 +100,6 @@ static const uint16_t ospf_lsa_minlen[] = { OSPF_OPAQUE_LSA_MIN_SIZE, /* OSPF_OPAQUE_AS_LSA */ }; -/* for ospf_check_auth() */ -static int ospf_check_sum(struct ospf_header *); - -/* OSPF authentication checking function */ -static int ospf_auth_type(struct ospf_interface *oi) -{ - int auth_type; - - if (OSPF_IF_PARAM(oi, auth_type) == OSPF_AUTH_NOTSET) - auth_type = oi->area->auth_type; - else - auth_type = OSPF_IF_PARAM(oi, auth_type); - - /* Handle case where MD5 key list is not configured aka Cisco */ - if (auth_type == OSPF_AUTH_CRYPTOGRAPHIC - && list_isempty(OSPF_IF_PARAM(oi, auth_crypt))) - return OSPF_AUTH_NULL; - - return auth_type; -} - static struct ospf_packet *ospf_packet_new(size_t size) { struct ospf_packet *new; @@ -258,8 +238,8 @@ static struct ospf_packet *ospf_packet_dup(struct ospf_packet *op) "ospf_packet_dup stream %lu ospf_packet %u size mismatch", (unsigned long)STREAM_SIZE(op->s), op->length); - /* Reserve space for MD5 authentication that may be added later. */ - new = ospf_packet_new(stream_get_endp(op->s) + OSPF_AUTH_MD5_SIZE); + /* Reserve space for MD5/HMAC SHA authentication that may be added later. */ + new = ospf_packet_new(stream_get_endp(op->s) + KEYCHAIN_MAX_HASH_SIZE); stream_copy(new->s, op->s); new->dst = op->dst; @@ -274,7 +254,7 @@ static unsigned int ospf_packet_authspace(struct ospf_interface *oi) int auth = 0; if (ospf_auth_type(oi) == OSPF_AUTH_CRYPTOGRAPHIC) - auth = OSPF_AUTH_MD5_SIZE; + auth = KEYCHAIN_MAX_HASH_SIZE; return auth; } @@ -290,155 +270,6 @@ static unsigned int ospf_packet_max(struct ospf_interface *oi) return max; } - -static int ospf_check_md5_digest(struct ospf_interface *oi, - struct ospf_header *ospfh) -{ -#ifdef CRYPTO_OPENSSL - EVP_MD_CTX *ctx; -#elif CRYPTO_INTERNAL - MD5_CTX ctx; -#endif - unsigned char digest[OSPF_AUTH_MD5_SIZE]; - struct crypt_key *ck; - struct ospf_neighbor *nbr; - uint16_t length = ntohs(ospfh->length); - - /* Get secret key. */ - ck = ospf_crypt_key_lookup(OSPF_IF_PARAM(oi, auth_crypt), - ospfh->u.crypt.key_id); - if (ck == NULL) { - flog_warn( - EC_OSPF_MD5, - "interface %s: ospf_check_md5 no key %d, Router-ID: %pI4", - IF_NAME(oi), ospfh->u.crypt.key_id, &ospfh->router_id); - return 0; - } - - /* check crypto seqnum. */ - nbr = ospf_nbr_lookup_by_routerid(oi->nbrs, &ospfh->router_id); - - if (nbr - && ntohl(nbr->crypt_seqnum) > ntohl(ospfh->u.crypt.crypt_seqnum)) { - flog_warn( - EC_OSPF_MD5, - "interface %s: ospf_check_md5 bad sequence %d (expect %d), Router-ID: %pI4", - IF_NAME(oi), ntohl(ospfh->u.crypt.crypt_seqnum), - ntohl(nbr->crypt_seqnum), &ospfh->router_id); - return 0; - } - - /* Generate a digest for the ospf packet - their digest + our digest. */ -#ifdef CRYPTO_OPENSSL - unsigned int md5_size = OSPF_AUTH_MD5_SIZE; - ctx = EVP_MD_CTX_new(); - EVP_DigestInit(ctx, EVP_md5()); - EVP_DigestUpdate(ctx, ospfh, length); - EVP_DigestUpdate(ctx, ck->auth_key, OSPF_AUTH_MD5_SIZE); - EVP_DigestFinal(ctx, digest, &md5_size); - EVP_MD_CTX_free(ctx); -#elif CRYPTO_INTERNAL - memset(&ctx, 0, sizeof(ctx)); - MD5Init(&ctx); - MD5Update(&ctx, ospfh, length); - MD5Update(&ctx, ck->auth_key, OSPF_AUTH_MD5_SIZE); - MD5Final(digest, &ctx); -#endif - - /* compare the two */ - if (memcmp((caddr_t)ospfh + length, digest, OSPF_AUTH_MD5_SIZE)) { - flog_warn( - EC_OSPF_MD5, - "interface %s: ospf_check_md5 checksum mismatch, Router-ID: %pI4", - IF_NAME(oi), &ospfh->router_id); - return 0; - } - - /* save neighbor's crypt_seqnum */ - if (nbr) - nbr->crypt_seqnum = ospfh->u.crypt.crypt_seqnum; - return 1; -} - -/* This function is called from ospf_write(), it will detect the - authentication scheme and if it is MD5, it will change the sequence - and update the MD5 digest. */ -static int ospf_make_md5_digest(struct ospf_interface *oi, - struct ospf_packet *op) -{ - struct ospf_header *ospfh; - unsigned char digest[OSPF_AUTH_MD5_SIZE] = {0}; -#ifdef CRYPTO_OPENSSL - EVP_MD_CTX *ctx; -#elif CRYPTO_INTERNAL - MD5_CTX ctx; -#endif - void *ibuf; - uint32_t t; - struct crypt_key *ck; - const uint8_t *auth_key; - - ibuf = STREAM_DATA(op->s); - ospfh = (struct ospf_header *)ibuf; - - if (ntohs(ospfh->auth_type) != OSPF_AUTH_CRYPTOGRAPHIC) - return 0; - - /* We do this here so when we dup a packet, we don't have to - waste CPU rewriting other headers. - - Note that frr_time /deliberately/ is not used here */ - t = (time(NULL) & 0xFFFFFFFF); - if (t > oi->crypt_seqnum) - oi->crypt_seqnum = t; - else - oi->crypt_seqnum++; - - ospfh->u.crypt.crypt_seqnum = htonl(oi->crypt_seqnum); - - /* Get MD5 Authentication key from auth_key list. */ - if (list_isempty(OSPF_IF_PARAM(oi, auth_crypt))) - auth_key = (const uint8_t *)digest; - else { - ck = listgetdata(listtail(OSPF_IF_PARAM(oi, auth_crypt))); - auth_key = ck->auth_key; - } - - /* Generate a digest for the entire packet + our secret key. */ -#ifdef CRYPTO_OPENSSL - unsigned int md5_size = OSPF_AUTH_MD5_SIZE; - ctx = EVP_MD_CTX_new(); - EVP_DigestInit(ctx, EVP_md5()); - EVP_DigestUpdate(ctx, ibuf, ntohs(ospfh->length)); - EVP_DigestUpdate(ctx, auth_key, OSPF_AUTH_MD5_SIZE); - EVP_DigestFinal(ctx, digest, &md5_size); - EVP_MD_CTX_free(ctx); -#elif CRYPTO_INTERNAL - memset(&ctx, 0, sizeof(ctx)); - MD5Init(&ctx); - MD5Update(&ctx, ibuf, ntohs(ospfh->length)); - MD5Update(&ctx, auth_key, OSPF_AUTH_MD5_SIZE); - MD5Final(digest, &ctx); -#endif - - /* Append md5 digest to the end of the stream. */ - stream_put(op->s, digest, OSPF_AUTH_MD5_SIZE); - - /* We do *NOT* increment the OSPF header length. */ - op->length = ntohs(ospfh->length) + OSPF_AUTH_MD5_SIZE; - - if (stream_get_endp(op->s) != op->length) - /* XXX size_t */ - flog_warn( - EC_OSPF_MD5, - "%s: length mismatch stream %lu ospf_packet %u, Router-ID %pI4", - __func__, (unsigned long)stream_get_endp(op->s), - op->length, &ospfh->router_id); - - return OSPF_AUTH_MD5_SIZE; -} - - static void ospf_ls_req_timer(struct event *thread) { struct ospf_neighbor *nbr; @@ -677,7 +508,7 @@ static void ospf_write(struct event *thread) ospf_if_ipmulticast(fd, oi->address, oi->ifp->ifindex); /* Rewrite the md5 signature & update the seq */ - ospf_make_md5_digest(oi, op); + ospf_auth_make(oi, op); /* Retrieve OSPF packet type. */ stream_set_getp(op->s, 1); @@ -2476,142 +2307,6 @@ static int ospf_check_network_mask(struct ospf_interface *oi, return 0; } -/* Return 1, if the packet is properly authenticated and checksummed, - 0 otherwise. In particular, check that AuType header field is valid and - matches the locally configured AuType, and that D.5 requirements are met. */ -static int ospf_check_auth(struct ospf_interface *oi, struct ospf_header *ospfh) -{ - struct crypt_key *ck; - uint16_t iface_auth_type; - uint16_t pkt_auth_type = ntohs(ospfh->auth_type); - - switch (pkt_auth_type) { - case OSPF_AUTH_NULL: /* RFC2328 D.5.1 */ - if (OSPF_AUTH_NULL != (iface_auth_type = ospf_auth_type(oi))) { - if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) - flog_warn( - EC_OSPF_PACKET, - "interface %s: auth-type mismatch, local %s, rcvd Null, Router-ID %pI4", - IF_NAME(oi), - lookup_msg(ospf_auth_type_str, - iface_auth_type, NULL), - &ospfh->router_id); - return 0; - } - if (!ospf_check_sum(ospfh)) { - if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) - flog_warn( - EC_OSPF_PACKET, - "interface %s: Null auth OK, but checksum error, Router-ID %pI4", - IF_NAME(oi), - &ospfh->router_id); - return 0; - } - return 1; - case OSPF_AUTH_SIMPLE: /* RFC2328 D.5.2 */ - if (OSPF_AUTH_SIMPLE - != (iface_auth_type = ospf_auth_type(oi))) { - if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) - flog_warn( - EC_OSPF_PACKET, - "interface %s: auth-type mismatch, local %s, rcvd Simple, Router-ID %pI4", - IF_NAME(oi), - lookup_msg(ospf_auth_type_str, - iface_auth_type, NULL), - &ospfh->router_id); - return 0; - } - if (memcmp(OSPF_IF_PARAM(oi, auth_simple), ospfh->u.auth_data, - OSPF_AUTH_SIMPLE_SIZE)) { - if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) - flog_warn( - EC_OSPF_PACKET, - "interface %s: Simple auth failed, Router-ID %pI4", - IF_NAME(oi), &ospfh->router_id); - return 0; - } - if (!ospf_check_sum(ospfh)) { - if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) - flog_warn( - EC_OSPF_PACKET, - "interface %s: Simple auth OK, checksum error, Router-ID %pI4", - IF_NAME(oi), - &ospfh->router_id); - return 0; - } - return 1; - case OSPF_AUTH_CRYPTOGRAPHIC: /* RFC2328 D.5.3 */ - if (OSPF_AUTH_CRYPTOGRAPHIC - != (iface_auth_type = ospf_auth_type(oi))) { - if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) - flog_warn( - EC_OSPF_PACKET, - "interface %s: auth-type mismatch, local %s, rcvd Cryptographic, Router-ID %pI4", - IF_NAME(oi), - lookup_msg(ospf_auth_type_str, - iface_auth_type, NULL), - &ospfh->router_id); - return 0; - } - if (ospfh->checksum) { - if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) - flog_warn( - EC_OSPF_PACKET, - "interface %s: OSPF header checksum is not 0, Router-ID %pI4", - IF_NAME(oi), &ospfh->router_id); - return 0; - } - /* only MD5 crypto method can pass ospf_packet_examin() */ - if (NULL == (ck = listgetdata( - listtail(OSPF_IF_PARAM(oi, auth_crypt)))) - || ospfh->u.crypt.key_id != ck->key_id || - /* Condition above uses the last key ID on the list, - which is - different from what ospf_crypt_key_lookup() does. A - bug? */ - !ospf_check_md5_digest(oi, ospfh)) { - if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) - flog_warn( - EC_OSPF_MD5, - "interface %s: MD5 auth failed, Router-ID %pI4", - IF_NAME(oi), &ospfh->router_id); - return 0; - } - return 1; - default: - if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) - flog_warn( - EC_OSPF_PACKET, - "interface %s: invalid packet auth-type (%02x), Router-ID %pI4", - IF_NAME(oi), pkt_auth_type, &ospfh->router_id); - return 0; - } -} - -static int ospf_check_sum(struct ospf_header *ospfh) -{ - uint32_t ret; - uint16_t sum; - - /* clear auth_data for checksum. */ - memset(ospfh->u.auth_data, 0, OSPF_AUTH_SIMPLE_SIZE); - - /* keep checksum and clear. */ - sum = ospfh->checksum; - memset(&ospfh->checksum, 0, sizeof(uint16_t)); - - /* calculate checksum. */ - ret = in_cksum(ospfh, ntohs(ospfh->length)); - - if (ret != sum) { - zlog_info("%s: checksum mismatch, my %X, his %X", __func__, ret, - sum); - return 0; - } - - return 1; -} - /* Verify, that given link/TOS records are properly sized/aligned and match Router-LSA "# links" and "# TOS" fields as specified in RFC2328 A.4.2. */ static unsigned ospf_router_lsa_links_examin(struct router_lsa_link *link, @@ -2835,14 +2530,14 @@ static unsigned ospf_packet_examin(struct ospf_header *oh, if (ntohs(oh->auth_type) != OSPF_AUTH_CRYPTOGRAPHIC) bytesauth = 0; else { - if (oh->u.crypt.auth_data_len != OSPF_AUTH_MD5_SIZE) { + if (oh->u.crypt.auth_data_len > KEYCHAIN_MAX_HASH_SIZE) { if (IS_DEBUG_OSPF_PACKET(0, RECV)) zlog_debug( "%s: unsupported crypto auth length (%u B)", __func__, oh->u.crypt.auth_data_len); return MSG_NG; } - bytesauth = OSPF_AUTH_MD5_SIZE; + bytesauth = oh->u.crypt.auth_data_len; } if (bytesdeclared + bytesauth > bytesonwire) { if (IS_DEBUG_OSPF_PACKET(0, RECV)) @@ -2953,7 +2648,7 @@ static int ospf_verify_header(struct stream *ibuf, struct ospf_interface *oi, /* Check authentication. The function handles logging actions, where * required. */ - if (!ospf_check_auth(oi, ospfh)) + if (!ospf_auth_check(oi, iph, ospfh)) return -1; return 0; @@ -3261,44 +2956,6 @@ static void ospf_make_header(int type, struct ospf_interface *oi, stream_forward_endp(s, OSPF_HEADER_SIZE); } -/* Make Authentication Data. */ -static int ospf_make_auth(struct ospf_interface *oi, struct ospf_header *ospfh) -{ - struct crypt_key *ck; - - switch (ospf_auth_type(oi)) { - case OSPF_AUTH_NULL: - /* memset (ospfh->u.auth_data, 0, sizeof(ospfh->u.auth_data)); - */ - break; - case OSPF_AUTH_SIMPLE: - memcpy(ospfh->u.auth_data, OSPF_IF_PARAM(oi, auth_simple), - OSPF_AUTH_SIMPLE_SIZE); - break; - case OSPF_AUTH_CRYPTOGRAPHIC: - /* If key is not set, then set 0. */ - if (list_isempty(OSPF_IF_PARAM(oi, auth_crypt))) { - ospfh->u.crypt.zero = 0; - ospfh->u.crypt.key_id = 0; - ospfh->u.crypt.auth_data_len = OSPF_AUTH_MD5_SIZE; - } else { - ck = listgetdata( - listtail(OSPF_IF_PARAM(oi, auth_crypt))); - ospfh->u.crypt.zero = 0; - ospfh->u.crypt.key_id = ck->key_id; - ospfh->u.crypt.auth_data_len = OSPF_AUTH_MD5_SIZE; - } - /* note: the seq is done in ospf_make_md5_digest() */ - break; - default: - /* memset (ospfh->u.auth_data, 0, sizeof(ospfh->u.auth_data)); - */ - break; - } - - return 0; -} - /* Fill rest of OSPF header. */ static void ospf_fill_header(struct ospf_interface *oi, struct stream *s, uint16_t length) @@ -3317,7 +2974,9 @@ static void ospf_fill_header(struct ospf_interface *oi, struct stream *s, ospfh->checksum = 0; /* Add Authentication Data. */ - ospf_make_auth(oi, ospfh); + oi->keychain = NULL; + oi->key = NULL; + ospf_auth_make_data(oi, ospfh); } static int ospf_make_hello(struct ospf_interface *oi, struct stream *s) diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 158e90df59..8c3ad7f372 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -21,6 +21,7 @@ #include <lib/json.h> #include "defaults.h" #include "lib/printfrr.h" +#include "keychain.h" #include "ospfd/ospfd.h" #include "ospfd/ospf_asbr.h" @@ -40,6 +41,7 @@ #include "ospfd/ospf_bfd.h" #include "ospfd/ospf_ldp_sync.h" #include "ospfd/ospf_network.h" +#include "ospfd/ospf_memory.h" FRR_CFG_DEFAULT_BOOL(OSPF_LOG_ADJACENCY_CHANGES, { .val_bool = true, .match_profile = "datacenter", }, @@ -808,6 +810,8 @@ struct ospf_vl_config_data { char *auth_key; /* simple password if present */ int crypto_key_id; /* Cryptographic key ID */ char *md5_key; /* MD5 authentication key */ + char *keychain; /* Cryptographic keychain */ + int del_keychain; int hello_interval; /* Obvious what these are... */ int retransmit_interval; int transmit_delay; @@ -890,6 +894,10 @@ static int ospf_vl_set_security(struct ospf_vl_data *vl_data, strlcpy((char *)IF_DEF_PARAMS(ifp)->auth_simple, vl_config->auth_key, sizeof(IF_DEF_PARAMS(ifp)->auth_simple)); + } else if (vl_config->keychain) { + SET_IF_PARAM(IF_DEF_PARAMS(ifp), keychain_name); + XFREE(MTYPE_OSPF_IF_PARAMS, IF_DEF_PARAMS(ifp)->keychain_name); + IF_DEF_PARAMS(ifp)->keychain_name = XSTRDUP(MTYPE_OSPF_IF_PARAMS, vl_config->keychain); } else if (vl_config->md5_key) { if (ospf_crypt_key_lookup(IF_DEF_PARAMS(ifp)->auth_crypt, vl_config->crypto_key_id) @@ -918,6 +926,9 @@ static int ospf_vl_set_security(struct ospf_vl_data *vl_data, ospf_crypt_key_delete(IF_DEF_PARAMS(ifp)->auth_crypt, vl_config->crypto_key_id); + } else if (vl_config->del_keychain) { + UNSET_IF_PARAM(IF_DEF_PARAMS(ifp), keychain_name); + XFREE(MTYPE_OSPF_IF_PARAMS, IF_DEF_PARAMS(ifp)->keychain_name); } return CMD_SUCCESS; @@ -1022,9 +1033,11 @@ static int ospf_vl_set(struct ospf *ospf, struct ospf_vl_config_data *vl_config) DEFUN (ospf_area_vlink, ospf_area_vlink_cmd, - "area <A.B.C.D|(0-4294967295)> virtual-link A.B.C.D [authentication [<message-digest|null>]] [<message-digest-key (1-255) md5 KEY|authentication-key AUTH_KEY>]", + "area <A.B.C.D|(0-4294967295)> virtual-link A.B.C.D [authentication [<key-chain KEYCHAIN_NAME|message-digest|null>]] [<message-digest-key (1-255) md5 KEY|authentication-key AUTH_KEY>]", VLINK_HELPSTR_IPADDR "Enable authentication on this virtual link\n" + "Use a key-chain for cryptographic authentication keys\n" + "Key-chain name\n" "Use message-digest authentication\n" "Use null authentication\n" VLINK_HELPSTR_AUTH_MD5 @@ -1067,7 +1080,10 @@ DEFUN (ospf_area_vlink, vl_config.auth_type = OSPF_AUTH_SIMPLE; } - if (argv_find(argv, argc, "message-digest", &idx)) { + if (argv_find(argv, argc, "key-chain", &idx)) { + vl_config.auth_type = OSPF_AUTH_CRYPTOGRAPHIC; + vl_config.keychain = argv[idx+1]->arg; + } else if (argv_find(argv, argc, "message-digest", &idx)) { /* authentication message-digest */ vl_config.auth_type = OSPF_AUTH_CRYPTOGRAPHIC; } else if (argv_find(argv, argc, "null", &idx)) { @@ -1097,10 +1113,12 @@ DEFUN (ospf_area_vlink, DEFUN (no_ospf_area_vlink, no_ospf_area_vlink_cmd, - "no area <A.B.C.D|(0-4294967295)> virtual-link A.B.C.D [authentication [<message-digest|null>]] [<message-digest-key (1-255) md5 KEY|authentication-key AUTH_KEY>]", + "no area <A.B.C.D|(0-4294967295)> virtual-link A.B.C.D [authentication [<key-chain KEYCHAIN_NAME|message-digest|null>]] [<message-digest-key (1-255) md5 KEY|authentication-key AUTH_KEY>]", NO_STR VLINK_HELPSTR_IPADDR "Enable authentication on this virtual link\n" + "Use a key-chain for cryptographic authentication keys\n" + "Key-chain name\n" "Use message-digest authentication\n" "Use null authentication\n" VLINK_HELPSTR_AUTH_MD5 @@ -1160,6 +1178,11 @@ DEFUN (no_ospf_area_vlink, vl_config.auth_type = OSPF_AUTH_NOTSET; } + if (argv_find(argv, argc, "key-chain", &idx)) { + vl_config.del_keychain = 1; + vl_config.keychain = NULL; + } + if (argv_find(argv, argc, "message-digest-key", &idx)) { vl_config.md5_key = NULL; vl_config.crypto_key_id = strtol(argv[idx + 1]->arg, NULL, 10); @@ -3597,18 +3620,41 @@ static void ospf_interface_auth_show(struct vty *vty, struct ospf_interface *oi, case OSPF_AUTH_CRYPTOGRAPHIC: { struct crypt_key *ckey; - if (list_isempty(OSPF_IF_PARAM(oi, auth_crypt))) - return; - - ckey = listgetdata(listtail(OSPF_IF_PARAM(oi, auth_crypt))); - if (ckey) { + if (OSPF_IF_PARAM(oi, keychain_name)) { if (use_json) { json_object_string_add(json, "authentication", - "authenticationMessageDigest"); + "authenticationKeyChain"); + json_object_string_add(json, "keychain", + OSPF_IF_PARAM(oi, keychain_name)); } else { vty_out(vty, " Cryptographic authentication enabled\n"); - vty_out(vty, " Algorithm:MD5\n"); + struct keychain *keychain = keychain_lookup(OSPF_IF_PARAM(oi, keychain_name)); + + if (keychain) { + struct key *key = key_lookup_for_send(keychain); + + if (key) { + vty_out(vty, " Sending SA: Key %u, Algorithm %s - key chain %s\n", + key->index, keychain_get_algo_name_by_id(key->hash_algo), + OSPF_IF_PARAM(oi, keychain_name)); + } + } + } + } else { + if (list_isempty(OSPF_IF_PARAM(oi, auth_crypt))) + return; + + ckey = listgetdata(listtail(OSPF_IF_PARAM(oi, auth_crypt))); + if (ckey) { + if (use_json) { + json_object_string_add(json, "authentication", + "authenticationMessageDigest"); + } else { + vty_out(vty, + " Cryptographic authentication enabled\n"); + vty_out(vty, " Algorithm:MD5\n"); + } } } break; @@ -7535,24 +7581,26 @@ DEFPY (show_ip_ospf_database, DEFUN (ip_ospf_authentication_args, ip_ospf_authentication_args_addr_cmd, - "ip ospf authentication <null|message-digest> [A.B.C.D]", + "ip ospf authentication <null|message-digest|key-chain KEYCHAIN_NAME> [A.B.C.D]", "IP Information\n" "OSPF interface commands\n" "Enable authentication on this interface\n" "Use null authentication\n" "Use message-digest authentication\n" + "Use a key-chain for cryptographic authentication keys\n" + "Key-chain name\n" "Address of interface\n") { VTY_DECLVAR_CONTEXT(interface, ifp); int idx_encryption = 3; - int idx_ipv4 = 4; + int idx_ipv4 = argc-1; struct in_addr addr; int ret; struct ospf_if_params *params; params = IF_DEF_PARAMS(ifp); - if (argc == 5) { + if (argv[idx_ipv4]->type == IPV4_TKN) { ret = inet_aton(argv[idx_ipv4]->arg, &addr); if (!ret) { vty_out(vty, @@ -7575,6 +7623,17 @@ DEFUN (ip_ospf_authentication_args, if (argv[idx_encryption]->arg[0] == 'm') { SET_IF_PARAM(params, auth_type); params->auth_type = OSPF_AUTH_CRYPTOGRAPHIC; + UNSET_IF_PARAM(params, keychain_name); + XFREE(MTYPE_OSPF_IF_PARAMS, params->keychain_name); + return CMD_SUCCESS; + } + + if (argv[idx_encryption]->arg[0] == 'k') { + SET_IF_PARAM(params, auth_type); + params->auth_type = OSPF_AUTH_CRYPTOGRAPHIC; + SET_IF_PARAM(params, keychain_name); + params->keychain_name = XSTRDUP(MTYPE_OSPF_IF_PARAMS, argv[idx_encryption+1]->arg); + UNSET_IF_PARAM(params, auth_crypt); return CMD_SUCCESS; } @@ -7618,18 +7677,20 @@ DEFUN (ip_ospf_authentication, DEFUN (no_ip_ospf_authentication_args, no_ip_ospf_authentication_args_addr_cmd, - "no ip ospf authentication <null|message-digest> [A.B.C.D]", + "no ip ospf authentication <null|message-digest|key-chain [KEYCHAIN_NAME]> [A.B.C.D]", NO_STR "IP Information\n" "OSPF interface commands\n" "Enable authentication on this interface\n" "Use null authentication\n" "Use message-digest authentication\n" + "Use a key-chain for cryptographic authentication keys\n" + "Key-chain name\n" "Address of interface\n") { VTY_DECLVAR_CONTEXT(interface, ifp); int idx_encryption = 4; - int idx_ipv4 = 5; + int idx_ipv4 = argc-1; struct in_addr addr; int ret; struct ospf_if_params *params; @@ -7638,7 +7699,7 @@ DEFUN (no_ip_ospf_authentication_args, params = IF_DEF_PARAMS(ifp); - if (argc == 6) { + if (argv[idx_ipv4]->type == IPV4_TKN) { ret = inet_aton(argv[idx_ipv4]->arg, &addr); if (!ret) { vty_out(vty, @@ -7653,6 +7714,10 @@ DEFUN (no_ip_ospf_authentication_args, } params->auth_type = OSPF_AUTH_NOTSET; UNSET_IF_PARAM(params, auth_type); + + XFREE(MTYPE_OSPF_IF_PARAMS, params->keychain_name); + UNSET_IF_PARAM(params, keychain_name); + if (params != IF_DEF_PARAMS(ifp)) { ospf_free_if_params(ifp, addr); ospf_if_update_params(ifp, addr); @@ -7660,7 +7725,8 @@ DEFUN (no_ip_ospf_authentication_args, } else { if (argv[idx_encryption]->arg[0] == 'n') { auth_type = OSPF_AUTH_NULL; - } else if (argv[idx_encryption]->arg[0] == 'm') { + } else if (argv[idx_encryption]->arg[0] == 'm' || + argv[idx_encryption]->arg[0] == 'k') { auth_type = OSPF_AUTH_CRYPTOGRAPHIC; } else { vty_out(vty, "Unexpected input encountered\n"); @@ -7676,6 +7742,8 @@ DEFUN (no_ip_ospf_authentication_args, if (params->auth_type == auth_type) { params->auth_type = OSPF_AUTH_NOTSET; UNSET_IF_PARAM(params, auth_type); + XFREE(MTYPE_OSPF_IF_PARAMS, params->keychain_name); + UNSET_IF_PARAM(params, keychain_name); } for (rn = route_top(IF_OIFS_PARAMS(ifp)); rn; @@ -7684,6 +7752,8 @@ DEFUN (no_ip_ospf_authentication_args, if (params->auth_type == auth_type) { params->auth_type = OSPF_AUTH_NOTSET; UNSET_IF_PARAM(params, auth_type); + XFREE(MTYPE_OSPF_IF_PARAMS, params->keychain_name); + UNSET_IF_PARAM(params, keychain_name); if (params != IF_DEF_PARAMS(ifp)) { ospf_free_if_params( ifp, rn->p.u.prefix4); @@ -12098,11 +12168,11 @@ static const char *const ospf_int_type_str[] = { "loopback" }; -static const char *interface_config_auth_str(struct ospf_if_params *params) +static int interface_config_auth_str(struct ospf_if_params *params, char *buf) { if (!OSPF_IF_PARAM_CONFIGURED(params, auth_type) || params->auth_type == OSPF_AUTH_NOTSET) - return NULL; + return 0; /* Translation tables are not that much help * here due to syntax @@ -12110,16 +12180,22 @@ static const char *interface_config_auth_str(struct ospf_if_params *params) switch (params->auth_type) { case OSPF_AUTH_NULL: - return " null"; + snprintf(buf, BUFSIZ, " null"); + break; case OSPF_AUTH_SIMPLE: - return ""; + snprintf(buf, BUFSIZ, " "); + break; case OSPF_AUTH_CRYPTOGRAPHIC: - return " message-digest"; + if (OSPF_IF_PARAM_CONFIGURED(params, keychain_name)) + snprintf(buf, BUFSIZ, " key-chain %s", params->keychain_name); + else + snprintf(buf, BUFSIZ, " message-digest"); + break; } - return ""; + return 1; } static int config_write_interface_one(struct vty *vty, struct vrf *vrf) @@ -12129,7 +12205,8 @@ static int config_write_interface_one(struct vty *vty, struct vrf *vrf) struct crypt_key *ck; struct route_node *rn = NULL; struct ospf_if_params *params; - const char *auth_str; + char buf[BUFSIZ]; + int ret = 0; int write = 0; FOR_ALL_INTERFACES (vrf, ifp) { @@ -12170,10 +12247,10 @@ static int config_write_interface_one(struct vty *vty, struct vrf *vrf) } /* OSPF interface authentication print */ - auth_str = interface_config_auth_str(params); - if (auth_str) { + ret = interface_config_auth_str(params, buf); + if (ret) { vty_out(vty, " ip ospf authentication%s", - auth_str); + buf); if (params != IF_DEF_PARAMS(ifp) && rn) vty_out(vty, " %pI4", &rn->p.u.prefix4); @@ -12613,8 +12690,9 @@ static int config_write_virtual_link(struct vty *vty, struct ospf *ospf) { struct listnode *node; struct ospf_vl_data *vl_data; - const char *auth_str; char buf[INET_ADDRSTRLEN]; + char buf2[BUFSIZ]; + int ret = 0; /* Virtual-Link print */ for (ALL_LIST_ELEMENTS_RO(ospf->vlinks, node, vl_data)) { @@ -12647,12 +12725,12 @@ static int config_write_virtual_link(struct vty *vty, struct ospf *ospf) vty_out(vty, " area %s virtual-link %pI4\n", buf, &vl_data->vl_peer); /* Auth type */ - auth_str = interface_config_auth_str( - IF_DEF_PARAMS(oi->ifp)); - if (auth_str) + ret = interface_config_auth_str( + IF_DEF_PARAMS(oi->ifp), buf2); + if (ret) vty_out(vty, " area %s virtual-link %pI4 authentication%s\n", - buf, &vl_data->vl_peer, auth_str); + buf, &vl_data->vl_peer, buf2); /* Auth key */ if (IF_DEF_PARAMS(vl_data->vl_oi->ifp)->auth_simple[0] != '\0') diff --git a/ospfd/subdir.am b/ospfd/subdir.am index 44ee3b0f13..4803aae68d 100644 --- a/ospfd/subdir.am +++ b/ospfd/subdir.am @@ -56,6 +56,7 @@ ospfd_libfrrospf_a_SOURCES = \ ospfd/ospf_zebra.c \ ospfd/ospfd.c \ ospfd/ospf_gr_helper.c \ + ospfd/ospf_auth.c \ # end if OSPFD @@ -106,6 +107,7 @@ noinst_HEADERS += \ ospfd/ospf_te.h \ ospfd/ospf_vty.h \ ospfd/ospf_zebra.h \ + ospfd/ospf_auth.h \ # end ospfd_ospfd_LDADD = ospfd/libfrrospf.a ospfd/libfrrospfclient.a lib/libfrr.la $(LIBCAP) $(LIBM) diff --git a/pathd/path_pcep_cli.c b/pathd/path_pcep_cli.c index 6c660a3d08..9880081f35 100644 --- a/pathd/path_pcep_cli.c +++ b/pathd/path_pcep_cli.c @@ -52,6 +52,7 @@ static int pcep_cli_pce_config_write(struct vty *vty); static int pcep_cli_pcep_pce_config_write(struct vty *vty); /* Internal Util Function declarations */ +static void reset_pcc_peer(const char *peer_name); static struct pce_opts_cli *pcep_cli_find_pce(const char *pce_name); static bool pcep_cli_add_pce(struct pce_opts_cli *pce_opts_cli); static struct pce_opts_cli *pcep_cli_create_pce_opts(const char *name); @@ -741,64 +742,86 @@ static int path_pcep_cli_show_srte_pcep_pce(struct vty *vty, return CMD_SUCCESS; } -static int path_pcep_cli_peer_sr_draft07(struct vty *vty) +static int path_pcep_cli_peer_sr_draft07(struct vty *vty, bool reset) { struct pcep_config_group_opts *pce_config = NULL; + struct pce_opts *pce_opts = ¤t_pce_opts_g->pce_opts; + bool pce_in_use = false; if (vty->node == PCEP_PCE_NODE) { - /* TODO need to see if the pce is in use, and reset the - * connection */ pce_config = ¤t_pce_opts_g->pce_config_group_opts; current_pce_opts_g->merged = false; + pce_in_use = pcep_cli_pcc_has_pce(pce_opts->pce_name); } else if (vty->node == PCEP_PCE_CONFIG_NODE) { pce_config = current_pcep_config_group_opts_g; } else { return CMD_ERR_NO_MATCH; } - pce_config->draft07 = true; + pce_config->draft07 = reset ? DEFAULT_SR_DRAFT07 : true; + + if (pce_in_use) { + vty_out(vty, "%% PCE in use, resetting pcc peer session...\n"); + reset_pcc_peer(pce_opts->pce_name); + } return CMD_SUCCESS; } -static int path_pcep_cli_peer_pce_initiated(struct vty *vty) +static int path_pcep_cli_peer_pce_initiated(struct vty *vty, bool reset) { struct pcep_config_group_opts *pce_config = NULL; + struct pce_opts *pce_opts = ¤t_pce_opts_g->pce_opts; + bool pce_in_use = false; if (vty->node == PCEP_PCE_NODE) { - /* TODO need to see if the pce is in use, and reset the - * connection */ pce_config = ¤t_pce_opts_g->pce_config_group_opts; current_pce_opts_g->merged = false; + pce_in_use = pcep_cli_pcc_has_pce(pce_opts->pce_name); } else if (vty->node == PCEP_PCE_CONFIG_NODE) { pce_config = current_pcep_config_group_opts_g; } else { return CMD_ERR_NO_MATCH; } - pce_config->pce_initiated = true; + pce_config->pce_initiated = reset ? DEFAULT_PCE_INITIATED : true; + + if (pce_in_use) { + vty_out(vty, "%% PCE in use, resetting pcc peer session...\n"); + reset_pcc_peer(pce_opts->pce_name); + } return CMD_SUCCESS; } static int path_pcep_cli_peer_tcp_md5_auth(struct vty *vty, - const char *tcp_md5_auth) + const char *tcp_md5_auth, + bool reset) { struct pcep_config_group_opts *pce_config = NULL; + struct pce_opts *pce_opts = ¤t_pce_opts_g->pce_opts; + bool pce_in_use = false; if (vty->node == PCEP_PCE_NODE) { - /* TODO need to see if the pce is in use, and reset the - * connection */ pce_config = ¤t_pce_opts_g->pce_config_group_opts; current_pce_opts_g->merged = false; + pce_in_use = pcep_cli_pcc_has_pce(pce_opts->pce_name); } else if (vty->node == PCEP_PCE_CONFIG_NODE) { pce_config = current_pcep_config_group_opts_g; } else { return CMD_ERR_NO_MATCH; } - strlcpy(pce_config->tcp_md5_auth, tcp_md5_auth, - sizeof(pce_config->tcp_md5_auth)); + if (reset) + pce_config->tcp_md5_auth[0] = '\0'; + else + strlcpy(pce_config->tcp_md5_auth, tcp_md5_auth, + sizeof(pce_config->tcp_md5_auth)); + + if (pce_in_use) { + vty_out(vty, "%% PCE in use, resetting pcc peer session...\n"); + reset_pcc_peer(pce_opts->pce_name); + } return CMD_SUCCESS; } @@ -841,20 +864,29 @@ static int path_pcep_cli_peer_source_address(struct vty *vty, struct in_addr *ip, const char *ipv6_str, struct in6_addr *ipv6, - const char *port_str, long port) + const char *port_str, long port, + bool reset) { struct pcep_config_group_opts *pce_config = NULL; + struct pce_opts *pce_opts = ¤t_pce_opts_g->pce_opts; + bool pce_in_use = false; + if (vty->node == PCEP_PCE_NODE) { - /* TODO need to see if the pce is in use, and reset the - * connection */ pce_config = ¤t_pce_opts_g->pce_config_group_opts; current_pce_opts_g->merged = false; + pce_in_use = pcep_cli_pcc_has_pce(pce_opts->pce_name); } else if (vty->node == PCEP_PCE_CONFIG_NODE) { pce_config = current_pcep_config_group_opts_g; } else { return CMD_ERR_NO_MATCH; } + if (reset) { + pce_config->source_ip.ipa_type = IPADDR_NONE; + pce_config->source_port = 0; + return CMD_SUCCESS; + } + /* Handle the optional source IP */ if (ipv6_str != NULL) { pce_config->source_ip.ipa_type = IPADDR_V6; @@ -870,6 +902,11 @@ static int path_pcep_cli_peer_source_address(struct vty *vty, PCEP_VTYSH_INT_ARG_CHECK(port_str, port, pce_config->source_port, 0, 65535); + if (pce_in_use) { + vty_out(vty, "%% PCE in use, resetting pcc peer session...\n"); + reset_pcc_peer(pce_opts->pce_name); + } + return CMD_SUCCESS; } @@ -910,11 +947,13 @@ static int path_pcep_cli_peer_timers( const char *delegation_timeout_str, long delegation_timeout) { struct pcep_config_group_opts *pce_config = NULL; + struct pce_opts *pce_opts = ¤t_pce_opts_g->pce_opts; + bool pce_in_use = false; + if (vty->node == PCEP_PCE_NODE) { - /* TODO need to see if the pce is in use, and reset the - * connection */ pce_config = ¤t_pce_opts_g->pce_config_group_opts; current_pce_opts_g->merged = false; + pce_in_use = pcep_cli_pcc_has_pce(pce_opts->pce_name); } else if (vty->node == PCEP_PCE_CONFIG_NODE) { pce_config = current_pcep_config_group_opts_g; } else { @@ -952,6 +991,11 @@ static int path_pcep_cli_peer_timers( PCEP_VTYSH_INT_ARG_CHECK(delegation_timeout_str, delegation_timeout, pce_config->delegation_timeout_seconds, 0, 61); + if (pce_in_use) { + vty_out(vty, "%% PCE in use, resetting pcc peer session...\n"); + reset_pcc_peer(pce_opts->pce_name); + } + return CMD_SUCCESS; } @@ -982,6 +1026,37 @@ static int path_pcep_cli_pcc_pcc_msd(struct vty *vty, const char *msd_str, return CMD_SUCCESS; } +void reset_pcc_peer(const char *peer_name) +{ + struct pce_opts_cli *pce_opts_cli = pcep_cli_find_pce(peer_name); + + /* Remove the pcc peer */ + pcep_cli_remove_pce_connection(&pce_opts_cli->pce_opts); + struct pce_opts *pce_opts_copy = + XMALLOC(MTYPE_PCEP, sizeof(struct pce_opts)); + memcpy(pce_opts_copy, &pce_opts_cli->pce_opts, sizeof(struct pce_opts)); + pcep_ctrl_remove_pcc(pcep_g->fpt, pce_opts_copy); + + /* Re-add the pcc peer */ + pcep_cli_merge_pcep_pce_config_options(pce_opts_cli); + pcep_cli_add_pce_connection(&pce_opts_cli->pce_opts); + + /* Update the pcc_opts */ + struct pcc_opts *pcc_opts_copy = + XMALLOC(MTYPE_PCEP, sizeof(struct pcc_opts)); + memcpy(&pcc_opts_copy->addr, + &pce_opts_cli->pce_opts.config_opts.source_ip, + sizeof(pcc_opts_copy->addr)); + pcc_opts_copy->msd = pcc_msd_g; + pcc_opts_copy->port = pce_opts_cli->pce_opts.config_opts.source_port; + pcep_ctrl_update_pcc_options(pcep_g->fpt, pcc_opts_copy); + + /* Update the pce_opts */ + pce_opts_copy = XMALLOC(MTYPE_PCEP, sizeof(struct pce_opts)); + memcpy(pce_opts_copy, &pce_opts_cli->pce_opts, sizeof(struct pce_opts)); + pcep_ctrl_update_pce_options(pcep_g->fpt, pce_opts_copy); +} + static int path_pcep_cli_pcc_pcc_peer(struct vty *vty, const char *peer_name, const char *precedence_str, long precedence) @@ -1810,27 +1885,30 @@ DEFPY(pcep_cli_show_srte_pcep_pce, DEFPY(pcep_cli_peer_sr_draft07, pcep_cli_peer_sr_draft07_cmd, - "sr-draft07", + "[no] sr-draft07", + NO_STR "Configure PCC to send PCEP Open with SR draft07\n") { - return path_pcep_cli_peer_sr_draft07(vty); + return path_pcep_cli_peer_sr_draft07(vty, no); } DEFPY(pcep_cli_peer_pce_initiated, pcep_cli_peer_pce_initiated_cmd, - "pce-initiated", + "[no] pce-initiated", + NO_STR "Configure PCC to accept PCE initiated LSPs\n") { - return path_pcep_cli_peer_pce_initiated(vty); + return path_pcep_cli_peer_pce_initiated(vty, no); } DEFPY(pcep_cli_peer_tcp_md5_auth, pcep_cli_peer_tcp_md5_auth_cmd, - "tcp-md5-auth WORD", + "[no] tcp-md5-auth WORD", + NO_STR "Configure PCC TCP-MD5 RFC2385 Authentication\n" "TCP-MD5 Authentication string\n") { - return path_pcep_cli_peer_tcp_md5_auth(vty, tcp_md5_auth); + return path_pcep_cli_peer_tcp_md5_auth(vty, tcp_md5_auth, no); } DEFPY(pcep_cli_peer_address, @@ -1850,7 +1928,8 @@ DEFPY(pcep_cli_peer_address, DEFPY(pcep_cli_peer_source_address, pcep_cli_peer_source_address_cmd, - "source-address [ip A.B.C.D | ipv6 X:X::X:X] [port (1024-65535)]", + "[no] source-address [ip A.B.C.D | ipv6 X:X::X:X] [port (1024-65535)]", + NO_STR "PCE source IP Address configuration\n" "PCE source IPv4 address\n" "PCE source IPv4 address value\n" @@ -1860,7 +1939,7 @@ DEFPY(pcep_cli_peer_source_address, "Source PCE server port value\n") { return path_pcep_cli_peer_source_address(vty, ip_str, &ip, ipv6_str, - &ipv6, port_str, port); + &ipv6, port_str, port, no); } DEFPY(pcep_cli_peer_pcep_pce_config_ref, diff --git a/python/xref2vtysh.py b/python/xref2vtysh.py index 6dd5c8866e..0a7e28ec7a 100644 --- a/python/xref2vtysh.py +++ b/python/xref2vtysh.py @@ -36,7 +36,7 @@ daemon_flags = { "lib/filter.c": "VTYSH_ACL", "lib/filter_cli.c": "VTYSH_ACL", "lib/if.c": "VTYSH_INTERFACE", - "lib/keychain.c": "VTYSH_RIPD|VTYSH_EIGRPD|VTYSH_OSPF6D", + "lib/keychain.c": "VTYSH_KEYS", "lib/mgmt_be_client.c": "VTYSH_STATICD", "lib/mgmt_fe_client.c": "VTYSH_MGMTD", "lib/lib_vty.c": "VTYSH_ALL", diff --git a/tests/topotests/bgp_dynamic_capability/r1/bgpd.conf b/tests/topotests/bgp_dynamic_capability/r1/bgpd.conf index ad2fe4cd0d..3d2c611775 100644 --- a/tests/topotests/bgp_dynamic_capability/r1/bgpd.conf +++ b/tests/topotests/bgp_dynamic_capability/r1/bgpd.conf @@ -4,6 +4,7 @@ router bgp 65001 no bgp ebgp-requires-policy bgp graceful-restart + bgp long-lived stale-time 10 neighbor 192.168.1.2 remote-as external neighbor 192.168.1.2 timers 1 3 neighbor 192.168.1.2 timers connect 1 diff --git a/tests/topotests/bgp_dynamic_capability/r2/bgpd.conf b/tests/topotests/bgp_dynamic_capability/r2/bgpd.conf index 9b7292163f..46f0b21a14 100644 --- a/tests/topotests/bgp_dynamic_capability/r2/bgpd.conf +++ b/tests/topotests/bgp_dynamic_capability/r2/bgpd.conf @@ -3,6 +3,7 @@ ! router bgp 65002 bgp graceful-restart + bgp long-lived stale-time 20 no bgp ebgp-requires-policy neighbor 192.168.1.1 remote-as external neighbor 192.168.1.1 timers 1 3 diff --git a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_graceful_restart.py b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_graceful_restart.py index a7feb3c580..846a68ea27 100644 --- a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_graceful_restart.py +++ b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_graceful_restart.py @@ -6,8 +6,9 @@ # """ -Test if BGP graceful restart capability's restart time and notification -flag are exchanged dynamically. +Test if BGP graceful restart / long-lived graceful restart capabilities +(restart time, stale time and notification flag) are exchanged dynamically +via BGP dynamic capability. """ import os @@ -70,11 +71,18 @@ def test_bgp_dynamic_capability_graceful_restart(): "neighborCapabilities": { "dynamic": "advertisedAndReceived", "gracefulRestart": "advertisedAndReceived", + "longLivedGracefulRestart": "advertisedAndReceived", }, "gracefulRestartInfo": { "nBit": True, "timers": { "receivedRestartTimer": 120, + "configuredLlgrStaleTime": 10, + }, + "ipv4Unicast": { + "timers": { + "llgrStaleTime": 10, + } }, }, "connectionsEstablished": 1, @@ -89,17 +97,20 @@ def test_bgp_dynamic_capability_graceful_restart(): _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) assert result is None, "Can't converge" - step("Change Graceful-Restart restart-time, and check if it's changed dynamically") + step( + "Change Graceful-Restart restart-time, LLGR stale-time and check if they changed dynamically" + ) r2.vtysh_cmd( """ configure terminal router bgp bgp graceful-restart restart-time 123 + bgp long-lived-graceful-restart stale-time 5 """ ) - def _bgp_check_if_session_not_reset_after_changing_restart_time(): + def _bgp_check_if_session_not_reset_after_changing_gr_settings(): output = json.loads(r1.vtysh_cmd("show bgp neighbor json")) expected = { "192.168.1.2": { @@ -107,11 +118,18 @@ def test_bgp_dynamic_capability_graceful_restart(): "neighborCapabilities": { "dynamic": "advertisedAndReceived", "gracefulRestart": "advertisedAndReceived", + "longLivedGracefulRestart": "advertisedAndReceived", }, "gracefulRestartInfo": { "nBit": True, "timers": { "receivedRestartTimer": 123, + "configuredLlgrStaleTime": 10, + }, + "ipv4Unicast": { + "timers": { + "llgrStaleTime": 5, + } }, }, "connectionsEstablished": 1, @@ -121,7 +139,7 @@ def test_bgp_dynamic_capability_graceful_restart(): return topotest.json_cmp(output, expected) test_func = functools.partial( - _bgp_check_if_session_not_reset_after_changing_restart_time, + _bgp_check_if_session_not_reset_after_changing_gr_settings, ) _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) assert ( @@ -148,11 +166,18 @@ def test_bgp_dynamic_capability_graceful_restart(): "neighborCapabilities": { "dynamic": "advertisedAndReceived", "gracefulRestart": "advertisedAndReceived", + "longLivedGracefulRestart": "advertisedAndReceived", }, "gracefulRestartInfo": { "nBit": False, "timers": { "receivedRestartTimer": 123, + "configuredLlgrStaleTime": 10, + }, + "ipv4Unicast": { + "timers": { + "llgrStaleTime": 5, + } }, }, "connectionsEstablished": 1, diff --git a/tests/topotests/bgp_remove_private_as_route_map/__init__.py b/tests/topotests/bgp_remove_private_as_route_map/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_remove_private_as_route_map/__init__.py diff --git a/tests/topotests/bgp_remove_private_as_route_map/r1/frr.conf b/tests/topotests/bgp_remove_private_as_route_map/r1/frr.conf new file mode 100644 index 0000000000..b2dba7d3fe --- /dev/null +++ b/tests/topotests/bgp_remove_private_as_route_map/r1/frr.conf @@ -0,0 +1,10 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 1 3 + neighbor 192.168.1.2 timers connect 1 +! diff --git a/tests/topotests/bgp_remove_private_as_route_map/r2/frr.conf b/tests/topotests/bgp_remove_private_as_route_map/r2/frr.conf new file mode 100644 index 0000000000..9c423cea8c --- /dev/null +++ b/tests/topotests/bgp_remove_private_as_route_map/r2/frr.conf @@ -0,0 +1,19 @@ +! +int r2-eth0 + ip address 192.168.1.2/24 + ip address 192.168.2.1/32 +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 + address-family ipv4 unicast + redistribute connected + neighbor 192.168.1.1 route-map r1 out + neighbor 192.168.1.1 remove-private-AS all + exit-address-family +! +route-map r1 permit 10 + set as-path prepend 65123 4200000001 +! diff --git a/tests/topotests/bgp_remove_private_as_route_map/test_bgp_remove_private_as_route_map.py b/tests/topotests/bgp_remove_private_as_route_map/test_bgp_remove_private_as_route_map.py new file mode 100644 index 0000000000..2ae6f7fc9e --- /dev/null +++ b/tests/topotests/bgp_remove_private_as_route_map/test_bgp_remove_private_as_route_map.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# + +""" +Test if private AS is removed from AS_PATH attribute when route-map is used (prepend). +""" + +import os +import re +import sys +import json +import pytest +import functools + +pytestmark = pytest.mark.bgpd + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_remove_private_as_route_map(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + def _check_routes(): + output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast json")) + expected = { + "routes": { + "192.168.2.1/32": [ + { + "valid": True, + "path": "65002", + } + ] + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _check_routes, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert ( + result is None + ), "65123 4200000001 ASNs should be removed from AS_PATH attribute" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/lib/ospf.py b/tests/topotests/lib/ospf.py index 5486e904df..5b18f8b679 100644 --- a/tests/topotests/lib/ospf.py +++ b/tests/topotests/lib/ospf.py @@ -426,6 +426,10 @@ def config_ospf_interface( cmd = "ip ospf authentication null" elif data_ospf_auth == "message-digest": cmd = "ip ospf authentication message-digest" + elif data_ospf_auth == "key-chain": + cmd = "ip ospf authentication key-chain {}".format( + ospf_data["keychain"] + ) else: cmd = "ip ospf authentication" diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py b/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py index 88219b8400..8dd103013b 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py @@ -64,7 +64,9 @@ TOPOOLOGY = TESTCASES = 1. Verify ospf authentication with Simple password authentication. 2. Verify ospf authentication with MD5 authentication. -3. Verify ospf authentication with different authentication methods. +3. Verify ospf authentication with MD5 keychain authentication. +4. Verify ospf authentication with SHA256 keychain authentication. +5. Verify ospf authentication with different authentication methods. """ @@ -535,7 +537,477 @@ def test_ospf_authentication_md5_tc29_p1(request): write_test_footer(tc_name) -def test_ospf_authentication_different_auths_tc30_p1(request): +def test_ospf_authentication_md5_keychain_tc30_p1(request): + """ + OSPF Authentication - Verify ospf authentication with MD5 authentication. + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + global topo + step("Bring up the base config.") + reset_config_on_routers(tgen) + step( + "Configure ospf with on R1 and R2, enable ospf on R1 interface " + "connected to R2 with message-digest authentication using ip " + "ospf authentication key-chain cmd." + ) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + + router1.vtysh_cmd( + """configure terminal + key chain auth + key 10 + key-string ospf + cryptographic-algorithm md5""" + ) + + r1_ospf_auth = { + "r1": { + "links": { + "r2": { + "ospf": { + "authentication": "key-chain", + "keychain": "auth", + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r1_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify that the neighbour is not FULL between R1 and R2.") + # wait for dead time expiry. + sleep(6) + dut = "r1" + ospf_covergence = verify_ospf_neighbor( + tgen, topo, dut=dut, expected=False, retry_timeout=6 + ) + assert ospf_covergence is not True, "Testcase Failed \n Error {}".format( + ospf_covergence + ) + + step( + "On R2 enable ospf on interface with message-digest authentication" + " using ip ospf authentication message-digest password cmd." + ) + + router2.vtysh_cmd( + """configure terminal + key chain auth + key 10 + key-string ospf + cryptographic-algorithm md5""" + ) + + r2_ospf_auth = { + "r2": { + "links": { + "r1": { + "ospf": { + "authentication": "key-chain", + "keychain": "auth", + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the neighbour is FULL between R1 and R2 " + "using show ip ospf neighbor cmd." + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( + ospf_covergence + ) + + step( + "Disable message-digest authentication on R2 using no ip ospf " + "authentication key-chain cmd." + ) + + r2_ospf_auth = { + "r2": { + "links": { + "r1": { + "ospf": { + "authentication": "key-chain", + "keychain": "auth", + "del_action": True, + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify on R1 ,nbr is deleted for R2 after dead interval expiry") + # wait till the dead timer expiry + sleep(6) + dut = "r2" + ospf_covergence = verify_ospf_neighbor( + tgen, topo, dut=dut, expected=False, retry_timeout=10 + ) + assert ospf_covergence is not True, "Testcase Failed \n Error {}".format( + ospf_covergence + ) + + step("Again On R2 enable ospf on interface with key-chain auth") + r2_ospf_auth = { + "r2": { + "links": { + "r1": { + "ospf": { + "authentication": "key-chain", + "keychain": "auth", + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the neighbour is FULL between R1 and R2 using" + " show ip ospf neighbor cmd." + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( + ospf_covergence + ) + + step("Shut no shut interface on R1") + dut = "r1" + intf = topo["routers"]["r1"]["links"]["r2"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + + dut = "r2" + step( + "Verify that the neighbour is not FULL between R1 and R2 using " + "show ip ospf neighbor cmd." + ) + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False) + assert ospf_covergence is not True, "Testcase Failed \n Error {}".format( + ospf_covergence + ) + + dut = "r1" + shutdown_bringup_interface(tgen, dut, intf, True) + + step( + "Verify that the neighbour is FULL between R1 and R2 using " + "show ip ospf neighbor cmd." + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( + ospf_covergence + ) + + step("Change Ip address on R1 and R2") + + topo_modify_change_ip = deepcopy(topo) + + intf_ip = topo_modify_change_ip["routers"]["r1"]["links"]["r2"]["ipv4"] + + topo_modify_change_ip["routers"]["r1"]["links"]["r2"]["ipv4"] = str( + IPv4Address(frr_unicode(intf_ip.split("/")[0])) + 3 + ) + "/{}".format(intf_ip.split("/")[1]) + + build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False) + + reset_config_on_routers(tgen, routerName="r1") + dut = "r1" + intf = topo["routers"]["r1"]["links"]["r2"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + shutdown_bringup_interface(tgen, dut, intf, True) + clear_ospf(tgen, "r1") + router1.vtysh_cmd( + """configure terminal + key chain auth + key 10 + key-string ospf + cryptographic-algorithm md5""" + ) + r1_ospf_auth = { + "r1": { + "links": { + "r2": { + "ospf": { + "authentication": "key-chain", + "keychain": "auth", + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r1_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the neighbour is FULL between R1 and R2 with new " + "ip address using show ip ospf " + ) + + dut = "r1" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( + ospf_covergence + ) + + write_test_footer(tc_name) + + +def test_ospf_authentication_sha256_keychain_tc32_p1(request): + """ + OSPF Authentication - Verify ospf authentication with MD5 authentication. + + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + global topo + step("Bring up the base config.") + reset_config_on_routers(tgen) + step( + "Configure ospf with on R1 and R2, enable ospf on R1 interface " + "connected to R2 with message-digest authentication using ip " + "ospf authentication key-chain cmd." + ) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + + router1.vtysh_cmd( + """configure terminal + key chain auth + key 10 + key-string ospf + cryptographic-algorithm hmac-sha-256""" + ) + + r1_ospf_auth = { + "r1": { + "links": { + "r2": { + "ospf": { + "authentication": "key-chain", + "keychain": "auth", + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r1_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify that the neighbour is not FULL between R1 and R2.") + # wait for dead time expiry. + sleep(6) + dut = "r1" + ospf_covergence = verify_ospf_neighbor( + tgen, topo, dut=dut, expected=False, retry_timeout=6 + ) + assert ospf_covergence is not True, "Testcase Failed \n Error {}".format( + ospf_covergence + ) + + step( + "On R2 enable ospf on interface with message-digest authentication" + " using ip ospf authentication message-digest password cmd." + ) + + router2.vtysh_cmd( + """configure terminal + key chain auth + key 10 + key-string ospf + cryptographic-algorithm hmac-sha-256""" + ) + + r2_ospf_auth = { + "r2": { + "links": { + "r1": { + "ospf": { + "authentication": "key-chain", + "keychain": "auth", + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the neighbour is FULL between R1 and R2 " + "using show ip ospf neighbor cmd." + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( + ospf_covergence + ) + + step( + "Disable message-digest authentication on R2 using no ip ospf " + "authentication key-chain cmd." + ) + + r2_ospf_auth = { + "r2": { + "links": { + "r1": { + "ospf": { + "authentication": "key-chain", + "keychain": "auth", + "del_action": True, + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify on R1 ,nbr is deleted for R2 after dead interval expiry") + # wait till the dead timer expiry + sleep(6) + dut = "r2" + ospf_covergence = verify_ospf_neighbor( + tgen, topo, dut=dut, expected=False, retry_timeout=10 + ) + assert ospf_covergence is not True, "Testcase Failed \n Error {}".format( + ospf_covergence + ) + + step("Again On R2 enable ospf on interface with key-chain auth") + r2_ospf_auth = { + "r2": { + "links": { + "r1": { + "ospf": { + "authentication": "key-chain", + "keychain": "auth", + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r2_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the neighbour is FULL between R1 and R2 using" + " show ip ospf neighbor cmd." + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( + ospf_covergence + ) + + step("Shut no shut interface on R1") + dut = "r1" + intf = topo["routers"]["r1"]["links"]["r2"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + + dut = "r2" + step( + "Verify that the neighbour is not FULL between R1 and R2 using " + "show ip ospf neighbor cmd." + ) + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False) + assert ospf_covergence is not True, "Testcase Failed \n Error {}".format( + ospf_covergence + ) + + dut = "r1" + shutdown_bringup_interface(tgen, dut, intf, True) + + step( + "Verify that the neighbour is FULL between R1 and R2 using " + "show ip ospf neighbor cmd." + ) + + dut = "r2" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( + ospf_covergence + ) + + step("Change Ip address on R1 and R2") + + topo_modify_change_ip = deepcopy(topo) + + intf_ip = topo_modify_change_ip["routers"]["r1"]["links"]["r2"]["ipv4"] + + topo_modify_change_ip["routers"]["r1"]["links"]["r2"]["ipv4"] = str( + IPv4Address(frr_unicode(intf_ip.split("/")[0])) + 3 + ) + "/{}".format(intf_ip.split("/")[1]) + + build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False) + + reset_config_on_routers(tgen, routerName="r1") + dut = "r1" + intf = topo["routers"]["r1"]["links"]["r2"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + shutdown_bringup_interface(tgen, dut, intf, True) + clear_ospf(tgen, "r1") + router1.vtysh_cmd( + """configure terminal + key chain auth + key 10 + key-string ospf + cryptographic-algorithm hmac-sha-256""" + ) + r1_ospf_auth = { + "r1": { + "links": { + "r2": { + "ospf": { + "authentication": "key-chain", + "keychain": "auth", + } + } + } + } + } + result = config_ospf_interface(tgen, topo, r1_ospf_auth) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that the neighbour is FULL between R1 and R2 with new " + "ip address using show ip ospf " + ) + + dut = "r1" + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( + ospf_covergence + ) + + write_test_footer(tc_name) + + +def test_ospf_authentication_different_auths_tc35_p1(request): """ OSPF Authentication - Verify ospf authentication with different authentication methods. @@ -553,6 +1025,9 @@ def test_ospf_authentication_different_auths_tc30_p1(request): "ospf authentication message-digest cmd." ) + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + r1_ospf_auth = { "r1": { "links": { @@ -769,16 +1244,23 @@ def test_ospf_authentication_different_auths_tc30_p1(request): ospf_covergence ) - step("Enable Md5 authentication on the interface") + step("Enable SHA-256 authentication on the interface") + + router1.vtysh_cmd( + """configure terminal + key chain auth + key 10 + key-string ospf + cryptographic-algorithm hmac-sha-256""" + ) r1_ospf_auth = { "r1": { "links": { "r2": { "ospf": { - "authentication": "message-digest", - "authentication-key": "ospf", - "message-digest-key": "10", + "authentication": "key-chain", + "keychain": "auth", } } } @@ -787,14 +1269,21 @@ def test_ospf_authentication_different_auths_tc30_p1(request): result = config_ospf_interface(tgen, topo, r1_ospf_auth) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + router2.vtysh_cmd( + """configure terminal + key chain auth + key 10 + key-string ospf + cryptographic-algorithm hmac-sha-256""" + ) + r2_ospf_auth = { "r2": { "links": { "r1": { "ospf": { - "authentication": "message-digest", - "authentication-key": "ospf", - "message-digest-key": "10", + "authentication": "key-chain", + "keychain": "auth", } } } @@ -814,39 +1303,27 @@ def test_ospf_authentication_different_auths_tc30_p1(request): ospf_covergence ) - step("Change the MD5 authentication password") + step("Change the SHA-256 authentication password") - r1_ospf_auth = { - "r1": { - "links": { - "r2": { - "ospf": { - "authentication": "message-digest", - "authentication-key": "OSPFv4", - "message-digest-key": "10", - } - } - } - } - } - result = config_ospf_interface(tgen, topo, r1_ospf_auth) - assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + router1.vtysh_cmd( + """configure terminal + key chain auth + key 10 + key-string OSPFv4 + cryptographic-algorithm hmac-sha-512""" + ) - r2_ospf_auth = { - "r2": { - "links": { - "r1": { - "ospf": { - "authentication": "message-digest", - "authentication-key": "OSPFv4", - "message-digest-key": "10", - } - } - } - } - } - result = config_ospf_interface(tgen, topo, r2_ospf_auth) - assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + router2.vtysh_cmd( + """configure terminal + key chain auth + key 10 + key-string OSPFv4 + cryptographic-algorithm hmac-sha-512""" + ) + ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( + ospf_covergence + ) write_test_footer(tc_name) diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index 9d67003f35..65733ca61b 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -59,7 +59,7 @@ extern struct event_loop *master; VTYSH_VRRPD #define VTYSH_INTERFACE VTYSH_INTERFACE_SUBSET | VTYSH_BGPD #define VTYSH_VRF VTYSH_INTERFACE_SUBSET | VTYSH_MGMTD -#define VTYSH_KEYS VTYSH_RIPD | VTYSH_EIGRPD | VTYSH_OSPF6D +#define VTYSH_KEYS VTYSH_RIPD | VTYSH_EIGRPD | VTYSH_OSPF6D | VTYSH_OSPFD /* Daemons who can process nexthop-group configs */ #define VTYSH_NH_GROUP VTYSH_PBRD|VTYSH_SHARPD #define VTYSH_SR VTYSH_ZEBRA|VTYSH_PATHD |
