]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
this code is no longer necessary
authorAlan T. DeKok <aland@freeradius.org>
Fri, 10 Mar 2023 15:24:36 +0000 (10:24 -0500)
committerAlan T. DeKok <aland@freeradius.org>
Fri, 10 Mar 2023 22:16:56 +0000 (17:16 -0500)
all of the relevant work has been ported to the new framework.

src/modules/proto_bfd/README.md [deleted file]
src/modules/proto_bfd/proto_bfd.c [deleted file]

diff --git a/src/modules/proto_bfd/README.md b/src/modules/proto_bfd/README.md
deleted file mode 100644 (file)
index 89d3a7a..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# proto_bfd
-## Metadata
-<dl>
-  <dt>category</dt><dd>protocols</dd>
-</dl>
-
-## Summary
-Implements BiDirectional Forwarding Protocol (RFC 5880) - allowing the server to quickly detect if a link is up or down.
-This module can be used to detect if a *peer* application is up or down and use the trigger module to execute certain functions when the link is started, goes up, down, or is administratively down.
-
diff --git a/src/modules/proto_bfd/proto_bfd.c b/src/modules/proto_bfd/proto_bfd.c
deleted file mode 100644 (file)
index 290904a..0000000
+++ /dev/null
@@ -1,1865 +0,0 @@
-/*
- * proto_bfd.c BFD processing.
- *
- * Version:    $Id$
- *
- *   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
- *
- * @copyright 2012 Network RADIUS SAS (legal@networkradius.com)
- *
- *  Check on RFC 5881 for BFD over IP issues.
- */
-
-#include <freeradius-devel/server/base.h>
-#include <freeradius-devel/server/module_rlm.h>
-#include <freeradius-devel/server/protocol.h>
-#include <freeradius-devel/util/debug.h>
-
-#include <freeradius-devel/unlang/call.h>
-
-#include <freeradius-devel/util/event.h>
-#include <freeradius-devel/util/base16.h>
-#include <freeradius-devel/util/md5.h>
-#include <freeradius-devel/util/rand.h>
-#include <freeradius-devel/util/sha1.h>
-#include <freeradius-devel/util/socket.h>
-#include <freeradius-devel/util/time.h>
-
-#define BFD_MAX_SECRET_LENGTH 20
-
-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 {
-       fr_rb_node_t    node;           //!< Entry in the tree of sessions.
-
-       int             number;
-
-       fr_socket_t socket;
-
-       fr_event_list_t *el;
-       CONF_SECTION    *server_cs;
-       CONF_SECTION    *unlang;
-
-       bool            blocked;
-       int             pipefd[2];
-       pthread_t       pthread_id;
-
-       bfd_auth_type_t auth_type;
-       uint8_t         secret[BFD_MAX_SECRET_LENGTH];
-       size_t          secret_len;
-
-       /*
-        *      To simplify sending the packets.
-        */
-       struct sockaddr_storage remote_sockaddr;
-       socklen_t       salen;
-
-       fr_event_timer_t const  *ev_timeout;
-       fr_event_timer_t const  *ev_packet;
-       fr_time_t       last_recv;
-       fr_time_t       next_recv;
-       fr_time_t       last_sent;
-
-       bfd_session_state_t session_state;
-       bfd_session_state_t remote_session_state;
-
-       uint32_t        local_disc;
-       uint32_t        remote_disc;
-
-       bfd_diag_t      local_diag;
-
-       uint32_t        desired_min_tx_interval; /* in usec */
-       uint32_t        required_min_rx_interval;
-       uint32_t        remote_min_rx_interval;
-       uint32_t        remote_min_echo_rx_interval;
-
-       uint32_t        next_min_tx_interval;
-
-       bool            demand_mode;
-       bool            remote_demand_mode;
-
-       int             detect_multi;
-
-       uint32_t        recv_auth_seq;
-       uint32_t        xmit_auth_seq;
-
-       int             auth_seq_known;
-
-       int             doing_poll;
-       uint32_t        my_min_echo_rx_interval;
-
-       uint32_t        detection_time;
-       int             detection_timeouts;
-
-       int             passive;
-} bfd_state_t;
-
-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 {
-       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;
-
-
-typedef struct {
-       fr_ipaddr_t     my_ipaddr;
-       uint16_t        my_port;
-
-       char            const *interface;
-
-       int             number;
-       CONF_SECTION    *server_cs;
-       CONF_SECTION    *unlang;
-
-       uint32_t        min_tx_interval;
-       uint32_t        min_rx_interval;
-       uint32_t        max_timeouts;
-       bool            demand;
-
-       bfd_auth_type_t auth_type;
-       uint8_t         secret[BFD_MAX_SECRET_LENGTH];
-       size_t          secret_len;
-
-       fr_rb_tree_t    *session_tree;
-} bfd_socket_t;
-
-static fr_dict_t const *dict_bfd;
-
-extern fr_dict_autoload_t proto_bfd_dict[];
-fr_dict_autoload_t proto_bfd_dict[] = {
-       { .out = &dict_bfd, .proto = "bfd" },
-       { NULL }
-};
-
-static int bfd_start_packets(bfd_state_t *session);
-static int bfd_start_control(bfd_state_t *session);
-static int bfd_stop_control(bfd_state_t *session);
-static void bfd_detection_timeout(UNUSED fr_event_list_t *eel, fr_time_t now, void *ctx);
-static int bfd_process(bfd_state_t *session, bfd_packet_t *bfd);
-
-static fr_event_list_t *event_list = NULL; /* don't ask */
-
-void bfd_init(fr_event_list_t *xel);
-
-void bfd_init(fr_event_list_t *xel)
-{
-       event_list = xel;
-}
-
-static void bfd_pthread_free(bfd_state_t *session)
-{
-       session->blocked = true;
-
-       close(session->pipefd[0]);
-       close(session->pipefd[1]);
-
-       talloc_free(session->el);
-
-       session->el = NULL;
-       session->pipefd[0] = session->pipefd[1] = -1;
-
-       session->blocked = false;
-}
-
-/*
- *     A child thread reads the packet from a pipe, and processes it.
- */
-static void bfd_pipe_recv(UNUSED fr_event_list_t *xel, int fd, UNUSED int flags, void *ctx)
-{
-       ssize_t num;
-       bfd_state_t *session = ctx;
-       bfd_packet_t bfd;
-
-       if (session->blocked) return;
-
-       /*
-        *      Read the header
-        */
-       num = read(fd, &bfd, 4);
-       if ((num < 4) || (bfd.length < 4)) {
-       fail:
-               ERROR("BFD Failed reading from pipe!");
-               session->blocked = true;
-               return;
-       }
-
-       /*
-        *      This is already checked in the caller, but what the heck...
-        */
-       if (bfd.length > sizeof(bfd)) goto fail;
-
-       /*
-        *      Read the rest of the packet.
-        */
-       num = read(fd, ((uint8_t *) &bfd) + 4, bfd.length - 4);
-       if ((num < 0) || ((num + 4) != bfd.length)) goto fail;
-
-       bfd_process(session, &bfd);
-}
-
-/*
- *     Do nothing more than read from the sockets and process the
- *     timers.
- */
-static void *bfd_child_thread(void *ctx)
-{
-       bfd_state_t *session = ctx;
-
-       DEBUG("BFD %d starting child thread", session->number);
-       bfd_start_control(session);
-
-       fr_event_loop(session->el);
-
-       bfd_pthread_free(session);
-
-       return NULL;
-}
-
-
-static int bfd_pthread_create(bfd_state_t *session)
-{
-       pthread_attr_t attr;
-
-       if (pipe(session->pipefd) < 0) {
-               ERROR("Failed opening pipe: %s", fr_syserror(errno));
-               return 0;
-       }
-
-       session->el = fr_event_list_alloc(session, NULL, NULL);
-       if (!session->el) {
-               ERROR("Failed creating event list");
-       close_pipes:
-               close(session->pipefd[0]);
-               close(session->pipefd[1]);
-               session->pipefd[0] = session->pipefd[1] = -1;
-               return 0;
-       }
-
-#ifdef O_NONBLOCK
-       fcntl(session->pipefd[0], F_SETFL, O_NONBLOCK | FD_CLOEXEC);
-       fcntl(session->pipefd[1], F_SETFL, O_NONBLOCK | FD_CLOEXEC);
-#endif
-
-       if (fr_event_fd_insert(session, session->el, session->pipefd[0],
-                              bfd_pipe_recv,
-                              NULL,
-                              NULL,
-                              session) < 0) {
-               PERROR("Failed inserting file descriptor into event list");
-               goto close_pipes;
-       }
-
-       pthread_attr_init(&attr);
-       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-
-       /*
-        *      Create the thread detached, so that it cleans up it's
-        *      own memory when it exits.
-        *
-        *      Note that the function returns non-zero on error, NOT
-        *      -1.  The return code is the error, and errno isn't set.
-        */
-       if (fr_schedule_pthread_create(&session->pthread_id, bfd_child_thread, session) < 0) {
-               talloc_free(session->el);
-               session->el = NULL;
-               PERROR("Thread create failed");
-               goto close_pipes;
-       }
-       pthread_attr_destroy(&attr);
-
-       return 1;
-}
-
-static const char *bfd_state[] = {
-       "admin-down",
-       "down",
-       "init",
-       "up"
-};
-
-
-static void bfd_request(bfd_state_t *session, request_t *request, fr_radius_packet_t *packet)
-{
-       memset(packet, 0, sizeof(*packet));
-
-       request->packet = packet;
-       packet->socket = session->socket;
-       /*      request->heap_offset = -1; */
-}
-
-
-static void bfd_trigger(bfd_state_t *session)
-{
-       fr_radius_packet_t      packet;
-       request_t               *request = request_local_alloc_external(session, NULL);
-       char                    buffer[256];
-
-       snprintf(buffer, sizeof(buffer), "server.bfd.%s",
-                bfd_state[session->session_state]);
-
-       bfd_request(session, request, &packet);
-
-       trigger_exec(unlang_interpret_get_thread_default(), NULL, buffer, false, NULL);
-}
-
-
-static void bfd_session_free(void *ctx)
-{
-       bfd_state_t *session = ctx;
-
-       if (event_list != session->el) {
-               /*
-                *      FIXME: this isn't particularly safe.
-                */
-               bfd_pthread_free(session);
-       }
-
-       talloc_free(session);
-}
-
-
-static ssize_t bfd_parse_secret(CONF_SECTION *cs, uint8_t secret[BFD_MAX_SECRET_LENGTH])
-{
-       int rcode;
-       size_t len;
-       char const *value = NULL;
-
-       rcode = cf_pair_parse(NULL, cs, "secret", FR_ITEM_POINTER(FR_TYPE_STRING, &value), NULL, T_INVALID);
-       if (rcode != 0) return 0;
-
-       len = strlen(value);
-
-       if ((value[0] == '0') && (value[1] == 'x')) {
-               if (len > 42) {
-                       cf_log_err(cf_section_to_item(cs), "Secret is too long");
-                       return -1;
-               }
-
-               if ((len & 0x01) != 0) {
-                       cf_log_err(cf_section_to_item(cs), "Invalid hex length");
-                       return -1;
-               }
-
-               return fr_base16_decode(NULL, &FR_DBUFF_TMP(secret, BFD_MAX_SECRET_LENGTH),
-                                 &FR_SBUFF_IN(value + 2, (len - 2)), false);
-       }
-
-       if (len >= 20) {
-               cf_log_err(cf_section_to_item(cs), "Secret is too long");
-               return -1;
-       }
-
-       memset(secret, 0, BFD_MAX_SECRET_LENGTH);
-       memcpy(secret, value, len);
-       return len;
-}
-
-
-
-/*
- *     Create a new session.
- */
-static bfd_state_t *bfd_new_session(bfd_socket_t *sock, int sockfd,
-                                   CONF_SECTION *cs,
-                                   const fr_ipaddr_t *ipaddr, uint16_t port)
-{
-       int rcode;
-       bool flag;
-       bfd_state_t *session;
-
-       session = talloc_zero(sock, bfd_state_t);
-
-       /*
-        *      Initialize according to RFC.
-        */
-       session->number = sock->number++;
-       session->socket.fd = sockfd;
-       session->session_state = BFD_STATE_DOWN;
-       session->server_cs = sock->server_cs;
-       session->unlang = sock->unlang;
-       session->local_disc = fr_rand();
-       session->remote_disc = 0;
-       session->local_diag = BFD_DIAG_NONE;
-       session->desired_min_tx_interval = sock->min_tx_interval * 1000;
-       session->required_min_rx_interval = sock->min_rx_interval * 1000;
-       session->remote_min_rx_interval = 1;
-       session->demand_mode = sock->demand;
-       session->remote_demand_mode = false;
-       session->detect_multi = sock->max_timeouts;
-       session->auth_type = BFD_AUTH_RESERVED;
-       session->recv_auth_seq = 0;
-       session->xmit_auth_seq = fr_rand();
-       session->auth_seq_known = 0;
-
-       /*
-        *      Allow over-riding of variables per session.
-        */
-       rcode = cf_pair_parse(NULL, cs, "demand", FR_ITEM_POINTER(FR_TYPE_BOOL, &flag), NULL, T_INVALID);
-       if (rcode == 0) {
-               session->demand_mode = flag;
-       }
-
-       /*
-        *      Number is moved out of scope to shut up lgtm
-        *      static analysis, but really these should be
-        *      moved into conf_parser callback functions.
-        */
-       {
-               uint32_t number;
-
-               rcode = cf_pair_parse(NULL, cs, "min_transmit_interval", FR_ITEM_POINTER(FR_TYPE_UINT32, &number), NULL, T_INVALID);
-               if (rcode == 0) {
-                       if (number < 100) number = 100;
-                       if (number > 10000) number = 10000;
-
-                       session->desired_min_tx_interval = number * 1000;
-               }
-       }
-
-       {
-               uint32_t number;
-
-               rcode = cf_pair_parse(NULL, cs, "min_receive_interval", FR_ITEM_POINTER(FR_TYPE_UINT32, &number), NULL, T_INVALID);
-               if (rcode == 0) {
-                       if (number < 100) number = 100;
-                       if (number > 10000) number = 10000;
-
-                       session->required_min_rx_interval = number * 1000;
-               }
-       }
-
-       {
-               uint32_t number;
-
-               rcode = cf_pair_parse(NULL, cs, "max_timeouts", FR_ITEM_POINTER(FR_TYPE_UINT32, &number), NULL, T_INVALID);
-               if (rcode == 0) {
-                       if (number == 0) number = 1;
-                       if (number > 10) number = 10;
-
-                       session->detect_multi = number;
-               }
-       }
-       session->auth_type = sock->auth_type;
-
-       /*
-        *      Parse / over-ride the secrets.
-        */
-       session->secret_len = bfd_parse_secret(cs, session->secret);
-       if ((session->secret_len == 0) &&
-           (session->auth_type != BFD_AUTH_RESERVED)) {
-               if (sock->secret_len == 0) {
-                       cf_log_err(cf_section_to_item(cs), "auth_type requires a secret");
-                       talloc_free(session);
-                       return NULL;
-               }
-
-               session->secret_len = sock->secret_len;
-               memcpy(session->secret, sock->secret, sizeof(session->secret));
-       }
-
-       /*
-        *      Initialize the detection time.
-        */
-       if (!session->demand_mode) {
-               session->detection_time = session->required_min_rx_interval;
-       } else {
-               session->detection_time = session->desired_min_tx_interval;
-       }
-       session->detection_time *= session->detect_multi;
-
-       /*
-        *      And finally remember the session.
-        */
-       session->socket.inet.dst_ipaddr = *ipaddr;
-       session->socket.inet.dst_port = port;
-
-       session->socket.inet.src_ipaddr = sock->my_ipaddr;
-       session->socket.inet.src_port = sock->my_port;
-
-       fr_ipaddr_to_sockaddr(&session->remote_sockaddr, &session->salen,
-                             ipaddr, port);
-
-       if (!fr_rb_insert(sock->session_tree, session)) {
-               ERROR("FAILED creating new session!");
-               talloc_free(session);
-               return NULL;
-       }
-
-       bfd_trigger(session);
-
-       /*
-        *      Check for threaded / non-threaded operation.
-        */
-       if (event_list) {
-               session->el = event_list;
-
-               bfd_start_control(session);
-
-               session->pipefd[0] = session->pipefd[1] = -1;
-               session->pthread_id = pthread_self();
-       } else {
-               if (!bfd_pthread_create(session)) {
-                       fr_rb_delete(sock->session_tree, session);
-                       talloc_free(session);
-                       return NULL;
-               }
-       }
-
-       return session;
-}
-
-
-static int bfd_verify_sequence(bfd_state_t *session, uint32_t sequence_no,
-                              int keyed)
-{
-       uint32_t start, stop;
-
-       start = session->recv_auth_seq;
-       if (keyed) {
-               start++;
-       }
-       stop = start + 3 * session->detect_multi;
-
-       if (start < stop) {
-               if ((sequence_no < start) ||
-                   (sequence_no > stop)) {
-                       return 0;
-               }
-
-       } else {        /* start is ~2^32, stop is ~10 */
-               if ((sequence_no > start) &&
-                   (sequence_no < stop)) {
-                       return 0;
-               }
-       }
-
-       return 1;
-}
-
-static void bfd_calc_md5(bfd_state_t *session, bfd_packet_t *bfd)
-{
-       bfd_auth_md5_t *md5 = &bfd->auth.md5;
-
-       fr_assert(session->secret_len <= sizeof(md5->digest));
-       fr_assert(md5->auth_len == sizeof(*md5));
-
-       memset(md5->digest, 0, sizeof(md5->digest));
-       memcpy(md5->digest, session->secret, session->secret_len);
-
-       fr_md5_calc(md5->digest,(const uint8_t *) bfd, bfd->length);
-}
-
-static void bfd_auth_md5(bfd_state_t *session, bfd_packet_t *bfd)
-{
-       bfd_auth_md5_t *md5 = &bfd->auth.md5;
-
-       md5->auth_type = session->auth_type;
-       md5->auth_len = sizeof(*md5);
-       bfd->length += md5->auth_len;
-
-       md5->key_id = 0;
-       md5->sequence_no = session->xmit_auth_seq++;
-
-       bfd_calc_md5(session, bfd);
-}
-
-static int bfd_verify_md5(bfd_state_t *session, bfd_packet_t *bfd)
-{
-       int rcode;
-       bfd_auth_md5_t *md5 = &bfd->auth.md5;
-       uint8_t digest[sizeof(md5->digest)];
-
-       if (md5->auth_len != sizeof(*md5)) return 0;
-
-       if (md5->key_id != 0) return 0;
-
-       memcpy(digest, md5->digest, sizeof(digest));
-
-       bfd_calc_md5(session, bfd);
-       rcode = fr_digest_cmp(digest, md5->digest, sizeof(digest));
-
-       memcpy(md5->digest, digest, sizeof(md5->digest)); /* pedantic */
-
-       if (rcode != 0) {
-               DEBUG("BFD %d MD5 Digest failed: **** RE-ENTER THE SECRET ON BOTH ENDS ****", session->number);
-               return 0;
-       }
-
-       /*
-        *      Do this AFTER the authentication instead of before!
-        */
-       if (!session->auth_seq_known) {
-               session->auth_seq_known = 1;
-
-       } else if (!bfd_verify_sequence(session, md5->sequence_no,
-                                       (md5->auth_type == BFD_AUTH_MET_KEYED_MD5))) {
-               DEBUG("MD5 sequence out of window");
-               return 0;
-       }
-
-       session->recv_auth_seq = md5->sequence_no;
-
-       return 1;
-}
-
-static void bfd_calc_sha1(bfd_state_t *session, bfd_packet_t *bfd)
-{
-       fr_sha1_ctx ctx;
-       bfd_auth_sha1_t *sha1 = &bfd->auth.sha1;
-
-       fr_assert(session->secret_len <= sizeof(sha1->digest));
-       fr_assert(sha1->auth_len == sizeof(*sha1));
-
-       memset(sha1->digest, 0, sizeof(sha1->digest));
-       memcpy(sha1->digest, session->secret, session->secret_len);
-
-       fr_sha1_init(&ctx);
-       fr_sha1_update(&ctx, (const uint8_t *) bfd, bfd->length);
-       fr_sha1_final(sha1->digest, &ctx);
-}
-
-static void bfd_auth_sha1(bfd_state_t *session, bfd_packet_t *bfd)
-{
-       bfd_auth_sha1_t *sha1 = &bfd->auth.sha1;
-
-       sha1->auth_type = session->auth_type;
-       sha1->auth_len = sizeof(*sha1);
-       bfd->length += sha1->auth_len;
-
-       sha1->key_id = 0;
-       sha1->sequence_no = session->xmit_auth_seq++;
-
-       bfd_calc_sha1(session, bfd);
-}
-
-static int bfd_verify_sha1(bfd_state_t *session, bfd_packet_t *bfd)
-{
-       int rcode;
-       bfd_auth_sha1_t *sha1 = &bfd->auth.sha1;
-       uint8_t digest[sizeof(sha1->digest)];
-
-       if (sha1->auth_len != sizeof(*sha1)) return 0;
-
-       if (sha1->key_id != 0) return 0;
-
-       memcpy(digest, sha1->digest, sizeof(digest));
-
-       bfd_calc_sha1(session, bfd);
-       rcode = fr_digest_cmp(digest, sha1->digest, sizeof(digest));
-
-       memcpy(sha1->digest, digest, sizeof(sha1->digest)); /* pedantic */
-
-       if (rcode != 0) {
-               DEBUG("BFD %d SHA1 Digest failed: **** RE-ENTER THE SECRET ON BOTH ENDS ****", session->number);
-               return 0;
-       }
-
-       /*
-        *      Do this AFTER the authentication instead of before!
-        */
-       if (!session->auth_seq_known) {
-               session->auth_seq_known = 1;
-
-       } else if (!bfd_verify_sequence(session, sha1->sequence_no,
-                                       (sha1->auth_type == BFD_AUTH_MET_KEYED_SHA1))) {
-               DEBUG("SHA1 sequence out of window");
-               return 0;
-       }
-
-       session->recv_auth_seq = sha1->sequence_no;
-
-       return 1;
-}
-
-
-static int bfd_authenticate(bfd_state_t *session, bfd_packet_t *bfd)
-{
-       switch (bfd->auth.basic.auth_type) {
-       case BFD_AUTH_RESERVED:
-               return 0;
-
-       case BFD_AUTH_SIMPLE:
-               break;
-
-       case BFD_AUTH_KEYED_MD5:
-       case BFD_AUTH_MET_KEYED_MD5:
-               return bfd_verify_md5(session, bfd);
-
-       case BFD_AUTH_KEYED_SHA1:
-       case BFD_AUTH_MET_KEYED_SHA1:
-               return bfd_verify_sha1(session, bfd);
-       }
-
-       return 0;
-}
-
-static void bfd_control_packet_init(bfd_state_t *session,
-                                  bfd_packet_t *bfd)
-{
-       memset(bfd, 0, sizeof(*bfd));
-
-       bfd->version = 1;
-       bfd->diag = session->local_diag;
-       bfd->state = session->session_state;
-       bfd->poll = 0;  /* fixed by poll response */
-       bfd->final = 0; /* fixed by poll response */
-       bfd->control_plane_independent = 0;
-
-       if (session->auth_type == BFD_AUTH_RESERVED) {
-               bfd->auth_present = 0;
-       } else {
-               bfd->auth_present = 1;
-       }
-
-       /*
-        *      If we're UP / UP, signal that we've entered demand
-        *      mode, and stop sending packets.
-        */
-       if (session->demand_mode &&
-           (session->session_state == BFD_STATE_UP) &&
-           (session->remote_session_state == BFD_STATE_UP)) {
-               bfd->demand = true;
-
-               DEBUG("BFD %d demand mode UP / UP, sending ACK and done.",
-                     session->number);
-               bfd_stop_control(session);
-       } else {
-               bfd->demand = false;
-       }
-
-       bfd->multipoint = 0;
-       bfd->detect_multi = session->detect_multi;
-       bfd->length = 24;       /* auth types add to this later */
-
-       bfd->my_disc = session->local_disc;
-       bfd->your_disc = session->remote_disc;
-
-       bfd->desired_min_tx_interval = session->desired_min_tx_interval;
-       bfd->required_min_rx_interval = session->required_min_rx_interval;
-
-       bfd->min_echo_rx_interval = session->my_min_echo_rx_interval;
-}
-
-
-static void bfd_sign(bfd_state_t *session, bfd_packet_t *bfd)
-{
-       if (bfd->auth_present) {
-               switch (session->auth_type) {
-               case BFD_AUTH_RESERVED:
-                       break;
-
-               case BFD_AUTH_SIMPLE:
-                       break;
-
-               case BFD_AUTH_KEYED_MD5:
-               case BFD_AUTH_MET_KEYED_MD5:
-                       bfd_auth_md5(session, bfd);
-                       break;
-
-               case BFD_AUTH_KEYED_SHA1:
-               case BFD_AUTH_MET_KEYED_SHA1:
-                       bfd_auth_sha1(session, bfd);
-                       break;
-               }
-       }
-}
-
-
-/*
- *     Send a packet.
- */
-static void bfd_send_packet(UNUSED fr_event_list_t *eel, UNUSED fr_time_t now, void *ctx)
-{
-       bfd_state_t *session = ctx;
-       bfd_packet_t bfd;
-
-       bfd_control_packet_init(session, &bfd);
-
-       if (session->doing_poll) {
-               bfd.poll = 1;
-       }
-
-       if (!bfd.demand) {
-               bfd_start_packets(session);
-       }
-
-       bfd_sign(session, &bfd);
-
-       DEBUG("BFD %d sending packet state %s",
-             session->number, bfd_state[session->session_state]);
-       if (sendto(session->socket.fd, &bfd, bfd.length, 0,
-                  (struct sockaddr *) &session->remote_sockaddr,
-                  session->salen) < 0) {
-               ERROR("Failed sending packet: %s", fr_syserror(errno));
-       }
-}
-
-static int bfd_start_packets(bfd_state_t *session)
-{
-       uint32_t        interval, base;
-       uint64_t        jitter;
-
-       /*
-        *      Reset the timers.
-        */
-       fr_event_timer_delete(&session->ev_packet);
-
-       session->last_sent = fr_time();
-
-       if (session->desired_min_tx_interval >= session->remote_min_rx_interval) {
-               interval = session->desired_min_tx_interval;
-       } else {
-               interval = session->remote_min_rx_interval;
-       }
-       base = (interval * 3) / 4;
-       jitter = fr_rand();     /* 32-bit number */
-
-       if (session->detect_multi == 1) {
-               jitter *= 644245094;    /* 15% of 2^32 */
-
-       } else {
-               jitter *= (1 << 30); /* 25% of 2^32 */
-       }
-
-       jitter >>= 32;
-       jitter *= interval;
-       jitter >>= 32;
-       interval = base;
-       interval += jitter;
-
-       if (fr_event_timer_in(session, session->el, &session->ev_packet,
-                             fr_time_delta_from_usec(interval),
-                             bfd_send_packet, session) < 0) {
-               fr_assert("Failed to insert event" == NULL);
-       }
-
-       return 0;
-}
-
-
-static void bfd_set_timeout(bfd_state_t *session, fr_time_t when)
-{
-       fr_time_t now;
-
-       fr_event_timer_delete(&session->ev_timeout);
-
-       now = fr_time_add(when, fr_time_delta_from_usec(session->detection_time));
-
-       if (session->detect_multi >= 2) {
-               uint32_t delay;
-
-               session->next_recv = when;
-               delay = session->detection_time / session->detect_multi;
-               delay += delay / 2;
-
-               session->next_recv = fr_time_add(session->next_recv, fr_time_delta_from_usec(delay));
-       }
-
-       if (fr_event_timer_at(session, session->el, &session->ev_timeout,
-                             now, bfd_detection_timeout, session) < 0) {
-               fr_assert("Failed to insert event" == NULL);
-       }
-}
-
-
-static int bfd_start_control(bfd_state_t *session)
-{
-       if (session->remote_min_rx_interval == 0) return 0;
-
-       if ((session->remote_disc == 0) && session->passive) return 0;
-
-       if (session->remote_demand_mode &&
-           (session->session_state == BFD_STATE_UP) &&
-           (session->remote_session_state == BFD_STATE_UP) &&
-           !session->doing_poll) {
-               DEBUG("BFD %d warning: asked to start UP / UP ?",
-                     session->number);
-               fr_assert(0 == 1);
-               bfd_stop_control(session);
-               return 0;
-       }
-
-       bfd_set_timeout(session, session->last_recv);
-
-       if (session->ev_packet) return 0;
-
-       return bfd_start_packets(session);
-}
-
-static int bfd_stop_control(bfd_state_t *session)
-{
-       fr_event_timer_delete(&session->ev_timeout);
-       fr_event_timer_delete(&session->ev_packet);
-       return 1;
-}
-
-
-static int bfd_start_poll(bfd_state_t *session)
-{
-       if (session->doing_poll) return 0;
-
-       /*
-        *      Already sending packets.  Reset the timers and set the
-        *      poll bit.
-        */
-       if (!session->remote_demand_mode) {
-               bfd_stop_control(session);
-       }
-
-       session->doing_poll = 1;
-
-       /*
-        *      Send POLL packets, even if we're not sending CONTROL
-        *      packets.
-        */
-       return bfd_start_packets(session);
-}
-
-static int bfd_stop_poll(bfd_state_t *session)
-{
-       if (!session->doing_poll) return 0;
-
-       /*
-        *      We tried to increase the min_tx during a polling
-        *      sequence.  That isn't kosher, so we instead waited
-        *      until now.
-        */
-       if (session->next_min_tx_interval) {
-               session->desired_min_tx_interval = session->next_min_tx_interval;
-               session->next_min_tx_interval = 0;
-       }
-
-       /*
-        *      Already sending packets.  Clear the poll bit and
-        *      re-set the timers.
-        */
-       if (!session->remote_demand_mode) {
-               fr_assert(session->ev_timeout != NULL);
-               fr_assert(session->ev_packet != NULL);
-               session->doing_poll = 0;
-
-               bfd_stop_control(session);
-               bfd_start_control(session);
-               return 1;
-       }
-
-       session->doing_poll = 0;
-
-       return bfd_stop_control(session);
-}
-
-static void bfd_set_desired_min_tx_interval(bfd_state_t *session, uint32_t value)
-{
-       /*
-        *      Increasing the value: don't change it if we're already
-        *      polling.
-        */
-       if (session->doing_poll &&
-           (session->session_state == BFD_STATE_UP) &&
-           (value > session->desired_min_tx_interval)) {
-               session->next_min_tx_interval = value;
-               return;
-       }
-
-       if (session->session_state != BFD_STATE_UP) {
-               if (value < USEC) value = USEC;
-       }
-
-       session->desired_min_tx_interval = value;
-       bfd_stop_control(session);
-       session->doing_poll = 0;
-
-       bfd_start_poll(session);
-}
-
-static void bfd_detection_timeout(UNUSED fr_event_list_t *eel, fr_time_t now, void *ctx)
-{
-       bfd_state_t *session = ctx;
-
-       DEBUG("BFD %d Timeout state %s ****** ", session->number,
-             bfd_state[session->session_state]);
-
-       if (!session->demand_mode) {
-               switch (session->session_state) {
-               case BFD_STATE_INIT:
-               case BFD_STATE_UP:
-                       goto start_poll;
-
-               default:
-                       break;
-               }
-
-       } else if (!session->doing_poll) {
-       start_poll:
-               DEBUG("BFD %d State <timeout> -> DOWN (control expired)", session->number);
-               session->session_state = BFD_STATE_DOWN;
-               session->local_diag =  BFD_CTRL_EXPIRED;
-               bfd_trigger(session);
-
-               bfd_set_desired_min_tx_interval(session, USEC);
-       }
-
-       session->remote_disc = 0;
-
-       if (session->detection_timeouts >= 2) {
-               session->auth_seq_known = 0;
-       }
-
-       session->detection_timeouts++;
-
-       bfd_set_timeout(session, now);
-}
-
-
-/*
- *     Send an immediate response to a poll request.
- *
- *     Note that this doesn't affect our "last_sent" timer.
- *     That's set only when we intend to send a packet.
- */
-static void bfd_poll_response(bfd_state_t *session)
-{
-       bfd_packet_t bfd;
-
-       bfd_control_packet_init(session, &bfd);
-       bfd.poll = 0;           /* Section 6.5 */
-       bfd.final = 1;
-
-       /*
-        *      TO DO: rate limit poll responses.
-        */
-
-       bfd_sign(session, &bfd);
-
-       if (sendto(session->socket.fd, &bfd, bfd.length, 0,
-                  (struct sockaddr *) &session->remote_sockaddr,
-                  session->salen) < 0) {
-               ERROR("Failed sending poll response: %s", fr_syserror(errno));
-       }
-}
-
-
-static int bfd_process(bfd_state_t *session, bfd_packet_t *bfd)
-{
-       if (bfd->auth_present &&
-           (session->auth_type == BFD_AUTH_RESERVED)) {
-               DEBUG("BFD %d packet asked to authenticate an unauthenticated session.", session->number);
-               return 0;
-       }
-
-       if (!bfd->auth_present &&
-           (session->auth_type != BFD_AUTH_RESERVED)) {
-               DEBUG("BFD %d packet failed to authenticate an authenticated session.", session->number);
-               return 0;
-       }
-
-       if (bfd->auth_present && !bfd_authenticate(session, bfd)) {
-               return 0;
-       }
-
-       DEBUG("BFD %d processing packet", session->number);
-       session->remote_disc = bfd->my_disc;
-       session->remote_session_state = bfd->state;
-       session->remote_demand_mode = bfd->demand;
-
-       /*
-        *      The other end is reducing the RX interval.  Do that
-        *      now.
-        */
-       if ((bfd->required_min_rx_interval < session->remote_min_rx_interval) &&
-           !session->demand_mode) {
-               bfd_stop_control(session);
-               bfd_start_control(session);
-       }
-       session->remote_min_rx_interval = bfd->required_min_rx_interval;
-
-       session->remote_min_echo_rx_interval = bfd->min_echo_rx_interval;
-       if (bfd->min_echo_rx_interval == 0) {
-#if 0
-               /*
-                *      Echo packets are BFD packets with
-                *      application-layer data echoed back to the
-                *      sender.  We don't do that.
-                */
-               bfd_stop_echo(session);
-#endif
-       }
-
-       if (session->doing_poll && bfd->final) {
-               bfd_stop_poll(session);
-       }
-
-       /*
-        *      Update transmit intervals as in 6.8.7
-        */
-
-       /*
-        *      Update detection times as in 6.8.4
-        */
-       if (!session->demand_mode) {
-               if (session->required_min_rx_interval >= bfd->desired_min_tx_interval) {
-                       session->detection_time = session->required_min_rx_interval;
-               } else {
-                       session->detection_time = bfd->desired_min_tx_interval;
-               }
-       } else {
-               if (session->desired_min_tx_interval >= session->remote_min_rx_interval) {
-                       session->detection_time = session->desired_min_tx_interval;
-               } else {
-                       session->detection_time = session->remote_min_rx_interval;
-               }
-       }
-       session->detection_time *= session->detect_multi;
-
-       if (session->session_state == BFD_STATE_ADMIN_DOWN) {
-               DEBUG("Discarding BFD packet (admin down)");
-               return 0;
-       }
-
-       if (bfd->state == BFD_STATE_ADMIN_DOWN) {
-               if (bfd->state != BFD_STATE_DOWN) {
-                       session->local_diag = BFD_NEIGHBOR_DOWN;
-               }
-
-               DEBUG("BFD %d State %s -> DOWN (admin down)",
-                     session->number, bfd_state[session->session_state]);
-               session->session_state = BFD_STATE_DOWN;
-               bfd_trigger(session);
-
-               bfd_set_desired_min_tx_interval(session, USEC);
-
-       } else {
-               switch (session->session_state) {
-               case BFD_STATE_DOWN:
-                       switch (bfd->state) {
-                       case BFD_STATE_DOWN:
-                               DEBUG("BFD %d State DOWN -> INIT (neighbor down)",
-                                     session->number);
-                               session->session_state = BFD_STATE_INIT;
-                               bfd_trigger(session);
-
-                               bfd_set_desired_min_tx_interval(session, USEC);
-                               break;
-
-                       case BFD_STATE_INIT:
-                               DEBUG("BFD %d State DOWN -> UP (neighbor INIT)",
-                                     session->number);
-                               session->session_state = BFD_STATE_UP;
-                               bfd_trigger(session);
-                               break;
-
-                       default: /* don't change anything */
-                               break;
-                       }
-                       break;
-
-               case BFD_STATE_INIT:
-                       switch (bfd->state) {
-                       case BFD_STATE_INIT:
-                       case BFD_STATE_UP:
-                               DEBUG("BFD %d State INIT -> UP",
-                                     session->number);
-                               session->session_state = BFD_STATE_UP;
-                               bfd_trigger(session);
-                               break;
-
-                       default: /* don't change anything */
-                               break;
-                       }
-                       break;
-
-               case BFD_STATE_UP:
-                       switch (bfd->state) {
-                       case BFD_STATE_DOWN:
-                               session->local_diag = BFD_NEIGHBOR_DOWN;
-
-                               DEBUG("BFD %d State UP -> DOWN (neighbor down)", session->number);
-                               session->session_state = BFD_STATE_DOWN;
-                               bfd_trigger(session);
-
-                               bfd_set_desired_min_tx_interval(session, USEC);
-                               break;
-
-                       default:
-                               break;
-                       }
-                       break;
-
-               default:
-                       DEBUG("Internal sanity check failed");
-                       return 0;
-               }
-       }
-
-       /*
-        *      Check if demand mode should be active (Section 6.6)
-        */
-       if (session->remote_demand_mode &&
-           (session->session_state == BFD_STATE_UP) &&
-           (session->remote_session_state == BFD_STATE_UP)) {
-               DEBUG("BFD %d demand mode UP / UP, stopping packets", session->number);
-               bfd_stop_control(session);
-       }
-
-       if (bfd->poll) {
-               bfd_poll_response(session);
-       }
-
-       /*
-        *      We've received the packet for the purpose of Section
-        *      6.8.4.
-        */
-       session->last_recv = fr_time();
-
-       /*
-        *      We've received a packet, but missed the previous one.
-        *      Warn about it.
-        */
-       if ((session->detect_multi >= 2) && (fr_time_gt(session->last_recv, session->next_recv))) {
-               fr_radius_packet_t packet;
-               request_t request;
-
-               bfd_request(session, &request, &packet);
-
-               trigger_exec(unlang_interpret_get_thread_default(), NULL, "server.bfd.warn", false, NULL);
-       }
-
-
-       if ((!session->remote_demand_mode) ||
-           (session->session_state != BFD_STATE_UP) ||
-           (session->remote_session_state != BFD_STATE_UP)) {
-               bfd_start_control(session);
-       }
-
-       if (session->server_cs) {
-               request_t *request;
-               fr_radius_packet_t *packet, *reply;
-
-               request = request_alloc_internal(session, NULL);
-               packet = fr_radius_packet_alloc(request, 0);
-               reply = fr_radius_packet_alloc(request, 0);
-
-               bfd_request(session, request, packet);
-
-               memset(reply, 0, sizeof(*reply));
-
-               request->reply = reply;
-               fr_socket_addr_swap(&request->reply->socket, &session->socket);
-
-               /*
-                *      FIXME: add my state, remote state as VPs?
-                */
-               if (fr_debug_lvl) {
-                       request->log.dst = talloc_zero(request, log_dst_t);
-                       request->log.dst->func = vlog_request;
-                       request->log.dst->uctx = &default_log;
-
-                       request->log.lvl = fr_debug_lvl;
-               }
-               request->component = NULL;
-               request->module = NULL;
-
-               DEBUG2("server %s {", cf_section_name2(unlang_call_current(request)));
-               if (unlang_interpret_push_section(request, session->unlang, RLM_MODULE_NOTFOUND, UNLANG_TOP_FRAME) < 0) {
-                       talloc_free(request);
-                       return 0;
-               }
-               unlang_interpret_synchronous(unlang_interpret_event_list(request), request);
-               DEBUG("}");
-
-               /*
-                *      FIXME: grab attributes from the reply
-                *      and cache them for use in the next request.
-                */
-               talloc_free(request);
-       }
-
-       return 1;
-}
-
-
-/*
- *     Requirements of 6.8.3
- *
- *     Changes to:
- *
- *             session->desired_min_tx_interval
- *             session->required_min_rx_interval
- *
- *     mean we start polling.
- */
-
-/*
- *     Check if an incoming request is "ok"
- *
- *     It takes packets, not requests.  It sees if the packet looks
- *     OK.  If so, it does a number of sanity checks on it.
- */
-static int bfd_socket_recv(rad_listen_t *listener)
-{
-       ssize_t         rcode;
-       bfd_socket_t    *sock = listener->data;
-       bfd_state_t     *session;
-       bfd_state_t     my_session;
-       struct sockaddr_storage src;
-       socklen_t       sizeof_src = sizeof(src);
-       bfd_packet_t    bfd;
-
-       rcode = recvfrom(listener->fd, &bfd, sizeof(bfd), 0,
-                        (struct sockaddr *)&src, &sizeof_src);
-       if (rcode < 0) {
-               ERROR("Failed receiving packet: %s", fr_syserror(errno));
-               return 0;
-       }
-
-       if (rcode < 24) {
-               DEBUG("BFD packet is too short (%d < 24)", (int) rcode);
-               return 0;
-       }
-
-       if (bfd.version != 1) {
-               DEBUG("BFD packet has wrong version (%d != 1)", bfd.version);
-               return 0;
-       }
-
-       if (bfd.length < 24) {
-               DEBUG("BFD packet has wrong length (%d < 24)", bfd.length);
-               return 0;
-       }
-
-       if (bfd.length > sizeof(bfd)) {
-               DEBUG("BFD packet has wrong length (%d > %zd)", bfd.length, sizeof(bfd));
-               return 0;
-       }
-
-       if (bfd.auth_present) {
-               if (bfd.length < 26) {
-                       DEBUG("BFD packet has wrong length (%d < 26)",
-                             bfd.length);
-                       return 0;
-               }
-
-               if (bfd.length < 24 + bfd.auth.basic.auth_len) {
-                       DEBUG("BFD packet is too short (%d < %d)",
-                             bfd.length, 24 + bfd.auth.basic.auth_len);
-                       return 0;
-
-               }
-
-               if (bfd.length != 24 + bfd.auth.basic.auth_len) {
-                       DEBUG("WARNING: What is the extra data?");
-               }
-
-       }
-
-       if (bfd.detect_multi == 0) {
-               DEBUG("BFD packet has detect_multi == 0");
-               return 0;
-       }
-
-       if (bfd.multipoint != 0) {
-               DEBUG("BFD packet has multi != 0");
-               return 0;
-       }
-
-       if (bfd.my_disc == 0) {
-               DEBUG("BFD packet has my_disc == 0");
-               return 0;
-       }
-
-       if ((bfd.your_disc == 0) &&
-           !((bfd.state == BFD_STATE_DOWN) ||
-             (bfd.state == BFD_STATE_ADMIN_DOWN))) {
-               DEBUG("BFD packet has invalid your-disc / state");
-               return 0;
-       }
-
-       /*
-        *      We SHOULD use "your_disc", but what the heck.
-        */
-       fr_ipaddr_from_sockaddr(&my_session.socket.inet.dst_ipaddr,
-                               &my_session.socket.inet.dst_port, &src, sizeof_src);
-
-       session = fr_rb_find(sock->session_tree, &my_session);
-       if (!session) {
-               DEBUG("BFD unknown peer");
-               return 0;
-       }
-
-       if (!event_list) {
-               uint8_t *p = (uint8_t *) &bfd;
-               size_t total = bfd.length;
-
-               /*
-                *      A child has had a problem.  Do some cleanups.
-                */
-               if (session->blocked) bfd_pthread_free(session);
-
-               /*
-                *      No event list, try to create a new one.
-                */
-               if (!session->el && !bfd_pthread_create(session)) {
-                       DEBUG("BFD %d - error trying to create child thread",
-                             session->number);
-                       return 0;
-               }
-
-               do {
-                       rcode = write(session->pipefd[1], p, total);
-                       if ((rcode < 0) && (errno == EINTR)) continue;
-
-                       if (rcode < 0) {
-                               session->blocked = true;
-                               return 0;
-                       }
-
-                       total -= rcode;
-                       p += rcode;
-               } while (total > 0);
-               return 0;
-       }
-
-       return bfd_process(session, &bfd);
-}
-
-static int bfd_parse_ip_port(CONF_SECTION *cs, fr_ipaddr_t *ipaddr, uint16_t *port)
-{
-       int rcode;
-
-       /*
-        *      Try IPv4 first
-        */
-       memset(ipaddr, 0, sizeof(*ipaddr));
-       ipaddr->addr.v4.s_addr = htonl(INADDR_NONE);
-       rcode = cf_pair_parse(NULL, cs, "ipaddr", FR_ITEM_POINTER(FR_TYPE_IPV4_ADDR, ipaddr), NULL, T_INVALID);
-       if (rcode < 0) return -1;
-
-       if (rcode == 0) { /* successfully parsed IPv4 */
-               ipaddr->af = AF_INET;
-
-       } else {        /* maybe IPv6? */
-               rcode = cf_pair_parse(NULL, cs, "ipv6addr", FR_ITEM_POINTER(FR_TYPE_IPV6_ADDR, ipaddr), NULL, T_INVALID);
-               if (rcode < 0) return -1;
-
-               if (rcode == 1) {
-                       cf_log_err(cf_section_to_item(cs),
-                                  "No address specified in section");
-                       return -1;
-               }
-               ipaddr->af = AF_INET6;
-       }
-
-       rcode = cf_pair_parse(NULL, cs, "port", FR_ITEM_POINTER(FR_TYPE_UINT16, port), "0", T_INVALID);
-       if (rcode < 0) return -1;
-
-       return 0;
-}
-
-/*
- *     @fixme: move some of this to parse
- */
-static int bfd_init_sessions(CONF_SECTION *cs, bfd_socket_t *sock, int sockfd)
-{
-       CONF_ITEM *ci;
-       CONF_SECTION *peer;
-       uint16_t port;
-       fr_ipaddr_t ipaddr;
-
-       for (ci=cf_item_next(cs, NULL);
-            ci != NULL;
-            ci=cf_item_next(cs, ci)) {
-               bfd_state_t *session, my_session;
-
-              if (!cf_item_is_section(ci)) continue;
-
-              peer = cf_item_to_section(ci);
-
-              if (strcmp(cf_section_name1(peer), "peer") != 0) continue;
-
-              if (bfd_parse_ip_port(peer, &ipaddr, &port) < 0) {
-                      return -1;
-              }
-
-              my_session.socket.inet.dst_ipaddr = ipaddr;
-              my_session.socket.inet.dst_port = port;
-              if (fr_rb_find(sock->session_tree, &my_session) != NULL) {
-                      cf_log_err(ci, "Peers must have unique IP addresses");
-                      return -1;
-              }
-
-              session = bfd_new_session(sock, sockfd, peer, &ipaddr, port);
-              if (!session) return -1;
-       }
-
-       return 0;
-}
-
-
-/*
- *     None of these functions are used.
- */
-static int bfd_socket_send(UNUSED rad_listen_t *listener, UNUSED request_t *request)
-{
-       fr_assert(0 == 1);
-       return 0;
-}
-
-
-static int bfd_socket_encode(UNUSED rad_listen_t *listener, UNUSED request_t *request)
-{
-       fr_assert(0 == 1);
-       return 0;
-}
-
-
-static int bfd_socket_decode(UNUSED rad_listen_t *listener, UNUSED request_t *request)
-{
-       fr_assert(0 == 1);
-       return 0;
-}
-
-static int8_t bfd_session_cmp(const void *one, const void *two)
-{
-       const bfd_state_t *a = one, *b = two;
-
-       return fr_ipaddr_cmp(&a->socket.inet.dst_ipaddr, &b->socket.inet.dst_ipaddr);
-}
-
-static fr_table_num_sorted_t const auth_types[] = {
-       { L("keyed-md5"),               BFD_AUTH_KEYED_MD5      },
-       { L("keyed-sha1"),              BFD_AUTH_KEYED_SHA1     },
-       { L("met-keyed-md5"),   BFD_AUTH_MET_KEYED_MD5  },
-       { L("met-keyed-sha1"),  BFD_AUTH_MET_KEYED_SHA1 },
-       { L("none"),            BFD_AUTH_RESERVED       },
-       { L("simple"),          BFD_AUTH_SIMPLE         }
-};
-static size_t auth_types_len = NUM_ELEMENTS(auth_types);
-
-static int bfd_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
-{
-       bfd_socket_t *sock = this->data;
-       char const *auth_type_str = NULL;
-       uint16_t listen_port;
-       fr_ipaddr_t ipaddr;
-
-       fr_assert(sock != NULL);
-
-       if (bfd_parse_ip_port(cs, &ipaddr, &listen_port) < 0) {
-               return -1;
-       }
-
-       sock->my_ipaddr = ipaddr;
-       sock->my_port = listen_port;
-
-       if (cf_pair_parse(sock, cs, "interface", FR_ITEM_POINTER(FR_TYPE_STRING, &sock->interface), NULL, T_INVALID) < 0) return -1;
-
-       if (cf_pair_parse(sock, cs, "min_receive_interval", FR_ITEM_POINTER(FR_TYPE_UINT32,
-                         &sock->min_rx_interval), "1000", T_BARE_WORD) < 0) return -1;
-       if (cf_pair_parse(sock, cs, "max_timeouts", FR_ITEM_POINTER(FR_TYPE_UINT32,
-                         &sock->max_timeouts), "3", T_BARE_WORD) < 0) return -1;
-       if (cf_pair_parse(sock, cs, "demand", FR_ITEM_POINTER(FR_TYPE_BOOL, &sock->demand),
-                         "no", T_DOUBLE_QUOTED_STRING) < 0) return -1;
-       if (cf_pair_parse(NULL, cs, "auth_type", FR_ITEM_POINTER(FR_TYPE_STRING, &auth_type_str),
-                         NULL, T_INVALID) < 0) return -1;
-
-       if (!this->server) {
-               char const *server;
-               if (cf_pair_parse(sock, cs, "server", FR_ITEM_POINTER(FR_TYPE_STRING, &server),
-                                 NULL, T_INVALID) < 0) return -1;
-               sock->server_cs = virtual_server_find(server);
-       } else {
-               sock->server_cs = this->server_cs;
-       }
-
-       if (sock->min_tx_interval < 100) sock->min_tx_interval = 100;
-       if (sock->min_tx_interval > 10000) sock->min_tx_interval = 10000;
-
-       if (sock->min_rx_interval < 100) sock->min_rx_interval = 100;
-       if (sock->min_rx_interval > 10000) sock->min_rx_interval = 10000;
-
-       if (sock->max_timeouts == 0) sock->max_timeouts = 1;
-       if (sock->max_timeouts > 10) sock->max_timeouts = 10;
-
-       sock->auth_type = fr_table_value_by_str(auth_types, auth_type_str, BFD_AUTH_INVALID);
-       if (sock->auth_type == BFD_AUTH_INVALID) {
-               ERROR("Unknown auth_type '%s'", auth_type_str);
-               return -1;
-       }
-
-       if (sock->auth_type == BFD_AUTH_SIMPLE) {
-               ERROR("'simple' authentication is insecure and is not supported");
-               return -1;
-       }
-
-       if (sock->auth_type != BFD_AUTH_RESERVED) {
-               sock->secret_len = bfd_parse_secret(cs, sock->secret);
-
-               if (sock->secret_len == 0) {
-                       ERROR("Cannot have empty secret");
-                       return -1;
-               }
-
-               if (((sock->auth_type == BFD_AUTH_KEYED_MD5) ||
-                    (sock->auth_type == BFD_AUTH_MET_KEYED_MD5)) &&
-                   (sock->secret_len > 16)) {
-                       ERROR("Secret must be no more than 16 bytes when using MD5");
-                       return -1;
-               }
-       }
-
-       sock->session_tree = fr_rb_inline_talloc_alloc(sock, bfd_state_t, node, bfd_session_cmp, bfd_session_free);
-       if (!sock->session_tree) {
-               ERROR("Failed creating session tree!");
-               return -1;
-       }
-
-       /*
-        *      Find the sibling "bfd" section of the "listen" section.
-        */
-       sock->unlang = cf_section_find(cf_item_to_section(cf_parent(cs)), "bfd", NULL);
-
-       return 0;
-}
-
-static int bfd_socket_open(CONF_SECTION *cs, rad_listen_t *this)
-{
-       int rcode;
-       int ttl = 255;
-       uint16_t port;
-       bfd_socket_t *sock = this->data;
-
-       port = sock->my_port;
-
-       this->fd = fr_socket_server_udp(&sock->my_ipaddr, &port, "bfd-control", true);
-       if (this->fd < 0) {
-               char buffer[256];
-
-               this->print(this, buffer, sizeof(buffer));
-
-               ERROR("Failed opening %s: %s", buffer, fr_syserror(errno));
-               return -1;
-       }
-
-       if (setsockopt(this->fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) < 0) goto fail;
-
-       rad_suid_up();
-       rcode = fr_socket_bind(this->fd, &sock->my_ipaddr, &port, sock->interface);
-       rad_suid_down();
-       sock->my_port = port;
-
-       if (rcode < 0) {
-               char buffer[256];
-
-       fail:
-               close(this->fd);
-
-               this->print(this, buffer, sizeof(buffer));
-               ERROR("Failed binding to %s: %s", buffer, fr_syserror(errno));
-               return -1;
-       }
-
-       /*
-        *      Bootstrap the initial set of connections.
-        */
-       if (bfd_init_sessions(cs, sock, this->fd) < 0) {
-               return -1;
-       }
-
-       return 0;
-}
-
-static int bfd_socket_print(const rad_listen_t *this, char *buffer, size_t bufsize)
-{
-       size_t len;
-       bfd_socket_t *sock = this->data;
-
-#define FORWARD len = strlen(buffer); if (len >= (bufsize + 1)) return 0;buffer += len;bufsize -= len
-
-       strlcpy(buffer, "bfd address ", bufsize);
-       FORWARD;
-
-       if ((sock->my_ipaddr.af == AF_INET) &&
-           (sock->my_ipaddr.addr.v4.s_addr == htonl(INADDR_ANY))) {
-               strlcpy(buffer, "*", bufsize);
-       } else {
-               fr_inet_ntoh(&sock->my_ipaddr, buffer, bufsize);
-       }
-       FORWARD;
-
-       strlcpy(buffer, " port ", bufsize);
-       FORWARD;
-
-       snprintf(buffer, bufsize, "%d", sock->my_port);
-       FORWARD;
-
-       return 1;
-}
-
-/*
- *     If there's no "bfd" section, we can't bootstrap anything.
- */
-static int bfd_socket_bootstrap(CONF_SECTION *server_cs, UNUSED CONF_SECTION *listen_cs)
-{
-       CONF_SECTION *cs;
-
-       cs = cf_section_find(server_cs, "bfd", NULL);
-       if (!cs) {
-               cf_log_err(server_cs, "No 'bfd' sub-section found");
-               return -1;
-       }
-
-       return 0;
-}
-
-/*
- *     Ensure that the "bfd" section is compiled.
- */
-static int bfd_socket_compile(CONF_SECTION *server_cs, UNUSED CONF_SECTION *listen_cs)
-{
-       CONF_SECTION *cs;
-
-       cs = cf_section_find(server_cs, "bfd", NULL);
-       if (!cs) {
-               cf_log_err(server_cs, "No 'bfd' sub-section found");
-               return -1;
-       }
-
-       cf_log_debug(cs, "Loading bfd {...}");
-
-       if (unlang_compile(cs, MOD_AUTHORIZE, NULL, NULL) < 0) {
-               cf_log_err(cs, "Failed compiling 'bfd' section");
-               return -1;
-       }
-
-       return 0;
-}
-
-
-extern rad_protocol_t proto_bfd;
-rad_protocol_t proto_bfd = {
-       .magic          = MODULE_MAGIC_INIT,
-       .name           = "bfd",
-       .inst_size      = sizeof(bfd_socket_t),
-       .transports     = TRANSPORT_UDP,
-       .tls            = false,
-       .bootstrap      = bfd_socket_bootstrap,
-       .compile        = bfd_socket_compile,
-       .parse          = bfd_socket_parse,
-       .open           = bfd_socket_open,
-       .recv           = bfd_socket_recv,
-       .send           = bfd_socket_send,
-       .print          = bfd_socket_print,
-       .debug          = common_packet_debug,
-       .encode         = bfd_socket_encode,
-       .decode         = bfd_socket_decode
-};