diff options
author | Rafael Zalamena <rzalamena@opensourcerouting.org> | 2020-08-18 18:06:48 +0200 |
---|---|---|
committer | Rafael Zalamena <rzalamena@opensourcerouting.org> | 2020-11-24 11:54:07 +0100 |
commit | efd04d60caa237de5c564355089c59794057ff1d (patch) | |
tree | cbb188f206626edacf5618a409799ebf7a2a08e6 /bfdd | |
parent | bfdd: disable sockets when using distributed BFD (diff) | |
download | frr-efd04d60caa237de5c564355089c59794057ff1d.tar.xz frr-efd04d60caa237de5c564355089c59794057ff1d.zip |
bfdd: integrate distributed BFD
Add hooks in the correct places so the BFD daemon uses the data plane
instead of the software packet sending implementation to monitor the
session.
This code also adds some handlers to support fallback to FRR BFD session
handling, however since this complicates the code it won't work at the
moment (the BFD sockets are disabled by default when using data plane).
Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
Diffstat (limited to 'bfdd')
-rw-r--r-- | bfdd/bfd.c | 49 | ||||
-rw-r--r-- | bfdd/bfd.h | 27 | ||||
-rw-r--r-- | bfdd/dplane.c | 138 |
3 files changed, 209 insertions, 5 deletions
diff --git a/bfdd/bfd.c b/bfdd/bfd.c index 4b46a0e1d..524989004 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 2a7629b33..8b06ccdd0 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 452ee3adf..5785c349c 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; +} |