From 166f9103d3e97f84d32e0e176beac79daa0b1d33 Mon Sep 17 00:00:00 2001 From: Abhinay Ramesh Date: Tue, 11 May 2021 08:43:25 +0000 Subject: [PATCH 1/9] lib: Support auto completion of configured keychain. Problem Statement: ================= When modules use keychain there is no option for auto completion of configured keychains. RCA: ==== Not implemented. Fix: ==== Changes to support auto completion of configured keychain names. Risk: ===== Low risk Tests Executed: =============== Have tested auto completion of configured keychain names with newly implemented auth CLI. frr(config-if)# ipv6 ospf6 authentication keychain KEYCHAIN_NAME Keychain name abcd pqr 12345 Signed-off-by: Abhinay Ramesh --- lib/keychain.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/keychain.c b/lib/keychain.c index 02f83ef0a8..1d678b949e 100644 --- a/lib/keychain.c +++ b/lib/keychain.c @@ -1051,10 +1051,28 @@ static int keychain_config_write(struct vty *vty) return 0; } +static void keychain_active_config(vector comps, struct cmd_token *token) +{ + struct keychain *keychain; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(keychain_list, node, keychain)) + vector_set(comps, XSTRDUP(MTYPE_COMPLETION, keychain->name)); +} + +static const struct cmd_variable_handler keychain_var_handlers[] = { + {.varname = "key_chain", .completions = keychain_active_config}, + {.tokenname = "KEYCHAIN_NAME", .completions = keychain_active_config}, + {.tokenname = "KCHAIN_NAME", .completions = keychain_active_config}, + {.completions = NULL} +}; + void keychain_init(void) { keychain_list = list_new(); + /* Register handler for keychain auto config support */ + cmd_variable_handler_register(keychain_var_handlers); install_node(&keychain_node); install_node(&keychain_key_node); From 72000880b02706f2375095e5ecad9f939840d62b Mon Sep 17 00:00:00 2001 From: Abhinay Ramesh Date: Tue, 11 May 2021 10:00:38 +0000 Subject: [PATCH 2/9] lib: Changes to support hash algo in keychain. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem Statement: ================== Currently there is no support for configuring hash algorithm in keychain.    RCA: ==== Not implemented yet.   Fix: ==== Changes are done to configure hash algorithm as part of keychain. which will easy the configuration from modules using keychain.   Risk: ===== Low risk   Tests Executed: =============== Have tested the configuration and unconfiguration flow for newly implemented CLI. ! key chain abcd key 100 key-string password cryptographic-algorithm sha1 exit key 200 key-string password cryptographic-algorithm sha256 exit ! Signed-off-by: Abhinay Ramesh --- lib/keychain.c | 137 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/keychain.h | 45 +++++++++++++++- 2 files changed, 180 insertions(+), 2 deletions(-) diff --git a/lib/keychain.c b/lib/keychain.c index 1d678b949e..6b03fbd560 100644 --- a/lib/keychain.c +++ b/lib/keychain.c @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "config.h" #include #include "command.h" @@ -207,6 +208,7 @@ static struct key *key_get(const struct keychain *keychain, uint32_t index) key = key_new(); key->index = index; + key->hash_algo = KEYCHAIN_ALGO_NULL; listnode_add_sort(keychain->key, key); return key; @@ -336,6 +338,133 @@ DEFUN (no_key_string, return CMD_SUCCESS; } +const struct keychain_algo_info algo_info[] = { + {KEYCHAIN_ALGO_NULL, "null", 0, 0, "NULL"}, + {KEYCHAIN_ALGO_MD5, "md5", KEYCHAIN_MD5_HASH_SIZE, + KEYCHAIN_ALGO_MD5_INTERNAL_BLK_SIZE, "MD5"}, + {KEYCHAIN_ALGO_HMAC_SHA1, "hmac-sha-1", KEYCHAIN_HMAC_SHA1_HASH_SIZE, + KEYCHAIN_ALGO_SHA1_INTERNAL_BLK_SIZE, "HMAC-SHA-1"}, + {KEYCHAIN_ALGO_HMAC_SHA256, "hmac-sha-256", + KEYCHAIN_HMAC_SHA256_HASH_SIZE, KEYCHAIN_ALGO_SHA256_INTERNAL_BLK_SIZE, + "HMAC-SHA-256"}, + {KEYCHAIN_ALGO_HMAC_SHA384, "hmac-sha-384", + KEYCHAIN_HMAC_SHA384_HASH_SIZE, KEYCHAIN_ALGO_SHA384_INTERNAL_BLK_SIZE, + "HMAC-SHA-384"}, + {KEYCHAIN_ALGO_HMAC_SHA512, "hmac-sha-512", + KEYCHAIN_HMAC_SHA512_HASH_SIZE, KEYCHAIN_ALGO_SHA512_INTERNAL_BLK_SIZE, + "HMAC-SHA-512"}, + {KEYCHAIN_ALGO_MAX, "max", KEYCHAIN_MAX_HASH_SIZE, + KEYCHAIN_ALGO_MAX_INTERNAL_BLK_SIZE, "Not defined"} +}; + +uint32_t keychain_get_block_size(enum keychain_hash_algo key) +{ + return algo_info[key].block; +} + +uint32_t keychain_get_hash_len(enum keychain_hash_algo key) +{ + return algo_info[key].length; +} + +const char *keychain_get_description(enum keychain_hash_algo key) +{ + return algo_info[key].desc; +} + +struct keychain_algo_info +keychain_get_hash_algo_info(enum keychain_hash_algo key) +{ + return algo_info[key]; +} + +enum keychain_hash_algo keychain_get_algo_id_by_name(const char *name) +{ +#ifdef CRYPTO_INTERNAL + if (!strncmp(name, "hmac-sha-2", 10)) + return KEYCHAIN_ALGO_HMAC_SHA256; + else if (!strncmp(name, "m", 1)) + return KEYCHAIN_ALGO_MD5; + else + return KEYCHAIN_ALGO_NULL; +#else + if (!strncmp(name, "m", 1)) + return KEYCHAIN_ALGO_MD5; + else if (!strncmp(name, "hmac-sha-1", 10)) + return KEYCHAIN_ALGO_HMAC_SHA1; + else if (!strncmp(name, "hmac-sha-2", 10)) + return KEYCHAIN_ALGO_HMAC_SHA256; + else if (!strncmp(name, "hmac-sha-3", 10)) + return KEYCHAIN_ALGO_HMAC_SHA384; + else if (!strncmp(name, "hmac-sha-5", 10)) + return KEYCHAIN_ALGO_HMAC_SHA512; + else + return KEYCHAIN_ALGO_NULL; +#endif +} + +const char *keychain_get_algo_name_by_id(enum keychain_hash_algo key) +{ + return algo_info[key].name; +} + +DEFUN(cryptographic_algorithm, cryptographic_algorithm_cmd, + "cryptographic-algorithm " + "", + "Cryptographic-algorithm\n" + "Use MD5 algorithm\n" + "Use HMAC-SHA-1 algorithm\n" + "Use HMAC-SHA-256 algorithm\n" + "Use HMAC-SHA-384 algorithm\n" + "Use HMAC-SHA-512 algorithm\n") +{ + int algo_idx = 1; + uint8_t hash_algo = KEYCHAIN_ALGO_NULL; + + VTY_DECLVAR_CONTEXT_SUB(key, key); + hash_algo = keychain_get_algo_id_by_name(argv[algo_idx]->arg); +#ifndef CRYPTO_OPENSSL + if (hash_algo == KEYCHAIN_ALGO_NULL) { + vty_out(vty, + "Hash algorithm not supported, compile with --with-crypto=openssl\n"); + return CMD_WARNING_CONFIG_FAILED; + } +#endif /* CRYPTO_OPENSSL */ + key->hash_algo = hash_algo; + return CMD_SUCCESS; +} + +DEFUN(no_cryptographic_algorithm, no_cryptographic_algorithm_cmd, + "no cryptographic-algorithm " + "[]", + NO_STR + "Cryptographic-algorithm\n" + "Use MD5 algorithm\n" + "Use HMAC-SHA-1 algorithm\n" + "Use HMAC-SHA-256 algorithm\n" + "Use HMAC-SHA-384 algorithm\n" + "Use HMAC-SHA-512 algorithm\n") +{ + int algo_idx = 2; + uint8_t hash_algo = KEYCHAIN_ALGO_NULL; + + VTY_DECLVAR_CONTEXT_SUB(key, key); + if (argc > algo_idx) { + hash_algo = keychain_get_algo_id_by_name(argv[algo_idx]->arg); + if (hash_algo == KEYCHAIN_ALGO_NULL) { + vty_out(vty, + "Hash algorithm not supported, try compiling with --with-crypto=openssl\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } + + if ((hash_algo != KEYCHAIN_ALGO_NULL) && (hash_algo != key->hash_algo)) + return CMD_SUCCESS; + + key->hash_algo = KEYCHAIN_ALGO_NULL; + return CMD_SUCCESS; +} + /* Convert HH:MM:SS MON DAY YEAR to time_t value. -1 is returned when given string is malformed. */ static time_t key_str2time(const char *time_str, const char *day_str, @@ -1004,6 +1133,11 @@ static int keychain_config_write(struct vty *vty) if (key->string) vty_out(vty, " key-string %s\n", key->string); + if (key->hash_algo != KEYCHAIN_ALGO_NULL) + vty_out(vty, " cryptographic-algorithm %s\n", + keychain_get_algo_name_by_id( + key->hash_algo)); + if (key->accept.start) { keychain_strftime(buf, BUFSIZ, &key->accept.start); @@ -1051,6 +1185,7 @@ static int keychain_config_write(struct vty *vty) return 0; } + static void keychain_active_config(vector comps, struct cmd_token *token) { struct keychain *keychain; @@ -1131,4 +1266,6 @@ void keychain_init(void) install_element(KEYCHAIN_KEY_NODE, &send_lifetime_duration_month_day_cmd); install_element(KEYCHAIN_KEY_NODE, &no_send_lifetime_cmd); + install_element(KEYCHAIN_KEY_NODE, &cryptographic_algorithm_cmd); + install_element(KEYCHAIN_KEY_NODE, &no_cryptographic_algorithm_cmd); } diff --git a/lib/keychain.h b/lib/keychain.h index eb6d2f175e..c47bb7a792 100644 --- a/lib/keychain.h +++ b/lib/keychain.h @@ -27,6 +27,47 @@ extern "C" { #endif +enum keychain_hash_algo { + KEYCHAIN_ALGO_NULL, + KEYCHAIN_ALGO_MD5, + KEYCHAIN_ALGO_HMAC_SHA1, + KEYCHAIN_ALGO_HMAC_SHA256, + KEYCHAIN_ALGO_HMAC_SHA384, + KEYCHAIN_ALGO_HMAC_SHA512, + KEYCHAIN_ALGO_MAX +}; + +#define KEYCHAIN_MD5_HASH_SIZE 16 +#define KEYCHAIN_HMAC_SHA1_HASH_SIZE 20 +#define KEYCHAIN_HMAC_SHA256_HASH_SIZE 32 +#define KEYCHAIN_HMAC_SHA384_HASH_SIZE 48 +#define KEYCHAIN_HMAC_SHA512_HASH_SIZE 64 +#define KEYCHAIN_MAX_HASH_SIZE 64 + +#define KEYCHAIN_ALGO_MD5_INTERNAL_BLK_SIZE 16 +#define KEYCHAIN_ALGO_SHA1_INTERNAL_BLK_SIZE 64 +#define KEYCHAIN_ALGO_SHA256_INTERNAL_BLK_SIZE 64 +#define KEYCHAIN_ALGO_SHA384_INTERNAL_BLK_SIZE 128 +#define KEYCHAIN_ALGO_SHA512_INTERNAL_BLK_SIZE 128 +#define KEYCHAIN_ALGO_MAX_INTERNAL_BLK_SIZE 128 + +struct keychain_algo_info { + enum keychain_hash_algo key; + const char *name; + uint32_t length; + uint32_t block; + const char *desc; +}; + +extern const struct keychain_algo_info algo_info[]; +uint32_t keychain_get_block_size(enum keychain_hash_algo key); +uint32_t keychain_get_hash_len(enum keychain_hash_algo key); +const char *keychain_get_description(enum keychain_hash_algo key); +struct keychain_algo_info +keychain_get_hash_algo_info(enum keychain_hash_algo key); +enum keychain_hash_algo keychain_get_algo_id_by_name(const char *name); +const char *keychain_get_algo_name_by_id(enum keychain_hash_algo key); + struct keychain { char *name; @@ -47,7 +88,7 @@ struct key { uint32_t index; char *string; - + enum keychain_hash_algo hash_algo; struct key_range send; struct key_range accept; @@ -60,7 +101,7 @@ extern struct keychain *keychain_lookup(const char *); extern struct key *key_lookup_for_accept(const struct keychain *, uint32_t); extern struct key *key_match_for_accept(const struct keychain *, const char *); extern struct key *key_lookup_for_send(const struct keychain *); - +const char *keychain_algo_str(enum keychain_hash_algo hash_algo); #ifdef __cplusplus } #endif From b25bd2ad6e1a5eb087bc1a12dfb1f6eb50a762f6 Mon Sep 17 00:00:00 2001 From: Abhinay Ramesh Date: Tue, 11 May 2021 12:50:05 +0000 Subject: [PATCH 3/9] ospf6d: support keychain for ospf6 authentication MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem Statement: ================== As of now there is no support for ospf6 authentication. To support ospf6 authentication need to have keychain support for managing the auth key.    RCA: ==== New support   Fix: ==== Enabling keychain for ospf6 authentication feature.   Risk: ===== Low risk   Tests Executed: =============== Have verified the support for ospf6 auth trailer feature. Signed-off-by: Abhinay Ramesh --- ospf6d/ospf6_main.c | 2 ++ vtysh/extract.pl.in | 2 +- vtysh/vtysh.h | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ospf6d/ospf6_main.c b/ospf6d/ospf6_main.c index 165a764c38..120f307adc 100644 --- a/ospf6d/ospf6_main.c +++ b/ospf6d/ospf6_main.c @@ -20,6 +20,7 @@ #include #include +#include #include #include "getopt.h" @@ -223,6 +224,7 @@ int main(int argc, char *argv[], char *envp[]) /* thread master */ master = om6->master; + keychain_init(); ospf6_vrf_init(); access_list_init(); prefix_list_init(); diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in index 334bd7affa..c2b4e779de 100755 --- a/vtysh/extract.pl.in +++ b/vtysh/extract.pl.in @@ -91,7 +91,7 @@ sub scan_file { # $protocol is VTYSH_PROTO format for redirection of user input if ($file =~ /lib\/keychain\.c$/) { - $protocol = "VTYSH_RIPD|VTYSH_EIGRPD"; + $protocol = "VTYSH_RIPD|VTYSH_EIGRPD|VTYSH_OSPF6D"; } elsif ($file =~ /lib\/routemap\.c$/ || $file =~ /lib\/routemap_cli\.c$/) { $protocol = "VTYSH_RMAP"; diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index e56d482da2..66af248354 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -57,7 +57,7 @@ DECLARE_MGROUP(MVTYSH); #define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_FABRICD #define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_PBRD|VTYSH_FABRICD|VTYSH_VRRPD #define VTYSH_VRF VTYSH_INTERFACE|VTYSH_STATICD -#define VTYSH_KEYS VTYSH_RIPD|VTYSH_EIGRPD +#define VTYSH_KEYS VTYSH_RIPD | VTYSH_EIGRPD | VTYSH_OSPF6D /* Daemons who can process nexthop-group configs */ #define VTYSH_NH_GROUP VTYSH_PBRD|VTYSH_SHARPD #define VTYSH_SR VTYSH_ZEBRA|VTYSH_PATHD From 1a5607eab984d64810aa47f9fcb57a8f8aeb9923 Mon Sep 17 00:00:00 2001 From: Abhinay Ramesh Date: Sun, 30 May 2021 16:22:41 +0000 Subject: [PATCH 4/9] ospf6d: Auth trailer CLI implementation. Problem Statement: ================== RFC 7166 support for OSPF6 in FRR code. RCA: ==== This feature is newly supported in FRR Fix: ==== Changes are done to add support for two new CLIs to configure ospf6 authentication trailer feature. One CLI is to support manual key configuration. Other CLI is to configure key using keychain. below CLIs are implemented as part of this commit. this configuration is applied on interface level. Without openssl: ipv6 ospf6 authentication key-id (1-65535) hash-algo key WORD With openssl: ipv6 ospf6 authentication key-id (1-65535) hash-algo key WORD With keychain support: ipv6 ospf6 authentication keychain KEYCHAIN_NAME Running config for these command: frr# show running-config Building configuration... Current configuration: ! interface ens192 ipv6 address 2001:DB8:1::2/64 ipv6 ospf6 authentication key-id 10 hash-algo hmac-sha-256 key abhinay ! interface ens224 ipv6 address 2001:DB8:2::2/64 ipv6 ospf6 authentication keychain abhinay ! Risk: ===== Low risk Tests Executed: =============== Have executed the combination of commands. Signed-off-by: Abhinay Ramesh --- ospf6d/ospf6_interface.c | 252 +++++++++++++++++++++++++++++++++++++++ ospf6d/ospf6_interface.h | 36 ++++++ 2 files changed, 288 insertions(+) diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index 0d9f15d08f..761a86444a 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -46,8 +46,13 @@ #include "ospf6_zebra.h" #include "ospf6_gr.h" #include "lib/json.h" +#include "ospf6_proto.h" +#include "lib/keychain.h" +#include "ospf6_auth_trailer.h" DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_IF, "OSPF6 interface"); +DEFINE_MTYPE(OSPF6D, OSPF6_AUTH_KEYCHAIN, "OSPF6 auth keychain"); +DEFINE_MTYPE(OSPF6D, OSPF6_AUTH_MANUAL_KEY, "OSPF6 auth key"); DEFINE_MTYPE_STATIC(OSPF6D, CFG_PLIST_NAME, "configured prefix list names"); DEFINE_QOBJ_TYPE(ospf6_interface); DEFINE_HOOK(ospf6_interface_change, @@ -252,6 +257,8 @@ struct ospf6_interface *ospf6_interface_create(struct interface *ifp) /* Compute cost. */ oi->cost = ospf6_interface_get_cost(oi); + oi->at_data.flags = 0; + return oi; } @@ -990,6 +997,7 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp, struct ospf6_lsa *lsa, *lsanext; json_object *json_arr; json_object *json_addr; + struct json_object *json_auth = NULL; default_iftype = ospf6_default_iftype(ifp); @@ -1238,6 +1246,57 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp, } } + json_auth = json_object_new_object(); + if (oi->at_data.flags != 0) { + if (use_json) { + if (CHECK_FLAG(oi->at_data.flags, + OSPF6_AUTH_TRAILER_KEYCHAIN)) { + json_object_string_add(json_auth, "authType", + "keychain"); + json_object_string_add(json_auth, + "keychainName", + oi->at_data.keychain); + } else if (CHECK_FLAG(oi->at_data.flags, + OSPF6_AUTH_TRAILER_MANUAL_KEY)) + json_object_string_add(json_auth, "authType", + "manualkey"); + json_object_int_add(json_auth, "higherSegNo", + oi->at_data.seqnum_h); + json_object_int_add(json_auth, "lowerSegNo", + oi->at_data.seqnum_l); + json_object_int_add(json_auth, "txPktDrop", + oi->at_data.tx_drop); + json_object_int_add(json_auth, "rxPktDrop", + oi->at_data.rx_drop); + } else { + if (CHECK_FLAG(oi->at_data.flags, + OSPF6_AUTH_TRAILER_KEYCHAIN)) + vty_out(vty, + " Authentication Trailer is enabled with key-chain %s\n", + oi->at_data.keychain); + else if (CHECK_FLAG(oi->at_data.flags, + OSPF6_AUTH_TRAILER_MANUAL_KEY)) + vty_out(vty, + " Authentication trailer is enabled with manual key\n"); + + vty_out(vty, + " Higher sequence no %u, Lower sequence no %u\n", + oi->at_data.seqnum_h, oi->at_data.seqnum_l); + vty_out(vty, + " Packet drop Tx %u, Packet drop Rx %u\n", + oi->at_data.tx_drop, oi->at_data.rx_drop); + } + } else { + if (use_json) { + json_object_string_add(json_auth, "authType", "NULL"); + } else { + vty_out(vty, " Authentication Trailer is disabled\n"); + } + } + + if (use_json) + json_object_object_add(json_obj, "authInfo", json_auth); + return 0; } @@ -2577,6 +2636,7 @@ static int config_write_ospf6_interface(struct vty *vty, struct vrf *vrf) ospf6_bfd_write_config(vty, oi); + ospf6_auth_write_config(vty, &oi->at_data); if_vty_config_end(vty); } return 0; @@ -2815,3 +2875,195 @@ void install_element_ospf6_debug_interface(void) install_element(CONFIG_NODE, &debug_ospf6_interface_cmd); install_element(CONFIG_NODE, &no_debug_ospf6_interface_cmd); } + +void ospf6_auth_write_config(struct vty *vty, struct ospf6_auth_data *at_data) +{ + if (CHECK_FLAG(at_data->flags, OSPF6_AUTH_TRAILER_KEYCHAIN)) + vty_out(vty, " ipv6 ospf6 authentication keychain %s\n", + at_data->keychain); + else if (CHECK_FLAG(at_data->flags, OSPF6_AUTH_TRAILER_MANUAL_KEY)) + vty_out(vty, + " ipv6 ospf6 authentication key-id %d hash-algo %s key %s\n", + at_data->key_id, + keychain_get_algo_name_by_id(at_data->hash_algo), + at_data->auth_key); +} + +DEFUN(ipv6_ospf6_intf_auth_trailer_keychain, + ipv6_ospf6_intf_auth_trailer_keychain_cmd, + "ipv6 ospf6 authentication keychain KEYCHAIN_NAME", + IP6_STR OSPF6_STR + "Enable authentication on this interface\n" + "Keychain\n" + "Keychain name\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int keychain_idx = 4; + struct ospf6_interface *oi; + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + + assert(oi); + if (CHECK_FLAG(oi->at_data.flags, OSPF6_AUTH_TRAILER_MANUAL_KEY)) { + vty_out(vty, + "Manual key configured, unconfigure it before configuring key chain\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + SET_FLAG(oi->at_data.flags, OSPF6_AUTH_TRAILER_KEYCHAIN); + if (oi->at_data.keychain) + XFREE(MTYPE_OSPF6_AUTH_KEYCHAIN, oi->at_data.keychain); + + oi->at_data.keychain = + XSTRDUP(MTYPE_OSPF6_AUTH_KEYCHAIN, argv[keychain_idx]->arg); + + return CMD_SUCCESS; +} + +DEFUN(no_ipv6_ospf6_intf_auth_trailer_keychain, + no_ipv6_ospf6_intf_auth_trailer_keychain_cmd, + "no ipv6 ospf6 authentication keychain [KEYCHAIN_NAME]", + NO_STR IP6_STR OSPF6_STR + "Enable authentication on this interface\n" + "Keychain\n" + "Keychain name\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi; + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + + assert(oi); + if (!CHECK_FLAG(oi->at_data.flags, OSPF6_AUTH_TRAILER_KEYCHAIN)) + return CMD_SUCCESS; + + if (oi->at_data.keychain) { + oi->at_data.flags = 0; + XFREE(MTYPE_OSPF6_AUTH_KEYCHAIN, oi->at_data.keychain); + oi->at_data.keychain = NULL; + } + + return CMD_SUCCESS; +} + +DEFUN(ipv6_ospf6_intf_auth_trailer_key, ipv6_ospf6_intf_auth_trailer_key_cmd, + "ipv6 ospf6 authentication key-id (1-65535) hash-algo " + " " + "key WORD", + IP6_STR OSPF6_STR + "Authentication\n" + "Key ID\n" + "Key ID value\n" + "Cryptographic-algorithm\n" + "Use MD5 algorithm\n" + "Use HMAC-SHA-1 algorithm\n" + "Use HMAC-SHA-256 algorithm\n" + "Use HMAC-SHA-384 algorithm\n" + "Use HMAC-SHA-512 algorithm\n" + "Password\n" + "Password string (key)\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int key_id_idx = 4; + int hash_algo_idx = 6; + int password_idx = 8; + struct ospf6_interface *oi; + uint8_t hash_algo = KEYCHAIN_ALGO_NULL; + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + + assert(oi); + if (CHECK_FLAG(oi->at_data.flags, OSPF6_AUTH_TRAILER_KEYCHAIN)) { + vty_out(vty, + "key chain configured, unconfigure it before configuring manual key\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + hash_algo = keychain_get_algo_id_by_name(argv[hash_algo_idx]->arg); +#ifndef CRYPTO_OPENSSL + if (hash_algo == KEYCHAIN_ALGO_NULL) { + vty_out(vty, + "Hash algorithm not supported, compile with --with-crypto=openssl\n"); + return CMD_WARNING_CONFIG_FAILED; + } +#endif /* CRYPTO_OPENSSL */ + + SET_FLAG(oi->at_data.flags, OSPF6_AUTH_TRAILER_MANUAL_KEY); + oi->at_data.hash_algo = hash_algo; + oi->at_data.key_id = (uint16_t)strtol(argv[key_id_idx]->arg, NULL, 10); + if (oi->at_data.auth_key) + XFREE(MTYPE_OSPF6_AUTH_MANUAL_KEY, oi->at_data.auth_key); + oi->at_data.auth_key = + XSTRDUP(MTYPE_OSPF6_AUTH_MANUAL_KEY, argv[password_idx]->arg); + + return CMD_SUCCESS; +} + +DEFUN(no_ipv6_ospf6_intf_auth_trailer_key, + no_ipv6_ospf6_intf_auth_trailer_key_cmd, + "no ipv6 ospf6 authentication key-id [(1-65535) hash-algo " + " " + "key WORD]", + NO_STR IP6_STR OSPF6_STR + "Authentication\n" + "Key ID\n" + "Key ID value\n" + "Cryptographic-algorithm\n" + "Use MD5 algorithm\n" + "Use HMAC-SHA-1 algorithm\n" + "Use HMAC-SHA-256 algorithm\n" + "Use HMAC-SHA-384 algorithm\n" + "Use HMAC-SHA-512 algorithm\n" + "Password\n" + "Password string (key)\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi; +#ifndef CRYPTO_OPENSSL + int hash_algo_idx = 7; + uint8_t hash_algo = KEYCHAIN_ALGO_NULL; +#endif /* CRYPTO_OPENSSL */ + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + + assert(oi); + if (!CHECK_FLAG(oi->at_data.flags, OSPF6_AUTH_TRAILER_MANUAL_KEY)) + return CMD_SUCCESS; + +#ifndef CRYPTO_OPENSSL + hash_algo = keychain_get_algo_id_by_name(argv[hash_algo_idx]->arg); + if (hash_algo == KEYCHAIN_ALGO_NULL) { + vty_out(vty, + "Hash algorithm not supported, compile with --with-crypto=openssl\n"); + return CMD_WARNING_CONFIG_FAILED; + } +#endif /* CRYPTO_OPENSSL */ + + if (oi->at_data.auth_key) { + oi->at_data.flags = 0; + XFREE(MTYPE_OSPF6_AUTH_MANUAL_KEY, oi->at_data.auth_key); + oi->at_data.auth_key = NULL; + } + + return CMD_SUCCESS; +} + +void ospf6_interface_auth_trailer_cmd_init(void) +{ + /*Install OSPF6 auth trailer commands at interface level */ + install_element(INTERFACE_NODE, + &ipv6_ospf6_intf_auth_trailer_keychain_cmd); + install_element(INTERFACE_NODE, + &no_ipv6_ospf6_intf_auth_trailer_keychain_cmd); + install_element(INTERFACE_NODE, &ipv6_ospf6_intf_auth_trailer_key_cmd); + install_element(INTERFACE_NODE, + &no_ipv6_ospf6_intf_auth_trailer_key_cmd); +} diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h index ee24b989bd..d068a9857a 100644 --- a/ospf6d/ospf6_interface.h +++ b/ospf6d/ospf6_interface.h @@ -24,6 +24,7 @@ #include "qobj.h" #include "hook.h" #include "if.h" +#include "ospf6d.h" /* Debug option */ extern unsigned char conf_debug_ospf6_interface; @@ -31,6 +32,35 @@ extern unsigned char conf_debug_ospf6_interface; #define OSPF6_DEBUG_INTERFACE_OFF() (conf_debug_ospf6_interface = 0) #define IS_OSPF6_DEBUG_INTERFACE (conf_debug_ospf6_interface) +struct ospf6_auth_data { + /* higher order Sequence Number */ + uint32_t seqnum_h; + + /* lower order Sequence Number */ + uint32_t seqnum_l; + + /* Packet drop due to auth failure while sending */ + uint32_t tx_drop; + + /* Packet drop due to auth failure while reading */ + uint32_t rx_drop; + + /* hash algorithm type */ + uint8_t hash_algo; + + /* Flags related to auth config */ + uint8_t flags; + + /* key-id used as security association in auth packet */ + uint16_t key_id; + + /* Auth key */ + char *auth_key; + + /* keychain name */ + char *keychain; +}; + /* Interface structure */ struct ospf6_interface { /* IF info from zebra */ @@ -95,6 +125,9 @@ struct ospf6_interface { /* MTU mismatch check */ uint8_t mtu_ignore; + /* Authentication trailer related config */ + struct ospf6_auth_data at_data; + /* Decision of DR Election */ in_addr_t drouter; in_addr_t bdrouter; @@ -221,6 +254,9 @@ extern void install_element_ospf6_debug_interface(void); extern int ospf6_interface_neighbor_count(struct ospf6_interface *oi); extern uint8_t dr_election(struct ospf6_interface *oi); +extern void ospf6_interface_auth_trailer_cmd_init(void); +extern void ospf6_auth_write_config(struct vty *vty, + struct ospf6_auth_data *at_data); DECLARE_HOOK(ospf6_interface_change, (struct ospf6_interface * oi, int state, int old_state), (oi, state, old_state)); From b592ec5ad037723887f6ba287b202bc33a9fe8b2 Mon Sep 17 00:00:00 2001 From: Abhinay Ramesh Date: Sun, 30 May 2021 16:27:13 +0000 Subject: [PATCH 5/9] ospf6d: Core functionality of auth trailer implementation.. Problem Statement: ================== Implement RFC 7166 support for OSPF6 in FRR code. RCA: ==== This feature is newly supported in FRR. Fix: ==== Changes are done to implement ospf6 ingress and egress packet processing. This commit has the core functionality. It supports below debugability commands: --------------------------------------- debug ospf6 authentication [] It supports below clear command: -------------------------------- clear ipv6 ospf6 auth-counters interface [IFNAME] It supports below show commands: -------------------------------- frr# show ipv6 ospf6 interface ens192 ens192 is up, type BROADCAST Interface ID: 5 Number of I/F scoped LSAs is 2 0 Pending LSAs for LSUpdate in Time 00:00:00 [thread off] 0 Pending LSAs for LSAck in Time 00:00:00 [thread off] Authentication trailer is enabled with manual key ==> new info added Packet drop Tx 0, Packet drop Rx 0 ==> drop counters frr# show ipv6 ospf6 neighbor 2.2.2.2 detail Neighbor 2.2.2.2%ens192 Area 1 via interface ens192 (ifindex 3) 0 Pending LSAs for LSUpdate in Time 00:00:00 [thread off] 0 Pending LSAs for LSAck in Time 00:00:00 [thread off] Authentication header present ==> new info added hello DBDesc LSReq LSUpd LSAck Higher sequence no 0x0 0x0 0x0 0x0 0x0 Lower sequence no 0x242E 0x1DC4 0x1DC3 0x23CC 0x1DDA frr# show ipv6 ospf6 OSPFv3 Routing Process (0) with Router-ID 2.2.2.2 Number of areas in this router is 1 Authentication Sequence number info ==> new info added Higher sequence no 3, Lower sequence no 1656 Risk: ===== Low risk Tests Executed: =============== Have executed the combination of commands. Signed-off-by: Abhinay Ramesh --- ospf6d/ospf6_auth_trailer.c | 828 ++++++++++++++++++++++++++++++++++++ ospf6d/ospf6_auth_trailer.h | 80 ++++ ospf6d/ospf6_interface.h | 2 + ospf6d/ospf6_message.h | 2 + 4 files changed, 912 insertions(+) create mode 100644 ospf6d/ospf6_auth_trailer.c create mode 100644 ospf6d/ospf6_auth_trailer.h diff --git a/ospf6d/ospf6_auth_trailer.c b/ospf6d/ospf6_auth_trailer.c new file mode 100644 index 0000000000..88d2fc240e --- /dev/null +++ b/ospf6d/ospf6_auth_trailer.c @@ -0,0 +1,828 @@ +/* + * Copyright (C) 2021 Abhinay Ramesh + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "zebra.h" +#include "memory.h" +#include "ospf6d.h" +#include "vty.h" +#include "command.h" +#include "md5.h" +#include "sha256.h" +#include "lib/zlog.h" +#include "ospf6_message.h" +#include "ospf6_interface.h" +#include "ospf6_neighbor.h" +#include "ospf6_proto.h" +#include "ospf6_auth_trailer.h" +#include "ospf6_route.h" +#include "ospf6_zebra.h" +#include "lib/keychain.h" + +unsigned char conf_debug_ospf6_auth[2]; +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_AUTH_PKT, "OSPF6 auth packet"); +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_AUTH_HASH, "OSPF6 auth hash"); +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_AUTH_HASH_XOR, "OSPF6 auth hash xor"); + +/*Apad is the hexadecimal value 0x878FE1F3. */ +const uint8_t ospf6_hash_apad_max[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, +}; + +const uint8_t ospf6_hash_ipad_max[KEYCHAIN_ALGO_MAX_INTERNAL_BLK_SIZE] = { + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +}; + +const uint8_t ospf6_hash_opad_max[KEYCHAIN_ALGO_MAX_INTERNAL_BLK_SIZE] = { + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, +}; + +void ospf6_auth_hdr_dump_send(struct ospf6_header *ospfh, uint16_t length) +{ + struct ospf6_auth_hdr *ospf6_at_hdr; + int at_len, oh_len, at_hdr_len, hash_len; + unsigned char temp[KEYCHAIN_MAX_HASH_SIZE+1]; + + oh_len = htons(ospfh->length); + at_len = length - oh_len; + if (at_len > 0) { + ospf6_at_hdr = (struct ospf6_auth_hdr *) + ((uint8_t *)ospfh + oh_len); + at_hdr_len = htons(ospf6_at_hdr->length); + hash_len = at_hdr_len - OSPF6_AUTH_HDR_MIN_SIZE; + memcpy(temp, ospf6_at_hdr->data, hash_len); + temp[hash_len] = '\0'; + zlog_debug("OSPF6 Authentication Trailer"); + zlog_debug(" Type %d", htons(ospf6_at_hdr->type)); + zlog_debug(" Length %d", at_hdr_len); + zlog_debug(" Reserved %d", ospf6_at_hdr->reserved); + zlog_debug(" SA ID %d", htons(ospf6_at_hdr->id)); + zlog_debug(" seqnum high 0x%08x", + htonl(ospf6_at_hdr->seqnum_h)); + zlog_debug(" seqnum high 0x%08x", + htonl(ospf6_at_hdr->seqnum_l)); + zlog_debug(" Data %s", temp); + } +} + +void ospf6_auth_hdr_dump_recv(struct ospf6_header *ospfh, uint16_t length) +{ + struct ospf6_auth_hdr *ospf6_at_hdr; + int at_len, oh_len, at_hdr_len, hash_len; + unsigned char temp[KEYCHAIN_MAX_HASH_SIZE+1]; + + oh_len = ntohs(ospfh->length); + at_len = length - oh_len; + if (at_len > 0) { + ospf6_at_hdr = (struct ospf6_auth_hdr *) + ((uint8_t *)ospfh + oh_len); + at_hdr_len = ntohs(ospf6_at_hdr->length); + hash_len = at_hdr_len - OSPF6_AUTH_HDR_MIN_SIZE; + memcpy(temp, ospf6_at_hdr->data, hash_len); + temp[hash_len] = '\0'; + zlog_debug("OSPF6 Authentication Trailer"); + zlog_debug(" Type %d", ntohs(ospf6_at_hdr->type)); + zlog_debug(" Length %d", at_hdr_len); + zlog_debug(" Reserved %d", ospf6_at_hdr->reserved); + zlog_debug(" SA ID %d", ntohs(ospf6_at_hdr->id)); + zlog_debug(" seqnum high 0x%08x", + ntohl(ospf6_at_hdr->seqnum_h)); + zlog_debug(" seqnum high 0x%08x", + ntohl(ospf6_at_hdr->seqnum_l)); + zlog_debug(" Data %s", temp); + } +} + +unsigned char *ospf6_hash_message_xor(unsigned char *mes1, + unsigned char *mes2, + uint32_t len) +{ + unsigned char *result; + uint32_t i; + + result = XCALLOC(MTYPE_OSPF6_AUTH_HASH_XOR, len); + if (!result) + return NULL; + + for (i = 0; i < len; i++) + result[i] = mes1[i] ^ mes2[i]; + + return result; +} + +static void md5_digest(unsigned char *mes, uint32_t len, + unsigned char *digest) +{ +#ifdef CRYPTO_OPENSSL + unsigned int size = KEYCHAIN_MD5_HASH_SIZE; + EVP_MD_CTX *ctx; +#elif CRYPTO_INTERNAL + MD5_CTX ctx; +#endif + +#ifdef CRYPTO_OPENSSL + ctx = EVP_MD_CTX_new(); + EVP_DigestInit(ctx, EVP_md5()); + EVP_DigestUpdate(ctx, mes, len); + EVP_DigestFinal(ctx, digest, &size); + EVP_MD_CTX_free(ctx); +#elif CRYPTO_INTERNAL + memset(&ctx, 0, sizeof(ctx)); + MD5Init(&ctx); + MD5Update(&ctx, mes, len); + MD5Final(digest, &ctx); +#endif +} + +static void sha256_digest(unsigned char *mes, uint32_t len, + unsigned char *digest) +{ +#ifdef CRYPTO_OPENSSL + unsigned int size = KEYCHAIN_HMAC_SHA256_HASH_SIZE; + EVP_MD_CTX *ctx; +#elif CRYPTO_INTERNAL + SHA256_CTX ctx; +#endif + +#ifdef CRYPTO_OPENSSL + ctx = EVP_MD_CTX_new(); + EVP_DigestInit(ctx, EVP_sha256()); + EVP_DigestUpdate(ctx, mes, len); + EVP_DigestFinal(ctx, digest, &size); + EVP_MD_CTX_free(ctx); +#elif CRYPTO_INTERNAL + memset(&ctx, 0, sizeof(ctx)); + SHA256_Init(&ctx); + SHA256_Update(&ctx, mes, len); + SHA256_Final(digest, &ctx); +#endif +} + +#ifdef CRYPTO_OPENSSL +static void sha1_digest(unsigned char *mes, uint32_t len, + unsigned char *digest) +{ + EVP_MD_CTX *ctx; + unsigned int size = KEYCHAIN_HMAC_SHA1_HASH_SIZE; + + ctx = EVP_MD_CTX_new(); + EVP_DigestInit(ctx, EVP_sha1()); + EVP_DigestUpdate(ctx, mes, len); + EVP_DigestFinal(ctx, digest, &size); + EVP_MD_CTX_free(ctx); +} + +static void sha384_digest(unsigned char *mes, uint32_t len, + unsigned char *digest) +{ + EVP_MD_CTX *ctx; + unsigned int size = KEYCHAIN_HMAC_SHA384_HASH_SIZE; + + ctx = EVP_MD_CTX_new(); + EVP_DigestInit(ctx, EVP_sha384()); + EVP_DigestUpdate(ctx, mes, len); + EVP_DigestFinal(ctx, digest, &size); + EVP_MD_CTX_free(ctx); +} + +static void sha512_digest(unsigned char *mes, uint32_t len, + unsigned char *digest) +{ + EVP_MD_CTX *ctx; + unsigned int size = KEYCHAIN_HMAC_SHA512_HASH_SIZE; + + ctx = EVP_MD_CTX_new(); + EVP_DigestInit(ctx, EVP_sha512()); + EVP_DigestUpdate(ctx, mes, len); + EVP_DigestFinal(ctx, digest, &size); + EVP_MD_CTX_free(ctx); +} +#endif /* CRYPTO_OPENSSL */ + +static void ospf6_hash_hmac_sha_digest(enum keychain_hash_algo key, + unsigned char *mes, uint32_t len, + unsigned char *digest) +{ + if ((key <= KEYCHAIN_ALGO_NULL) || (key >= KEYCHAIN_ALGO_MAX)) + return; + + switch (key) { + case KEYCHAIN_ALGO_MD5: + md5_digest(mes, len, digest); + break; + case KEYCHAIN_ALGO_HMAC_SHA1: +#ifdef CRYPTO_OPENSSL + sha1_digest(mes, len, digest); +#endif + break; + case KEYCHAIN_ALGO_HMAC_SHA256: + sha256_digest(mes, len, digest); + break; + case KEYCHAIN_ALGO_HMAC_SHA384: +#ifdef CRYPTO_OPENSSL + sha384_digest(mes, len, digest); +#endif + break; + case KEYCHAIN_ALGO_HMAC_SHA512: +#ifdef CRYPTO_OPENSSL + sha512_digest(mes, len, digest); +#endif + break; + case KEYCHAIN_ALGO_NULL: + case KEYCHAIN_ALGO_MAX: + default: + /* no action */ + break; + } +} + +unsigned int ospf6_auth_len_get(struct ospf6_interface *oi) +{ + unsigned int at_len = 0; + char *keychain_name = NULL; + struct keychain *keychain = NULL; + struct key *key = NULL; + + if (CHECK_FLAG(oi->at_data.flags, OSPF6_AUTH_TRAILER_KEYCHAIN)) { + if (CHECK_FLAG(oi->at_data.flags, + OSPF6_AUTH_TRAILER_KEYCHAIN_VALID)) { + at_len = OSPF6_AUTH_HDR_MIN_SIZE + + keychain_get_hash_len(oi->at_data.hash_algo); + } else { + keychain_name = oi->at_data.keychain; + keychain = keychain_lookup(keychain_name); + if (keychain) { + key = key_lookup_for_send(keychain); + if (key && key->string + && key->hash_algo != KEYCHAIN_ALGO_NULL) { + at_len = OSPF6_AUTH_HDR_MIN_SIZE + + keychain_get_hash_len( + key->hash_algo); + } + } + } + } else if (CHECK_FLAG(oi->at_data.flags, + OSPF6_AUTH_TRAILER_MANUAL_KEY)) { + at_len = OSPF6_AUTH_HDR_MIN_SIZE + + keychain_get_hash_len(oi->at_data.hash_algo); + } + + return at_len; +} + +int ospf6_auth_validate_pkt(struct ospf6_interface *oi, unsigned int *pkt_len, + struct ospf6_header *oh, unsigned int *at_len) +{ + struct ospf6_hello *hello = NULL; + struct ospf6_dbdesc *dbdesc = NULL; + struct ospf6_neighbor *on = NULL; + struct ospf6_auth_hdr ospf6_auth_info; + uint16_t hdr_len = 0; + uint32_t oh_seqnum_h = 0; + uint32_t oh_seqnum_l = 0; + + on = ospf6_neighbor_lookup(oh->router_id, oi); + hdr_len = ntohs(oh->length); + if (*pkt_len < hdr_len) { + if (IS_OSPF6_DEBUG_AUTH_RX) + zlog_warn("RECV[%s] Wrong %s packet auth length", + oi->interface->name, + lookup_msg(ospf6_message_type_str, oh->type, + NULL)); + oi->at_data.rx_drop++; + return -1; + } else if (*pkt_len == hdr_len) { + /* no auth data in packet + */ + return -1; + } + + switch (oh->type) { + case OSPF6_MESSAGE_TYPE_HELLO: + hello = (struct ospf6_hello *)((uint8_t *)oh + + sizeof(struct ospf6_header)); + if (OSPF6_OPT_ISSET_EXT(hello->options, OSPF6_OPT_AT)) { + if (on) + on->auth_present = true; + } else { + if (on) + on->auth_present = false; + + if (oi->at_data.flags != 0) { + if (IS_OSPF6_DEBUG_AUTH_RX) + zlog_warn("RECV[%s] : Auth option miss-match in hello pkt", + oi->interface->name); + oi->at_data.rx_drop++; + } + + return -1; + } + break; + case OSPF6_MESSAGE_TYPE_DBDESC: + dbdesc = (struct ospf6_dbdesc *)((uint8_t *)oh + + sizeof(struct ospf6_header)); + + if (OSPF6_OPT_ISSET_EXT(dbdesc->options, OSPF6_OPT_AT)) { + if (on) + on->auth_present = true; + } else { + if (on) + on->auth_present = false; + + if (oi->at_data.flags != 0) { + if (IS_OSPF6_DEBUG_AUTH_RX) + zlog_warn("RECV[%s] : Auth option miss-match in DB desc pkt", + oi->interface->name); + oi->at_data.rx_drop++; + } + + return -1; + } + break; + case OSPF6_MESSAGE_TYPE_LSREQ: + case OSPF6_MESSAGE_TYPE_LSUPDATE: + case OSPF6_MESSAGE_TYPE_LSACK: + if ((on && on->auth_present == false) + && (oi->at_data.flags != 0)) { + if (IS_OSPF6_DEBUG_AUTH_RX) + zlog_warn("RECV[%s] : Auth config miss-match in %s", + oi->interface->name, + lookup_msg(ospf6_message_type_str, + oh->type, NULL)); + oi->at_data.rx_drop++; + return -1; + } + break; + default: + if (IS_OSPF6_DEBUG_AUTH_RX) + zlog_warn("RECV[%s] : Wrong packet type %d", + oi->interface->name, oh->type); + return -1; + } + + memset(&ospf6_auth_info, 0, sizeof(struct ospf6_auth_hdr)); + memcpy(&ospf6_auth_info, (uint8_t *)oh + hdr_len, *pkt_len - hdr_len); + if (ntohs(ospf6_auth_info.length) > OSPF6_AUTH_HDR_FULL) { + if (IS_OSPF6_DEBUG_AUTH_RX) + zlog_warn("RECV[%s] : Auth config miss-match in %s", + oi->interface->name, + lookup_msg(ospf6_message_type_str, oh->type, + NULL)); + oi->at_data.rx_drop++; + return -1; + } + + /* after authentication header validation is done + * reduce the auth hdr size from the packet length + */ + *at_len = ntohs(ospf6_auth_info.length); + *pkt_len = *pkt_len - *at_len; + + if (on) { + oh_seqnum_h = ntohl(ospf6_auth_info.seqnum_h); + oh_seqnum_l = ntohl(ospf6_auth_info.seqnum_l); + if ((oh_seqnum_h >= on->seqnum_h) && + (oh_seqnum_l > on->seqnum_l)) { + /* valid sequence number received */ + on->seqnum_h = oh_seqnum_h; + on->seqnum_l = oh_seqnum_l; + } else { + if (IS_OSPF6_DEBUG_AUTH_RX) { + zlog_warn("RECV[%s] : Nbr(%s) Auth Sequence number mismatch", + oi->interface->name, on->name); + zlog_warn("nbr_seq_l %u, nbr_seq_h %u, hdr_seq_l %u, hdr_seq_h %u", + on->seqnum_l, on->seqnum_h, + oh_seqnum_l, oh_seqnum_h); + } + + oi->at_data.rx_drop++; + return -1; + } + } + + return 0; +} + +/* Starting point of packet process function. */ +int ospf6_auth_check_digest(struct ospf6_header *oh, struct ospf6_interface *oi, + struct in6_addr *src) +{ + uint32_t hash_len = KEYCHAIN_MAX_HASH_SIZE; + unsigned char apad[hash_len]; + unsigned char temp_hash[hash_len]; + struct ospf6_auth_hdr *ospf6_auth; + uint32_t ipv6_addr_size = sizeof(struct in6_addr); + struct keychain *keychain = NULL; + struct key *key = NULL; + char *auth_str = NULL; + uint16_t auth_len = 0; + uint8_t hash_algo = 0; + uint16_t oh_len = ntohs(oh->length); + + if (oi->at_data.flags == 0) + return -2; + + ospf6_auth = (struct ospf6_auth_hdr *)((uint8_t *)oh + oh_len); + if (CHECK_FLAG(oi->at_data.flags, OSPF6_AUTH_TRAILER_KEYCHAIN)) { + keychain = keychain_lookup(oi->at_data.keychain); + if (!keychain) { + if (IS_OSPF6_DEBUG_AUTH_RX) + zlog_warn("RECV[%s]: Keychain does't exist for %s", + oi->interface->name, + lookup_msg(ospf6_message_type_str, + oh->type, NULL)); + oi->at_data.rx_drop++; + return -1; + } + + key = key_lookup_for_accept(keychain, ntohs(ospf6_auth->id)); + if (!key) { + if (IS_OSPF6_DEBUG_AUTH_RX) + zlog_warn("RECV[%s]: Auth, Invalid SA for %s", + oi->interface->name, + lookup_msg(ospf6_message_type_str, + oh->type, NULL)); + oi->at_data.rx_drop++; + return -1; + } + + if (key && key->string + && key->hash_algo != KEYCHAIN_ALGO_NULL) { + auth_str = key->string; + hash_algo = key->hash_algo; + } else { + if (IS_OSPF6_DEBUG_AUTH_RX) + zlog_warn("RECV[%s]: Incomplete keychain config for %s", + oi->interface->name, + lookup_msg(ospf6_message_type_str, + oh->type, NULL)); + oi->at_data.rx_drop++; + return -1; + } + } else if (CHECK_FLAG(oi->at_data.flags, + OSPF6_AUTH_TRAILER_MANUAL_KEY)) { + auth_str = oi->at_data.auth_key; + hash_algo = oi->at_data.hash_algo; + } + + if (!auth_str) + return -2; + + hash_len = keychain_get_hash_len(hash_algo); + memset(apad, 0, sizeof(hash_len)); + memset(temp_hash, 0, sizeof(hash_len)); + + /* start digest verification */ + memcpy(apad, src, ipv6_addr_size); + memcpy(apad + ipv6_addr_size, ospf6_hash_apad_max, + (hash_len - ipv6_addr_size)); + + auth_len = ntohs(ospf6_auth->length); + + memcpy(temp_hash, ospf6_auth->data, hash_len); + memcpy(ospf6_auth->data, apad, hash_len); + + ospf6_auth_update_digest(oi, oh, ospf6_auth, auth_str, auth_len, + (oh_len + auth_len), hash_algo); + +#ifdef CRYPTO_OPENSSL + return !(CRYPTO_memcmp(temp_hash, ospf6_auth->data, hash_len)); +#else + return !(memcmp(temp_hash, ospf6_auth->data, hash_len)); +#endif +} + +void ospf6_auth_digest_send(struct in6_addr *src, struct ospf6_interface *oi, + struct ospf6_header *oh, uint16_t auth_len, + uint32_t pkt_len) +{ + struct ospf6_auth_hdr *ospf6_auth; + char *keychain_name = NULL; + struct keychain *keychain = NULL; + struct key *key = NULL; + char *auth_str = NULL; + uint16_t key_id = 0; + enum keychain_hash_algo hash_algo = KEYCHAIN_ALGO_NULL; + uint32_t hash_len = KEYCHAIN_MAX_HASH_SIZE; + unsigned char apad[hash_len]; + int ipv6_addr_size = sizeof(struct in6_addr); + + if (CHECK_FLAG(oi->at_data.flags, OSPF6_AUTH_TRAILER_KEYCHAIN)) { + if (CHECK_FLAG(oi->at_data.flags, + OSPF6_AUTH_TRAILER_KEYCHAIN_VALID)) { + auth_str = oi->at_data.auth_key; + hash_algo = oi->at_data.hash_algo; + key_id = oi->at_data.key_id; + } else { + keychain_name = oi->at_data.keychain; + keychain = keychain_lookup(keychain_name); + if (keychain) { + key = key_lookup_for_send(keychain); + if (key && key->string + && key->hash_algo != KEYCHAIN_ALGO_NULL) { + auth_str = key->string; + hash_algo = key->hash_algo; + key_id = key->index; + } + } + } + } else if (CHECK_FLAG(oi->at_data.flags, + OSPF6_AUTH_TRAILER_MANUAL_KEY)) { + auth_str = oi->at_data.auth_key; + hash_algo = oi->at_data.hash_algo; + key_id = oi->at_data.key_id; + } else { + if (IS_OSPF6_DEBUG_AUTH_TX) + zlog_warn("SEND[%s]: Authentication not configured for %s", + oi->interface->name, + lookup_msg(ospf6_message_type_str, + oh->type, NULL)); + return; + } + + if (!auth_str) { + if (IS_OSPF6_DEBUG_AUTH_TX) + zlog_warn("SEND[%s]: Authentication key is not configured for %s", + oi->interface->name, + lookup_msg(ospf6_message_type_str, + oh->type, NULL)); + return; + } + + hash_len = keychain_get_hash_len(hash_algo); + oi->at_data.seqnum_l++; + + if (oi->at_data.seqnum_l == 0xFFFFFFFF) { + oi->at_data.seqnum_h++; + oi->at_data.seqnum_l = 0; + } + + /* Key must be reset. which is not handled as of now. */ + if ((oi->at_data.seqnum_l == 0xFFFFFFFF) + && (oi->at_data.seqnum_h == 0xFFFFFFFF)) { + oi->at_data.seqnum_l = 0; + oi->at_data.seqnum_h = 0; + } + + memset(apad, 0, hash_len); + + if (src) + memcpy(apad, src, ipv6_addr_size); + + memcpy(apad + ipv6_addr_size, ospf6_hash_apad_max, + (hash_len - ipv6_addr_size)); + + ospf6_auth = + (struct ospf6_auth_hdr *)((uint8_t *)oh + ntohs(oh->length)); + ospf6_auth->type = htons(OSPF6_AUTHENTICATION_CRYPTOGRAPHIC); + ospf6_auth->length = htons(auth_len); + ospf6_auth->reserved = 0; + ospf6_auth->id = htons(key_id); + ospf6_auth->seqnum_h = htonl(oi->at_data.seqnum_h); + ospf6_auth->seqnum_l = htonl(oi->at_data.seqnum_l); + memcpy(ospf6_auth->data, apad, hash_len); + + ospf6_auth_update_digest(oi, oh, ospf6_auth, auth_str, auth_len, + pkt_len, hash_algo); + + if (CHECK_FLAG(oi->at_data.flags, OSPF6_AUTH_TRAILER_KEYCHAIN_VALID)) { + oi->at_data.hash_algo = KEYCHAIN_ALGO_NULL; + if (oi->at_data.auth_key) { + XFREE(MTYPE_OSPF6_AUTH_MANUAL_KEY, + oi->at_data.auth_key); + oi->at_data.auth_key = NULL; + } + + oi->at_data.key_id = 0; + UNSET_FLAG(oi->at_data.flags, + OSPF6_AUTH_TRAILER_KEYCHAIN_VALID); + } +} + +void ospf6_auth_update_digest(struct ospf6_interface *oi, + struct ospf6_header *oh, + struct ospf6_auth_hdr *ospf6_auth, char *auth_str, + uint16_t auth_len, uint32_t pkt_len, + enum keychain_hash_algo algo) +{ + uint32_t hash_len = keychain_get_hash_len(algo); + uint32_t block_s = keychain_get_block_size(algo); + uint32_t k_len = strlen(auth_str); + uint32_t ks_len = strlen(auth_str) + sizeof(CPID); + unsigned char ipad[block_s]; + unsigned char opad[block_s]; + unsigned char ko[block_s], ks[ks_len], tmp[hash_len]; + unsigned char *first = NULL; + unsigned char *second = NULL; + unsigned char *first_mes, *second_mes; + unsigned char *first_hash, *second_hash; + + memset(ko, 0, block_s); + memcpy(ks, auth_str, k_len); + memcpy(ks + k_len, &CPID, sizeof(CPID)); + if (ks_len > hash_len) { + ospf6_hash_hmac_sha_digest(algo, ks, ks_len, tmp); + memcpy(ko, tmp, hash_len); + } else + memcpy(ko, ks, ks_len); + + memcpy(ipad, ospf6_hash_ipad_max, block_s); + memcpy(opad, ospf6_hash_opad_max, block_s); + + first = ospf6_hash_message_xor((unsigned char *)&ipad, ko, block_s); + second = ospf6_hash_message_xor((unsigned char *)&opad, ko, block_s); + + first_mes = XMALLOC(MTYPE_OSPF6_AUTH_PKT, (block_s + pkt_len)); + + memcpy(first_mes, first, block_s); + memcpy(first_mes + block_s, oh, pkt_len); + + first_hash = XMALLOC(MTYPE_OSPF6_AUTH_HASH, hash_len); + + ospf6_hash_hmac_sha_digest(algo, first_mes, (block_s + pkt_len), + first_hash); + + second_mes = XMALLOC(MTYPE_OSPF6_AUTH_PKT, (block_s + hash_len)); + + memcpy(second_mes, second, block_s); + memcpy(second_mes + block_s, first_hash, hash_len); + + second_hash = XMALLOC(MTYPE_OSPF6_AUTH_HASH, hash_len); + + ospf6_hash_hmac_sha_digest(algo, second_mes, (block_s + hash_len), + second_hash); + + memcpy(ospf6_auth->data, second_hash, hash_len); + XFREE(MTYPE_OSPF6_AUTH_PKT, first_mes); + XFREE(MTYPE_OSPF6_AUTH_PKT, second_mes); + XFREE(MTYPE_OSPF6_AUTH_HASH, first_hash); + XFREE(MTYPE_OSPF6_AUTH_HASH, second_hash); + XFREE(MTYPE_OSPF6_AUTH_HASH_XOR, first); + XFREE(MTYPE_OSPF6_AUTH_HASH_XOR, second); +} + +DEFUN (debug_ospf6_auth, + debug_ospf6_auth_cmd, + "debug ospf6 authentication []", + DEBUG_STR + OSPF6_STR + "debug OSPF6 authentication\n" + "debug authentication tx\n" + "debug authentication rx\n") +{ + int auth_opt_idx = 3; + + if (argc == 4) { + if (!strncmp(argv[auth_opt_idx]->arg, "t", 1)) + OSPF6_DEBUG_AUTH_TX_ON(); + else if (!strncmp(argv[auth_opt_idx]->arg, "r", 1)) + OSPF6_DEBUG_AUTH_RX_ON(); + } else { + OSPF6_DEBUG_AUTH_TX_ON(); + OSPF6_DEBUG_AUTH_RX_ON(); + } + + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf6_auth, + no_debug_ospf6_auth_cmd, + "no debug ospf6 authentication []", + NO_STR + DEBUG_STR + OSPF6_STR + "debug OSPF6 authentication\n" + "debug authentication tx\n" + "debug authentication rx\n") +{ + int auth_opt_idx = 3; + + if (argc == 5) { + if (!strncmp(argv[auth_opt_idx]->arg, "t", 1)) + OSPF6_DEBUG_AUTH_TX_OFF(); + else if (!strncmp(argv[auth_opt_idx]->arg, "r", 1)) + OSPF6_DEBUG_AUTH_RX_OFF(); + } else { + OSPF6_DEBUG_AUTH_TX_OFF(); + OSPF6_DEBUG_AUTH_RX_OFF(); + } + + return CMD_SUCCESS; +} + +int config_write_ospf6_debug_auth(struct vty *vty) +{ + if (IS_OSPF6_DEBUG_AUTH_TX) + vty_out(vty, "debug ospf6 authentication tx\n"); + if (IS_OSPF6_DEBUG_AUTH_RX) + vty_out(vty, "debug ospf6 authentication rx\n"); + return 0; +} + +void install_element_ospf6_debug_auth(void) +{ + install_element(ENABLE_NODE, &debug_ospf6_auth_cmd); + install_element(ENABLE_NODE, &no_debug_ospf6_auth_cmd); + install_element(CONFIG_NODE, &debug_ospf6_auth_cmd); + install_element(CONFIG_NODE, &no_debug_ospf6_auth_cmd); +} + +/* Clear the specified interface structure */ +static void ospf6_intf_auth_clear(struct vty *vty, struct interface *ifp) +{ + struct ospf6_interface *oi; + + if (!if_is_operative(ifp)) + return; + + if (ifp->info == NULL) + return; + + oi = (struct ospf6_interface *)ifp->info; + + if (IS_OSPF6_DEBUG_INTERFACE) + zlog_debug( + "Interface %s: clear authentication rx/tx drop counters", + ifp->name); + + /* Reset the interface rx/tx drop counters */ + oi->at_data.tx_drop = 0; + oi->at_data.rx_drop = 0; +} + +/* Clear interface */ +DEFUN(clear_ipv6_ospf6_intf_auth, + clear_ipv6_ospf6_intf_auth_cmd, + "clear ipv6 ospf6 auth-counters interface [IFNAME]", + CLEAR_STR + IP6_STR + OSPF6_STR + "authentication rx/tx drop counters\n" + INTERFACE_STR + IFNAME_STR) +{ + struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + int idx_ifname = 5; + struct interface *ifp; + + if (argc == 5) { /* Clear all the ospfv3 interfaces. */ + FOR_ALL_INTERFACES (vrf, ifp) + ospf6_intf_auth_clear(vty, ifp); + } else { /* Interface name is specified. */ + ifp = if_lookup_by_name(argv[idx_ifname]->arg, VRF_DEFAULT); + if (ifp == NULL) { + vty_out(vty, "Error: No such Interface: %s\n", + argv[idx_ifname]->arg); + return CMD_WARNING; + } + ospf6_intf_auth_clear(vty, ifp); + } + + return CMD_SUCCESS; +} + +void install_element_ospf6_clear_intf_auth(void) +{ + install_element(ENABLE_NODE, &clear_ipv6_ospf6_intf_auth_cmd); +} diff --git a/ospf6d/ospf6_auth_trailer.h b/ospf6d/ospf6_auth_trailer.h new file mode 100644 index 0000000000..fa2de28efa --- /dev/null +++ b/ospf6d/ospf6_auth_trailer.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2021 Abhinay Ramesh + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __OSPF6_AUTH_TRAILER_H__ +#define __OSPF6_AUTH_TRAILER_H__ + +#include "lib/keychain.h" +#include "ospf6_message.h" + +#define OSPF6_AUTH_HDR_MIN_SIZE 16 +#define OSPF6_AUTH_HDR_FULL KEYCHAIN_MAX_HASH_SIZE + OSPF6_AUTH_HDR_MIN_SIZE + +#define OSPF6_AUTHENTICATION_NULL 0 +#define OSPF6_AUTHENTICATION_CRYPTOGRAPHIC 1 +static const uint16_t CPID = 1; + +/* Auth debug options */ +extern unsigned char conf_debug_ospf6_auth[2]; +#define OSPF6_AUTH_TX 0 +#define OSPF6_AUTH_RX 1 +#define OSPF6_DEBUG_AUTH_TX_ON() (conf_debug_ospf6_auth[OSPF6_AUTH_TX] = 1) +#define OSPF6_DEBUG_AUTH_TX_OFF() (conf_debug_ospf6_auth[OSPF6_AUTH_TX] = 0) +#define OSPF6_DEBUG_AUTH_RX_ON() (conf_debug_ospf6_auth[OSPF6_AUTH_RX] = 1) +#define OSPF6_DEBUG_AUTH_RX_OFF() (conf_debug_ospf6_auth[OSPF6_AUTH_RX] = 0) +#define IS_OSPF6_DEBUG_AUTH_TX (conf_debug_ospf6_auth[OSPF6_AUTH_TX]) +#define IS_OSPF6_DEBUG_AUTH_RX (conf_debug_ospf6_auth[OSPF6_AUTH_RX]) + +#define OSPF6_AUTH_TRAILER_KEYCHAIN (1 << 0) +#define OSPF6_AUTH_TRAILER_MANUAL_KEY (1 << 1) +#define OSPF6_AUTH_TRAILER_KEYCHAIN_VALID (1 << 2) + +/* According to sesion 4.1 of RFC7166 defining the trailer struct */ +struct ospf6_auth_hdr { + uint16_t type; + uint16_t length; + uint16_t reserved; + uint16_t id; + uint32_t seqnum_h; + uint32_t seqnum_l; + unsigned char data[KEYCHAIN_MAX_HASH_SIZE]; +}; + +void ospf6_auth_hdr_dump_send(struct ospf6_header *ospfh, uint16_t length); +void ospf6_auth_hdr_dump_recv(struct ospf6_header *ospfh, uint16_t length); +unsigned char *ospf6_hash_message_xor(unsigned char *mes1, unsigned char *mes2, + uint32_t len); +unsigned int ospf6_auth_len_get(struct ospf6_interface *oi); +int ospf6_auth_validate_pkt(struct ospf6_interface *oi, unsigned int *pkt_len, + struct ospf6_header *oh, unsigned int *at_len); +int ospf6_auth_check_digest(struct ospf6_header *oh, struct ospf6_interface *oi, + struct in6_addr *src); +void ospf6_auth_update_digest(struct ospf6_interface *oi, + struct ospf6_header *oh, + struct ospf6_auth_hdr *ospf6_auth, char *auth_str, + uint16_t auth_len, uint32_t pkt_len, + enum keychain_hash_algo algo); +void ospf6_auth_digest_send(struct in6_addr *src, struct ospf6_interface *oi, + struct ospf6_header *oh, uint16_t auth_len, + uint32_t pkt_len); +void install_element_ospf6_debug_auth(void); +int config_write_ospf6_debug_auth(struct vty *vty); +void install_element_ospf6_clear_intf_auth(void); +#endif /* __OSPF6_AUTH_TRAILER_H__ */ diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h index d068a9857a..d6a2b52e12 100644 --- a/ospf6d/ospf6_interface.h +++ b/ospf6d/ospf6_interface.h @@ -26,6 +26,8 @@ #include "if.h" #include "ospf6d.h" +DECLARE_MTYPE(OSPF6_AUTH_MANUAL_KEY); + /* Debug option */ extern unsigned char conf_debug_ospf6_interface; #define OSPF6_DEBUG_INTERFACE_ON() (conf_debug_ospf6_interface = 1) diff --git a/ospf6d/ospf6_message.h b/ospf6d/ospf6_message.h index 0cd10ef825..68cb4c7794 100644 --- a/ospf6d/ospf6_message.h +++ b/ospf6d/ospf6_message.h @@ -23,6 +23,8 @@ #define OSPF6_MESSAGE_BUFSIZ 4096 +extern const struct message ospf6_message_type_str[]; + /* Debug option */ extern unsigned char conf_debug_ospf6_message[]; #define OSPF6_ACTION_SEND 0x01 From 6cb85350df7fdcea106947fdda42a91ddebcdd5e Mon Sep 17 00:00:00 2001 From: Abhinay Ramesh Date: Sun, 30 May 2021 16:33:41 +0000 Subject: [PATCH 6/9] ospf6d: Stitching the auth trailer code with rest of ospf6. Problem Statement: ================== RFC 7166 support for OSPF6 in FRR code. RCA: ==== This feature is newly supported in FRR Fix: ==== Core functionality implemented in previous commit is stitched with rest of ospf6 code as part of this commit. Risk: ===== Low risk Tests Executed: =============== Have executed the combination of commands. Signed-off-by: Abhinay Ramesh --- configure.ac | 1 + ospf6d/ospf6_auth_trailer.c | 500 ++++++++++++++++++++++++------------ ospf6d/ospf6_auth_trailer.h | 29 ++- ospf6d/ospf6_interface.c | 13 +- ospf6d/ospf6_interface.h | 34 +-- ospf6d/ospf6_intra.c | 2 +- ospf6d/ospf6_message.c | 221 ++++++++++++++-- ospf6d/ospf6_message.h | 16 +- ospf6d/ospf6_neighbor.c | 67 ++++- ospf6d/ospf6_neighbor.h | 8 + ospf6d/ospf6_network.c | 16 +- ospf6d/ospf6_proto.c | 8 +- ospf6d/ospf6_proto.h | 15 +- ospf6d/ospf6_route.c | 6 +- ospf6d/ospf6_top.c | 20 ++ ospf6d/ospf6_top.h | 2 + 16 files changed, 697 insertions(+), 261 deletions(-) diff --git a/configure.ac b/configure.ac index e7bbe329b1..bdddf4b846 100644 --- a/configure.ac +++ b/configure.ac @@ -2587,6 +2587,7 @@ AC_DEFINE_UNQUOTED([ZEBRA_SERV_PATH], ["$frr_statedir%s%s/zserv.api"], [zebra ap AC_DEFINE_UNQUOTED([BFDD_CONTROL_SOCKET], ["$frr_statedir%s%s/bfdd.sock"], [bfdd control socket]) AC_DEFINE_UNQUOTED([OSPFD_GR_STATE], ["$frr_statedir%s/ospfd-gr.json"], [ospfd GR state information]) AC_DEFINE_UNQUOTED([OSPF6D_GR_STATE], ["$frr_statedir/ospf6d-gr.json"], [ospf6d GR state information]) +AC_DEFINE_UNQUOTED([OSPF6_AUTH_SEQ_NUM_FILE], ["$frr_statedir/ospf6d-at-seq-no.dat"], [ospf6d AT Sequence number information]) AC_DEFINE_UNQUOTED([DAEMON_VTY_DIR], ["$frr_statedir%s%s"], [daemon vty directory]) AC_DEFINE_UNQUOTED([DAEMON_DB_DIR], ["$frr_statedir"], [daemon database directory]) diff --git a/ospf6d/ospf6_auth_trailer.c b/ospf6d/ospf6_auth_trailer.c index 88d2fc240e..2d0621cc2c 100644 --- a/ospf6d/ospf6_auth_trailer.c +++ b/ospf6d/ospf6_auth_trailer.c @@ -19,6 +19,7 @@ */ #include "zebra.h" +#include "config.h" #include "memory.h" #include "ospf6d.h" #include "vty.h" @@ -30,14 +31,14 @@ #include "ospf6_interface.h" #include "ospf6_neighbor.h" #include "ospf6_proto.h" +#include "ospf6_top.h" +#include "ospf6_area.h" #include "ospf6_auth_trailer.h" #include "ospf6_route.h" #include "ospf6_zebra.h" #include "lib/keychain.h" unsigned char conf_debug_ospf6_auth[2]; -DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_AUTH_PKT, "OSPF6 auth packet"); -DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_AUTH_HASH, "OSPF6 auth hash"); DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_AUTH_HASH_XOR, "OSPF6 auth hash xor"); /*Apad is the hexadecimal value 0x878FE1F3. */ @@ -106,17 +107,18 @@ void ospf6_auth_hdr_dump_send(struct ospf6_header *ospfh, uint16_t length) } } -void ospf6_auth_hdr_dump_recv(struct ospf6_header *ospfh, uint16_t length) +void ospf6_auth_hdr_dump_recv(struct ospf6_header *ospfh, uint16_t length, + unsigned int lls_len) { struct ospf6_auth_hdr *ospf6_at_hdr; int at_len, oh_len, at_hdr_len, hash_len; - unsigned char temp[KEYCHAIN_MAX_HASH_SIZE+1]; + unsigned char temp[KEYCHAIN_MAX_HASH_SIZE + 1]; oh_len = ntohs(ospfh->length); - at_len = length - oh_len; + at_len = length - (oh_len + lls_len); if (at_len > 0) { - ospf6_at_hdr = (struct ospf6_auth_hdr *) - ((uint8_t *)ospfh + oh_len); + ospf6_at_hdr = + (struct ospf6_auth_hdr *)((uint8_t *)ospfh + oh_len); at_hdr_len = ntohs(ospf6_at_hdr->length); hash_len = at_hdr_len - OSPF6_AUTH_HDR_MIN_SIZE; memcpy(temp, ospf6_at_hdr->data, hash_len); @@ -312,7 +314,8 @@ unsigned int ospf6_auth_len_get(struct ospf6_interface *oi) } int ospf6_auth_validate_pkt(struct ospf6_interface *oi, unsigned int *pkt_len, - struct ospf6_header *oh, unsigned int *at_len) + struct ospf6_header *oh, unsigned int *at_len, + unsigned int *lls_block_len) { struct ospf6_hello *hello = NULL; struct ospf6_dbdesc *dbdesc = NULL; @@ -321,132 +324,146 @@ int ospf6_auth_validate_pkt(struct ospf6_interface *oi, unsigned int *pkt_len, uint16_t hdr_len = 0; uint32_t oh_seqnum_h = 0; uint32_t oh_seqnum_l = 0; + bool auth_present = false; + bool lls_present = false; + struct ospf6_lls_hdr *lls_hdr = NULL; on = ospf6_neighbor_lookup(oh->router_id, oi); hdr_len = ntohs(oh->length); if (*pkt_len < hdr_len) { if (IS_OSPF6_DEBUG_AUTH_RX) - zlog_warn("RECV[%s] Wrong %s packet auth length", - oi->interface->name, - lookup_msg(ospf6_message_type_str, oh->type, - NULL)); - oi->at_data.rx_drop++; - return -1; + zlog_err("RECV[%s] Received incomplete %s packet", + oi->interface->name, + ospf6_message_type(oh->type)); + return OSPF6_AUTH_VALIDATE_FAILURE; } else if (*pkt_len == hdr_len) { - /* no auth data in packet + if (oi->at_data.flags != 0) + return OSPF6_AUTH_VALIDATE_FAILURE; + /* No auth info to be considered. */ - return -1; + return OSPF6_AUTH_PROCESS_NORMAL; } switch (oh->type) { case OSPF6_MESSAGE_TYPE_HELLO: hello = (struct ospf6_hello *)((uint8_t *)oh + sizeof(struct ospf6_header)); - if (OSPF6_OPT_ISSET_EXT(hello->options, OSPF6_OPT_AT)) { - if (on) - on->auth_present = true; - } else { - if (on) - on->auth_present = false; + if (OSPF6_OPT_ISSET_EXT(hello->options, OSPF6_OPT_L)) + lls_present = true; - if (oi->at_data.flags != 0) { - if (IS_OSPF6_DEBUG_AUTH_RX) - zlog_warn("RECV[%s] : Auth option miss-match in hello pkt", - oi->interface->name); - oi->at_data.rx_drop++; - } - - return -1; - } + if (OSPF6_OPT_ISSET_EXT(hello->options, OSPF6_OPT_AT)) + auth_present = true; break; case OSPF6_MESSAGE_TYPE_DBDESC: dbdesc = (struct ospf6_dbdesc *)((uint8_t *)oh + sizeof(struct ospf6_header)); + if (OSPF6_OPT_ISSET_EXT(dbdesc->options, OSPF6_OPT_L)) + lls_present = true; - if (OSPF6_OPT_ISSET_EXT(dbdesc->options, OSPF6_OPT_AT)) { - if (on) - on->auth_present = true; - } else { - if (on) - on->auth_present = false; - - if (oi->at_data.flags != 0) { - if (IS_OSPF6_DEBUG_AUTH_RX) - zlog_warn("RECV[%s] : Auth option miss-match in DB desc pkt", - oi->interface->name); - oi->at_data.rx_drop++; - } - - return -1; - } + if (OSPF6_OPT_ISSET_EXT(dbdesc->options, OSPF6_OPT_AT)) + auth_present = true; break; case OSPF6_MESSAGE_TYPE_LSREQ: case OSPF6_MESSAGE_TYPE_LSUPDATE: case OSPF6_MESSAGE_TYPE_LSACK: - if ((on && on->auth_present == false) - && (oi->at_data.flags != 0)) { - if (IS_OSPF6_DEBUG_AUTH_RX) - zlog_warn("RECV[%s] : Auth config miss-match in %s", - oi->interface->name, - lookup_msg(ospf6_message_type_str, - oh->type, NULL)); - oi->at_data.rx_drop++; - return -1; + if (on) { + lls_present = on->lls_present; + auth_present = on->auth_present; } break; default: if (IS_OSPF6_DEBUG_AUTH_RX) - zlog_warn("RECV[%s] : Wrong packet type %d", - oi->interface->name, oh->type); - return -1; + zlog_err("RECV[%s] : Wrong packet type %d", + oi->interface->name, oh->type); + return OSPF6_AUTH_VALIDATE_FAILURE; + } + + if ((oh->type == OSPF6_MESSAGE_TYPE_HELLO) + || (oh->type == OSPF6_MESSAGE_TYPE_DBDESC)) { + if (on) { + on->auth_present = auth_present; + on->lls_present = lls_present; + } + } + + if ((!auth_present && (oi->at_data.flags != 0)) + || (auth_present && (oi->at_data.flags == 0))) { + if (IS_OSPF6_DEBUG_AUTH_RX) + zlog_err("RECV[%s] : Auth option miss-match in %s pkt", + oi->interface->name, + ospf6_message_type(oh->type)); + return OSPF6_AUTH_VALIDATE_FAILURE; + } + + if (lls_present) { + lls_hdr = (struct ospf6_lls_hdr *)(oh + hdr_len); + *lls_block_len = ntohs(lls_hdr->length) * 4; + } + + if (*lls_block_len > (*pkt_len - hdr_len)) { + if (IS_OSPF6_DEBUG_AUTH_RX) + zlog_err("RECV[%s] : Wrong lls data in %s packet", + oi->interface->name, + ospf6_message_type(oh->type)); + return OSPF6_AUTH_VALIDATE_FAILURE; } memset(&ospf6_auth_info, 0, sizeof(struct ospf6_auth_hdr)); - memcpy(&ospf6_auth_info, (uint8_t *)oh + hdr_len, *pkt_len - hdr_len); + if ((*pkt_len - hdr_len - (*lls_block_len)) > sizeof(ospf6_auth_info)) { + if (IS_OSPF6_DEBUG_AUTH_RX) + zlog_err("RECV[%s] : Wrong auth data in %s packet", + oi->interface->name, + ospf6_message_type(oh->type)); + return OSPF6_AUTH_VALIDATE_FAILURE; + } + + memcpy(&ospf6_auth_info, ((uint8_t *)oh + hdr_len + (*lls_block_len)), + (*pkt_len - hdr_len - (*lls_block_len))); if (ntohs(ospf6_auth_info.length) > OSPF6_AUTH_HDR_FULL) { if (IS_OSPF6_DEBUG_AUTH_RX) - zlog_warn("RECV[%s] : Auth config miss-match in %s", - oi->interface->name, - lookup_msg(ospf6_message_type_str, oh->type, - NULL)); - oi->at_data.rx_drop++; - return -1; + zlog_err("RECV[%s] : Wrong auth header length in %s", + oi->interface->name, + ospf6_message_type(oh->type)); + return OSPF6_AUTH_VALIDATE_FAILURE; } /* after authentication header validation is done * reduce the auth hdr size from the packet length */ *at_len = ntohs(ospf6_auth_info.length); - *pkt_len = *pkt_len - *at_len; + *pkt_len = (*pkt_len) - (*at_len) - (*lls_block_len); if (on) { oh_seqnum_h = ntohl(ospf6_auth_info.seqnum_h); oh_seqnum_l = ntohl(ospf6_auth_info.seqnum_l); - if ((oh_seqnum_h >= on->seqnum_h) && - (oh_seqnum_l > on->seqnum_l)) { + if ((oh_seqnum_h >= on->seqnum_h[oh->type]) + && (oh_seqnum_l > on->seqnum_l[oh->type])) { /* valid sequence number received */ - on->seqnum_h = oh_seqnum_h; - on->seqnum_l = oh_seqnum_l; + on->seqnum_h[oh->type] = oh_seqnum_h; + on->seqnum_l[oh->type] = oh_seqnum_l; } else { if (IS_OSPF6_DEBUG_AUTH_RX) { - zlog_warn("RECV[%s] : Nbr(%s) Auth Sequence number mismatch", - oi->interface->name, on->name); - zlog_warn("nbr_seq_l %u, nbr_seq_h %u, hdr_seq_l %u, hdr_seq_h %u", - on->seqnum_l, on->seqnum_h, - oh_seqnum_l, oh_seqnum_h); + zlog_err( + "RECV[%s] : Nbr(%s) Auth Sequence number mismatch in %s ", + oi->interface->name, on->name, + ospf6_message_type(oh->type)); + zlog_err( + "nbr_seq_l %u, nbr_seq_h %u, hdr_seq_l %u, hdr_seq_h %u", + on->seqnum_l[oh->type], + on->seqnum_h[oh->type], oh_seqnum_l, + oh_seqnum_h); } - oi->at_data.rx_drop++; - return -1; + return OSPF6_AUTH_VALIDATE_FAILURE; } } - return 0; + return OSPF6_AUTH_VALIDATE_SUCCESS; } /* Starting point of packet process function. */ int ospf6_auth_check_digest(struct ospf6_header *oh, struct ospf6_interface *oi, - struct in6_addr *src) + struct in6_addr *src, unsigned int lls_block_len) { uint32_t hash_len = KEYCHAIN_MAX_HASH_SIZE; unsigned char apad[hash_len]; @@ -459,32 +476,31 @@ int ospf6_auth_check_digest(struct ospf6_header *oh, struct ospf6_interface *oi, uint16_t auth_len = 0; uint8_t hash_algo = 0; uint16_t oh_len = ntohs(oh->length); + int ret = 0; if (oi->at_data.flags == 0) - return -2; + return OSPF6_AUTH_PROCESS_NORMAL; - ospf6_auth = (struct ospf6_auth_hdr *)((uint8_t *)oh + oh_len); + ospf6_auth = (struct ospf6_auth_hdr *)((uint8_t *)oh + oh_len + + lls_block_len); if (CHECK_FLAG(oi->at_data.flags, OSPF6_AUTH_TRAILER_KEYCHAIN)) { keychain = keychain_lookup(oi->at_data.keychain); if (!keychain) { if (IS_OSPF6_DEBUG_AUTH_RX) - zlog_warn("RECV[%s]: Keychain does't exist for %s", - oi->interface->name, - lookup_msg(ospf6_message_type_str, - oh->type, NULL)); - oi->at_data.rx_drop++; - return -1; + zlog_err( + "RECV[%s]: Keychain does't exist for %s", + oi->interface->name, + ospf6_message_type(oh->type)); + return OSPF6_AUTH_VALIDATE_FAILURE; } key = key_lookup_for_accept(keychain, ntohs(ospf6_auth->id)); if (!key) { if (IS_OSPF6_DEBUG_AUTH_RX) - zlog_warn("RECV[%s]: Auth, Invalid SA for %s", - oi->interface->name, - lookup_msg(ospf6_message_type_str, - oh->type, NULL)); - oi->at_data.rx_drop++; - return -1; + zlog_err("RECV[%s]: Auth, Invalid SA for %s", + oi->interface->name, + ospf6_message_type(oh->type)); + return OSPF6_AUTH_VALIDATE_FAILURE; } if (key && key->string @@ -493,12 +509,11 @@ int ospf6_auth_check_digest(struct ospf6_header *oh, struct ospf6_interface *oi, hash_algo = key->hash_algo; } else { if (IS_OSPF6_DEBUG_AUTH_RX) - zlog_warn("RECV[%s]: Incomplete keychain config for %s", - oi->interface->name, - lookup_msg(ospf6_message_type_str, - oh->type, NULL)); - oi->at_data.rx_drop++; - return -1; + zlog_err( + "RECV[%s]: Incomplete keychain config for %s", + oi->interface->name, + ospf6_message_type(oh->type)); + return OSPF6_AUTH_VALIDATE_FAILURE; } } else if (CHECK_FLAG(oi->at_data.flags, OSPF6_AUTH_TRAILER_MANUAL_KEY)) { @@ -507,11 +522,11 @@ int ospf6_auth_check_digest(struct ospf6_header *oh, struct ospf6_interface *oi, } if (!auth_str) - return -2; + return OSPF6_AUTH_VALIDATE_FAILURE; hash_len = keychain_get_hash_len(hash_algo); - memset(apad, 0, sizeof(hash_len)); - memset(temp_hash, 0, sizeof(hash_len)); + memset(apad, 0, sizeof(apad)); + memset(temp_hash, 0, sizeof(temp_hash)); /* start digest verification */ memcpy(apad, src, ipv6_addr_size); @@ -523,14 +538,19 @@ int ospf6_auth_check_digest(struct ospf6_header *oh, struct ospf6_interface *oi, memcpy(temp_hash, ospf6_auth->data, hash_len); memcpy(ospf6_auth->data, apad, hash_len); - ospf6_auth_update_digest(oi, oh, ospf6_auth, auth_str, auth_len, - (oh_len + auth_len), hash_algo); + ospf6_auth_update_digest(oi, oh, ospf6_auth, auth_str, + (oh_len + auth_len + lls_block_len), + hash_algo); #ifdef CRYPTO_OPENSSL - return !(CRYPTO_memcmp(temp_hash, ospf6_auth->data, hash_len)); + ret = CRYPTO_memcmp(temp_hash, ospf6_auth->data, hash_len); #else - return !(memcmp(temp_hash, ospf6_auth->data, hash_len)); + ret = memcmp(temp_hash, ospf6_auth->data, hash_len); #endif + if (ret == 0) + return OSPF6_AUTH_VALIDATE_SUCCESS; + + return OSPF6_AUTH_VALIDATE_FAILURE; } void ospf6_auth_digest_send(struct in6_addr *src, struct ospf6_interface *oi, @@ -547,6 +567,7 @@ void ospf6_auth_digest_send(struct in6_addr *src, struct ospf6_interface *oi, uint32_t hash_len = KEYCHAIN_MAX_HASH_SIZE; unsigned char apad[hash_len]; int ipv6_addr_size = sizeof(struct in6_addr); + struct ospf6 *ospf6 = NULL; if (CHECK_FLAG(oi->at_data.flags, OSPF6_AUTH_TRAILER_KEYCHAIN)) { if (CHECK_FLAG(oi->at_data.flags, @@ -576,8 +597,7 @@ void ospf6_auth_digest_send(struct in6_addr *src, struct ospf6_interface *oi, if (IS_OSPF6_DEBUG_AUTH_TX) zlog_warn("SEND[%s]: Authentication not configured for %s", oi->interface->name, - lookup_msg(ospf6_message_type_str, - oh->type, NULL)); + ospf6_message_type(oh->type)); return; } @@ -585,27 +605,33 @@ void ospf6_auth_digest_send(struct in6_addr *src, struct ospf6_interface *oi, if (IS_OSPF6_DEBUG_AUTH_TX) zlog_warn("SEND[%s]: Authentication key is not configured for %s", oi->interface->name, - lookup_msg(ospf6_message_type_str, - oh->type, NULL)); + ospf6_message_type(oh->type)); return; } hash_len = keychain_get_hash_len(hash_algo); - oi->at_data.seqnum_l++; + if (oi->area && oi->area->ospf6) + ospf6 = oi->area->ospf6; + else + return; - if (oi->at_data.seqnum_l == 0xFFFFFFFF) { - oi->at_data.seqnum_h++; - oi->at_data.seqnum_l = 0; + ospf6->seqnum_l++; + if (ospf6->seqnum_l == 0xFFFFFFFF) { + ospf6->seqnum_h++; + ospf6->seqnum_l = 0; + ospf6_auth_seqno_nvm_update(ospf6); } /* Key must be reset. which is not handled as of now. */ - if ((oi->at_data.seqnum_l == 0xFFFFFFFF) - && (oi->at_data.seqnum_h == 0xFFFFFFFF)) { - oi->at_data.seqnum_l = 0; - oi->at_data.seqnum_h = 0; + if ((ospf6->seqnum_l == 0xFFFFFFFF) + && (ospf6->seqnum_h == 0xFFFFFFFF)) { + ospf6->seqnum_l = 0; + ospf6->seqnum_h = 0; + zlog_err( + "Both Higher and Lower sequence number has wrapped. Need to reset the key"); } - memset(apad, 0, hash_len); + memset(apad, 0, sizeof(apad)); if (src) memcpy(apad, src, ipv6_addr_size); @@ -619,13 +645,18 @@ void ospf6_auth_digest_send(struct in6_addr *src, struct ospf6_interface *oi, ospf6_auth->length = htons(auth_len); ospf6_auth->reserved = 0; ospf6_auth->id = htons(key_id); - ospf6_auth->seqnum_h = htonl(oi->at_data.seqnum_h); - ospf6_auth->seqnum_l = htonl(oi->at_data.seqnum_l); + ospf6_auth->seqnum_h = htonl(ospf6->seqnum_h); + ospf6_auth->seqnum_l = htonl(ospf6->seqnum_l); memcpy(ospf6_auth->data, apad, hash_len); - ospf6_auth_update_digest(oi, oh, ospf6_auth, auth_str, auth_len, - pkt_len, hash_algo); + ospf6_auth_update_digest(oi, oh, ospf6_auth, auth_str, pkt_len, + hash_algo); + /* There is a optimisation that is done to ensure that + * for every packet flow keychain lib API are called + * only once and the result are stored in oi->at_data. + * So, After processing the flow it is reset back here. + */ if (CHECK_FLAG(oi->at_data.flags, OSPF6_AUTH_TRAILER_KEYCHAIN_VALID)) { oi->at_data.hash_algo = KEYCHAIN_ALGO_NULL; if (oi->at_data.auth_key) { @@ -643,24 +674,26 @@ void ospf6_auth_digest_send(struct in6_addr *src, struct ospf6_interface *oi, void ospf6_auth_update_digest(struct ospf6_interface *oi, struct ospf6_header *oh, struct ospf6_auth_hdr *ospf6_auth, char *auth_str, - uint16_t auth_len, uint32_t pkt_len, - enum keychain_hash_algo algo) + uint32_t pkt_len, enum keychain_hash_algo algo) { + static const uint16_t cpid = 1; uint32_t hash_len = keychain_get_hash_len(algo); uint32_t block_s = keychain_get_block_size(algo); uint32_t k_len = strlen(auth_str); - uint32_t ks_len = strlen(auth_str) + sizeof(CPID); + uint32_t ks_len = strlen(auth_str) + sizeof(cpid); unsigned char ipad[block_s]; unsigned char opad[block_s]; unsigned char ko[block_s], ks[ks_len], tmp[hash_len]; unsigned char *first = NULL; unsigned char *second = NULL; - unsigned char *first_mes, *second_mes; - unsigned char *first_hash, *second_hash; + unsigned char first_mes[block_s + pkt_len]; + unsigned char second_mes[block_s + pkt_len]; + unsigned char first_hash[hash_len]; + unsigned char second_hash[hash_len]; - memset(ko, 0, block_s); + memset(ko, 0, sizeof(ko)); memcpy(ks, auth_str, k_len); - memcpy(ks + k_len, &CPID, sizeof(CPID)); + memcpy(ks + k_len, &cpid, sizeof(cpid)); if (ks_len > hash_len) { ospf6_hash_hmac_sha_digest(algo, ks, ks_len, tmp); memcpy(ko, tmp, hash_len); @@ -673,31 +706,19 @@ void ospf6_auth_update_digest(struct ospf6_interface *oi, first = ospf6_hash_message_xor((unsigned char *)&ipad, ko, block_s); second = ospf6_hash_message_xor((unsigned char *)&opad, ko, block_s); - first_mes = XMALLOC(MTYPE_OSPF6_AUTH_PKT, (block_s + pkt_len)); - memcpy(first_mes, first, block_s); memcpy(first_mes + block_s, oh, pkt_len); - first_hash = XMALLOC(MTYPE_OSPF6_AUTH_HASH, hash_len); - ospf6_hash_hmac_sha_digest(algo, first_mes, (block_s + pkt_len), first_hash); - second_mes = XMALLOC(MTYPE_OSPF6_AUTH_PKT, (block_s + hash_len)); - memcpy(second_mes, second, block_s); memcpy(second_mes + block_s, first_hash, hash_len); - second_hash = XMALLOC(MTYPE_OSPF6_AUTH_HASH, hash_len); - ospf6_hash_hmac_sha_digest(algo, second_mes, (block_s + hash_len), second_hash); memcpy(ospf6_auth->data, second_hash, hash_len); - XFREE(MTYPE_OSPF6_AUTH_PKT, first_mes); - XFREE(MTYPE_OSPF6_AUTH_PKT, second_mes); - XFREE(MTYPE_OSPF6_AUTH_HASH, first_hash); - XFREE(MTYPE_OSPF6_AUTH_HASH, second_hash); XFREE(MTYPE_OSPF6_AUTH_HASH_XOR, first); XFREE(MTYPE_OSPF6_AUTH_HASH_XOR, second); } @@ -792,31 +813,50 @@ static void ospf6_intf_auth_clear(struct vty *vty, struct interface *ifp) } /* Clear interface */ -DEFUN(clear_ipv6_ospf6_intf_auth, - clear_ipv6_ospf6_intf_auth_cmd, - "clear ipv6 ospf6 auth-counters interface [IFNAME]", - CLEAR_STR - IP6_STR - OSPF6_STR - "authentication rx/tx drop counters\n" - INTERFACE_STR - IFNAME_STR) +DEFUN(clear_ipv6_ospf6_intf_auth, clear_ipv6_ospf6_intf_auth_cmd, + "clear ipv6 ospf6 [vrf VRF] auth-counters interface [IFNAME]", + CLEAR_STR IP6_STR OSPF6_STR VRF_CMD_HELP_STR + "authentication rx/tx drop counters\n" INTERFACE_STR IFNAME_STR) { - struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); - int idx_ifname = 5; + int idx_ifname = 0; + int idx_vrf = 0; struct interface *ifp; + struct listnode *node; + struct ospf6 *ospf6 = NULL; + char *vrf_name = NULL; + vrf_id_t vrf_id = VRF_DEFAULT; + struct vrf *vrf = NULL; - if (argc == 5) { /* Clear all the ospfv3 interfaces. */ - FOR_ALL_INTERFACES (vrf, ifp) - ospf6_intf_auth_clear(vty, ifp); - } else { /* Interface name is specified. */ - ifp = if_lookup_by_name(argv[idx_ifname]->arg, VRF_DEFAULT); - if (ifp == NULL) { - vty_out(vty, "Error: No such Interface: %s\n", - argv[idx_ifname]->arg); - return CMD_WARNING; + if (argv_find(argv, argc, "vrf", &idx_vrf)) + vrf_name = argv[idx_vrf + 1]->arg; + + if (vrf_name && strmatch(vrf_name, VRF_DEFAULT_NAME)) + vrf_name = NULL; + + if (vrf_name) { + vrf = vrf_lookup_by_name(vrf_name); + if (vrf) + vrf_id = vrf->vrf_id; + } + + if (!argv_find(argv, argc, "IFNAME", &idx_ifname)) { + /* Clear all the ospfv3 interfaces auth data. */ + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (vrf_id != ospf6->vrf_id) + continue; + + if (!vrf) + vrf = vrf_lookup_by_id(ospf6->vrf_id); + FOR_ALL_INTERFACES (vrf, ifp) + ospf6_intf_auth_clear(vty, ifp); } - ospf6_intf_auth_clear(vty, ifp); + } else { + /* Interface name is specified. */ + ifp = if_lookup_by_name(argv[idx_ifname]->arg, vrf_id); + if (ifp == NULL) + vty_out(vty, "No such interface name\n"); + else + ospf6_intf_auth_clear(vty, ifp); } return CMD_SUCCESS; @@ -826,3 +866,135 @@ void install_element_ospf6_clear_intf_auth(void) { install_element(ENABLE_NODE, &clear_ipv6_ospf6_intf_auth_cmd); } + +enum ospf6_auth_err ospf6_auth_nvm_file_exist(void) +{ + struct stat buffer; + int exist; + + exist = stat(OSPF6_AUTH_SEQ_NUM_FILE, &buffer); + if (exist == 0) + return OSPF6_AUTH_FILE_EXIST; + else + return OSPF6_AUTH_FILE_DO_NOT_EXIST; +} + +/* + * Record in non-volatile memory the given ospf6 process, + * authentication trailer higher order sequence number. + */ +void ospf6_auth_seqno_nvm_update(struct ospf6 *ospf6) +{ + const char *inst_name; + json_object *json; + json_object *json_instances; + json_object *json_instance; + + zlog_err("Higher order sequence number %d update for %s process", + ospf6->seqnum_h, ospf6->name); + + inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME; + + json = json_object_from_file((char *)OSPF6_AUTH_SEQ_NUM_FILE); + if (json == NULL) + json = json_object_new_object(); + + json_object_object_get_ex(json, "instances", &json_instances); + if (!json_instances) { + json_instances = json_object_new_object(); + json_object_object_add(json, "instances", json_instances); + } + + json_object_object_get_ex(json_instances, inst_name, &json_instance); + if (!json_instance) { + json_instance = json_object_new_object(); + json_object_object_add(json_instances, inst_name, + json_instance); + } + + /* + * Record higher order sequence number in non volatile memory. + */ + json_object_int_add(json_instance, "sequence_number", ospf6->seqnum_h); + + json_object_to_file_ext((char *)OSPF6_AUTH_SEQ_NUM_FILE, json, + JSON_C_TO_STRING_PRETTY); + json_object_free(json); +} + +/* + * Delete authentication sequence number for a given OSPF6 process + * from non-volatile memory. + */ +void ospf6_auth_seqno_nvm_delete(struct ospf6 *ospf6) +{ + const char *inst_name; + json_object *json; + json_object *json_instances; + + zlog_err("Higher order sequence number delete for %s process", + ospf6->name); + + inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME; + + json = json_object_from_file((char *)OSPF6_AUTH_SEQ_NUM_FILE); + if (json == NULL) + json = json_object_new_object(); + + json_object_object_get_ex(json, "instances", &json_instances); + if (!json_instances) { + json_instances = json_object_new_object(); + json_object_object_add(json, "instances", json_instances); + } + + json_object_object_del(json_instances, inst_name); + + json_object_to_file_ext((char *)OSPF6_AUTH_SEQ_NUM_FILE, json, + JSON_C_TO_STRING_PRETTY); + json_object_free(json); +} + + +/* + * Fetch from non-volatile memory the stored ospf6 process + * authentication sequence number. + */ +void ospf6_auth_seqno_nvm_read(struct ospf6 *ospf6) +{ + const char *inst_name; + json_object *json; + json_object *json_instances; + json_object *json_instance; + json_object *json_seqnum; + + inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME; + + json = json_object_from_file((char *)OSPF6_AUTH_SEQ_NUM_FILE); + if (json == NULL) + json = json_object_new_object(); + + json_object_object_get_ex(json, "instances", &json_instances); + if (!json_instances) { + json_instances = json_object_new_object(); + json_object_object_add(json, "instances", json_instances); + } + + json_object_object_get_ex(json_instances, inst_name, &json_instance); + if (!json_instance) { + json_instance = json_object_new_object(); + json_object_object_add(json_instances, inst_name, + json_instance); + } + + json_object_object_get_ex(json_instance, "sequence_number", + &json_seqnum); + ospf6->seqnum_h = json_object_get_int(json_seqnum); + + zlog_err("Higher order sequence number %d read for %s process %s", + ospf6->seqnum_h, ospf6->name, strerror(errno)); + + json_object_object_del(json_instances, inst_name); + json_object_to_file_ext((char *)OSPF6_AUTH_SEQ_NUM_FILE, json, + JSON_C_TO_STRING_PRETTY); + json_object_free(json); +} diff --git a/ospf6d/ospf6_auth_trailer.h b/ospf6d/ospf6_auth_trailer.h index fa2de28efa..86ec49beba 100644 --- a/ospf6d/ospf6_auth_trailer.h +++ b/ospf6d/ospf6_auth_trailer.h @@ -27,12 +27,12 @@ #define OSPF6_AUTH_HDR_MIN_SIZE 16 #define OSPF6_AUTH_HDR_FULL KEYCHAIN_MAX_HASH_SIZE + OSPF6_AUTH_HDR_MIN_SIZE -#define OSPF6_AUTHENTICATION_NULL 0 -#define OSPF6_AUTHENTICATION_CRYPTOGRAPHIC 1 -static const uint16_t CPID = 1; +#define OSPF6_AUTHENTICATION_NULL 0 +#define OSPF6_AUTHENTICATION_CRYPTOGRAPHIC 1 /* Auth debug options */ extern unsigned char conf_debug_ospf6_auth[2]; + #define OSPF6_AUTH_TX 0 #define OSPF6_AUTH_RX 1 #define OSPF6_DEBUG_AUTH_TX_ON() (conf_debug_ospf6_auth[OSPF6_AUTH_TX] = 1) @@ -57,24 +57,37 @@ struct ospf6_auth_hdr { unsigned char data[KEYCHAIN_MAX_HASH_SIZE]; }; +enum ospf6_auth_err { + OSPF6_AUTH_VALIDATE_SUCCESS = 0, + OSPF6_AUTH_VALIDATE_FAILURE, + OSPF6_AUTH_PROCESS_NORMAL, + OSPF6_AUTH_FILE_EXIST, + OSPF6_AUTH_FILE_DO_NOT_EXIST +}; + void ospf6_auth_hdr_dump_send(struct ospf6_header *ospfh, uint16_t length); -void ospf6_auth_hdr_dump_recv(struct ospf6_header *ospfh, uint16_t length); +void ospf6_auth_hdr_dump_recv(struct ospf6_header *ospfh, uint16_t length, + unsigned int lls_len); unsigned char *ospf6_hash_message_xor(unsigned char *mes1, unsigned char *mes2, uint32_t len); unsigned int ospf6_auth_len_get(struct ospf6_interface *oi); int ospf6_auth_validate_pkt(struct ospf6_interface *oi, unsigned int *pkt_len, - struct ospf6_header *oh, unsigned int *at_len); + struct ospf6_header *oh, unsigned int *at_len, + unsigned int *lls_block_len); int ospf6_auth_check_digest(struct ospf6_header *oh, struct ospf6_interface *oi, - struct in6_addr *src); + struct in6_addr *src, unsigned int lls_len); void ospf6_auth_update_digest(struct ospf6_interface *oi, struct ospf6_header *oh, struct ospf6_auth_hdr *ospf6_auth, char *auth_str, - uint16_t auth_len, uint32_t pkt_len, - enum keychain_hash_algo algo); + uint32_t pkt_len, enum keychain_hash_algo algo); void ospf6_auth_digest_send(struct in6_addr *src, struct ospf6_interface *oi, struct ospf6_header *oh, uint16_t auth_len, uint32_t pkt_len); void install_element_ospf6_debug_auth(void); int config_write_ospf6_debug_auth(struct vty *vty); void install_element_ospf6_clear_intf_auth(void); +enum ospf6_auth_err ospf6_auth_nvm_file_exist(void); +void ospf6_auth_seqno_nvm_update(struct ospf6 *ospf6); +void ospf6_auth_seqno_nvm_delete(struct ospf6 *ospf6); +void ospf6_auth_seqno_nvm_read(struct ospf6 *ospf6); #endif /* __OSPF6_AUTH_TRAILER_H__ */ diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index 761a86444a..f39d208bb5 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -1260,10 +1260,6 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp, OSPF6_AUTH_TRAILER_MANUAL_KEY)) json_object_string_add(json_auth, "authType", "manualkey"); - json_object_int_add(json_auth, "higherSegNo", - oi->at_data.seqnum_h); - json_object_int_add(json_auth, "lowerSegNo", - oi->at_data.seqnum_l); json_object_int_add(json_auth, "txPktDrop", oi->at_data.tx_drop); json_object_int_add(json_auth, "rxPktDrop", @@ -1278,20 +1274,15 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp, OSPF6_AUTH_TRAILER_MANUAL_KEY)) vty_out(vty, " Authentication trailer is enabled with manual key\n"); - - vty_out(vty, - " Higher sequence no %u, Lower sequence no %u\n", - oi->at_data.seqnum_h, oi->at_data.seqnum_l); vty_out(vty, " Packet drop Tx %u, Packet drop Rx %u\n", oi->at_data.tx_drop, oi->at_data.rx_drop); } } else { - if (use_json) { + if (use_json) json_object_string_add(json_auth, "authType", "NULL"); - } else { + else vty_out(vty, " Authentication Trailer is disabled\n"); - } } if (use_json) diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h index d6a2b52e12..59e4888a5f 100644 --- a/ospf6d/ospf6_interface.h +++ b/ospf6d/ospf6_interface.h @@ -35,32 +35,18 @@ extern unsigned char conf_debug_ospf6_interface; #define IS_OSPF6_DEBUG_INTERFACE (conf_debug_ospf6_interface) struct ospf6_auth_data { - /* higher order Sequence Number */ - uint32_t seqnum_h; + /* config data */ + uint8_t hash_algo; /* hash algorithm type */ + uint16_t key_id; /* key-id used as SA in auth packet */ + char *auth_key; /* Auth key */ + char *keychain; /* keychain name */ - /* lower order Sequence Number */ - uint32_t seqnum_l; + /* operational data */ + uint8_t flags; /* Flags related to auth config */ - /* Packet drop due to auth failure while sending */ - uint32_t tx_drop; - - /* Packet drop due to auth failure while reading */ - uint32_t rx_drop; - - /* hash algorithm type */ - uint8_t hash_algo; - - /* Flags related to auth config */ - uint8_t flags; - - /* key-id used as security association in auth packet */ - uint16_t key_id; - - /* Auth key */ - char *auth_key; - - /* keychain name */ - char *keychain; + /* Counters and Statistics */ + uint32_t tx_drop; /* Pkt drop due to auth fail while sending */ + uint32_t rx_drop; /* Pkt drop due to auth fail while reading */ }; /* Interface structure */ diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index 6626b4bed5..da5cad0537 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -2148,7 +2148,7 @@ static void ospf6_brouter_debug_print(struct ospf6_route *brouter) char installed[64], changed[64]; struct timeval now, res; char id[16], adv_router[16]; - char capa[16], options[16]; + char capa[16], options[32]; brouter_id = ADV_ROUTER_IN_PREFIX(&brouter->prefix); inet_ntop(AF_INET, &brouter_id, brouter_name, sizeof(brouter_name)); diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index 352cb137ed..c1004a7080 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -27,6 +27,7 @@ #include "thread.h" #include "linklist.h" #include "lib_errors.h" +#include "checksum.h" #include "ospf6_proto.h" #include "ospf6_lsa.h" @@ -48,19 +49,34 @@ #include "ospf6d.h" #include "ospf6_gr.h" #include +#include "lib/libospf.h" +#include "lib/keychain.h" +#include "ospf6_auth_trailer.h" DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_MESSAGE, "OSPF6 message"); DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_PACKET, "OSPF6 packet"); DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_FIFO, "OSPF6 FIFO queue"); unsigned char conf_debug_ospf6_message[6] = {0x03, 0, 0, 0, 0, 0}; -static const struct message ospf6_message_type_str[] = { - {OSPF6_MESSAGE_TYPE_HELLO, "Hello"}, - {OSPF6_MESSAGE_TYPE_DBDESC, "DbDesc"}, - {OSPF6_MESSAGE_TYPE_LSREQ, "LSReq"}, - {OSPF6_MESSAGE_TYPE_LSUPDATE, "LSUpdate"}, - {OSPF6_MESSAGE_TYPE_LSACK, "LSAck"}, - {0}}; + +const char *ospf6_message_type(int type) +{ + switch (type) { + case OSPF6_MESSAGE_TYPE_HELLO: + return "Hello"; + case OSPF6_MESSAGE_TYPE_DBDESC: + return "DbDesc"; + case OSPF6_MESSAGE_TYPE_LSREQ: + return "LSReq"; + case OSPF6_MESSAGE_TYPE_LSUPDATE: + return "LSUpdate"; + case OSPF6_MESSAGE_TYPE_LSACK: + return "LSAck"; + case OSPF6_MESSAGE_TYPE_UNKNOWN: + default: + return "unknown"; + } +} /* Minimum (besides the standard OSPF packet header) lengths for OSPF packets of particular types, offset is the "type" field. */ @@ -101,7 +117,7 @@ static void ospf6_header_print(struct ospf6_header *oh) void ospf6_hello_print(struct ospf6_header *oh, int action) { struct ospf6_hello *hello; - char options[16]; + char options[32]; char *p; ospf6_header_print(oh); @@ -136,7 +152,7 @@ void ospf6_hello_print(struct ospf6_header *oh, int action) void ospf6_dbdesc_print(struct ospf6_header *oh, int action) { struct ospf6_dbdesc *dbdesc; - char options[16]; + char options[32]; char *p; ospf6_header_print(oh); @@ -439,6 +455,20 @@ static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst, return; } + if (((OSPF6_OPT_ISSET_EXT(hello->options, OSPF6_OPT_AT) == + OSPF6_OPT_AT) && + (oi->at_data.flags == 0)) || + ((OSPF6_OPT_ISSET_EXT(hello->options, OSPF6_OPT_AT) != + OSPF6_OPT_AT) && + (oi->at_data.flags != 0))) { + if (IS_OSPF6_DEBUG_AUTH_RX) + zlog_warn( + "VRF %s: IF %s AT-bit mismatch in hello packet", + oi->interface->vrf->name, oi->interface->name); + oi->at_data.rx_drop++; + return; + } + /* Find neighbor, create if not exist */ on = ospf6_neighbor_lookup(oh->router_id, oi); if (on == NULL) { @@ -1006,6 +1036,20 @@ static void ospf6_dbdesc_recv(struct in6_addr *src, struct in6_addr *dst, dbdesc = (struct ospf6_dbdesc *)((caddr_t)oh + sizeof(struct ospf6_header)); + if (((OSPF6_OPT_ISSET_EXT(dbdesc->options, OSPF6_OPT_AT) == + OSPF6_OPT_AT) && + (oi->at_data.flags == 0)) || + ((OSPF6_OPT_ISSET_EXT(dbdesc->options, OSPF6_OPT_AT) != + OSPF6_OPT_AT) && + (oi->at_data.flags != 0))) { + if (IS_OSPF6_DEBUG_AUTH_RX) + zlog_warn( + "VRF %s: IF %s AT-bit mismatch in dbdesc packet", + oi->interface->vrf->name, oi->interface->name); + oi->at_data.rx_drop++; + return; + } + /* Interface MTU check */ if (!oi->mtu_ignore && ntohs(dbdesc->ifmtu) != oi->ifmtu) { zlog_warn("VRF %s: I/F %s MTU mismatch (my %d rcvd %d)", @@ -1412,14 +1456,15 @@ static unsigned ospf6_packet_examin(struct ospf6_header *oh, bytesonwire); return MSG_NG; } + /* Now it is safe to access header fields. */ if (bytesonwire != ntohs(oh->length)) { zlog_warn("%s: %s packet length error (%u real, %u declared)", - __func__, - lookup_msg(ospf6_message_type_str, oh->type, NULL), - bytesonwire, ntohs(oh->length)); + __func__, ospf6_message_type(oh->type), bytesonwire, + ntohs(oh->length)); return MSG_NG; } + /* version check */ if (oh->version != OSPFV3_VERSION) { zlog_warn("%s: invalid (%u) protocol version", __func__, @@ -1431,8 +1476,7 @@ static unsigned ospf6_packet_examin(struct ospf6_header *oh, && bytesonwire < OSPF6_HEADER_SIZE + ospf6_packet_minlen[oh->type]) { zlog_warn("%s: undersized (%u B) %s packet", __func__, - bytesonwire, - lookup_msg(ospf6_message_type_str, oh->type, NULL)); + bytesonwire, ospf6_message_type(oh->type)); return MSG_NG; } /* type-specific deeper validation */ @@ -1446,7 +1490,7 @@ static unsigned ospf6_packet_examin(struct ospf6_header *oh, % 4) return MSG_OK; zlog_warn("%s: alignment error in %s packet", __func__, - lookup_msg(ospf6_message_type_str, oh->type, NULL)); + ospf6_message_type(oh->type)); return MSG_NG; case OSPF6_MESSAGE_TYPE_DBDESC: /* RFC5340 A.3.3, packet header + OSPF6_DB_DESC_MIN_SIZE bytes @@ -1467,7 +1511,7 @@ static unsigned ospf6_packet_examin(struct ospf6_header *oh, % OSPF6_LSREQ_LSDESC_FIX_SIZE) return MSG_OK; zlog_warn("%s: alignment error in %s packet", __func__, - lookup_msg(ospf6_message_type_str, oh->type, NULL)); + ospf6_message_type(oh->type)); return MSG_NG; case OSPF6_MESSAGE_TYPE_LSUPDATE: /* RFC5340 A.3.5, packet header + OSPF6_LS_UPD_MIN_SIZE bytes @@ -1497,7 +1541,7 @@ static unsigned ospf6_packet_examin(struct ospf6_header *oh, } if (test != MSG_OK) zlog_warn("%s: anomaly in %s packet", __func__, - lookup_msg(ospf6_message_type_str, oh->type, NULL)); + ospf6_message_type(oh->type)); return test; } @@ -1727,6 +1771,9 @@ static int ospf6_read_helper(int sockfd, struct ospf6 *ospf6) struct iovec iovector[2]; struct ospf6_interface *oi; struct ospf6_header *oh; + enum ospf6_auth_err ret = OSPF6_AUTH_PROCESS_NORMAL; + uint32_t at_len = 0; + uint32_t lls_len = 0; /* initialize */ memset(&src, 0, sizeof(src)); @@ -1772,6 +1819,24 @@ static int ospf6_read_helper(int sockfd, struct ospf6 *ospf6) return OSPF6_READ_CONTINUE; oh = (struct ospf6_header *)recvbuf; + ret = ospf6_auth_validate_pkt(oi, (uint32_t *)&len, oh, &at_len, + &lls_len); + if (ret == OSPF6_AUTH_VALIDATE_SUCCESS) { + ret = ospf6_auth_check_digest(oh, oi, &src, lls_len); + if (ret == OSPF6_AUTH_VALIDATE_FAILURE) { + if (IS_OSPF6_DEBUG_AUTH_RX) + zlog_err( + "RECV[%s]: OSPF packet auth digest miss-match on %s", + oi->interface->name, + ospf6_message_type(oh->type)); + oi->at_data.rx_drop++; + return OSPF6_READ_CONTINUE; + } + } else if (ret == OSPF6_AUTH_VALIDATE_FAILURE) { + oi->at_data.rx_drop++; + return OSPF6_READ_CONTINUE; + } + if (ospf6_rxpacket_examin(oi, oh, len) != MSG_OK) return OSPF6_READ_CONTINUE; @@ -1782,8 +1847,7 @@ static int ospf6_read_helper(int sockfd, struct ospf6 *ospf6) /* Log */ if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) { - zlog_debug("%s received on %s", - lookup_msg(ospf6_message_type_str, oh->type, NULL), + zlog_debug("%s received on %s", ospf6_message_type(oh->type), oi->interface->name); zlog_debug(" src: %pI6", &src); zlog_debug(" dst: %pI6", &dst); @@ -1807,6 +1871,10 @@ static int ospf6_read_helper(int sockfd, struct ospf6 *ospf6) default: assert(0); } + + if ((at_len != 0) && IS_OSPF6_DEBUG_AUTH_RX) + ospf6_auth_hdr_dump_recv(oh, (len + at_len + lls_len), + lls_len); } switch (oh->type) { @@ -1863,6 +1931,27 @@ int ospf6_receive(struct thread *thread) return 0; } +static void ospf6_fill_hdr_checksum(struct ospf6_interface *oi, + struct ospf6_packet *op) +{ + struct ipv6_ph ph = {}; + struct ospf6_header *oh; + void *offset = NULL; + + if (oi->at_data.flags != 0) + return; + + memcpy(&ph.src, oi->linklocal_addr, sizeof(struct in6_addr)); + memcpy(&ph.dst, &op->dst, sizeof(struct in6_addr)); + ph.ulpl = htonl(op->length); + ph.next_hdr = IPPROTO_OSPFIGP; + + /* Suppress static analysis warnings about accessing icmp6 oob */ + oh = (struct ospf6_header *)STREAM_DATA(op->s); + offset = oh; + oh->checksum = in_cksum_with_ph6(&ph, offset, op->length); +} + static void ospf6_make_header(uint8_t type, struct ospf6_interface *oi, struct stream *s) { @@ -1873,6 +1962,7 @@ static void ospf6_make_header(uint8_t type, struct ospf6_interface *oi, oh->version = (uint8_t)OSPFV3_VERSION; oh->type = type; oh->length = 0; + oh->router_id = oi->area->ospf6->router_id; oh->area_id = oi->area->area_id; oh->checksum = 0; @@ -1904,9 +1994,48 @@ static void ospf6_fill_lsupdate_header(struct stream *s, uint32_t lsa_num) lsu->lsa_number = htonl(lsa_num); } +static void ospf6_auth_trailer_copy_keychain_key(struct ospf6_interface *oi) +{ + char *keychain_name = NULL; + struct keychain *keychain = NULL; + struct key *key = NULL; + + keychain_name = oi->at_data.keychain; + keychain = keychain_lookup(keychain_name); + if (keychain) { + key = key_lookup_for_send(keychain); + if (key && key->string && + key->hash_algo != KEYCHAIN_ALGO_NULL) { + /* storing the values so that further + * lookup can be avoided. after + * processing the digest need to reset + * these values + */ + oi->at_data.hash_algo = key->hash_algo; + oi->at_data.auth_key = XSTRDUP( + MTYPE_OSPF6_AUTH_MANUAL_KEY, key->string); + oi->at_data.key_id = key->index; + SET_FLAG(oi->at_data.flags, + OSPF6_AUTH_TRAILER_KEYCHAIN_VALID); + } + } +} + static uint32_t ospf6_packet_max(struct ospf6_interface *oi) { + int at_len = 0; + assert(oi->ifmtu > sizeof(struct ip6_hdr)); + + if (oi->at_data.flags != 0) { + if (CHECK_FLAG(oi->at_data.flags, OSPF6_AUTH_TRAILER_KEYCHAIN)) + ospf6_auth_trailer_copy_keychain_key(oi); + + at_len += OSPF6_AUTH_HDR_MIN_SIZE; + at_len += keychain_get_hash_len(oi->at_data.hash_algo); + return oi->ifmtu - (sizeof(struct ip6_hdr)) - at_len; + } + return oi->ifmtu - (sizeof(struct ip6_hdr)); } @@ -1915,11 +2044,15 @@ static uint16_t ospf6_make_hello(struct ospf6_interface *oi, struct stream *s) struct listnode *node, *nnode; struct ospf6_neighbor *on; uint16_t length = OSPF6_HELLO_MIN_SIZE; + uint8_t options1 = oi->area->options[1]; + + if (oi->at_data.flags != 0) + options1 |= OSPF6_OPT_AT; stream_putl(s, oi->interface->ifindex); stream_putc(s, oi->priority); stream_putc(s, oi->area->options[0]); - stream_putc(s, oi->area->options[1]); + stream_putc(s, options1); stream_putc(s, oi->area->options[2]); stream_putw(s, oi->hello_interval); stream_putw(s, oi->dead_interval); @@ -1959,6 +2092,7 @@ static int ospf6_write(struct thread *thread) int len; int64_t latency = 0; struct timeval timestamp; + unsigned int at_len = 0; if (ospf6->fd < 0) { zlog_warn("ospf6_write failed to send, fd %d", ospf6->fd); @@ -1983,17 +2117,32 @@ static int ospf6_write(struct thread *thread) oh = (struct ospf6_header *)STREAM_DATA(op->s); + if (oi->at_data.flags != 0) { + at_len = ospf6_auth_len_get(oi); + if (at_len) { + iovector[0].iov_len = + ntohs(oh->length) + at_len; + ospf6_auth_digest_send(oi->linklocal_addr, oi, + oh, at_len, + iovector[0].iov_len); + } else { + iovector[0].iov_len = ntohs(oh->length); + } + } else { + iovector[0].iov_len = ntohs(oh->length); + } + len = ospf6_sendmsg(oi->linklocal_addr, &op->dst, oi->interface->ifindex, iovector, ospf6->fd); - if (len != op->length) + + if (len != (op->length + (int)at_len)) flog_err(EC_LIB_DEVELOPMENT, "Could not send entire message"); if (IS_OSPF6_DEBUG_MESSAGE(oh->type, SEND_HDR)) { zlog_debug("%s send on %s", - lookup_msg(ospf6_message_type_str, oh->type, - NULL), + ospf6_message_type(oh->type), oi->interface->name); zlog_debug(" src: %pI6", oi->linklocal_addr); zlog_debug(" dst: %pI6", &op->dst); @@ -2052,6 +2201,15 @@ static int ospf6_write(struct thread *thread) assert(0); break; } + + if ((oi->at_data.flags != 0) && + (IS_OSPF6_DEBUG_MESSAGE(oh->type, SEND_HDR)) && + (IS_OSPF6_DEBUG_AUTH_TX)) + ospf6_auth_hdr_dump_send(oh, iovector[0].iov_len); + + /* initialize at_len to 0 for next packet */ + at_len = 0; + /* Now delete packet from queue. */ ospf6_packet_delete(oi); @@ -2117,6 +2275,8 @@ int ospf6_hello_send(struct thread *thread) op->dst = allspfrouters6; + ospf6_fill_hdr_checksum(oi, op); + /* Add packet to the top of the interface output queue, so that they * can't get delayed by things like long queues of LS Update packets */ @@ -2135,6 +2295,10 @@ static uint16_t ospf6_make_dbdesc(struct ospf6_neighbor *on, struct stream *s) { uint16_t length = OSPF6_DB_DESC_MIN_SIZE; struct ospf6_lsa *lsa, *lsanext; + uint8_t options1 = on->ospf6_if->area->options[1]; + + if (on->ospf6_if->at_data.flags != 0) + options1 |= OSPF6_OPT_AT; /* if this is initial one, initialize sequence number for DbDesc */ if (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT) @@ -2145,7 +2309,7 @@ static uint16_t ospf6_make_dbdesc(struct ospf6_neighbor *on, struct stream *s) /* reserved */ stream_putc(s, 0); /* reserved 1 */ stream_putc(s, on->ospf6_if->area->options[0]); - stream_putc(s, on->ospf6_if->area->options[1]); + stream_putc(s, options1); stream_putc(s, on->ospf6_if->area->options[2]); stream_putw(s, on->ospf6_if->ifmtu); stream_putc(s, 0); /* reserved 2 */ @@ -2212,6 +2376,8 @@ int ospf6_dbdesc_send(struct thread *thread) else op->dst = on->linklocal_addr; + ospf6_fill_hdr_checksum(on->ospf6_if, op); + ospf6_packet_add(on->ospf6_if, op); OSPF6_MESSAGE_WRITE_ON(on->ospf6_if); @@ -2319,6 +2485,7 @@ static uint16_t ospf6_make_lsack_neighbor(struct ospf6_neighbor *on, (*op)->length = length + OSPF6_HEADER_SIZE; (*op)->dst = on->linklocal_addr; + ospf6_fill_hdr_checksum(on->ospf6_if, *op); ospf6_packet_add(on->ospf6_if, *op); OSPF6_MESSAGE_WRITE_ON(on->ospf6_if); /* new packet */ @@ -2388,6 +2555,7 @@ int ospf6_lsreq_send(struct thread *thread) else op->dst = on->linklocal_addr; + ospf6_fill_hdr_checksum(on->ospf6_if, op); ospf6_packet_add(on->ospf6_if, op); OSPF6_MESSAGE_WRITE_ON(on->ospf6_if); @@ -2425,6 +2593,7 @@ static void ospf6_send_lsupdate(struct ospf6_neighbor *on, op->dst = alldrouters6; } if (oi) { + ospf6_fill_hdr_checksum(oi, op); ospf6_packet_add(oi, op); /* If ospf instance is being deleted, send the packet * immediately @@ -2502,6 +2671,7 @@ static uint16_t ospf6_make_ls_retrans_list(struct ospf6_neighbor *on, else (*op)->dst = on->linklocal_addr; + ospf6_fill_hdr_checksum(on->ospf6_if, *op); ospf6_packet_add(on->ospf6_if, *op); OSPF6_MESSAGE_WRITE_ON(on->ospf6_if); @@ -2573,6 +2743,7 @@ int ospf6_lsupdate_send_neighbor(struct thread *thread) op->dst = allspfrouters6; else op->dst = on->linklocal_addr; + ospf6_fill_hdr_checksum(on->ospf6_if, op); ospf6_packet_add(on->ospf6_if, op); OSPF6_MESSAGE_WRITE_ON(on->ospf6_if); } else @@ -2739,6 +2910,7 @@ int ospf6_lsack_send_neighbor(struct thread *thread) /* Set packet length, dst and queue to FIFO. */ op->length = length; op->dst = on->linklocal_addr; + ospf6_fill_hdr_checksum(on->ospf6_if, op); ospf6_packet_add(on->ospf6_if, op); OSPF6_MESSAGE_WRITE_ON(on->ospf6_if); @@ -2823,6 +2995,7 @@ int ospf6_lsack_send_interface(struct thread *thread) else op->dst = alldrouters6; + ospf6_fill_hdr_checksum(oi, op); ospf6_packet_add(oi, op); OSPF6_MESSAGE_WRITE_ON(oi); diff --git a/ospf6d/ospf6_message.h b/ospf6d/ospf6_message.h index 68cb4c7794..437dd5685b 100644 --- a/ospf6d/ospf6_message.h +++ b/ospf6d/ospf6_message.h @@ -23,10 +23,9 @@ #define OSPF6_MESSAGE_BUFSIZ 4096 -extern const struct message ospf6_message_type_str[]; - /* Debug option */ extern unsigned char conf_debug_ospf6_message[]; + #define OSPF6_ACTION_SEND 0x01 #define OSPF6_ACTION_RECV 0x02 #define OSPF6_DEBUG_MESSAGE_SEND 0x01 @@ -64,6 +63,7 @@ extern unsigned char conf_debug_ospf6_message[]; #define OSPF6_MESSAGE_TYPE_LSUPDATE 0x4 /* Database update */ #define OSPF6_MESSAGE_TYPE_LSACK 0x5 /* Flooding acknowledgment */ #define OSPF6_MESSAGE_TYPE_ALL 0x6 /* For debug option */ +#define OSPF6_MESSAGE_TYPE_MAX 0x6 /* same as OSPF6_MESSAGE_TYPE_ALL */ struct ospf6_packet { struct ospf6_packet *next; @@ -148,8 +148,16 @@ struct ospf6_lsupdate { /* Followed by LSAs */ }; +/* LLS is not supported, but used to derive + * offset of Auth_trailer + */ +struct ospf6_lls_hdr { + uint16_t checksum; + uint16_t length; +}; + /* Link State Acknowledgement */ -#define OSPF6_LS_ACK_MIN_SIZE 0U +#define OSPF6_LS_ACK_MIN_SIZE 0U /* It is just a sequence of LSA Headers */ /* Function definition */ @@ -178,5 +186,5 @@ extern int ospf6_lsack_send_neighbor(struct thread *thread); extern int config_write_ospf6_debug_message(struct vty *); extern void install_element_ospf6_debug_message(void); - +extern const char *ospf6_message_type(int type); #endif /* OSPF6_MESSAGE_H */ diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c index afa504d13c..5d14bfc981 100644 --- a/ospf6d/ospf6_neighbor.c +++ b/ospf6d/ospf6_neighbor.c @@ -112,6 +112,7 @@ struct ospf6_neighbor *ospf6_neighbor_create(uint32_t router_id, { struct ospf6_neighbor *on; char buf[16]; + int type; on = XCALLOC(MTYPE_OSPF6_NEIGHBOR, sizeof(struct ospf6_neighbor)); inet_ntop(AF_INET, &router_id, buf, sizeof(buf)); @@ -131,6 +132,13 @@ struct ospf6_neighbor *ospf6_neighbor_create(uint32_t router_id, on->lsupdate_list = ospf6_lsdb_create(on); on->lsack_list = ospf6_lsdb_create(on); + for (type = 0; type < OSPF6_MESSAGE_TYPE_MAX; type++) { + on->seqnum_l[type] = 0; + on->seqnum_h[type] = 0; + } + + on->auth_present = false; + listnode_add_sort(oi->neighbor_list, on); ospf6_bfd_info_nbr_create(oi, on); @@ -935,8 +943,44 @@ static void ospf6_neighbor_show_detail(struct vty *vty, bfd_sess_show(vty, json_neighbor, on->bfd_session); - json_object_object_add(json, on->name, json_neighbor); + if (on->auth_present == true) { + json_object_string_add(json_neighbor, "authStatus", + "enabled"); + json_object_int_add( + json_neighbor, "recvdHelloHigherSeqNo", + on->seqnum_h[OSPF6_MESSAGE_TYPE_HELLO]); + json_object_int_add( + json_neighbor, "recvdHelloLowerSeqNo", + on->seqnum_l[OSPF6_MESSAGE_TYPE_HELLO]); + json_object_int_add( + json_neighbor, "recvdDBDescHigherSeqNo", + on->seqnum_h[OSPF6_MESSAGE_TYPE_DBDESC]); + json_object_int_add( + json_neighbor, "recvdDBDescLowerSeqNo", + on->seqnum_l[OSPF6_MESSAGE_TYPE_DBDESC]); + json_object_int_add( + json_neighbor, "recvdLSReqHigherSeqNo", + on->seqnum_h[OSPF6_MESSAGE_TYPE_LSREQ]); + json_object_int_add( + json_neighbor, "recvdLSReqLowerSeqNo", + on->seqnum_l[OSPF6_MESSAGE_TYPE_LSREQ]); + json_object_int_add( + json_neighbor, "recvdLSUpdHigherSeqNo", + on->seqnum_h[OSPF6_MESSAGE_TYPE_LSUPDATE]); + json_object_int_add( + json_neighbor, "recvdLSUpdLowerSeqNo", + on->seqnum_l[OSPF6_MESSAGE_TYPE_LSUPDATE]); + json_object_int_add( + json_neighbor, "recvdLSAckHigherSeqNo", + on->seqnum_h[OSPF6_MESSAGE_TYPE_LSACK]); + json_object_int_add( + json_neighbor, "recvdLSAckLowerSeqNo", + on->seqnum_l[OSPF6_MESSAGE_TYPE_LSACK]); + } else + json_object_string_add(json_neighbor, "authStatus", + "disabled"); + json_object_object_add(json, on->name, json_neighbor); } else { vty_out(vty, " Neighbor %s\n", on->name); @@ -1022,6 +1066,27 @@ static void ospf6_neighbor_show_detail(struct vty *vty, vty_out(vty, " %s\n", lsa->name); bfd_sess_show(vty, NULL, on->bfd_session); + + if (on->auth_present == true) { + vty_out(vty, " Authentication header present\n"); + vty_out(vty, + "\t\t\t hello DBDesc LSReq LSUpd LSAck\n"); + vty_out(vty, + " Higher sequence no 0x%-10X 0x%-10X 0x%-10X 0x%-10X 0x%-10X\n", + on->seqnum_h[OSPF6_MESSAGE_TYPE_HELLO], + on->seqnum_h[OSPF6_MESSAGE_TYPE_DBDESC], + on->seqnum_h[OSPF6_MESSAGE_TYPE_LSREQ], + on->seqnum_h[OSPF6_MESSAGE_TYPE_LSUPDATE], + on->seqnum_h[OSPF6_MESSAGE_TYPE_LSACK]); + vty_out(vty, + " Lower sequence no 0x%-10X 0x%-10X 0x%-10X 0x%-10X 0x%-10X\n", + on->seqnum_l[OSPF6_MESSAGE_TYPE_HELLO], + on->seqnum_l[OSPF6_MESSAGE_TYPE_DBDESC], + on->seqnum_l[OSPF6_MESSAGE_TYPE_LSREQ], + on->seqnum_l[OSPF6_MESSAGE_TYPE_LSUPDATE], + on->seqnum_l[OSPF6_MESSAGE_TYPE_LSACK]); + } else + vty_out(vty, " Authentication header not present\n"); } } diff --git a/ospf6d/ospf6_neighbor.h b/ospf6d/ospf6_neighbor.h index de59a1ccf5..2dd640099c 100644 --- a/ospf6d/ospf6_neighbor.h +++ b/ospf6d/ospf6_neighbor.h @@ -144,6 +144,14 @@ struct ospf6_neighbor { /* ospf6 graceful restart HELPER info */ struct ospf6_helper_info gr_helper_info; + + /* seqnum_h/l is used to compare sequence + * number in received packet Auth header + */ + uint32_t seqnum_h[OSPF6_MESSAGE_TYPE_MAX]; + uint32_t seqnum_l[OSPF6_MESSAGE_TYPE_MAX]; + bool auth_present; + bool lls_present; }; /* Neighbor state */ diff --git a/ospf6d/ospf6_network.c b/ospf6d/ospf6_network.c index 5961cfe66a..7501f49fe0 100644 --- a/ospf6d/ospf6_network.c +++ b/ospf6d/ospf6_network.c @@ -33,6 +33,7 @@ #include "ospf6_top.h" #include "ospf6_network.h" #include "ospf6d.h" +#include "ospf6_message.h" struct in6_addr allspfrouters6; struct in6_addr alldrouters6; @@ -60,20 +61,6 @@ static void ospf6_set_transport_class(int ospf6_sock) #endif } -static void ospf6_set_checksum(int ospf6_sock) -{ - int offset = 12; -#ifndef DISABLE_IPV6_CHECKSUM - if (setsockopt(ospf6_sock, IPPROTO_IPV6, IPV6_CHECKSUM, &offset, - sizeof(offset)) - < 0) - zlog_warn("Network: set IPV6_CHECKSUM failed: %s", - safe_strerror(errno)); -#else - zlog_warn("Network: Don't set IPV6_CHECKSUM"); -#endif /* DISABLE_IPV6_CHECKSUM */ -} - void ospf6_serv_close(int *ospf6_sock) { if (*ospf6_sock != -1) { @@ -113,7 +100,6 @@ int ospf6_serv_sock(struct ospf6 *ospf6) ospf6_reset_mcastloop(ospf6_sock); ospf6_set_pktinfo(ospf6_sock); ospf6_set_transport_class(ospf6_sock); - ospf6_set_checksum(ospf6_sock); ospf6->fd = ospf6_sock; /* setup global in6_addr, allspf6 and alldr6 for later use */ diff --git a/ospf6d/ospf6_proto.c b/ospf6d/ospf6_proto.c index e60d2c7e0e..4d0c4ee59e 100644 --- a/ospf6d/ospf6_proto.c +++ b/ospf6d/ospf6_proto.c @@ -82,12 +82,16 @@ void ospf6_capability_printbuf(char capability, char *buf, int size) void ospf6_options_printbuf(uint8_t *options, char *buf, int size) { - const char *dc, *r, *n, *mc, *e, *v6; + const char *dc, *r, *n, *mc, *e, *v6, *af, *at, *l; dc = (OSPF6_OPT_ISSET(options, OSPF6_OPT_DC) ? "DC" : "--"); r = (OSPF6_OPT_ISSET(options, OSPF6_OPT_R) ? "R" : "-"); n = (OSPF6_OPT_ISSET(options, OSPF6_OPT_N) ? "N" : "-"); mc = (OSPF6_OPT_ISSET(options, OSPF6_OPT_MC) ? "MC" : "--"); e = (OSPF6_OPT_ISSET(options, OSPF6_OPT_E) ? "E" : "-"); v6 = (OSPF6_OPT_ISSET(options, OSPF6_OPT_V6) ? "V6" : "--"); - snprintf(buf, size, "%s|%s|%s|%s|%s|%s", dc, r, n, mc, e, v6); + af = (OSPF6_OPT_ISSET_EXT(options, OSPF6_OPT_AF) ? "AF" : "--"); + at = (OSPF6_OPT_ISSET_EXT(options, OSPF6_OPT_AT) ? "AT" : "--"); + l = (OSPF6_OPT_ISSET_EXT(options, OSPF6_OPT_L) ? "L" : "-"); + snprintf(buf, size, "%s|%s|%s|-|-|%s|%s|%s|%s|%s|%s", at, l, af, dc, r, + n, mc, e, v6); } diff --git a/ospf6d/ospf6_proto.h b/ospf6d/ospf6_proto.h index b98dc38b72..ea476b3a8d 100644 --- a/ospf6d/ospf6_proto.h +++ b/ospf6d/ospf6_proto.h @@ -40,13 +40,20 @@ /* OSPF options */ /* present in HELLO, DD, LSA */ -#define OSPF6_OPT_SET(x,opt) ((x)[2] |= (opt)) -#define OSPF6_OPT_ISSET(x,opt) ((x)[2] & (opt)) -#define OSPF6_OPT_CLEAR(x,opt) ((x)[2] &= ~(opt)) +#define OSPF6_OPT_SET(x, opt) ((x)[2] |= (opt)) +#define OSPF6_OPT_ISSET(x, opt) ((x)[2] & (opt)) +#define OSPF6_OPT_CLEAR(x, opt) ((x)[2] &= ~(opt)) +#define OSPF6_OPT_SET_EXT(x, opt) ((x)[1] |= (opt)) +#define OSPF6_OPT_ISSET_EXT(x, opt) ((x)[1] & (opt)) +#define OSPF6_OPT_CLEAR_EXT(x, opt) ((x)[1] &= ~(opt)) #define OSPF6_OPT_CLEAR_ALL(x) ((x)[0] = (x)[1] = (x)[2] = 0) +#define OSPF6_OPT_AT (1 << 2) /* Authentication trailer Capability */ +#define OSPF6_OPT_L (1 << 1) /* Link local signalling Capability */ +#define OSPF6_OPT_AF (1 << 0) /* Address family Capability */ +/* 2 bits reserved for OSPFv2 migrated options */ #define OSPF6_OPT_DC (1 << 5) /* Demand Circuit handling Capability */ -#define OSPF6_OPT_R (1 << 4) /* Forwarding Capability (Any Protocol) */ +#define OSPF6_OPT_R (1 << 4) /* Forwarding Capability (Any Protocol) */ #define OSPF6_OPT_N (1 << 3) /* Handling Type-7 LSA Capability */ #define OSPF6_OPT_MC (1 << 2) /* Multicasting Capability */ #define OSPF6_OPT_E (1 << 1) /* AS External Capability */ diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c index f5d60d80fa..3c74ca55c1 100644 --- a/ospf6d/ospf6_route.c +++ b/ospf6d/ospf6_route.c @@ -1168,7 +1168,7 @@ void ospf6_route_show_detail(struct vty *vty, struct ospf6_route *route, json_object *json_routes, bool use_json) { char destination[PREFIX2STR_BUFFER], nexthop[64]; - char area_id[16], id[16], adv_router[16], capa[16], options[16]; + char area_id[16], id[16], adv_router[16], capa[16], options[32]; char pfx_options[16]; struct timeval now, res; char duration[64]; @@ -1653,7 +1653,7 @@ static void ospf6_linkstate_show_header(struct vty *vty) static void ospf6_linkstate_show(struct vty *vty, struct ospf6_route *route) { uint32_t router, id; - char routername[16], idname[16], rbits[16], options[16]; + char routername[16], idname[16], rbits[16], options[32]; router = ospf6_linkstate_prefix_adv_router(&route->prefix); inet_ntop(AF_INET, &router, routername, sizeof(routername)); @@ -1779,7 +1779,7 @@ void ospf6_brouter_show_header(struct vty *vty) void ospf6_brouter_show(struct vty *vty, struct ospf6_route *route) { uint32_t adv_router; - char adv[16], rbits[16], options[16], area[16]; + char adv[16], rbits[16], options[32], area[16]; adv_router = ospf6_linkstate_prefix_adv_router(&route->prefix); inet_ntop(AF_INET, &adv_router, adv, sizeof(adv)); diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index 57a55a6ef4..9381fa7faf 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -54,6 +54,7 @@ #include "ospf6_gr.h" #include "lib/json.h" #include "ospf6_nssa.h" +#include "ospf6_auth_trailer.h" DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_TOP, "OSPF6 top"); @@ -445,6 +446,17 @@ static struct ospf6 *ospf6_create(const char *name) /* Make ospf protocol socket. */ ospf6_serv_sock(o); + /* If sequence number is stored in persistent storage, read it. + */ + if (ospf6_auth_nvm_file_exist() == OSPF6_AUTH_FILE_EXIST) { + ospf6_auth_seqno_nvm_read(o); + o->seqnum_h = o->seqnum_h + 1; + ospf6_auth_seqno_nvm_update(o); + } else { + o->seqnum_l = o->seqnum_h = 0; + ospf6_auth_seqno_nvm_update(o); + } + return o; } @@ -1386,6 +1398,10 @@ static void ospf6_show(struct vty *vty, struct ospf6 *o, json_object *json, json_object_int_add(json, "numberOfAreaInRouter", listcount(o->area_list)); + json_object_int_add(json, "AuthTrailerHigherSeqNo", + o->seqnum_h); + json_object_int_add(json, "AuthTrailerLowerSeqNo", o->seqnum_l); + if (CHECK_FLAG(o->config_flags, OSPF6_LOG_ADJACENCY_CHANGES)) { if (CHECK_FLAG(o->config_flags, OSPF6_LOG_ADJACENCY_DETAIL)) @@ -1466,6 +1482,10 @@ static void ospf6_show(struct vty *vty, struct ospf6 *o, json_object *json, vty_out(vty, " Number of areas in this router is %u\n", listcount(o->area_list)); + vty_out(vty, " Authentication Sequence number info\n"); + vty_out(vty, " Higher sequence no %u, Lower sequence no %u\n", + o->seqnum_h, o->seqnum_l); + if (CHECK_FLAG(o->config_flags, OSPF6_LOG_ADJACENCY_CHANGES)) { if (CHECK_FLAG(o->config_flags, OSPF6_LOG_ADJACENCY_DETAIL)) diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h index b9f7235b46..f06a3254ac 100644 --- a/ospf6d/ospf6_top.h +++ b/ospf6d/ospf6_top.h @@ -228,6 +228,8 @@ struct ospf6 { /* Action for aggregation of external LSAs */ int aggr_action; + uint32_t seqnum_l; /* lower order Sequence Number */ + uint32_t seqnum_h; /* higher order Sequence Number */ #define OSPF6_EXTL_AGGR_DEFAULT_DELAY 5 /* For ASBR summary delay timer */ uint16_t aggr_delay_interval; From 9b596bed0f5c7ee03790446e9876ca68d4dd16eb Mon Sep 17 00:00:00 2001 From: Abhinay Ramesh Date: Tue, 8 Jun 2021 07:54:18 +0000 Subject: [PATCH 7/9] ospf6d: Documentation for authentication trailer support. Problem Statement: ================= This commit is to add document support for OSPF6 authentication trailer feature, which is adding support for RFC7166. RCA: ==== NA Fix: ==== To add detailed description for feature support. This document caputres Configuration CLI Show commands Debug commands Clear command That are added as part of the feature with examples. Risk: ===== Low Tests Executed: =============== NA Signed-off-by: Abhinay Ramesh --- doc/user/ospf6d.rst | 250 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 250 insertions(+) diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst index 624510323c..77656651e4 100644 --- a/doc/user/ospf6d.rst +++ b/doc/user/ospf6d.rst @@ -383,6 +383,256 @@ Graceful Restart This is an EXEC-level command. +.. _Authentication-trailer: + +Authentication trailer support: +=============================== +IPv4 version of OSPF supports authentication as part of the base RFC. +When IPv6 version of OSPF was developed there was IPSec support for IPv6, +Hence OSPFv3(IPv6 version of OSPF) suggest to use IPSec as authentication +and encryption mechanism. IPSec supports authentication using AH header and +Encryption using ESP. + +There are few disadvantages of using IPSec with OSPFv3. + 1. If encryption is enabled for OSPFv3 packets, then its not + possible to give priority to control packets. + 2. IPSec has platform dependency and may not be supported + in all platforms. + 3. It is performance intensive. + 4. Its difficult to configure. + + +Some advantages of OSPFv3 authentication trailer feature. + 1. It provides replay protection via sequence number. + 2. It provides IPv6 source address protection. + 3. No platform dependency. + 4. Easy to implement and maintain. + + +This feature is support for ``RFC7166``. + +FRR supports MD5 and SHA256 internally and relays on openssl for other hash +algorithms. If user wants to use only MD5 and SHA256, no special action is +required. If user wants complete support of authentication trailer with all +hash algorithms follow below steps. + + +Installing Dependencies: +------------------------ + +.. code-block:: console + + sudo apt update + sudo apt-get install openssl + + +Compile: +-------- +Follow normal compilation as mentioned in the build page. If you want to +use all the hash algorithms then follow the steps mentioned in note before +compiling. + + +.. note:: + + If your platform supports ``openssl``, please make sure to add + ``--with-crypto=openssl`` to your configure options. + Default value is ``--with-crypto=internal`` + + +CLI Configuration: +------------------ +There are two ways in which authentication trailer can be configured for +OSPFv3. These commands are mutually exclusive, only one can be configured +at any time. + + 1. Using manual key configuration. + 2. Using keychain. + + +List of hash algorithms supported: +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Without openssl: +++++++++++++++++ + ``MD5`` + ``HMAC-SHA-256`` + + +With openssl: ++++++++++++++ + ``MD5`` + ``HMAC-SHA-1`` + ``HMAC-SHA-256`` + ``HMAC-SHA-384`` + ``HMAC-SHA-512`` + + +Example configuration of manual key: +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Without openssl: +++++++++++++++++ + +.. clicmd:: ipv6 ospf6 authentication key-id (1-65535) hash-algo key WORD + +With openssl: ++++++++++++++ + +.. clicmd:: ipv6 ospf6 authentication key-id (1-65535) hash-algo key WORD + + +Example configuration of keychain: +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. clicmd:: ipv6 ospf6 authentication keychain KEYCHAIN_NAME + + +Running configuration: +---------------------- + +Manual key: +^^^^^^^^^^^ + +.. code-block:: frr + + frr# show running-config + Building configuration... + + Current configuration: + ! + interface ens192 + ipv6 address 2001:DB8::2/64 + ipv6 ospf6 authentication key-id 10 hash-algo hmac-sha-256 key abhinay + +Keychain: +^^^^^^^^^ + +.. code-block:: frr + + frr# show running-config + Building configuration... + + Current configuration: + ! + interface ens192 + ipv6 address 2001:DB8::2/64 + ipv6 ospf6 authentication keychain abhinay + + +Example keychain config: +^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: frr + + frr#show running-config + Building configuration... + + Current configuration: + ! + key chain abcd + key 100 + key-string password + cryptographic-algorithm sha1 + exit + key 200 + key-string password + cryptographic-algorithm sha256 + exit + ! + key chain pqr + key 300 + key-string password + cryptographic-algorithm sha384 + exit + key 400 + key-string password + cryptographic-algorithm sha384 + exit + ! + +Show commands: +-------------- +There is an interface show command that displays if authentication trailer +is enabled or not. json output is also supported. + +There is support for drop counters, which will help in debugging the feature. + +.. code-block:: frr + + frr# show ipv6 ospf6 interface ens192 + ens192 is up, type BROADCAST + Interface ID: 5 + Number of I/F scoped LSAs is 2 + 0 Pending LSAs for LSUpdate in Time 00:00:00 [thread off] + 0 Pending LSAs for LSAck in Time 00:00:00 [thread off] + Authentication trailer is enabled with manual key ==> new info added + Packet drop Tx 0, Packet drop Rx 0 + + +OSPFv3 supports options in hello and database description packets hence +the presence of authentication trailer needs to be stored in OSPFv3 +neighbor info. Since RFC specifies that we need to handled sequence number +for every ospf6 packet type, sequence number recvd in authentication header +from the neighbor is stored in neighbor to validate the packet. +json output is also supported. + +.. code-block:: frr + + frr# show ipv6 ospf6 neighbor 2.2.2.2 detail + Neighbor 2.2.2.2%ens192 + Area 1 via interface ens192 (ifindex 3) + 0 Pending LSAs for LSUpdate in Time 00:00:00 [thread off] + 0 Pending LSAs for LSAck in Time 00:00:00 [thread off] + Authentication header present ==> new info added + hello DBDesc LSReq LSUpd LSAck + Higher sequence no 0x0 0x0 0x0 0x0 0x0 + Lower sequence no 0x242E 0x1DC4 0x1DC3 0x23CC 0x1DDA + +Sent packet sequence number is maintained per ospf6 router for every packet +that is sent out of router, so sequence number is maintained per ospf6 process. + +.. code-block:: frr + + frr# show ipv6 ospf6 + OSPFv3 Routing Process (0) with Router-ID 2.2.2.2 + Number of areas in this router is 1 + Authentication Sequence number info + Higher sequence no 3, Lower sequence no 1656 + +Debug command: +-------------- +Below command can be used to enable ospfv3 authentication trailer +specific logs if you have to debug the feature. + +.. clicmd:: debug ospf6 authentication [] + +Feature supports authentication trailer tx/rx drop counters for debugging, +which can be used to see if packets are getting dropped due to error in +processing authentication trailer information in OSPFv3 packet. +json output is also supported. + +.. code-block:: frr + + frr# show ipv6 ospf6 interface ens192 + ens192 is up, type BROADCAST + Interface ID: 5 + Number of I/F scoped LSAs is 2 + 0 Pending LSAs for LSUpdate in Time 00:00:00 [thread off] + 0 Pending LSAs for LSAck in Time 00:00:00 [thread off] + Authentication trailer is enabled with manual key + Packet drop Tx 0, Packet drop Rx 0 ==> new counters + +Clear command: +-------------- +Below command can be used to clear the tx/rx drop counters in interface. +Below command can be used to clear all ospfv3 interface or specific +interface by specifying the interface name. + +.. clicmd:: clear ipv6 ospf6 auth-counters interface [IFNAME] + + + .. _showing-ospf6-information: Showing OSPF6 information From 6fc7e0c3d12be6a98350a1be25ca0ce16ea36584 Mon Sep 17 00:00:00 2001 From: Abhinay Ramesh Date: Tue, 8 Jun 2021 12:40:21 +0000 Subject: [PATCH 8/9] ospf6d: Enable the feature using configure.ac Problem Statement: ================= The feature is not enabled, needs to be enabled by doing required initialization. RCA: ==== Changes to support the feature is present, but the feature macro needs to be enabled. Fix: ==== This commit has changes to enable the code. Risk: ===== Medium Need to ensure all existing ospf6 related topotests pass. to ensure packet processing is not impacted. Tests Executed: =============== Have tested the functionality with enabling openssl and also disabling openssl. Signed-off-by: Abhinay Ramesh --- ospf6d/ospf6d.c | 5 +++++ ospf6d/subdir.am | 3 +++ 2 files changed, 8 insertions(+) diff --git a/ospf6d/ospf6d.c b/ospf6d/ospf6d.c index cc5a0f7870..a16f4f73eb 100644 --- a/ospf6d/ospf6d.c +++ b/ospf6d/ospf6d.c @@ -48,6 +48,7 @@ #include "ospf6_gr.h" #include "lib/json.h" #include "ospf6_nssa.h" +#include "ospf6_auth_trailer.h" DEFINE_MGROUP(OSPF6D, "ospf6d"); @@ -98,6 +99,7 @@ static int config_write_ospf6_debug(struct vty *vty) config_write_ospf6_debug_flood(vty); config_write_ospf6_debug_nssa(vty); config_write_ospf6_debug_gr_helper(vty); + config_write_ospf6_debug_auth(vty); return 0; } @@ -1458,4 +1460,7 @@ void ospf6_init(struct thread_master *master) VIEW_NODE, &show_ipv6_ospf6_database_type_self_originated_linkstate_id_cmd); install_element(VIEW_NODE, &show_ipv6_ospf6_database_aggr_router_cmd); + install_element_ospf6_debug_auth(); + ospf6_interface_auth_trailer_cmd_init(); + install_element_ospf6_clear_intf_auth(); } diff --git a/ospf6d/subdir.am b/ospf6d/subdir.am index 34aabc205b..cf863ff523 100644 --- a/ospf6d/subdir.am +++ b/ospf6d/subdir.am @@ -24,6 +24,7 @@ vtysh_scan += \ ospf6d/ospf6_top.c \ ospf6d/ospf6_zebra.c \ ospf6d/ospf6d.c \ + ospf6d/ospf6_auth_trailer.c \ # end vtysh_daemons += ospf6d if SNMP @@ -56,6 +57,7 @@ ospf6d_libospf6_a_SOURCES = \ ospf6d/ospf6_top.c \ ospf6d/ospf6_zebra.c \ ospf6d/ospf6d.c \ + ospf6d/ospf6_auth_trailer.c \ # end noinst_HEADERS += \ @@ -80,6 +82,7 @@ noinst_HEADERS += \ ospf6d/ospf6_top.h \ ospf6d/ospf6_zebra.h \ ospf6d/ospf6d.h \ + ospf6d/ospf6_auth_trailer.h \ # end ospf6d_ospf6d_LDADD = ospf6d/libospf6.a lib/libfrr.la $(LIBCAP) From 0afa645317cc51f6213024bd2f15843f791475bd Mon Sep 17 00:00:00 2001 From: Abhinay Ramesh Date: Mon, 28 Jun 2021 09:47:17 +0000 Subject: [PATCH 9/9] tests: Added ospf6 authentication trailer topotest Have added topotest to verify below combination. Auth support for md5 Auth support for hmac-sha-256 Auth support with keychain for md5 Auth support with keychain for hmac-sha-256 Have sussessfully run all 4 test cases in my local setup. Signed-off-by: Abhinay Ramesh --- tests/topotests/lib/ospf.py | 19 +- .../ospfv3_authentication.json | 169 ++ .../test_ospfv3_authentication.py | 1446 +++++++++++++++++ 3 files changed, 1630 insertions(+), 4 deletions(-) create mode 100644 tests/topotests/ospfv3_basic_functionality/ospfv3_authentication.json create mode 100644 tests/topotests/ospfv3_basic_functionality/test_ospfv3_authentication.py diff --git a/tests/topotests/lib/ospf.py b/tests/topotests/lib/ospf.py index 92d29ad1ab..8d2bf12af2 100644 --- a/tests/topotests/lib/ospf.py +++ b/tests/topotests/lib/ospf.py @@ -1933,7 +1933,7 @@ def verify_ospf6_interface(tgen, topo=None, dut=None, lan=False, input_dict=None True or False (Error Message) """ - logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + logger.debug("Entering lib API: verify_ospf6_interface") result = False if topo is None: @@ -2311,6 +2311,7 @@ def config_ospf6_interface( ------- True or False """ + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) result = False if topo is None: @@ -2337,6 +2338,7 @@ def config_ospf6_interface( ospf_data = input_dict[router]["links"][lnk]["ospf6"] data_ospf_area = ospf_data.setdefault("area", None) data_ospf_auth = ospf_data.setdefault("hash-algo", None) + data_ospf_keychain = ospf_data.setdefault("keychain", None) data_ospf_dr_priority = ospf_data.setdefault("priority", None) data_ospf_cost = ospf_data.setdefault("cost", None) data_ospf_mtu = ospf_data.setdefault("mtu_ignore", None) @@ -2369,9 +2371,18 @@ def config_ospf6_interface( ospf_data["hash-algo"], ospf_data["key"], ) - if "del_action" in ospf_data: - cmd = "no {}".format(cmd) - config_data.append(cmd) + config_data.append(cmd) + + # interface ospf auth with keychain + if data_ospf_keychain: + cmd = "ipv6 ospf6 authentication" + + if "del_action" in ospf_data: + cmd = "no {}".format(cmd) + + if "keychain" in ospf_data: + cmd = "{} keychain {}".format(cmd, ospf_data["keychain"]) + config_data.append(cmd) # interface ospf dr priority if data_ospf_dr_priority: diff --git a/tests/topotests/ospfv3_basic_functionality/ospfv3_authentication.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_authentication.json new file mode 100644 index 0000000000..08ff253b75 --- /dev/null +++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_authentication.json @@ -0,0 +1,169 @@ +{ + "address_types": [ + "ipv6" + ], + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r0": { + "links": { + "r1": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf6": { + "router_id": "100.1.1.0", + "neighbors": { + "r1": {}, + "r2": {}, + "r3": {} + }, + "redistribute": [ + { + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "r1": { + "links": { + "r0": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf6": { + "router_id": "100.1.1.1", + "neighbors": { + "r0": {}, + "r2": {}, + "r3": {} + } + } + }, + "r2": { + "links": { + "r0": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r3": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf6": { + "router_id": "100.1.1.2", + "neighbors": { + "r1": {}, + "r0": {}, + "r3": {} + } + } + }, + "r3": { + "links": { + "r0": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r1": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + }, + "r2": { + "ipv6": "auto", + "ospf6": { + "area": "0.0.0.0", + "hello_interval": 1, + "dead_interval": 4 + } + } + }, + "ospf6": { + "router_id": "100.1.1.3", + "neighbors": { + "r0": {}, + "r1": {}, + "r2": {} + } + } + } + } +} \ No newline at end of file diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_authentication.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_authentication.py new file mode 100644 index 0000000000..baa0071f9c --- /dev/null +++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_authentication.py @@ -0,0 +1,1446 @@ +#!/usr/bin/python + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + + +"""OSPF Basic Functionality Automation.""" +import os +import sys +import time +import pytest +from time import sleep +from copy import deepcopy +import json +from lib.topotest import frr_unicode + +pytestmark = pytest.mark.ospf6d + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + step, + shutdown_bringup_interface, + topo_daemons, +) +from lib.topolog import logger +from lib.topojson import build_topo_from_json, build_config_from_json +from lib.ospf import verify_ospf6_neighbor, config_ospf6_interface, clear_ospf +from ipaddress import IPv4Address + +# Global variables +topo = None +# Reading the data from JSON File for topology creation +jsonFile = "{}/ospfv3_authentication.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) +""" +TOPOOLOGY = + Please view in a fixed-width font such as Courier. + +---+ A1 +---+ + +R1 +------------+R2 | + +-+-+- +--++ + | -- -- | + | -- A0 -- | + A0| ---- | + | ---- | A2 + | -- -- | + | -- -- | + +-+-+- +-+-+ + +R0 +-------------+R3 | + +---+ A3 +---+ + +TESTCASES = +1. OSPFv3 Authentication Trailer - Verify ospfv3 authentication trailer + using MD5 manual key configuration. +2. OSPFv3 Authentication Trailer - Verify ospfv3 authentication trailer + using HMAC-SHA-256 manual key configuration. +3. OSPFv3 Authentication Trailer - Verify ospfv3 authentication trailer + using MD5 keychain configuration. +4. OSPFv3 Authentication Trailer - Verify ospfv3 authentication trailer + using HMAC-SHA-256 keychain configuration. + + """ + +def setup_module(mod): + """ + Sets up the pytest environment + * `mod`: module name + """ + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/ospfv3_single_area.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # get list of daemons needs to be started for this suite. + daemons = topo_daemons(tgen, topo) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen, daemons) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + ospf6_covergence = verify_ospf6_neighbor(tgen, topo) + assert ospf6_covergence is True, "setup_module :Failed \n Error:" " {}".format( + ospf6_covergence + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment. + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +# ################################## +# Test cases start here. +# ################################## + +def test_ospf6_auth_trailer_tc1_md5(request): + """ + OSPFv3 Authentication Trailer - Verify ospfv3 authentication trailer + using MD5 manual key configuration. + + """ + 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 ospf6 between R1 and R2, enable ospf6 auth on R1 interface " + "connected to R2 with auth trailer" + ) + + r1_ospf6_auth = { + "r1": { + "links": { + "r2": { + "ospf6": { + "hash-algo": "md5", + "key": "ospf6", + "key-id": "10", + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r1_ospf6_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" + ospf6_covergence = verify_ospf6_neighbor( + tgen, topo, dut=dut, expected=False, retry_timeout=3 + ) + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + step( + "Configure ospf6 between R1 and R2, enable ospf6 on R2 interface " + "connected to R1 with auth trailer" + ) + + r2_ospf6_auth = { + "r2": { + "links": { + "r1": { + "ospf6": { + "hash-algo": "md5", + "key": "ospf6", + "key-id": "10", + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r2_ospf6_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 ipv6 ospf6 neighbor cmd." + ) + + dut = "r2" + ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) + assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + step( + "Disable authentication on R2 " + ) + + r2_ospf6_auth = { + "r2": { + "links": { + "r1": { + "ospf6": { + "hash-algo": "md5", + "key": "ospf6", + "key-id": "10", + "del_action": True + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r2_ospf6_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" + ospf6_covergence = verify_ospf6_neighbor( + tgen, topo, dut=dut, expected=False, retry_timeout=5 + ) + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + step("Again On R2 enable ospf6 on interface with message-digest auth") + r2_ospf6_auth = { + "r2": { + "links": { + "r1": { + "ospf6": { + "hash-algo": "md5", + "key": "ospf6", + "key-id": "10", + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r2_ospf6_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 ospf6 neighbor cmd." + ) + + dut = "r2" + ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) + assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_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 ospf6 neighbor cmd." + ) + ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, expected=False) + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + dut = "r1" + shutdown_bringup_interface(tgen, dut, intf, True) + + step( + "Verify that the neighbour is FULL between R1 and R2 using " + "show ip ospf6 neighbor cmd." + ) + + dut = "r2" + ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) + assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + write_test_footer(tc_name) + + +def test_ospf6_auth_trailer_tc2_sha256(request): + """ + OSPFv3 Authentication Trailer - Verify ospfv3 authentication trailer + using HMAC-SHA-256 manual key configuration. + + """ + 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 ospf6 between R1 and R2, enable ospf6 auth on R1 interface " + "connected to R2 with auth trailer" + ) + + r1_ospf6_auth = { + "r1": { + "links": { + "r2": { + "ospf6": { + "hash-algo": "hmac-sha-256", + "key": "ospf6", + "key-id": "10", + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r1_ospf6_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" + ospf6_covergence = verify_ospf6_neighbor( + tgen, topo, dut=dut, expected=False, retry_timeout=3 + ) + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + step( + "Configure ospf6 between R1 and R2, enable ospf6 on R2 interface " + "connected to R1 with auth trailer" + ) + + r2_ospf6_auth = { + "r2": { + "links": { + "r1": { + "ospf6": { + "hash-algo": "hmac-sha-256", + "key": "ospf6", + "key-id": "10", + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r2_ospf6_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 ipv6 ospf6 neighbor cmd." + ) + + dut = "r2" + ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) + assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + step( + "Disable authentication on R2 " + ) + + r2_ospf6_auth = { + "r2": { + "links": { + "r1": { + "ospf6": { + "hash-algo": "hmac-sha-256", + "key": "ospf6", + "key-id": "10", + "del_action": True + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r2_ospf6_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" + ospf6_covergence = verify_ospf6_neighbor( + tgen, topo, dut=dut, expected=False, retry_timeout=5 + ) + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + step("Again On R2 enable ospf6 on interface with message-digest auth") + r2_ospf6_auth = { + "r2": { + "links": { + "r1": { + "ospf6": { + "hash-algo": "hmac-sha-256", + "key": "ospf6", + "key-id": "10", + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r2_ospf6_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 ospf6 neighbor cmd." + ) + + dut = "r2" + ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) + assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_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 ospf6 neighbor cmd." + ) + ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, expected=False) + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + dut = "r1" + shutdown_bringup_interface(tgen, dut, intf, True) + + step( + "Verify that the neighbour is FULL between R1 and R2 using " + "show ip ospf6 neighbor cmd." + ) + + dut = "r2" + ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) + assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + write_test_footer(tc_name) + +def test_ospf6_auth_trailer_tc3_keychain_md5(request): + """ + OSPFv3 Authentication Trailer - Verify ospfv3 authentication trailer + using MD5 keychain configuration. + + """ + 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 ospf6 between R1 and R2, enable ospf6 auth on R1 interface " + "connected to R2 with auth trailer" + ) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + + router1.vtysh_cmd( + """configure terminal + key chain auth + key 10 + key-string ospf6 + cryptographic-algorithm md5""" + ) + + router2.vtysh_cmd( + """configure terminal + key chain auth + key 10 + key-string ospf6 + cryptographic-algorithm md5""" + ) + + r1_ospf6_auth = { + "r1": { + "links": { + "r2": { + "ospf6": { + "keychain": "auth", + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r1_ospf6_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" + ospf6_covergence = verify_ospf6_neighbor( + tgen, topo, dut=dut, expected=False, retry_timeout=3 + ) + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + step( + "Configure ospf6 between R1 and R2, enable ospf6 on R2 interface " + "connected to R1 with auth trailer" + ) + + r2_ospf6_auth = { + "r2": { + "links": { + "r1": { + "ospf6": { + "keychain": "auth", + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r2_ospf6_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 ipv6 ospf6 neighbor cmd." + ) + + dut = "r2" + ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) + assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + step( + "Disable authentication on R2 " + ) + + r2_ospf6_auth = { + "r2": { + "links": { + "r1": { + "ospf6": { + "keychain": "auth", + "del_action": True + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r2_ospf6_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" + ospf6_covergence = verify_ospf6_neighbor( + tgen, topo, dut=dut, expected=False, retry_timeout=5 + ) + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + step("Again On R2 enable ospf6 on interface with message-digest auth") + r2_ospf6_auth = { + "r2": { + "links": { + "r1": { + "ospf6": { + "keychain": "auth", + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r2_ospf6_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 ospf6 neighbor cmd." + ) + + dut = "r2" + ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) + assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_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 ospf6 neighbor cmd." + ) + ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, expected=False) + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + dut = "r1" + shutdown_bringup_interface(tgen, dut, intf, True) + + step( + "Verify that the neighbour is FULL between R1 and R2 using " + "show ip ospf6 neighbor cmd." + ) + + dut = "r2" + ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) + assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + write_test_footer(tc_name) + +def test_ospf6_auth_trailer_tc4_keychain_sha256(request): + """ + OSPFv3 Authentication Trailer - Verify ospfv3 authentication trailer + using HMAC-SHA-256 keychain configuration. + + """ + 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 ospf6 between R1 and R2, enable ospf6 auth on R1 interface " + "connected to R2 with auth trailer" + ) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + + router1.vtysh_cmd( + """configure terminal + key chain auth + key 10 + key-string ospf6 + cryptographic-algorithm hmac-sha-256""" + ) + + router2.vtysh_cmd( + """configure terminal + key chain auth + key 10 + key-string ospf6 + cryptographic-algorithm hmac-sha-256""" + ) + + r1_ospf6_auth = { + "r1": { + "links": { + "r2": { + "ospf6": { + "keychain": "auth", + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r1_ospf6_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" + ospf6_covergence = verify_ospf6_neighbor( + tgen, topo, dut=dut, expected=False, retry_timeout=3 + ) + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + step( + "Configure ospf6 between R1 and R2, enable ospf6 on R2 interface " + "connected to R1 with auth trailer" + ) + + r2_ospf6_auth = { + "r2": { + "links": { + "r1": { + "ospf6": { + "keychain": "auth", + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r2_ospf6_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 ipv6 ospf6 neighbor cmd." + ) + + dut = "r2" + ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) + assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + step( + "Disable authentication on R2 " + ) + + r2_ospf6_auth = { + "r2": { + "links": { + "r1": { + "ospf6": { + "keychain": "auth", + "del_action": True + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r2_ospf6_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" + ospf6_covergence = verify_ospf6_neighbor( + tgen, topo, dut=dut, expected=False, retry_timeout=5 + ) + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + step("Again On R2 enable ospf6 on interface with message-digest auth") + r2_ospf6_auth = { + "r2": { + "links": { + "r1": { + "ospf6": { + "keychain": "auth", + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r2_ospf6_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 ospf6 neighbor cmd." + ) + + dut = "r2" + ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) + assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_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 ospf6 neighbor cmd." + ) + ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, expected=False) + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + dut = "r1" + shutdown_bringup_interface(tgen, dut, intf, True) + + step( + "Verify that the neighbour is FULL between R1 and R2 using " + "show ip ospf6 neighbor cmd." + ) + + dut = "r2" + ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) + assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + write_test_footer(tc_name) + +def test_ospf6_auth_trailer_tc5_md5_keymissmatch(request): + """ + OSPFv3 Authentication Trailer - Verify ospfv3 authentication trailer + using MD5 manual key configuration. + + """ + 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 ospf6 between R1 and R2, enable ospf6 auth on R1 interface " + "connected to R2 with auth trailer" + ) + + r1_ospf6_auth = { + "r1": { + "links": { + "r2": { + "ospf6": { + "hash-algo": "md5", + "key": "ospf6", + "key-id": "10", + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r1_ospf6_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" + ospf6_covergence = verify_ospf6_neighbor( + tgen, topo, dut=dut, expected=False, retry_timeout=3 + ) + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + step( + "Configure ospf6 between R1 and R2, enable ospf6 on R2 interface " + "connected to R1 with auth trailer wrong key" + ) + + r2_ospf6_auth = { + "r2": { + "links": { + "r1": { + "ospf6": { + "hash-algo": "md5", + "key": "ospf6-missmatch", + "key-id": "10", + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r2_ospf6_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 " + "using show ipv6 ospf6 neighbor cmd." + ) + + step("Verify that the neighbour is FULL between R1 and R2.") + # wait for dead time expiry. + sleep(6) + dut = "r2" + ospf6_covergence = verify_ospf6_neighbor( + tgen, topo, dut=dut, expected=False, retry_timeout=3 + ) + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + step( + "Configure ospf6 between R1 and R2, enable ospf6 on R2 interface " + "connected to R1 with auth trailer correct key" + ) + + r2_ospf6_auth = { + "r2": { + "links": { + "r1": { + "ospf6": { + "hash-algo": "md5", + "key": "ospf6", + "key-id": "10", + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r2_ospf6_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 ipv6 ospf6 neighbor cmd." + ) + + dut = "r2" + ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) + assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + write_test_footer(tc_name) + +def test_ospf6_auth_trailer_tc6_sha256_mismatch(request): + """ + OSPFv3 Authentication Trailer - Verify ospfv3 authentication trailer + using HMAC-SHA-256 manual key configuration. + + """ + 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 ospf6 between R1 and R2, enable ospf6 auth on R1 interface " + "connected to R2 with auth trailer" + ) + + r1_ospf6_auth = { + "r1": { + "links": { + "r2": { + "ospf6": { + "hash-algo": "hmac-sha-256", + "key": "ospf6", + "key-id": "10", + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r1_ospf6_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" + ospf6_covergence = verify_ospf6_neighbor( + tgen, topo, dut=dut, expected=False, retry_timeout=3 + ) + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + step( + "Configure ospf6 with on R1 and R2, enable ospf6 on R2 interface " + "connected to R1 with auth trailer wrong key" + ) + + r2_ospf6_auth = { + "r2": { + "links": { + "r1": { + "ospf6": { + "hash-algo": "hmac-sha-256", + "key": "ospf6-missmatch", + "key-id": "10", + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r2_ospf6_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 = "r2" + ospf6_covergence = verify_ospf6_neighbor( + tgen, topo, dut=dut, expected=False, retry_timeout=3 + ) + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + step( + "Configure ospf6 with on R1 and R2, enable ospf6 on R2 interface " + "connected to R1 with auth trailer wrong key" + ) + + r2_ospf6_auth = { + "r2": { + "links": { + "r1": { + "ospf6": { + "hash-algo": "hmac-sha-256", + "key": "ospf6", + "key-id": "10", + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r2_ospf6_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 ipv6 ospf6 neighbor cmd." + ) + + dut = "r2" + ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) + assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + write_test_footer(tc_name) + +def test_ospf6_auth_trailer_tc7_keychain_md5_missmatch(request): + """ + OSPFv3 Authentication Trailer - Verify ospfv3 authentication trailer + using MD5 keychain configuration. + + """ + 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 ospf6 between R1 and R2, enable ospf6 auth on R1 interface " + "connected to R2 with auth trailer" + ) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + + router1.vtysh_cmd( + """configure terminal + key chain auth + key 10 + key-string ospf6 + cryptographic-algorithm md5""" + ) + + router2.vtysh_cmd( + """configure terminal + key chain auth + key 10 + key-string ospf6 + cryptographic-algorithm md5""" + ) + + router2.vtysh_cmd( + """configure terminal + key chain auth-missmatch + key 10 + key-string ospf6-missmatch + cryptographic-algorithm md5""" + ) + + r1_ospf6_auth = { + "r1": { + "links": { + "r2": { + "ospf6": { + "keychain": "auth", + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r1_ospf6_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" + ospf6_covergence = verify_ospf6_neighbor( + tgen, topo, dut=dut, expected=False, retry_timeout=3 + ) + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + step( + "Configure ospf6 between R1 and R2, enable ospf6 on R2 interface " + "connected to R1 with auth trailer with wrong keychain" + ) + + r2_ospf6_auth = { + "r2": { + "links": { + "r1": { + "ospf6": { + "keychain": "auth-missmatch", + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r2_ospf6_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 = "r2" + ospf6_covergence = verify_ospf6_neighbor( + tgen, topo, dut=dut, expected=False, retry_timeout=3 + ) + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + step( + "Configure ospf6 between R1 and R2, enable ospf6 on R2 interface " + "connected to R1 with auth trailer with correct keychain" + ) + + r2_ospf6_auth = { + "r2": { + "links": { + "r1": { + "ospf6": { + "keychain": "auth", + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r2_ospf6_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 ipv6 ospf6 neighbor cmd." + ) + + dut = "r2" + ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) + assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + write_test_footer(tc_name) + +def test_ospf6_auth_trailer_tc8_keychain_sha256_missmatch(request): + """ + OSPFv3 Authentication Trailer - Verify ospfv3 authentication trailer + using HMAC-SHA-256 keychain configuration. + + """ + 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 ospf6 between R1 and R2, enable ospf6 auth on R1 interface " + "connected to R2 with auth trailer" + ) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + + router1.vtysh_cmd( + """configure terminal + key chain auth + key 10 + key-string ospf6 + cryptographic-algorithm hmac-sha-256""" + ) + + router2.vtysh_cmd( + """configure terminal + key chain auth + key 10 + key-string ospf6 + cryptographic-algorithm hmac-sha-256""" + ) + + router2.vtysh_cmd( + """configure terminal + key chain auth-missmatch + key 10 + key-string ospf6-missmatch + cryptographic-algorithm hmac-sha-256""" + ) + + r1_ospf6_auth = { + "r1": { + "links": { + "r2": { + "ospf6": { + "keychain": "auth", + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r1_ospf6_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" + ospf6_covergence = verify_ospf6_neighbor( + tgen, topo, dut=dut, expected=False, retry_timeout=3 + ) + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + step( + "Configure ospf6 between R1 and R2, enable ospf6 on R2 interface " + "connected to R1 with auth trailer wrong keychain" + ) + + r2_ospf6_auth = { + "r2": { + "links": { + "r1": { + "ospf6": { + "keychain": "auth-missmatch", + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r2_ospf6_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 = "r2" + ospf6_covergence = verify_ospf6_neighbor( + tgen, topo, dut=dut, expected=False, retry_timeout=3 + ) + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + step( + "Configure ospf6 between R1 and R2, enable ospf6 on R2 interface " + "connected to R1 with auth trailer correct keychain" + ) + + r2_ospf6_auth = { + "r2": { + "links": { + "r1": { + "ospf6": { + "keychain": "auth", + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r2_ospf6_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 ipv6 ospf6 neighbor cmd." + ) + + dut = "r2" + ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) + assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + write_test_footer(tc_name) + +def test_ospf6_auth_trailer_tc9_keychain_not_configured(request): + """ + OSPFv3 Neighborship without Authentication Trailer - + Verify ospfv3 neighborship when no authentication trailer is configured. + + """ + 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 ospf6 between R1 and R2, enable ospf6 auth on R1 interface " + "connected to R2 with auth trailer" + ) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + + r1_ospf6_auth = { + "r1": { + "links": { + "r2": { + "ospf6": { + "keychain": "auth", + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r1_ospf6_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" + ospf6_covergence = verify_ospf6_neighbor( + tgen, topo, dut=dut, expected=False, retry_timeout=3 + ) + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + step( + "Configure ospf6 between R1 and R2, enable ospf6 on R2 interface " + "connected to R1 with auth trailer non existing keychain" + ) + + r2_ospf6_auth = { + "r2": { + "links": { + "r1": { + "ospf6": { + "keychain": "auth", + } + } + } + } + } + result = config_ospf6_interface(tgen, topo, r2_ospf6_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 = "r2" + ospf6_covergence = verify_ospf6_neighbor( + tgen, topo, dut=dut, expected=False, retry_timeout=3 + ) + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + write_test_footer(tc_name) + +def test_ospf6_auth_trailer_tc10_no_auth_trailer(request): + """ + OSPFv3 Neighborship without Authentication Trailer - + Verify ospfv3 neighborship when no authentication trailer is configured. + + """ + 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) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + + step( + "Verify that the neighbour is FULL between R1 and R2 " + "using show ipv6 ospf6 neighbor cmd." + ) + + dut = "r2" + ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) + assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + tc_name, ospf6_covergence + ) + + write_test_footer(tc_name) + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args))