]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
add encoder for BFD, and test cases.
authorAlan T. DeKok <aland@freeradius.org>
Tue, 28 Feb 2023 20:31:16 +0000 (15:31 -0500)
committerAlan T. DeKok <aland@freeradius.org>
Tue, 28 Feb 2023 20:32:54 +0000 (15:32 -0500)
There's no decoder, and no authentication tests yet

src/protocols/bfd/all.mk [new file with mode: 0644]
src/protocols/bfd/attrs.h [new file with mode: 0644]
src/protocols/bfd/base.c [new file with mode: 0644]
src/protocols/bfd/bfd.h [new file with mode: 0644]
src/protocols/bfd/encode.c [new file with mode: 0644]
src/tests/unit/protocols/bfd/base.txt [new file with mode: 0644]

diff --git a/src/protocols/bfd/all.mk b/src/protocols/bfd/all.mk
new file mode 100644 (file)
index 0000000..23b7bad
--- /dev/null
@@ -0,0 +1,5 @@
+TARGET         := libfreeradius-bfd$(L)
+
+SOURCES                := base.c encode.c
+
+TGT_PREREQS    := libfreeradius-util$(L)
diff --git a/src/protocols/bfd/attrs.h b/src/protocols/bfd/attrs.h
new file mode 100644 (file)
index 0000000..46dd09e
--- /dev/null
@@ -0,0 +1,34 @@
+#pragma once
+/*
+ *   This program 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 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program 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; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ * @file src/protocols/bfd/attrs.h
+ * @brief BFD attributes
+ *
+ * @copyright 20223 Network RADIUS SAS (legal@networkradius.com)
+ */
+RCSIDH(radius_attrs_h, "$Id$")
+
+#include <freeradius-devel/util/dict.h>
+#include <freeradius-devel/bfd/bfd.h>
+
+extern HIDDEN fr_dict_t const *dict_freeradius;
+extern HIDDEN fr_dict_t const *dict_bfd;
+
+extern HIDDEN fr_dict_attr_t const *attr_packet_type;
+extern HIDDEN fr_dict_attr_t const *attr_bfd_packet;
diff --git a/src/protocols/bfd/base.c b/src/protocols/bfd/base.c
new file mode 100644 (file)
index 0000000..db350df
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) any later version.
+ *
+ *   This library 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
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ *
+ * @file protocols/bfd/base.c
+ * @brief Functions to send/receive BFD packets.
+ *
+ * @copyright 2023 Network RADIUS SAS (legal@networkradius.com)
+ */
+
+RCSID("$Id$")
+
+#include <fcntl.h>
+#include <ctype.h>
+
+#include "attrs.h"
+
+#include <freeradius-devel/io/pair.h>
+#include <freeradius-devel/util/net.h>
+#include <freeradius-devel/util/proto.h>
+#include <freeradius-devel/util/udp.h>
+
+static uint32_t instance_count = 0;
+
+fr_dict_t const *dict_freeradius;
+fr_dict_t const *dict_bfd;
+
+extern fr_dict_autoload_t libfreeradius_bfd_dict[];
+fr_dict_autoload_t libfreeradius_bfd_dict[] = {
+       { .out = &dict_freeradius, .proto = "freeradius" },
+       { .out = &dict_bfd, .proto = "bfd" },
+       { NULL }
+};
+
+fr_dict_attr_t const *attr_packet_type;
+fr_dict_attr_t const *attr_bfd_packet;
+
+extern fr_dict_attr_autoload_t libfreeradius_bfd_dict_attr[];
+fr_dict_attr_autoload_t libfreeradius_bfd_dict_attr[] = {
+       { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_bfd },
+       { .out = &attr_bfd_packet, .name = "Packet", .type = FR_TYPE_STRUCT, .dict = &dict_bfd },
+
+       { NULL }
+};
+
+
+int fr_bfd_init(void)
+{
+       if (instance_count > 0) {
+               instance_count++;
+               return 0;
+       }
+
+       if (fr_dict_autoload(libfreeradius_bfd_dict) < 0) return -1;
+       if (fr_dict_attr_autoload(libfreeradius_bfd_dict_attr) < 0) {
+               fr_dict_autofree(libfreeradius_bfd_dict);
+               return -1;
+       }
+
+       instance_count++;
+
+       return 0;
+}
+
+void fr_bfd_free(void)
+{
+       if (--instance_count > 0) return;
+
+       fr_dict_autofree(libfreeradius_bfd_dict);
+}
+
+extern fr_dict_protocol_t libfreeradius_bfd_dict_protocol;
+fr_dict_protocol_t libfreeradius_bfd_dict_protocol = {
+       .name = "bfd",
+       .default_type_size = 1,
+       .default_type_length = 1,
+};
diff --git a/src/protocols/bfd/bfd.h b/src/protocols/bfd/bfd.h
new file mode 100644 (file)
index 0000000..4f0462d
--- /dev/null
@@ -0,0 +1,153 @@
+#pragma once
+/*
+ *  This program 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 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/*
+ * $Id$
+ *
+ * @file protocols/bfd/bfd.h
+ * @brief Structures and prototypes for base BFD functionality.
+ *
+ * @copyright 2023 Network RADIUS SAS (legal@networkradius.com)
+ */
+#include <freeradius-devel/util/rand.h>
+#include <freeradius-devel/util/log.h>
+#include <freeradius-devel/util/pair.h>
+#include <freeradius-devel/util/md5.h>
+#include <freeradius-devel/util/sha1.h>
+#include <freeradius-devel/util/dbuff.h>
+
+typedef enum bfd_session_state_t {
+       BFD_STATE_ADMIN_DOWN = 0,
+       BFD_STATE_DOWN,
+       BFD_STATE_INIT,
+       BFD_STATE_UP
+} bfd_session_state_t;
+
+typedef enum bfd_diag_t {
+       BFD_DIAG_NONE = 0,
+       BFD_CTRL_EXPIRED,
+       BFD_ECHO_FAILED,
+       BFD_NEIGHBOR_DOWN,
+       BFD_FORWARD_PLANE_RESET,
+       BFD_PATH_DOWN,
+       BFD_CONCATENATED_PATH_DOWN,
+       BFD_ADMIN_DOWN,
+       BFD_REVERSE_CONCAT_PATH_DOWN
+} bfd_diag_t;
+
+typedef enum bfd_auth_type_t {
+       BFD_AUTH_RESERVED = 0,
+       BFD_AUTH_SIMPLE,
+       BFD_AUTH_KEYED_MD5,
+       BFD_AUTH_MET_KEYED_MD5,
+       BFD_AUTH_KEYED_SHA1,
+       BFD_AUTH_MET_KEYED_SHA1,
+} bfd_auth_type_t;
+
+#define BFD_AUTH_INVALID (BFD_AUTH_MET_KEYED_SHA1 + 1)
+
+typedef struct {
+       uint8_t         auth_type;
+       uint8_t         auth_len;
+       uint8_t         key_id;
+} __attribute__ ((packed)) bfd_auth_basic_t;
+
+
+typedef struct {
+       uint8_t         auth_type;
+       uint8_t         auth_len;
+       uint8_t         key_id;
+       uint8_t         password[16];
+} __attribute__ ((packed)) bfd_auth_simple_t;
+
+typedef struct {
+       uint8_t         auth_type;
+       uint8_t         auth_len;
+       uint8_t         key_id;
+       uint8_t         reserved;
+       uint32_t        sequence_no;
+       uint8_t         digest[MD5_DIGEST_LENGTH];
+} __attribute__ ((packed)) bfd_auth_md5_t;
+
+typedef struct {
+       uint8_t         auth_type;
+       uint8_t         auth_len;
+       uint8_t         key_id;
+       uint8_t         reserved;
+       uint32_t        sequence_no;
+       uint8_t         digest[SHA1_DIGEST_LENGTH];
+} __attribute__ ((packed)) bfd_auth_sha1_t;
+
+typedef union bfd_auth_t {
+       union {
+               bfd_auth_basic_t        basic;
+               bfd_auth_simple_t       password;
+               bfd_auth_md5_t          md5;
+               bfd_auth_sha1_t         sha1;
+       };
+} __attribute__ ((packed)) bfd_auth_t;
+
+
+/*
+ *     A packet
+ */
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+       unsigned int    version : 3;
+       unsigned int    diag : 5;
+       unsigned int    state : 2;
+       unsigned int    poll : 1;
+       unsigned int    final : 1;
+       unsigned int    control_plane_independent : 1;
+       unsigned int    auth_present : 1;
+       unsigned int    demand : 1;
+       unsigned int    multipoint : 1;
+#else
+       unsigned int    diag : 5;
+       unsigned int    version : 3;
+
+       unsigned int    multipoint : 1;
+       unsigned int    demand : 1;
+       unsigned int    auth_present : 1;
+       unsigned int    control_plane_independent : 1;
+       unsigned int    final : 1;
+       unsigned int    poll : 1;
+       unsigned int    state : 2;
+#endif
+       uint8_t         detect_multi;
+       uint8_t         length;
+       uint32_t        my_disc;
+       uint32_t        your_disc;
+       uint32_t        desired_min_tx_interval;
+       uint32_t        required_min_rx_interval;
+       uint32_t        min_echo_rx_interval;
+       bfd_auth_t      auth;
+} __attribute__ ((packed)) bfd_packet_t;
+
+#define FR_BFD_HEADER_LENGTH (24)
+
+typedef struct {
+       TALLOC_CTX              *tmp_ctx;               //!< for temporary things cleaned up during decoding
+       char const              *secret;                //!< shared secret.  MUST be talloc'd
+} fr_bfd_ctx_t;
+
+ssize_t                fr_bfd_encode(uint8_t *packet, size_t packet_len,  uint8_t const *original,
+                             char const *secret,  size_t secret_len, fr_pair_list_t *vps);
+
+
+int    fr_bfd_init(void);
+void   fr_bfd_free(void);
diff --git a/src/protocols/bfd/encode.c b/src/protocols/bfd/encode.c
new file mode 100644 (file)
index 0000000..ff6c3e4
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) any later version.
+ *
+ *   This library 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
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ *
+ * @file protocols/bfd/encode.c
+ * @brief Functions to encode BFD packets
+ *
+ * @copyright 2023 Network RADIUS SAS (legal@networkradius.com)
+ */
+RCSID("$Id$")
+
+#include <freeradius-devel/util/dbuff.h>
+#include <freeradius-devel/util/md5.h>
+#include <freeradius-devel/util/struct.h>
+#include <freeradius-devel/io/test_point.h>
+#include "attrs.h"
+
+extern fr_dict_attr_t const *attr_bfd_packet;
+
+/** Encodes the data portion of an attribute
+ *
+ * @return
+ *     > 0, Length of the data portion.
+ *      = 0, we could not encode anything, skip this attribute (and don't encode the header)
+ *       unless it's one of a list of exceptions.
+ *     < 0, How many additional bytes we'd need as a negative integer.
+ *     PAIR_ENCODE_FATAL_ERROR - Abort encoding the packet.
+ *     PAIR_ENCODE_SKIPPED - Unencodable value
+ */
+static ssize_t encode_value(fr_dbuff_t *dbuff,
+                           fr_da_stack_t *da_stack, unsigned int depth,
+                           fr_dcursor_t *cursor, UNUSED void *encode_ctx)
+{
+       ssize_t                 slen;
+       fr_pair_t const         *vp = fr_dcursor_current(cursor);
+       fr_dict_attr_t const    *da = da_stack->da[depth];
+//     fr_bfd_ctx_t            *packet_ctx = encode_ctx;
+       fr_dbuff_t              work_dbuff = FR_DBUFF(dbuff);
+
+       PAIR_VERIFY(vp);
+       FR_PROTO_STACK_PRINT(da_stack, depth);
+
+       /*
+        *      This has special requirements.
+        */
+       if ((vp->da->type == FR_TYPE_STRUCT) || (da->type == FR_TYPE_STRUCT)) {
+               slen = fr_struct_to_network(&work_dbuff, da_stack, depth, cursor, encode_ctx, encode_value, NULL);
+               goto done;
+       }
+
+       /*
+        *      If it's not a TLV, it should be a value type RFC
+        *      attribute make sure that it is.
+        */
+       if (da_stack->da[depth + 1] != NULL) {
+               fr_strerror_printf("%s: Encoding value but not at top of stack", __FUNCTION__);
+               return PAIR_ENCODE_FATAL_ERROR;
+       }
+
+       if (vp->da != da) {
+               fr_strerror_printf("%s: Top of stack does not match vp->da", __FUNCTION__);
+               return PAIR_ENCODE_FATAL_ERROR;
+       }
+
+       if (fr_type_is_structural(da->type)) {
+               fr_strerror_printf("%s: Called with structural type %s", __FUNCTION__,
+                                  fr_type_to_str(da_stack->da[depth]->type));
+               return PAIR_ENCODE_FATAL_ERROR;
+       }
+
+       slen = fr_value_box_to_network(&work_dbuff, &vp->data);
+
+done:
+       if (slen < 0) return slen;
+
+       FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), fr_dbuff_used(&work_dbuff), "%pP", vp);
+
+       vp = fr_dcursor_next(cursor);
+       fr_proto_da_stack_build(da_stack, vp ? vp->da : NULL);
+
+       return fr_dbuff_set(dbuff, &work_dbuff);
+}
+
+/** Encode VPS into a BFD packet.
+ *
+ */
+ssize_t fr_bfd_encode(uint8_t *out, size_t outlen, UNUSED uint8_t const *original,
+                     char const *secret, size_t secret_len, fr_pair_list_t *vps)
+{
+       ssize_t                 slen;
+       fr_bfd_ctx_t            packet_ctx;
+       bfd_packet_t            *packet;
+       fr_dcursor_t            cursor;
+       fr_dbuff_t              work_dbuff = FR_DBUFF_TMP(out, outlen);
+       fr_da_stack_t           da_stack;
+
+       fr_pair_dcursor_init(&cursor, vps);
+
+       packet_ctx.secret = secret;
+
+       fr_proto_da_stack_build(&da_stack, attr_bfd_packet);
+       FR_PROTO_STACK_PRINT(&da_stack, 0);
+
+       slen = fr_struct_to_network(&work_dbuff, &da_stack, 0, &cursor, &packet_ctx, encode_value, NULL);
+       if (slen < 0) return slen;
+
+       /*
+        *      The length is only 8 bits.  :(
+        */
+       if (slen > UINT8_MAX) {
+               fr_strerror_const("Packet is larger than 255 octets");
+               return -1;
+       }
+
+       /*
+        *      For various reasons the base BFD struct has "auth-type" as the last MEMBER, even if it's not
+        *      always used.  The struct encoder will fill it in with zeros, so we have to check for
+        *      "auth_present" and then remove the last byte if there's no authentication stuff present.
+        */
+       packet = (bfd_packet_t *) out;
+
+       if (!packet->auth_present) {
+               if (slen > FR_BFD_HEADER_LENGTH) slen = FR_BFD_HEADER_LENGTH;
+
+       } else if (!secret || secret_len == 0) {
+               fr_strerror_const("Cannot sign packets without a secret");
+               return -1;
+
+       } else {
+
+#if 0
+               /*
+                *      @todo - sign the packet with the chosen auth type
+                */
+               if (fr_bfd_sign(data, NULL, (uint8_t const *) secret, secret_len - 1) < 0) {
+                       return -1;
+               }
+#endif
+       }
+
+       packet->length = slen;
+
+       FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), slen, "BFD Packet");
+
+       return slen;
+}
+
+
+static int _test_ctx_free(UNUSED fr_bfd_ctx_t *ctx)
+{
+       fr_bfd_free();
+
+       return 0;
+}
+
+static int encode_test_ctx(void **out, TALLOC_CTX *ctx)
+{
+       fr_bfd_ctx_t    *test_ctx;
+
+       if (fr_bfd_init() < 0) return -1;
+
+       test_ctx = talloc_zero(ctx, fr_bfd_ctx_t);
+       if (!test_ctx) return -1;
+
+       test_ctx->secret = talloc_strdup(test_ctx, "testing123");
+       talloc_set_destructor(test_ctx, _test_ctx_free);
+
+       *out = test_ctx;
+
+       return 0;
+}
+
+static ssize_t fr_bfd_encode_proto(UNUSED TALLOC_CTX *ctx, fr_pair_list_t *vps, uint8_t *data, size_t data_len, void *proto_ctx)
+{
+       fr_bfd_ctx_t    *test_ctx = talloc_get_type_abort(proto_ctx, fr_bfd_ctx_t);
+       ssize_t         slen;
+
+       /*
+        *      @todo - pass in test_ctx to this function, so that we
+        *      can leverage a consistent random number generator.
+        */
+       slen = fr_bfd_encode(data, data_len, NULL, test_ctx->secret, talloc_array_length(test_ctx->secret) - 1, vps);
+       if (slen <= 0) return slen;
+
+       return slen;
+}
+
+/*
+ *     No one else should be using this.
+ */
+extern void *fr_bfd_next_encodable(fr_dlist_head_t *list, void *to_eval, void *uctx);
+
+/*
+ *     Test points
+ */
+extern fr_test_point_proto_encode_t bfd_tp_encode_proto;
+fr_test_point_proto_encode_t bfd_tp_encode_proto = {
+       .test_ctx       = encode_test_ctx,
+       .func           = fr_bfd_encode_proto
+};
diff --git a/src/tests/unit/protocols/bfd/base.txt b/src/tests/unit/protocols/bfd/base.txt
new file mode 100644 (file)
index 0000000..754690a
--- /dev/null
@@ -0,0 +1,16 @@
+#  Test vectors for BFD Packets
+#
+#  Copyright 2023 Network RADIUS SAS (legal@networkradius.com)
+#
+proto bfd
+proto-dictionary bfd
+fuzzer-out bfd
+
+#
+#  A basic BFD packet.
+#
+encode-proto Packet = { version = 1, diagnostic = none, state = up, poll = false, final = false, control-plane-independent = false, auth-present = false, demand = false, multipoint = false, detect-multi = 3, my-discriminator = 0xdeadbeef, your-discriminator = 0x21126809, desired-min-tx-interval = 31us, required-min-tx-interval = 127us, required-min-echo-interval   = 255us }
+match 20 c0 03 18 de ad be ef 21 12 68 09 00 00 00 1f 00 00 00 7f 00 00 00 ff
+
+count
+match 5