]> git.puffer.fish Git - matthieu/frr.git/commitdiff
ospf6d: Core functionality of auth trailer implementation..
authorAbhinay Ramesh <rabhinay@vmware.com>
Sun, 30 May 2021 16:27:13 +0000 (16:27 +0000)
committerAbhinay Ramesh <rabhinay@vmware.com>
Wed, 9 Feb 2022 01:57:08 +0000 (01:57 +0000)
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 [<tx|rx>]

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 <rabhinay@vmware.com>
ospf6d/ospf6_auth_trailer.c [new file with mode: 0644]
ospf6d/ospf6_auth_trailer.h [new file with mode: 0644]
ospf6d/ospf6_interface.h
ospf6d/ospf6_message.h

diff --git a/ospf6d/ospf6_auth_trailer.c b/ospf6d/ospf6_auth_trailer.c
new file mode 100644 (file)
index 0000000..88d2fc2
--- /dev/null
@@ -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 [<tx|rx>]",
+       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 [<tx|rx>]",
+       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 (file)
index 0000000..fa2de28
--- /dev/null
@@ -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__ */
index d068a9857ac586bf3377772bb7f89c6d95ab2c15..d6a2b52e1291eaed1f5aa9ac2f3ed17cb4d3f0c4 100644 (file)
@@ -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)
index 0cd10ef82519c34e837dbe3c7bc7429f0128f1a4..68cb4c7794021614d0791540e5045d01a699af06 100644 (file)
@@ -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