diff --git a/bfdd/bfd.c b/bfdd/bfd.c index 4b46a0e1d5..5249890043 100644 --- a/bfdd/bfd.c +++ b/bfdd/bfd.c @@ -216,6 +216,9 @@ void bfd_session_apply(struct bfd_session *bs) && (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) @@ -293,6 +296,10 @@ int bfd_session_enable(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. @@ -332,9 +339,14 @@ int bfd_session_enable(struct bfd_session *bs) 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) { @@ -383,6 +395,10 @@ int bfd_session_enable(struct bfd_session *bs) */ 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); @@ -804,6 +820,9 @@ void bfd_session_free(struct bfd_session *bs) 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); @@ -1267,14 +1286,18 @@ void bfd_set_echo(struct bfd_session *bs, bool echo) 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); } } @@ -1299,6 +1322,14 @@ void bfd_set_shutdown(struct bfd_session *bs, bool shutdown) 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); @@ -1319,6 +1350,14 @@ void bfd_set_shutdown(struct bfd_session *bs, bool shutdown) 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); diff --git a/bfdd/bfd.h b/bfdd/bfd.h index 2a7629b333..8b06ccdd0d 100644 --- a/bfdd/bfd.h +++ b/bfdd/bfd.h @@ -767,4 +767,31 @@ int ptm_bfd_notify(struct bfd_session *bs, uint8_t notify_state); */ void bfd_dplane_init(const struct sockaddr *sa, socklen_t salen); +/** + * Attempts to delegate the BFD session liveness detection to hardware. + * + * \param bs the BFD session data structure. + * + * \returns + * `0` on success and BFD daemon should do nothing or `-1` on failure + * and we should fallback to software implementation. + */ +int bfd_dplane_add_session(struct bfd_session *bs); + +/** + * Send new session settings to data plane. + * + * \param bs the BFD session to update. + */ +int bfd_dplane_update_session(const struct bfd_session *bs); + +/** + * Deletes session from data plane. + * + * \param bs the BFD session to delete. + * + * \returns `0` on success otherwise `-1`. + */ +int bfd_dplane_delete_session(struct bfd_session *bs); + #endif /* _BFD_H_ */ diff --git a/bfdd/dplane.c b/bfdd/dplane.c index 452ee3adf0..5785c349c1 100644 --- a/bfdd/dplane.c +++ b/bfdd/dplane.c @@ -90,6 +90,8 @@ struct bfd_dplane_ctx { 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. @@ -580,6 +582,21 @@ static int bfd_dplane_read(struct thread *t) 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; @@ -593,6 +610,9 @@ static struct bfd_dplane_ctx *bfd_dplane_ctx_new(int sock) 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; } @@ -605,6 +625,9 @@ static void _bfd_session_unregister_dplane(struct hash_bucket *hb, void *arg) return; bs->bdc = NULL; + + /* Fallback to software. */ + bfd_session_enable(bs); } static void bfd_dplane_ctx_free(struct bfd_dplane_ctx *bdc) @@ -629,6 +652,71 @@ 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. */ @@ -738,3 +826,53 @@ void bfd_dplane_init(const struct sockaddr *sa, socklen_t salen) /* 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; +}