&& (bs->timers.desired_min_tx != min_tx
|| bs->timers.required_min_rx != min_rx))
bfd_set_polling(bs);
+
+ /* Send updated information to data plane. */
+ bfd_dplane_update_session(bs);
}
void bfd_profile_remove(struct bfd_session *bs)
struct vrf *vrf = NULL;
int psock;
+ /* We are using data plane, we don't need software. */
+ if (bs->bdc)
+ return 0;
+
/*
* If the interface or VRF doesn't exist, then we must register
* the session but delay its start.
bs->vrf = vrf_lookup_by_id(VRF_DEFAULT);
assert(bs->vrf);
- if (bs->key.ifname[0]
- && CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) == 0)
- bs->ifp = ifp;
+ /* Assign interface pointer (if any). */
+ bs->ifp = ifp;
+
+ /* Attempt to use data plane. */
+ if (bglobal.bg_use_dplane && bfd_dplane_add_session(bs) == 0) {
+ control_notify_config(BCM_NOTIFY_CONFIG_ADD, bs);
+ return 0;
+ }
/* Sanity check: don't leak open sockets. */
if (bs->sock != -1) {
*/
void bfd_session_disable(struct bfd_session *bs)
{
+ /* We are using data plane, we don't need software. */
+ if (bs->bdc)
+ return;
+
/* Free up socket resources. */
if (bs->sock != -1) {
close(bs->sock);
bfd_session_disable(bs);
+ /* Remove session from data plane if any. */
+ bfd_dplane_delete_session(bs);
+
bfd_key_delete(bs->key);
bfd_id_delete(bs->discrs.my_discr);
SET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
/* Activate/update echo receive timeout timer. */
- bs_echo_timer_handler(bs);
+ if (bs->bdc == NULL)
+ bs_echo_timer_handler(bs);
} else {
/* Check if echo mode is already disabled. */
if (!CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
return;
UNSET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
- ptm_bfd_echo_stop(bs);
+
+ /* Deactivate timeout timer. */
+ if (bs->bdc == NULL)
+ ptm_bfd_echo_stop(bs);
}
}
SET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
+ /* Handle data plane shutdown case. */
+ if (bs->bdc) {
+ bs->ses_state = PTM_BFD_ADM_DOWN;
+ bfd_dplane_update_session(bs);
+ control_notify(bs, bs->ses_state);
+ return;
+ }
+
/* Disable all events. */
bfd_recvtimer_delete(bs);
bfd_echo_recvtimer_delete(bs);
UNSET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
+ /* Handle data plane shutdown case. */
+ if (bs->bdc) {
+ bs->ses_state = PTM_BFD_DOWN;
+ bfd_dplane_update_session(bs);
+ control_notify(bs, bs->ses_state);
+ return;
+ }
+
/* Change and notify state change. */
bs->ses_state = PTM_BFD_DOWN;
control_notify(bs, bs->ses_state);
typedef void (*bfd_dplane_expect_cb)(struct bfddp_message *msg, void *arg);
static void bfd_dplane_ctx_free(struct bfd_dplane_ctx *bdc);
+static int _bfd_dplane_add_session(struct bfd_dplane_ctx *bdc,
+ struct bfd_session *bs);
/*
* BFD data plane helper functions.
return 0;
}
+static void _bfd_session_register_dplane(struct hash_bucket *hb, void *arg)
+{
+ struct bfd_session *bs = hb->data;
+ struct bfd_dplane_ctx *bdc = arg;
+
+ if (bs->bdc != NULL)
+ return;
+
+ /* Disable software session. */
+ bfd_session_disable(bs);
+
+ /* Move session to data plane. */
+ _bfd_dplane_add_session(bdc, bs);
+}
+
static struct bfd_dplane_ctx *bfd_dplane_ctx_new(int sock)
{
struct bfd_dplane_ctx *bdc;
bdc->outbuf = stream_new(BFD_DPLANE_CLIENT_BUF_SIZE);
thread_add_read(master, bfd_dplane_read, bdc, sock, &bdc->inbufev);
+ /* Register all unattached sessions. */
+ bfd_key_iterate(_bfd_session_register_dplane, bdc);
+
return bdc;
}
return;
bs->bdc = NULL;
+
+ /* Fallback to software. */
+ bfd_session_enable(bs);
}
static void bfd_dplane_ctx_free(struct bfd_dplane_ctx *bdc)
XFREE(MTYPE_BFDD_DPLANE_CTX, bdc);
}
+static void _bfd_dplane_session_fill(const struct bfd_session *bs,
+ struct bfddp_message *msg)
+{
+ uint16_t msglen = sizeof(msg->header) + sizeof(msg->data.session);
+
+ /* Message header. */
+ msg->header.version = BFD_DP_VERSION;
+ msg->header.length = ntohs(msglen);
+ msg->header.type = ntohs(DP_ADD_SESSION);
+
+ /* Message payload. */
+ msg->data.session.dst = bs->key.peer;
+ msg->data.session.src = bs->key.local;
+ msg->data.session.detect_mult = bs->detect_mult;
+
+ if (bs->ifp) {
+ msg->data.session.ifindex = htonl(bs->ifp->ifindex);
+ strlcpy(msg->data.session.ifname, bs->ifp->name,
+ sizeof(msg->data.session.ifname));
+ }
+ if (bs->flags & BFD_SESS_FLAG_MH) {
+ msg->data.session.flags |= SESSION_MULTIHOP;
+ msg->data.session.ttl = bs->mh_ttl;
+ } else
+ msg->data.session.ttl = BFD_TTL_VAL;
+
+ if (bs->flags & BFD_SESS_FLAG_IPV6)
+ msg->data.session.flags |= SESSION_IPV6;
+ if (bs->flags & BFD_SESS_FLAG_ECHO)
+ msg->data.session.flags |= SESSION_ECHO;
+ if (bs->flags & BFD_SESS_FLAG_CBIT)
+ msg->data.session.flags |= SESSION_CBIT;
+ if (bs->flags & BFD_SESS_FLAG_PASSIVE)
+ msg->data.session.flags |= SESSION_PASSIVE;
+ if (bs->flags & BFD_SESS_FLAG_SHUTDOWN)
+ msg->data.session.flags |= SESSION_SHUTDOWN;
+
+ msg->data.session.flags = htonl(msg->data.session.flags);
+ msg->data.session.lid = htonl(bs->discrs.my_discr);
+ msg->data.session.min_tx = htonl(bs->timers.desired_min_tx);
+ msg->data.session.min_rx = htonl(bs->timers.required_min_rx);
+ msg->data.session.min_echo_rx = htonl(bs->timers.required_min_echo);
+}
+
+static int _bfd_dplane_add_session(struct bfd_dplane_ctx *bdc,
+ struct bfd_session *bs)
+{
+ int rv;
+
+ /* Associate session. */
+ bs->bdc = bdc;
+
+ /* Reset previous state. */
+ bs->remote_diag = 0;
+ bs->local_diag = 0;
+ bs->ses_state = PTM_BFD_DOWN;
+
+ /* Enqueue message to data plane client. */
+ rv = bfd_dplane_update_session(bs);
+ if (rv != 0)
+ bs->bdc = NULL;
+
+ return rv;
+}
+
/*
* Data plane listening socket.
*/
/* Observe shutdown events. */
hook_register(frr_fini, bfd_dplane_finish_late);
}
+
+int bfd_dplane_add_session(struct bfd_session *bs)
+{
+ struct bfd_dplane_ctx *bdc;
+
+ /* Select a data plane client to install session. */
+ TAILQ_FOREACH (bdc, &bglobal.bg_dplaneq, entry) {
+ if (_bfd_dplane_add_session(bdc, bs) == 0)
+ return 0;
+ }
+
+ return -1;
+}
+
+int bfd_dplane_update_session(const struct bfd_session *bs)
+{
+ struct bfddp_message msg = {};
+
+ if (bs->bdc == NULL)
+ return 0;
+
+ _bfd_dplane_session_fill(bs, &msg);
+
+ /* Enqueue message to data plane client. */
+ return bfd_dplane_enqueue(bs->bdc, &msg, ntohs(msg.header.length));
+}
+
+int bfd_dplane_delete_session(struct bfd_session *bs)
+{
+ struct bfddp_message msg = {};
+ int rv;
+
+ /* Not using data plane, just return success. */
+ if (bs->bdc == NULL)
+ return 0;
+
+ /* Fill most of the common fields. */
+ _bfd_dplane_session_fill(bs, &msg);
+
+ /* Change the message type. */
+ msg.header.type = ntohs(DP_DELETE_SESSION);
+
+ /* Enqueue message to data plane client. */
+ rv = bfd_dplane_enqueue(bs->bdc, &msg, ntohs(msg.header.length));
+
+ /* Remove association. */
+ bs->bdc = NULL;
+
+ return rv;
+}