From fee58fd8867ed181de76f4652824b6e5428c707f Mon Sep 17 00:00:00 2001 From: Katerina Kubecova Date: Tue, 23 Apr 2024 16:30:08 +0200 Subject: [PATCH] bfd: when all (not dummy) requests from a bfd session removed, timeout starts. If timeout ends without adding a request to that session, session is deleted. (Keeping empty session is done by having dummy request) --- nest/bfd.h | 1 + proto/bfd/bfd.c | 49 ++++++++++++++++++++++++++++++++++++++-------- proto/bfd/bfd.h | 10 +++++++++- proto/bfd/config.Y | 4 +++- 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/nest/bfd.h b/nest/bfd.h index d6819fd8..37fc6e6f 100644 --- a/nest/bfd.h +++ b/nest/bfd.h @@ -17,6 +17,7 @@ struct bfd_options { u32 min_rx_int; u32 min_tx_int; u32 idle_tx_int; + u32 session_delete_timeout; u8 multiplier; u8 passive; u8 passive_set; diff --git a/proto/bfd/bfd.c b/proto/bfd/bfd.c index 272d27c0..f1547574 100644 --- a/proto/bfd/bfd.c +++ b/proto/bfd/bfd.c @@ -159,6 +159,7 @@ static void bfd_session_set_min_tx(struct bfd_session *s, u32 val); static struct bfd_iface *bfd_get_iface(struct bfd_proto *p, ip_addr local, struct iface *iface); static void bfd_free_iface(struct bfd_iface *ifa); static inline void bfd_notify_kick(struct bfd_proto *p); +static void bfd_request_free(resource *r); /* @@ -693,6 +694,22 @@ bfd_request_notify(struct bfd_request *req, u8 state, u8 remote, u8 diag) req->hook(req); } +void +bfd_delete_dummy_request(timer *t) +{ + struct bfd_session *s = t->data; + struct bfd_request *req = s->dummy_request.req; + s->dummy_request.req = NULL; + if (list_length(&s->request_list) != 1) + { + log(L_WARN "BFD: Attempt to delete dummy request from session with % requests in total.", list_length(&s->request_list)); + return; + } + rfree(&req->r); +} + +static struct resclass bfd_request_class; + static int bfd_add_request(struct bfd_proto *p, struct bfd_request *req) { @@ -712,7 +729,18 @@ bfd_add_request(struct bfd_proto *p, struct bfd_request *req) u8 loc_state, rem_state, diag; if (!s) + { s = bfd_add_session(p, req->addr, req->local, req->iface, &req->opts); + // Create one dummy request in order to be able keep session some time after last real request will be removed + struct bfd_request *dummy_request = ralloc(p->p.pool, &bfd_request_class); + add_tail(&s->request_list, &dummy_request->n); + dummy_request->session = s; + + s->dummy_request.t = tm_new_init(p->p.pool, bfd_delete_dummy_request, s, 0, 0); + s->dummy_request.req = dummy_request; + s->dummy_request.p = p; + } else + tm_stop(s->dummy_request.t); rem_node(&req->n); add_tail(&s->request_list, &req->n); @@ -767,8 +795,6 @@ bfd_drop_requests(struct bfd_proto *p) HASH_WALK_END(p->session_hash_id); } -static struct resclass bfd_request_class; - struct bfd_request * bfd_request_session(pool *p, ip_addr addr, ip_addr local, struct iface *iface, struct iface *vrf, @@ -821,8 +847,20 @@ bfd_request_free(resource *r) /* Remove the session if there is no request for it. Skip that if inside notify hooks, will be handled by bfd_notify_hook() itself */ - if (s && EMPTY_LIST(s->request_list) && !s->notify_running) + if (s && EMPTY_LIST(s->request_list)) bfd_remove_session(s->ifa->bfd, s); + else if (s && list_length(&s->request_list) == 1) + { + struct bfd_request *f_req = SKIP_BACK(struct bfd_request, n, HEAD(s->request_list)); + if (f_req != s->dummy_request.req) + bug("Last bfd request is not the dummy one"); + if (!tm_active(s->dummy_request.t)) + { + struct bfd_config *conf = (struct bfd_config *) (s->dummy_request.p->p.cf); + btime time = (btime) conf->session_delete_timeout S; + tm_start(s->dummy_request.t, time); + } + } } static void @@ -986,14 +1024,9 @@ bfd_notify_hook(sock *sk, uint len UNUSED) diag = s->loc_diag; bfd_unlock_sessions(p); - s->notify_running = 1; WALK_LIST_DELSAFE(n, nn, s->request_list) bfd_request_notify(SKIP_BACK(struct bfd_request, n, n), loc_state, rem_state, diag); - s->notify_running = 0; - /* Remove the session if all requests were removed in notify hooks */ - if (EMPTY_LIST(s->request_list)) - bfd_remove_session(p, s); } return 0; diff --git a/proto/bfd/bfd.h b/proto/bfd/bfd.h index 847c6b14..07be733f 100644 --- a/proto/bfd/bfd.h +++ b/proto/bfd/bfd.h @@ -43,6 +43,7 @@ struct bfd_config list patt_list; /* List of iface configs (struct bfd_iface_config) */ list neigh_list; /* List of configured neighbors (struct bfd_neighbor) */ struct bfd_iface_config *multihop; /* Multihop pseudoiface config */ + u32 session_delete_timeout; u8 accept_ipv4; u8 accept_ipv6; u8 accept_direct; @@ -124,6 +125,13 @@ struct bfd_iface u8 changed; }; +struct dummy_request +{ + struct bfd_request *req; + struct bfd_proto *p; // to get actual timeout + timer *t; +}; + struct bfd_session { node n; @@ -165,8 +173,8 @@ struct bfd_session timer *hold_timer; /* Timer for session down detection time */ list request_list; /* List of client requests (struct bfd_request) */ + struct dummy_request dummy_request; /* Request and timer for delating removing session */ btime last_state_change; /* Time of last state change */ - u8 notify_running; /* 1 if notify hooks are running */ u8 rx_csn_known; /* Received crypto sequence number is known */ u32 rx_csn; /* Last received crypto sequence number */ diff --git a/proto/bfd/config.Y b/proto/bfd/config.Y index 1a7474b0..fec9116e 100644 --- a/proto/bfd/config.Y +++ b/proto/bfd/config.Y @@ -24,7 +24,7 @@ CF_DECLS CF_KEYWORDS(BFD, MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE, INTERFACE, MULTIHOP, NEIGHBOR, DEV, LOCAL, AUTHENTICATION, NONE, SIMPLE, METICULOUS, KEYED, MD5, SHA1, IPV4, IPV6, DIRECT, - STRICT, BIND) + STRICT, BIND, SESSION, DELETE, TIMEOUT) %type bfd_neigh_iface %type bfd_neigh_local @@ -42,6 +42,7 @@ bfd_proto_start: proto_start BFD init_list(&BFD_CFG->neigh_list); BFD_CFG->accept_ipv4 = BFD_CFG->accept_ipv6 = 1; BFD_CFG->accept_direct = BFD_CFG->accept_multihop = 1; + BFD_CFG->session_delete_timeout = 30; }; bfd_proto_item: @@ -51,6 +52,7 @@ bfd_proto_item: | MULTIHOP bfd_multihop | NEIGHBOR bfd_neighbor | STRICT BIND bool { BFD_CFG->strict_bind = $3; } + | SESSION DELETE TIMEOUT expr { BFD_CFG->session_delete_timeout = $4; } ; bfd_proto_opts: