From 910adaa08bbd416288797505399ab47f990817e6 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Sun, 10 Jan 2021 15:29:02 +0100 Subject: [PATCH] BFD: Dispatch sessions also by interface index Direct BFD sessions needs to be dispatched not only by IP addresses, but also by interfaces, in order to avoid collisions between neighbors with the same IPv6 link-local addresses. Extend BFD session hash_ip key by interface index to handle that. Use 0 for multihop sessions. Thanks to Sebastian Hahn for the original patch. --- proto/bfd/bfd.c | 28 +++++++++++++++------------- proto/bfd/bfd.h | 3 ++- proto/bfd/packets.c | 3 ++- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/proto/bfd/bfd.c b/proto/bfd/bfd.c index 8dfca790..dac184c5 100644 --- a/proto/bfd/bfd.c +++ b/proto/bfd/bfd.c @@ -27,13 +27,13 @@ * related to the session and two timers (TX timer for periodic packets and hold * timer for session timeout). These sessions are allocated from @session_slab * and are accessible by two hash tables, @session_hash_id (by session ID) and - * @session_hash_ip (by IP addresses of neighbors). Slab and both hashes are in - * the main protocol structure &bfd_proto. The protocol logic related to BFD - * sessions is implemented in internal functions bfd_session_*(), which are - * expected to be called from the context of BFD thread, and external functions - * bfd_add_session(), bfd_remove_session() and bfd_reconfigure_session(), which - * form an interface to the BFD core for the rest and are expected to be called - * from the context of main thread. + * @session_hash_ip (by IP addresses of neighbors and associated interfaces). + * Slab and both hashes are in the main protocol structure &bfd_proto. The + * protocol logic related to BFD sessions is implemented in internal functions + * bfd_session_*(), which are expected to be called from the context of BFD + * thread, and external functions bfd_add_session(), bfd_remove_session() and + * bfd_reconfigure_session(), which form an interface to the BFD core for the + * rest and are expected to be called from the context of main thread. * * Each BFD session has an associated BFD interface, represented by structure * &bfd_iface. A BFD interface contains a socket used for TX (the one for RX is @@ -108,10 +108,10 @@ #define HASH_ID_EQ(a,b) a == b #define HASH_ID_FN(k) k -#define HASH_IP_KEY(n) n->addr +#define HASH_IP_KEY(n) n->addr, n->ifindex #define HASH_IP_NEXT(n) n->next_ip -#define HASH_IP_EQ(a,b) ipa_equal(a,b) -#define HASH_IP_FN(k) ipa_hash(k) +#define HASH_IP_EQ(a1,n1,a2,n2) ipa_equal(a1, a2) && n1 == n2 +#define HASH_IP_FN(a,n) ipa_hash(a) ^ u32_hash(n) static list bfd_proto_list; static list bfd_wait_list; @@ -385,9 +385,9 @@ bfd_find_session_by_id(struct bfd_proto *p, u32 id) } struct bfd_session * -bfd_find_session_by_addr(struct bfd_proto *p, ip_addr addr) +bfd_find_session_by_addr(struct bfd_proto *p, ip_addr addr, uint ifindex) { - return HASH_FIND(p->session_hash_ip, HASH_IP, addr); + return HASH_FIND(p->session_hash_ip, HASH_IP, addr, ifindex); } static void @@ -426,6 +426,7 @@ bfd_add_session(struct bfd_proto *p, ip_addr addr, ip_addr local, struct iface * struct bfd_session *s = sl_allocz(p->session_slab); s->addr = addr; s->ifa = ifa; + s->ifindex = iface ? iface->index : 0; s->loc_id = bfd_get_free_id(p); HASH_INSERT(p->session_hash_id, HASH_ID, s); @@ -658,7 +659,8 @@ bfd_add_request(struct bfd_proto *p, struct bfd_request *req) if (req->iface ? !cf->accept_direct : !cf->accept_multihop) return 0; - struct bfd_session *s = bfd_find_session_by_addr(p, req->addr); + uint ifindex = req->iface ? req->iface->index : 0; + struct bfd_session *s = bfd_find_session_by_addr(p, req->addr, ifindex); u8 state, diag; if (!s) diff --git a/proto/bfd/bfd.h b/proto/bfd/bfd.h index 83e2a991..91fdaa60 100644 --- a/proto/bfd/bfd.h +++ b/proto/bfd/bfd.h @@ -153,6 +153,7 @@ struct bfd_session u8 detect_mult; /* Announced detect_mult, local option */ u8 rem_detect_mult; /* Last received detect_mult */ + uint ifindex; /* Iface index, for hashing in bfd.session_hash_ip */ btime last_tx; /* Time of last sent periodic control packet */ btime last_rx; /* Time of last received valid control packet */ @@ -213,7 +214,7 @@ static inline void bfd_unlock_sessions(struct bfd_proto *p) { pthread_spin_unloc /* bfd.c */ struct bfd_session * bfd_find_session_by_id(struct bfd_proto *p, u32 id); -struct bfd_session * bfd_find_session_by_addr(struct bfd_proto *p, ip_addr addr); +struct bfd_session * bfd_find_session_by_addr(struct bfd_proto *p, ip_addr addr, uint ifindex); void bfd_session_process_ctl(struct bfd_session *s, u8 flags, u32 old_tx_int, u32 old_rx_int); void bfd_show_sessions(struct proto *P); diff --git a/proto/bfd/packets.c b/proto/bfd/packets.c index 703c6e28..7618e20f 100644 --- a/proto/bfd/packets.c +++ b/proto/bfd/packets.c @@ -366,7 +366,8 @@ bfd_rx_hook(sock *sk, uint len) if (ps > BFD_STATE_DOWN) DROP("invalid init state", ps); - s = bfd_find_session_by_addr(p, sk->faddr); + uint ifindex = (sk->sport == BFD_CONTROL_PORT) ? sk->lifindex : 0; + s = bfd_find_session_by_addr(p, sk->faddr, ifindex); /* FIXME: better session matching and message */ if (!s)