};
static const CONF_PARSER peer_config[] = {
- { FR_CONF_OFFSET("min_transmit_interval", FR_TYPE_TIME_DELTA, proto_bfd_peer_t, desired_min_tx_interval ) },
- { FR_CONF_OFFSET("min_receive_interval", FR_TYPE_TIME_DELTA, proto_bfd_peer_t, required_min_rx_interval ) },
- { FR_CONF_OFFSET("max_timeouts", FR_TYPE_UINT32, proto_bfd_peer_t, detect_multi ) },
- { FR_CONF_OFFSET("demand", FR_TYPE_BOOL, proto_bfd_peer_t, demand_mode ) },
+ { FR_CONF_OFFSET("min_transmit_interval", FR_TYPE_TIME_DELTA, bfd_session_t, desired_min_tx_interval ) },
+ { FR_CONF_OFFSET("min_receive_interval", FR_TYPE_TIME_DELTA, bfd_session_t, required_min_rx_interval ) },
+ { FR_CONF_OFFSET("max_timeouts", FR_TYPE_UINT32, bfd_session_t, detect_multi ) },
+ { FR_CONF_OFFSET("demand", FR_TYPE_BOOL, bfd_session_t, demand_mode ) },
- { FR_CONF_OFFSET("auth_type", FR_TYPE_VOID, proto_bfd_peer_t, auth_type ),
+ { FR_CONF_OFFSET("auth_type", FR_TYPE_VOID, bfd_session_t, auth_type ),
.func = auth_type_parse },
- { FR_CONF_OFFSET("port", FR_TYPE_UINT16, proto_bfd_peer_t, port ) },
+ { FR_CONF_OFFSET("port", FR_TYPE_UINT16, bfd_session_t, port ) },
CONF_PARSER_TERMINATOR
};
while ((cs = cf_section_find_next(server, cs, "peer", CF_IDENT_ANY))) {
fr_client_t *c;
- proto_bfd_peer_t *peer;
+ bfd_session_t *peer;
if (cf_section_rules_push(cs, peer_config) < 0) return -1;
- c = client_afrom_cs(cs, cs, server, sizeof(proto_bfd_peer_t));
+ c = client_afrom_cs(cs, cs, server, sizeof(bfd_session_t));
if (!c) {
error:
cf_log_err(cs, "Failed to parse peer %s", cf_section_name2(cs));
goto error;
}
- peer = (proto_bfd_peer_t *) c;
+ peer = (bfd_session_t *) c;
FR_TIME_DELTA_BOUND_CHECK("peer.min_transmit_interval", peer->desired_min_tx_interval, >=, fr_time_delta_from_usec(32));
FR_TIME_DELTA_BOUND_CHECK("peer.min_transmit_interval", peer->desired_min_tx_interval, <=, fr_time_delta_from_sec(2));
*/
#include <freeradius-devel/io/master.h>
#include <freeradius-devel/bfd/bfd.h>
+#include "session.h"
/** An instance of a proto_radius listen section
*
fr_rb_tree_t *peers;
} proto_bfd_t;
-
-typedef struct {
- fr_client_t client; //!< might as well re-use this, others need it
-
- uint16_t port; //!< peer port where packets are sent to
-
- /*
- * Peers are defined globally to a virtual server. Each
- * peer can only have one session associated with it.
- */
- void *inst; //!< proto_bfd_udp instance using this session
- fr_listen_t *listen; //!< associated listener
-
- int sockfd; //!< cached for laziness
- fr_event_list_t *el; //!< event list
- fr_network_t *nr; //!< network side of things
-
- struct sockaddr_storage remote_sockaddr; //!< cached for laziness
- socklen_t remote_salen;
-
- struct sockaddr_storage local_sockaddr; //!< cached for laziness
- socklen_t local_salen;
-
- /*
- * Internal state management
- */
- fr_event_timer_t const *ev_timeout; //!< when we time out for not receiving a packet
- fr_event_timer_t const *ev_packet; //!< for when we next send a packet
- fr_time_t last_recv; //!< last received packet
- fr_time_t next_recv; //!< when we next expect to receive a packet
- fr_time_t last_sent; //!< the last time we sent a packet
-
- bfd_session_state_t session_state; //!< our view of the session state
- bfd_session_state_t remote_session_state; //!< their view of the session state
-
- /*
- * BFD state machine, and fields we use to manage it.
- *
- * The public names in the configuration files are what makes sense.
- *
- * The names here are the names from the protocol, so that we can be sure the state machine is
- * implemented correctly.
- */
- uint32_t local_disc; //!< our session ID, which is unique to this session
- uint32_t remote_disc; //!< their session ID
-
- bfd_diag_t local_diag; //!< diagnostics for errors
-
- uint32_t detect_multi;
-
- fr_time_delta_t desired_min_tx_interval; //!< intervals between transmits
- fr_time_delta_t required_min_rx_interval; //!< intervals between receives
-
- fr_time_delta_t remote_min_rx_interval; //!< their min_rx_interval
-
- fr_time_delta_t my_min_echo_rx_interval; //!< what we send for echo_rx_interval
-
- fr_time_delta_t next_min_tx_interval; //!< how to update this when we're polling
-
-
- bool demand_mode; //!< demand is "once session is up, stop sending packets"
- bool remote_demand_mode; //!< their demand mode
-
- bool doing_poll;
-
- /*
- * Authentication configuration and states.
- */
- bool auth_seq_known; //!< do we know the authentication sequence number?
-
- bfd_auth_type_t auth_type; //!< what kind of authentication is used
-
- uint32_t recv_auth_seq; //!< their auth_seq number
- uint32_t xmit_auth_seq; //!< our auth_seq number
-
- size_t secret_len; //!< doesn't change while we're running
-
- fr_time_delta_t detection_time; //!< used to set ev_timeout
- int detection_timeouts; //!< too many timeouts means !auth_seq_known
-
- bool passive; //!< active or passive role from RFC 5880 - unused
-} proto_bfd_peer_t;
#include <freeradius-devel/util/sha1.h>
#include <freeradius-devel/util/socket.h>
#include <freeradius-devel/util/time.h>
-#include <freeradius-devel/server/trigger.h>
#include "session.h"
-static void bfd_sign(proto_bfd_peer_t *session, bfd_packet_t *bfd);
-static int bfd_authenticate(proto_bfd_peer_t *session, bfd_packet_t *bfd);
-static void bfd_control_packet_init(proto_bfd_peer_t *session, bfd_packet_t *bfd);
+static void bfd_sign(bfd_session_t *session, bfd_packet_t *bfd);
+static int bfd_authenticate(bfd_session_t *session, bfd_packet_t *bfd);
+static void bfd_control_packet_init(bfd_session_t *session, bfd_packet_t *bfd);
-static void bfd_set_desired_min_tx_interval(proto_bfd_peer_t *session, fr_time_delta_t value);
+static void bfd_set_desired_min_tx_interval(bfd_session_t *session, fr_time_delta_t value);
-static void bfd_start_packets(proto_bfd_peer_t *session);
-static void bfd_start_control(proto_bfd_peer_t *session);
-static int bfd_stop_control(proto_bfd_peer_t *session);
-static void bfd_set_timeout(proto_bfd_peer_t *session, fr_time_t when);
+static void bfd_start_packets(bfd_session_t *session);
+static void bfd_start_control(bfd_session_t *session);
+static int bfd_stop_control(bfd_session_t *session);
+static void bfd_set_timeout(bfd_session_t *session, fr_time_t when);
/*
* we will need to define some other kind of fake packet to send to the process
* module.
*/
-static void bfd_trigger(proto_bfd_peer_t *session)
+static void bfd_trigger(bfd_session_t *session, UNUSED bfd_state_change_t change)
{
// fr_radius_packet_t packet;
// request_t *request = request_local_alloc_external(session, NULL);
/*
* Stop polling for packets.
*/
-static int bfd_stop_poll(proto_bfd_peer_t *session)
+static int bfd_stop_poll(bfd_session_t *session)
{
if (!session->doing_poll) return 0;
* 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(proto_bfd_peer_t *session)
+static void bfd_poll_response(bfd_session_t *session)
{
bfd_packet_t bfd;
/*
* Implement the requirements of RFC 5880 Section 6.8.6.
*/
-int bfd_session_process(proto_bfd_peer_t *session, bfd_packet_t *bfd)
+bfd_state_change_t bfd_session_process(bfd_session_t *session, bfd_packet_t *bfd)
{
- bool state_change = false;
+ bfd_state_change_t state_change = BFD_STATE_CHANGE_NONE;
/*
*
if ((bfd->your_disc != 0) && (bfd->your_disc != session->local_disc)) {
DEBUG("BFD %s packet has unexpected Your-Discriminator (got %08x, expected %08x",
session->client.shortname, bfd->your_disc, session->local_disc);
- return 0;
+ return BFD_STATE_CHANGE_INVALID;
}
/*
if (bfd->auth_present &&
(session->auth_type == BFD_AUTH_RESERVED)) {
DEBUG("BFD %s packet asked to authenticate an unauthenticated session.", session->client.shortname);
- return 0;
+ return BFD_STATE_CHANGE_INVALID;
}
/*
if (!bfd->auth_present &&
(session->auth_type != BFD_AUTH_RESERVED)) {
DEBUG("BFD %s packet failed to authenticate an authenticated session.", session->client.shortname);
- return 0;
+ return BFD_STATE_CHANGE_INVALID;
}
/*
*/
if (bfd->auth_present && !bfd_authenticate(session, bfd)) {
DEBUG("BFD %s authentication failed", session->client.shortname);
- return 0;
+ return BFD_STATE_CHANGE_INVALID;
}
DEBUG("BFD %s processing packet", session->client.shortname);
*/
if (session->session_state == BFD_STATE_ADMIN_DOWN) {
DEBUG("Discarding BFD packet (admin down)");
- return 0;
+ return BFD_STATE_CHANGE_ADMIN_DOWN;
}
/*
fr_bfd_packet_names[session->session_state],
fr_bfd_packet_names[bfd->state]);
session->session_state = BFD_STATE_DOWN;
- bfd_trigger(session);
- state_change = true;
+ state_change = BFD_STATE_CHANGE_PEER_DOWN;
}
} else {
DEBUG("BFD %s State DOWN -> INIT (neighbor DOWN)",
session->client.shortname);
session->session_state = BFD_STATE_INIT;
- bfd_trigger(session);
- state_change = true;
+ state_change = BFD_STATE_CHANGE_INIT;
break;
case BFD_STATE_INIT:
DEBUG("BFD %s State DOWN -> UP (neighbor INIT)",
session->client.shortname);
session->session_state = BFD_STATE_UP;
- bfd_trigger(session);
- state_change = true;
+ state_change = BFD_STATE_CHANGE_UP;
break;
default: /* don't change anything */
DEBUG("BFD %s State INIT -> UP",
session->client.shortname);
session->session_state = BFD_STATE_UP;
- bfd_trigger(session);
- state_change = true;
+ state_change = BFD_STATE_CHANGE_UP;
break;
default: /* don't change anything */
default:
DEBUG("Internal sanity check failed");
- return 0;
+ return BFD_STATE_CHANGE_INVALID;
}
}
/*
* Verify and/or calculate passwords
*/
-static void bfd_calc_simple(proto_bfd_peer_t *session, bfd_packet_t *bfd)
+static void bfd_calc_simple(bfd_session_t *session, bfd_packet_t *bfd)
{
bfd_auth_simple_t *simple = &bfd->auth.password;
simple->auth_len = session->secret_len;
}
-static void bfd_auth_simple(proto_bfd_peer_t *session, bfd_packet_t *bfd)
+static void bfd_auth_simple(bfd_session_t *session, bfd_packet_t *bfd)
{
bfd_auth_simple_t *simple = &bfd->auth.password;
/*
* Verify and/or calculate auth-type digests.
*/
-static void bfd_calc_md5(proto_bfd_peer_t *session, bfd_packet_t *bfd)
+static void bfd_calc_md5(bfd_session_t *session, bfd_packet_t *bfd)
{
bfd_auth_md5_t *md5 = &bfd->auth.md5;
fr_md5_calc(md5->digest,(const uint8_t *) bfd, bfd->length);
}
-static void bfd_auth_md5(proto_bfd_peer_t *session, bfd_packet_t *bfd)
+static void bfd_auth_md5(bfd_session_t *session, bfd_packet_t *bfd)
{
bfd_auth_md5_t *md5 = &bfd->auth.md5;
bfd_calc_md5(session, bfd);
}
-static void bfd_calc_sha1(proto_bfd_peer_t *session, bfd_packet_t *bfd)
+static void bfd_calc_sha1(bfd_session_t *session, bfd_packet_t *bfd)
{
fr_sha1_ctx ctx;
bfd_auth_sha1_t *sha1 = &bfd->auth.sha1;
fr_sha1_final(sha1->digest, &ctx);
}
-static void bfd_auth_sha1(proto_bfd_peer_t *session, bfd_packet_t *bfd)
+static void bfd_auth_sha1(bfd_session_t *session, bfd_packet_t *bfd)
{
bfd_auth_sha1_t *sha1 = &bfd->auth.sha1;
bfd_calc_sha1(session, bfd);
}
-static int bfd_verify_sequence(proto_bfd_peer_t *session, uint32_t sequence_no,
+static int bfd_verify_sequence(bfd_session_t *session, uint32_t sequence_no,
int keyed)
{
uint32_t start, stop;
return 1;
}
-static int bfd_verify_simple(proto_bfd_peer_t *session, bfd_packet_t *bfd)
+static int bfd_verify_simple(bfd_session_t *session, bfd_packet_t *bfd)
{
bfd_auth_simple_t *simple = &bfd->auth.password;
return (fr_digest_cmp((uint8_t const *) session->client.secret, simple->password, session->secret_len) == 0);
}
-static int bfd_verify_md5(proto_bfd_peer_t *session, bfd_packet_t *bfd)
+static int bfd_verify_md5(bfd_session_t *session, bfd_packet_t *bfd)
{
int rcode;
bfd_auth_md5_t *md5 = &bfd->auth.md5;
return 1;
}
-static int bfd_verify_sha1(proto_bfd_peer_t *session, bfd_packet_t *bfd)
+static int bfd_verify_sha1(bfd_session_t *session, bfd_packet_t *bfd)
{
int rcode;
bfd_auth_sha1_t *sha1 = &bfd->auth.sha1;
return 1;
}
-static int bfd_authenticate(proto_bfd_peer_t *session, bfd_packet_t *bfd)
+static int bfd_authenticate(bfd_session_t *session, bfd_packet_t *bfd)
{
switch (bfd->auth.basic.auth_type) {
case BFD_AUTH_RESERVED:
}
-static void bfd_sign(proto_bfd_peer_t *session, bfd_packet_t *bfd)
+static void bfd_sign(bfd_session_t *session, bfd_packet_t *bfd)
{
if (bfd->auth_present) {
switch (session->auth_type) {
/*
* Initialize a control packet.
*/
-static void bfd_control_packet_init(proto_bfd_peer_t *session, bfd_packet_t *bfd)
+static void bfd_control_packet_init(bfd_session_t *session, bfd_packet_t *bfd)
{
memset(bfd, 0, sizeof(*bfd));
*/
static void bfd_send_packet(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *ctx)
{
- proto_bfd_peer_t *session = ctx;
+ bfd_session_t *session = ctx;
bfd_packet_t bfd;
bfd_control_packet_init(session, &bfd);
DEBUG("BFD %s sending packet state %s",
session->client.shortname, fr_bfd_packet_names[session->session_state]);
+ session->last_sent = fr_time();
+
if (sendfromto(session->sockfd, &bfd, bfd.length, 0, 0,
(struct sockaddr *) &session->local_sockaddr, session->local_salen,
(struct sockaddr *) &session->remote_sockaddr, session->remote_salen) < 0) {
/*
* Start sending packets.
*/
-static void bfd_start_packets(proto_bfd_peer_t *session)
+static void bfd_start_packets(bfd_session_t *session)
{
uint64_t interval, base;
uint64_t jitter;
*/
fr_event_timer_delete(&session->ev_packet);
- session->last_sent = fr_time();
-
if (fr_time_delta_cmp(session->desired_min_tx_interval, session->remote_min_rx_interval) >= 0) {
interval = fr_time_delta_unwrap(session->desired_min_tx_interval);
} else {
/*
* Start polling for the peer.
*/
-static void bfd_start_poll(proto_bfd_peer_t *session)
+static void bfd_start_poll(bfd_session_t *session)
{
if (session->doing_poll) return;
/*
* Timer functions
*/
-static void bfd_set_desired_min_tx_interval(proto_bfd_peer_t *session, fr_time_delta_t value)
+static void bfd_set_desired_min_tx_interval(bfd_session_t *session, fr_time_delta_t value)
{
/*
* Increasing the value: don't change it if we're already
*/
static void bfd_detection_timeout(UNUSED fr_event_list_t *el, fr_time_t now, void *ctx)
{
- proto_bfd_peer_t *session = ctx;
+ bfd_session_t *session = ctx;
DEBUG("BFD %s Timeout state %s ****** ", session->client.shortname,
fr_bfd_packet_names[session->session_state]);
DEBUG("BFD %s State <timeout> -> DOWN (control expired)", session->client.shortname);
session->session_state = BFD_STATE_DOWN;
session->local_diag = BFD_CTRL_EXPIRED;
- bfd_trigger(session); /* @todo - send timeout state change to unlang */
+ bfd_trigger(session, BFD_STATE_CHANGE_TIMEOUT_DOWN);
bfd_set_desired_min_tx_interval(session, fr_time_delta_from_sec(1));
}
* Set the timeout for when we've lost enough packets to be
* worried.
*/
-static void bfd_set_timeout(proto_bfd_peer_t *session, fr_time_t when)
+static void bfd_set_timeout(bfd_session_t *session, fr_time_t when)
{
fr_time_t timeout;
uint64_t delay;
/*
* Start / stop control packets.
*/
-static int bfd_stop_control(proto_bfd_peer_t *session)
+static int bfd_stop_control(bfd_session_t *session)
{
fr_event_timer_delete(&session->ev_timeout);
fr_event_timer_delete(&session->ev_packet);
return 1;
}
-static void bfd_start_control(proto_bfd_peer_t *session)
+static void bfd_start_control(bfd_session_t *session)
{
/*
* @todo - change our discriminator?
}
-int bfd_session_init(proto_bfd_peer_t *session)
+int bfd_session_init(bfd_session_t *session)
{
session->session_state = BFD_STATE_DOWN;
session->local_disc = fr_rand();
return 0;
}
-void bfd_session_start(proto_bfd_peer_t *session)
+void bfd_session_start(bfd_session_t *session)
{
DEBUG("Starting BFD for %s", session->client.shortname);
*/
#include "proto_bfd.h"
-int bfd_session_init(proto_bfd_peer_t *session);
+typedef struct {
+ fr_client_t client; //!< might as well re-use this, others need it
-void bfd_session_start(proto_bfd_peer_t *session);
+ uint16_t port; //!< peer port where packets are sent to
-int bfd_session_process(proto_bfd_peer_t *session, bfd_packet_t *bfd);
+ /*
+ * Peers are defined globally to a virtual server. Each
+ * peer can only have one session associated with it.
+ */
+ void *inst; //!< proto_bfd_udp instance using this session
+ fr_listen_t *listen; //!< associated listener
+
+ int sockfd; //!< cached for laziness
+ fr_event_list_t *el; //!< event list
+ fr_network_t *nr; //!< network side of things
+
+ struct sockaddr_storage remote_sockaddr; //!< cached for laziness
+ socklen_t remote_salen;
+
+ struct sockaddr_storage local_sockaddr; //!< cached for laziness
+ socklen_t local_salen;
+
+ /*
+ * Internal state management
+ */
+ fr_event_timer_t const *ev_timeout; //!< when we time out for not receiving a packet
+ fr_event_timer_t const *ev_packet; //!< for when we next send a packet
+ fr_time_t last_recv; //!< last received packet
+ fr_time_t next_recv; //!< when we next expect to receive a packet
+ fr_time_t last_sent; //!< the last time we sent a packet
+
+ bfd_session_state_t session_state; //!< our view of the session state
+ bfd_session_state_t remote_session_state; //!< their view of the session state
+
+ /*
+ * BFD state machine, and fields we use to manage it.
+ *
+ * The public names in the configuration files are what makes sense.
+ *
+ * The names here are the names from the protocol, so that we can be sure the state machine is
+ * implemented correctly.
+ */
+ uint32_t local_disc; //!< our session ID, which is unique to this session
+ uint32_t remote_disc; //!< their session ID
+
+ bfd_diag_t local_diag; //!< diagnostics for errors
+
+ uint32_t detect_multi;
+
+ fr_time_delta_t desired_min_tx_interval; //!< intervals between transmits
+ fr_time_delta_t required_min_rx_interval; //!< intervals between receives
+
+ fr_time_delta_t remote_min_rx_interval; //!< their min_rx_interval
+
+ fr_time_delta_t my_min_echo_rx_interval; //!< what we send for echo_rx_interval
+
+ fr_time_delta_t next_min_tx_interval; //!< how to update this when we're polling
+
+
+ bool demand_mode; //!< demand is "once session is up, stop sending packets"
+ bool remote_demand_mode; //!< their demand mode
+
+ bool doing_poll;
+
+ /*
+ * Authentication configuration and states.
+ */
+ bool auth_seq_known; //!< do we know the authentication sequence number?
+
+ bfd_auth_type_t auth_type; //!< what kind of authentication is used
+
+ uint32_t recv_auth_seq; //!< their auth_seq number
+ uint32_t xmit_auth_seq; //!< our auth_seq number
+
+ size_t secret_len; //!< doesn't change while we're running
+
+ fr_time_delta_t detection_time; //!< used to set ev_timeout
+ int detection_timeouts; //!< too many timeouts means !auth_seq_known
+
+ bool passive; //!< active or passive role from RFC 5880 - unused
+} bfd_session_t;
+
+/*
+ * Common APIs between the listen and process routines. There's no real reason for these definitions to
+ * be here, other than it's an easy place to put common code and definitions.
+ *
+ * Unlike other protocols, BFD has no association between request and reply. Instead, there are two
+ * independent streams of packets. One is sent by us to the peer, and the other is sent by the peer to
+ * us.
+ *
+ * In addition, there are state changes associated with BFD packets.
+ */
+typedef enum {
+ BFD_WRAPPER_INVALID = 0,
+ BFD_WRAPPER_RECV_PACKET,
+ BFD_WRAPPER_SEND_PACKET,
+ BFD_WRAPPER_STATE_CHANGE,
+} bfd_wrapper_type_t;
+
+typedef enum {
+ BFD_STATE_CHANGE_INVALID = 0,
+ BFD_STATE_CHANGE_NONE, //!< no state change
+ BFD_STATE_CHANGE_ADMIN_DOWN, //!< we are admin-down
+ BFD_STATE_CHANGE_PEER_DOWN, //!< the peer has signalled us that he's Down.
+ BFD_STATE_CHANGE_INIT, //!< we are going to INIT
+ BFD_STATE_CHANGE_UP, //!< we are going to UP
+ BFD_STATE_CHANGE_TIMEOUT_DOWN,
+} bfd_state_change_t;
+
+typedef struct {
+ uint32_t type;
+ union {
+ bfd_packet_t packet;
+ struct {
+ bfd_state_change_t state_change;
+ bfd_session_t *peer;
+ };
+ };
+} bfd_wrapper_t;
+
+int bfd_session_init(bfd_session_t *session);
+
+void bfd_session_start(bfd_session_t *session);
+
+bfd_state_change_t bfd_session_process(bfd_session_t *session, bfd_packet_t *bfd);