diff options
41 files changed, 1153 insertions, 456 deletions
diff --git a/bfdd/bfd.c b/bfdd/bfd.c index f9e572db4d..e5432af553 100644 --- a/bfdd/bfd.c +++ b/bfdd/bfd.c @@ -550,25 +550,6 @@ int bfd_session_update_label(struct bfd_session *bs, const char *nlabel) static void _bfd_session_update(struct bfd_session *bs, struct bfd_peer_cfg *bpc) { - if (bpc->bpc_echo) { - /* Check if echo mode is already active. */ - if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) - goto skip_echo; - - SET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO); - - /* Activate/update echo receive timeout timer. */ - bs_echo_timer_handler(bs); - } else { - /* Check if echo mode is already disabled. */ - if (!CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) - goto skip_echo; - - UNSET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO); - ptm_bfd_echo_stop(bs); - } - -skip_echo: if (bpc->bpc_has_txinterval) bs->timers.desired_min_tx = bpc->bpc_txinterval * 1000; @@ -584,52 +565,18 @@ skip_echo: if (bpc->bpc_has_label) bfd_session_update_label(bs, bpc->bpc_label); - if (bpc->bpc_shutdown) { - /* Check if already shutdown. */ - if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN)) - return; - - SET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN); - - /* Disable all events. */ - bfd_recvtimer_delete(bs); - bfd_echo_recvtimer_delete(bs); - bfd_xmttimer_delete(bs); - bfd_echo_xmttimer_delete(bs); - - /* Change and notify state change. */ - bs->ses_state = PTM_BFD_ADM_DOWN; - control_notify(bs, bs->ses_state); - - /* Don't try to send packets with a disabled session. */ - if (bs->sock != -1) - ptm_bfd_snd(bs, 0); - } else { - /* Check if already working. */ - if (!CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN)) - return; - - UNSET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN); - - /* Change and notify state change. */ - bs->ses_state = PTM_BFD_DOWN; - control_notify(bs, bs->ses_state); - - /* Enable all timers. */ - bfd_recvtimer_update(bs); - bfd_xmttimer_update(bs, bs->xmt_TO); - } - if (bpc->bpc_cbit) { - if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CBIT)) - return; - + if (bpc->bpc_cbit) SET_FLAG(bs->flags, BFD_SESS_FLAG_CBIT); - } else { - if (!CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CBIT)) - return; - + else UNSET_FLAG(bs->flags, BFD_SESS_FLAG_CBIT); - } + + bfd_set_echo(bs, bpc->bpc_echo); + + /* + * Shutdown needs to be the last in order to avoid timers enable when + * the session is disabled. + */ + bfd_set_shutdown(bs, bpc->bpc_shutdown); } static int bfd_session_update(struct bfd_session *bs, struct bfd_peer_cfg *bpc) @@ -1100,6 +1047,66 @@ void bs_set_slow_timers(struct bfd_session *bs) bs->xmt_TO = BFD_DEF_SLOWTX; } +void bfd_set_echo(struct bfd_session *bs, bool echo) +{ + if (echo) { + /* Check if echo mode is already active. */ + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) + return; + + SET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO); + + /* Activate/update echo receive timeout timer. */ + 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); + } +} + +void bfd_set_shutdown(struct bfd_session *bs, bool shutdown) +{ + if (shutdown) { + /* Already shutdown. */ + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN)) + return; + + SET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN); + + /* Disable all events. */ + bfd_recvtimer_delete(bs); + bfd_echo_recvtimer_delete(bs); + bfd_xmttimer_delete(bs); + bfd_echo_xmttimer_delete(bs); + + /* Change and notify state change. */ + bs->ses_state = PTM_BFD_ADM_DOWN; + control_notify(bs, bs->ses_state); + + /* Don't try to send packets with a disabled session. */ + if (bs->sock != -1) + ptm_bfd_snd(bs, 0); + } else { + /* Already working. */ + if (!CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN)) + return; + + UNSET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN); + + /* Change and notify state change. */ + bs->ses_state = PTM_BFD_DOWN; + control_notify(bs, bs->ses_state); + + /* Enable all timers. */ + bfd_recvtimer_update(bs); + bfd_xmttimer_update(bs, bs->xmt_TO); + } +} + /* * Helper functions. */ diff --git a/bfdd/bfd.h b/bfdd/bfd.h index 93873eed94..5a81d80424 100644 --- a/bfdd/bfd.h +++ b/bfdd/bfd.h @@ -550,6 +550,22 @@ const struct bfd_session *bfd_session_next(const struct bfd_session *bs, bool mhop); void bfd_sessions_remove_manual(void); +/** + * Set the BFD session echo state. + * + * \param bs the BFD session. + * \param echo the echo operational state. + */ +void bfd_set_echo(struct bfd_session *bs, bool echo); + +/** + * Set the BFD session functional state. + * + * \param bs the BFD session. + * \param shutdown the operational value. + */ +void bfd_set_shutdown(struct bfd_session *bs, bool shutdown); + /* BFD hash data structures interface */ void bfd_initialize(void); void bfd_shutdown(void); diff --git a/bfdd/bfdd_nb_config.c b/bfdd/bfdd_nb_config.c index 9e136c3fc5..df8a644e78 100644 --- a/bfdd/bfdd_nb_config.c +++ b/bfdd/bfdd_nb_config.c @@ -367,42 +367,7 @@ int bfdd_bfd_sessions_single_hop_administrative_down_modify( } bs = nb_running_get_entry(args->dnode, NULL, true); - - if (!shutdown) { - if (!CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN)) - return NB_OK; - - UNSET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN); - - /* Change and notify state change. */ - bs->ses_state = PTM_BFD_DOWN; - control_notify(bs, bs->ses_state); - - /* Enable all timers. */ - bfd_recvtimer_update(bs); - bfd_xmttimer_update(bs, bs->xmt_TO); - if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) { - bfd_echo_recvtimer_update(bs); - bfd_echo_xmttimer_update(bs, bs->echo_xmt_TO); - } - } else { - if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN)) - return NB_OK; - - SET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN); - - /* Disable all events. */ - bfd_recvtimer_delete(bs); - bfd_echo_recvtimer_delete(bs); - bfd_xmttimer_delete(bs); - bfd_echo_xmttimer_delete(bs); - - /* Change and notify state change. */ - bs->ses_state = PTM_BFD_ADM_DOWN; - control_notify(bs, bs->ses_state); - - ptm_bfd_snd(bs, 0); - } + bfd_set_shutdown(bs, shutdown); return NB_OK; } @@ -429,22 +394,7 @@ int bfdd_bfd_sessions_single_hop_echo_mode_modify( } bs = nb_running_get_entry(args->dnode, NULL, true); - - if (!echo) { - if (!CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) - return NB_OK; - - UNSET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO); - ptm_bfd_echo_stop(bs); - } else { - if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) - return NB_OK; - - SET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO); - /* Apply setting immediately. */ - if (!CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN)) - bs_echo_timer_handler(bs); - } + bfd_set_echo(bs, echo); return NB_OK; } diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 9890a3f071..51a9684235 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -16234,6 +16234,10 @@ void bgp_vty_init(void) &no_neighbor_route_reflector_client_cmd); install_element(BGP_EVPN_NODE, &neighbor_route_reflector_client_cmd); install_element(BGP_EVPN_NODE, &no_neighbor_route_reflector_client_cmd); + install_element(BGP_EVPN_NODE, &neighbor_nexthop_self_cmd); + install_element(BGP_EVPN_NODE, &no_neighbor_nexthop_self_cmd); + install_element(BGP_EVPN_NODE, &neighbor_nexthop_self_force_cmd); + install_element(BGP_EVPN_NODE, &no_neighbor_nexthop_self_force_cmd); /* "neighbor route-server" commands.*/ install_element(BGP_NODE, &neighbor_route_server_client_hidden_cmd); diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index cca3f4aaa3..b7c255f16f 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -1143,7 +1143,8 @@ static bool update_ipv6nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp, api_nh->ifindex = 0; } } - api_nh->gate.ipv6 = *nexthop; + if (nexthop) + api_nh->gate.ipv6 = *nexthop; return true; } diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index cf6335d373..faee7dad4a 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1227,10 +1227,6 @@ struct peer *peer_new(struct bgp *bgp) peer->addpath_type[afi][safi] = BGP_ADDPATH_NONE; } - /* set nexthop-unchanged for l2vpn evpn by default */ - SET_FLAG(peer->af_flags[AFI_L2VPN][SAFI_EVPN], - PEER_FLAG_NEXTHOP_UNCHANGED); - SET_FLAG(peer->sflags, PEER_STATUS_CAPABILITY_OPEN); /* Initialize per peer bgp GR FSM */ diff --git a/doc/developer/lists.rst b/doc/developer/lists.rst index 853c65ddf3..9355141aa4 100644 --- a/doc/developer/lists.rst +++ b/doc/developer/lists.rst @@ -105,7 +105,8 @@ Functions provided: +====================================+======+======+======+=========+============+ | _init, _fini | yes | yes | yes | yes | yes | +------------------------------------+------+------+------+---------+------------+ -| _first, _next, _next_safe | yes | yes | yes | yes | yes | +| _first, _next, _next_safe, | yes | yes | yes | yes | yes | +| _const_first, _const_next | | | | | | +------------------------------------+------+------+------+---------+------------+ | _add_head, _add_tail, _add_after | yes | -- | -- | -- | -- | +------------------------------------+------+------+------+---------+------------+ @@ -113,9 +114,10 @@ Functions provided: +------------------------------------+------+------+------+---------+------------+ | _del, _pop | yes | yes | yes | yes | yes | +------------------------------------+------+------+------+---------+------------+ -| _find | -- | -- | yes | yes | -- | +| _find, _const_find | -- | -- | yes | yes | -- | +------------------------------------+------+------+------+---------+------------+ -| _find_lt, _find_gteq | -- | -- | -- | yes | yes | +| _find_lt, _find_gteq, | -- | -- | -- | yes | yes | +| _const_find_lt, _const_find_gteq | | | | | | +------------------------------------+------+------+------+---------+------------+ | use with frr_each() macros | yes | yes | yes | yes | yes | +------------------------------------+------+------+------+---------+------------+ @@ -226,6 +228,10 @@ The following iteration macros work across all data structures: resume iteration after breaking out of the loop by keeping the ``from`` value persistent and reusing it for the next loop. +To iterate over ``const`` pointers, add ``_const`` to the name of the +datastructure (``Z`` above), e.g. ``frr_each (mylist, head, item)`` becomes +``frr_each (mylist_const, head, item)``. + Common API ---------- @@ -248,7 +254,7 @@ The following documentation assumes that a list has been defined using This function may ``assert()`` if the list is not empty. -.. c:function:: size_t Z_count(struct Z_head *) +.. c:function:: size_t Z_count(const struct Z_head *) Returns the number of items in a structure. All structures store a counter in their `Z_head` so that calling this function completes @@ -260,6 +266,7 @@ The following documentation assumes that a list has been defined using outdated by the time this function returns and can therefore only be used as an estimate. +.. c:function:: const itemtype *Z_const_first(const struct Z_head *) .. c:function:: itemtype *Z_first(struct Z_head *) Returns the first item in the structure, or ``NULL`` if the structure is @@ -288,6 +295,7 @@ The following documentation assumes that a list has been defined using affected by the "modification while iterating" problem. To remove all items from a hash table, use the loop demonstrated above. +.. c:function:: const itemtype *Z_next(const struct Z_head *, const itemtype *prev) .. c:function:: itemtype *Z_next(struct Z_head *, itemtype *prev) Return the item that follows after ``prev``, or ``NULL`` if ``prev`` is @@ -421,6 +429,7 @@ sorted lists can be searched for a value. For ``_NONUNIQ`` lists, this function always returns NULL since ``item`` can always be successfully added to the list. +.. c:function:: const itemtype *Z_find(const struct Z_head *, const itemtype *ref) .. c:function:: itemtype *Z_find(struct Z_head *, const itemtype *ref) Search the list for an item that compares equal to ``ref``. If no equal @@ -442,11 +451,13 @@ sorted lists can be searched for a value. containing non-unique items, more than one item may compare as equal to the item that is searched for. +.. c:function:: const itemtype *Z_find_gteq(const struct Z_head *, const itemtype *ref) .. c:function:: itemtype *Z_find_gteq(struct Z_head *, const itemtype *ref) Search the list for an item that compares greater or equal to ``ref``. See :c:func:`Z_find()` above. +.. c:function:: const itemtype *Z_find_lt(const struct Z_head *, const itemtype *ref) .. c:function:: itemtype *Z_find_lt(struct Z_head *, const itemtype *ref) Search the list for an item that compares less than @@ -616,21 +627,9 @@ Head removal (pop) and deallocation: FAQ --- -Why is the list head not ``const`` in the list APIs? - The semantics that a ``const`` list head would imply are not obvious. It - could mean any of the following: - - * the list just shouldn't be allocated/deallocated, but may be modified. - This doesn't actually work since the list head needs to be modified for - inserting or deleting items. - - * the list shouldn't be modified, but items can. This may make sense for - iterating, but it's not exactly consistent - an item might be on more - than one list, does it apply to all of them? If not, which one? - - * neither the list nor the items should be modified. This is consistent, - but hard to do without creating a ``const`` copy of every single list - function. Ease of use trumps this. +What are the semantics of ``const`` in the list APIs? + ``const`` pointers to list heads and/or items are interpreted to mean that + both the list itself as well as the data items are read-only. Why is there no "is this item on a/the list" test? It's slow for several of the data structures, and the work of adding it diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 84be461cce..7c86cac11c 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -2391,6 +2391,27 @@ the same behavior of using same next-hop and RMAC values. Enables or disables advertise-pip feature, specifiy system-IP and/or system-MAC parameters. +Support with VRF network namespace backend +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +It is possible to separate overlay networks contained in VXLAN interfaces from +underlay networks by using VRFs. VRF-lite and VRF-netns backends can be used for +that. In the latter case, this is necessary to set both bridge and vxlan interface +on the same network namespace, as below example illustrates: + +.. code-block:: shell + + # linux shell + ip netns add vrf1 + ip link add name vxlan101 type vxlan id 101 dstport 4789 dev eth0 local 10.1.1.1 + ip link set dev vxlan101 netns vrf1 + ip netns exec vrf1 ip link set dev lo up + ip netns exec vrf1 brctl addbr bridge101 + ip netns exec vrf1 brctl addif bridge101 vxlan101 + +This makes possible to separate not only layer 3 networks like VRF-lite networks. +Also, VRF netns based make possible to separate layer 2 networks on separate VRF +instances. + .. _bgp-cisco-compatibility: Cisco Compatibility diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index 4cb35c7433..f105bd72bc 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -870,6 +870,20 @@ FPM Commands will not attempt to connect to it anymore. +.. index:: fpm use-next-hop-groups +.. clicmd:: fpm use-next-hop-groups + + Use the new netlink messages ``RTM_NEWNEXTHOP`` / ``RTM_DELNEXTHOP`` to + group repeated route next hop information. + + +.. index:: no fpm use-next-hop-groups +.. clicmd:: no fpm use-next-hop-groups + + Use the old known FPM behavior of including next hop information in the + route (e.g. ``RTM_NEWROUTE``) messages. + + .. index:: show fpm counters [json] .. clicmd:: show fpm counters [json] diff --git a/include/linux/net_namespace.h b/include/linux/net_namespace.h index 0187c74d88..0ed9dd61d3 100644 --- a/include/linux/net_namespace.h +++ b/include/linux/net_namespace.h @@ -16,6 +16,7 @@ enum { NETNSA_NSID, NETNSA_PID, NETNSA_FD, + NETNSA_TARGET_NSID, __NETNSA_MAX, }; @@ -1277,7 +1277,7 @@ struct if_link_params *if_link_params_get(struct interface *ifp) /* Compute default bandwidth based on interface */ iflp->default_bw = ((ifp->bandwidth ? ifp->bandwidth : DEFAULT_BANDWIDTH) - * TE_KILO_BIT / TE_BYTE); + * TE_MEGA_BIT / TE_BYTE); /* Set Max, Reservable and Unreserved Bandwidth */ iflp->max_bw = iflp->default_bw; @@ -143,7 +143,7 @@ struct if_stats { #define TE_EXT_MASK 0x0FFFFFFF #define TE_EXT_ANORMAL 0x80000000 #define LOSS_PRECISION 0.000003 -#define TE_KILO_BIT 1000 +#define TE_MEGA_BIT 1000000 #define TE_BYTE 8 #define DEFAULT_BANDWIDTH 10000 #define MAX_CLASS_TYPE 8 diff --git a/lib/netns_linux.c b/lib/netns_linux.c index 98f359401e..e1c0159fc5 100644 --- a/lib/netns_linux.c +++ b/lib/netns_linux.c @@ -379,12 +379,20 @@ struct ns *ns_lookup(ns_id_t ns_id) return ns_lookup_internal(ns_id); } -void ns_walk_func(int (*func)(struct ns *)) +void ns_walk_func(int (*func)(struct ns *, + void *param_in, + void **param_out), + void *param_in, + void **param_out) { struct ns *ns = NULL; + int ret; - RB_FOREACH (ns, ns_head, &ns_tree) - func(ns); + RB_FOREACH (ns, ns_head, &ns_tree) { + ret = func(ns, param_in, param_out); + if (ret == NS_WALK_STOP) + return; + } } const char *ns_get_name(struct ns *ns) @@ -584,9 +592,33 @@ int ns_socket(int domain, int type, int protocol, ns_id_t ns_id) return ret; } +/* if relative link_nsid matches default netns, + * then return default absolute netns value + * otherwise, return NS_UNKNOWN + */ +ns_id_t ns_id_get_absolute(ns_id_t ns_id_reference, ns_id_t link_nsid) +{ + struct ns *ns; + + ns = ns_lookup(ns_id_reference); + if (!ns) + return NS_UNKNOWN; + if (ns->relative_default_ns != link_nsid) + return NS_UNKNOWN; + ns = ns_get_default(); + assert(ns); + return ns->ns_id; +} + ns_id_t ns_get_default_id(void) { if (default_ns) return default_ns->ns_id; return NS_DEFAULT_INTERNAL; } + +struct ns *ns_get_default(void) +{ + return default_ns; +} + @@ -53,6 +53,11 @@ struct ns { /* Identifier, mapped on the NSID value */ ns_id_t internal_ns_id; + /* Identifier, value of NSID of default netns, + * relative value in that local netns + */ + ns_id_t relative_default_ns; + /* Name */ char *name; @@ -120,7 +125,14 @@ int ns_socket(int domain, int type, int protocol, ns_id_t ns_id); extern char *ns_netns_pathname(struct vty *vty, const char *name); /* Parse and execute a function on all the NETNS */ -extern void ns_walk_func(int (*func)(struct ns *)); +#define NS_WALK_CONTINUE 0 +#define NS_WALK_STOP 1 + +extern void ns_walk_func(int (*func)(struct ns *, + void *, + void **), + void *param_in, + void **param_out); /* API to get the NETNS name, from the ns pointer */ extern const char *ns_get_name(struct ns *ns); @@ -174,7 +186,9 @@ extern struct ns *ns_lookup_name(const char *name); */ extern int ns_enable(struct ns *ns, void (*func)(ns_id_t, void *)); extern struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id); +extern ns_id_t ns_id_get_absolute(ns_id_t ns_id_reference, ns_id_t link_nsid); extern void ns_disable(struct ns *ns); +extern struct ns *ns_get_default(void); #ifdef __cplusplus } diff --git a/lib/typerb.c b/lib/typerb.c index 7e8cd9d8f7..092faa4cc9 100644 --- a/lib/typerb.c +++ b/lib/typerb.c @@ -377,12 +377,13 @@ struct typed_rb_entry *typed_rb_insert(struct rbt_tree *rbt, } /* Finds the node with the same key as elm */ -struct rb_entry *typed_rb_find(struct rbt_tree *rbt, const struct rb_entry *key, +const struct rb_entry *typed_rb_find(const struct rbt_tree *rbt, + const struct rb_entry *key, int (*cmpfn)( const struct typed_rb_entry *a, const struct typed_rb_entry *b)) { - struct rb_entry *tmp = RBH_ROOT(rbt); + const struct rb_entry *tmp = RBH_ROOT(rbt); int comp; while (tmp != NULL) { @@ -398,13 +399,13 @@ struct rb_entry *typed_rb_find(struct rbt_tree *rbt, const struct rb_entry *key, return NULL; } -struct rb_entry *typed_rb_find_gteq(struct rbt_tree *rbt, +const struct rb_entry *typed_rb_find_gteq(const struct rbt_tree *rbt, const struct rb_entry *key, int (*cmpfn)( const struct typed_rb_entry *a, const struct typed_rb_entry *b)) { - struct rb_entry *tmp = RBH_ROOT(rbt), *best = NULL; + const struct rb_entry *tmp = RBH_ROOT(rbt), *best = NULL; int comp; while (tmp != NULL) { @@ -421,13 +422,13 @@ struct rb_entry *typed_rb_find_gteq(struct rbt_tree *rbt, return best; } -struct rb_entry *typed_rb_find_lt(struct rbt_tree *rbt, +const struct rb_entry *typed_rb_find_lt(const struct rbt_tree *rbt, const struct rb_entry *key, int (*cmpfn)( const struct typed_rb_entry *a, const struct typed_rb_entry *b)) { - struct rb_entry *tmp = RBH_ROOT(rbt), *best = NULL; + const struct rb_entry *tmp = RBH_ROOT(rbt), *best = NULL; int comp; while (tmp != NULL) { @@ -443,8 +444,10 @@ struct rb_entry *typed_rb_find_lt(struct rbt_tree *rbt, return best; } -struct rb_entry *typed_rb_next(struct rb_entry *rbe) +struct rb_entry *typed_rb_next(const struct rb_entry *rbe_const) { + struct rb_entry *rbe = (struct rb_entry *)rbe_const; + if (RBE_RIGHT(rbe) != NULL) { rbe = RBE_RIGHT(rbe); while (RBE_LEFT(rbe) != NULL) @@ -463,7 +466,7 @@ struct rb_entry *typed_rb_next(struct rb_entry *rbe) return rbe; } -struct rb_entry *typed_rb_min(struct rbt_tree *rbt) +struct rb_entry *typed_rb_min(const struct rbt_tree *rbt) { struct rb_entry *rbe = RBH_ROOT(rbt); struct rb_entry *parent = NULL; diff --git a/lib/typerb.h b/lib/typerb.h index 2d7b0ba637..fca45e20d1 100644 --- a/lib/typerb.h +++ b/lib/typerb.h @@ -45,23 +45,23 @@ struct typed_rb_entry *typed_rb_insert(struct typed_rb_root *rbt, const struct typed_rb_entry *b)); struct typed_rb_entry *typed_rb_remove(struct typed_rb_root *rbt, struct typed_rb_entry *rbe); -struct typed_rb_entry *typed_rb_find(struct typed_rb_root *rbt, +const struct typed_rb_entry *typed_rb_find(const struct typed_rb_root *rbt, const struct typed_rb_entry *rbe, int (*cmpfn)( const struct typed_rb_entry *a, const struct typed_rb_entry *b)); -struct typed_rb_entry *typed_rb_find_gteq(struct typed_rb_root *rbt, +const struct typed_rb_entry *typed_rb_find_gteq(const struct typed_rb_root *rbt, const struct typed_rb_entry *rbe, int (*cmpfn)( const struct typed_rb_entry *a, const struct typed_rb_entry *b)); -struct typed_rb_entry *typed_rb_find_lt(struct typed_rb_root *rbt, +const struct typed_rb_entry *typed_rb_find_lt(const struct typed_rb_root *rbt, const struct typed_rb_entry *rbe, int (*cmpfn)( const struct typed_rb_entry *a, const struct typed_rb_entry *b)); -struct typed_rb_entry *typed_rb_min(struct typed_rb_root *rbt); -struct typed_rb_entry *typed_rb_next(struct typed_rb_entry *rbe); +struct typed_rb_entry *typed_rb_min(const struct typed_rb_root *rbt); +struct typed_rb_entry *typed_rb_next(const struct typed_rb_entry *rbe); #define _PREDECL_RBTREE(prefix) \ struct prefix ## _head { struct typed_rb_root rr; }; \ @@ -86,20 +86,21 @@ macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \ re = typed_rb_insert(&h->rr, &item->field.re, cmpfn_uq); \ return container_of_null(re, type, field.re); \ } \ -macro_inline type *prefix ## _find_gteq(struct prefix##_head *h, \ - const type *item) \ +macro_inline const type *prefix ## _const_find_gteq( \ + const struct prefix##_head *h, const type *item) \ { \ - struct typed_rb_entry *re; \ + const struct typed_rb_entry *re; \ re = typed_rb_find_gteq(&h->rr, &item->field.re, cmpfn_nuq); \ return container_of_null(re, type, field.re); \ } \ -macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \ - const type *item) \ +macro_inline const type *prefix ## _const_find_lt( \ + const struct prefix##_head *h, const type *item) \ { \ - struct typed_rb_entry *re; \ + const struct typed_rb_entry *re; \ re = typed_rb_find_lt(&h->rr, &item->field.re, cmpfn_nuq); \ return container_of_null(re, type, field.re); \ } \ +TYPESAFE_FIND_CMP(prefix, type) \ macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \ { \ struct typed_rb_entry *re; \ @@ -115,18 +116,20 @@ macro_inline type *prefix ## _pop(struct prefix##_head *h) \ typed_rb_remove(&h->rr, re); \ return container_of(re, type, field.re); \ } \ -macro_pure type *prefix ## _first(struct prefix##_head *h) \ +macro_pure const type *prefix ## _const_first(const struct prefix##_head *h) \ { \ - struct typed_rb_entry *re; \ + const struct typed_rb_entry *re; \ re = typed_rb_min(&h->rr); \ return container_of_null(re, type, field.re); \ } \ -macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \ +macro_pure const type *prefix ## _const_next(const struct prefix##_head *h, \ + const type *item) \ { \ - struct typed_rb_entry *re; \ + const struct typed_rb_entry *re; \ re = typed_rb_next(&item->field.re); \ return container_of_null(re, type, field.re); \ } \ +TYPESAFE_FIRST_NEXT(prefix, type) \ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ { \ struct typed_rb_entry *re; \ @@ -149,12 +152,14 @@ macro_inline int prefix ## __cmp(const struct typed_rb_entry *a, \ return cmpfn(container_of(a, type, field.re), \ container_of(b, type, field.re)); \ } \ -macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \ +macro_inline const type *prefix ## _const_find(const struct prefix##_head *h, \ + const type *item) \ { \ - struct typed_rb_entry *re; \ + const struct typed_rb_entry *re; \ re = typed_rb_find(&h->rr, &item->field.re, &prefix ## __cmp); \ return container_of_null(re, type, field.re); \ } \ +TYPESAFE_FIND(prefix, type) \ \ _DECLARE_RBTREE(prefix, type, field, prefix ## __cmp, prefix ## __cmp) \ /* ... */ diff --git a/lib/typesafe.c b/lib/typesafe.c index a52b55b734..69796e2d81 100644 --- a/lib/typesafe.c +++ b/lib/typesafe.c @@ -158,7 +158,7 @@ void typesafe_hash_shrink(struct thash_head *head) /* skiplist */ -static inline struct sskip_item *sl_level_get(struct sskip_item *item, +static inline struct sskip_item *sl_level_get(const struct sskip_item *item, size_t level) { if (level < SKIPLIST_OVERFLOW) @@ -263,13 +263,14 @@ struct sskip_item *typesafe_skiplist_add(struct sskip_head *head, /* NOTE: level counting below is 1-based since that makes the code simpler! */ -struct sskip_item *typesafe_skiplist_find(struct sskip_head *head, +const struct sskip_item *typesafe_skiplist_find( + const struct sskip_head *head, const struct sskip_item *item, int (*cmpfn)( const struct sskip_item *a, const struct sskip_item *b)) { size_t level = SKIPLIST_MAXDEPTH; - struct sskip_item *prev = &head->hitem, *next; + const struct sskip_item *prev = &head->hitem, *next; int cmpval; while (level) { @@ -290,13 +291,14 @@ struct sskip_item *typesafe_skiplist_find(struct sskip_head *head, return NULL; } -struct sskip_item *typesafe_skiplist_find_gteq(struct sskip_head *head, +const struct sskip_item *typesafe_skiplist_find_gteq( + const struct sskip_head *head, const struct sskip_item *item, int (*cmpfn)( const struct sskip_item *a, const struct sskip_item *b)) { size_t level = SKIPLIST_MAXDEPTH; - struct sskip_item *prev = &head->hitem, *next; + const struct sskip_item *prev = &head->hitem, *next; int cmpval; while (level) { @@ -317,13 +319,14 @@ struct sskip_item *typesafe_skiplist_find_gteq(struct sskip_head *head, return next; } -struct sskip_item *typesafe_skiplist_find_lt(struct sskip_head *head, +const struct sskip_item *typesafe_skiplist_find_lt( + const struct sskip_head *head, const struct sskip_item *item, int (*cmpfn)( const struct sskip_item *a, const struct sskip_item *b)) { size_t level = SKIPLIST_MAXDEPTH; - struct sskip_item *prev = &head->hitem, *next, *best = NULL; + const struct sskip_item *prev = &head->hitem, *next, *best = NULL; int cmpval; while (level) { diff --git a/lib/typesafe.h b/lib/typesafe.h index c30d73d1b3..e134316dd9 100644 --- a/lib/typesafe.h +++ b/lib/typesafe.h @@ -44,6 +44,41 @@ extern "C" { item; \ item = from, from = prefix##_next_safe(head, from)) + +/* non-const variants. these wrappers are the same for all the types, so + * bundle them together here. + */ +#define TYPESAFE_FIRST_NEXT(prefix, type) \ +macro_pure type *prefix ## _first(struct prefix##_head *h) \ +{ \ + return (type *)prefix ## _const_first(h); \ +} \ +macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \ +{ \ + return (type *)prefix ## _const_next(h, item); \ +} \ +/* ... */ +#define TYPESAFE_FIND(prefix, type) \ +macro_inline type *prefix ## _find(struct prefix##_head *h, \ + const type *item) \ +{ \ + return (type *)prefix ## _const_find(h, item); \ +} \ +/* ... */ +#define TYPESAFE_FIND_CMP(prefix, type) \ +macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \ + const type *item) \ +{ \ + return (type *)prefix ## _const_find_lt(h, item); \ +} \ +macro_inline type *prefix ## _find_gteq(struct prefix##_head *h, \ + const type *item) \ +{ \ + return (type *)prefix ## _const_find_gteq(h, item); \ +} \ +/* ... */ + + /* single-linked list, unsorted/arbitrary. * can be used as queue with add_tail / pop */ @@ -133,15 +168,17 @@ macro_inline type *prefix ## _pop(struct prefix##_head *h) \ h->sh.last_next = &h->sh.first; \ return container_of(sitem, type, field.si); \ } \ -macro_pure type *prefix ## _first(struct prefix##_head *h) \ +macro_pure const type *prefix ## _const_first(const struct prefix##_head *h) \ { \ return container_of_null(h->sh.first, type, field.si); \ } \ -macro_pure type *prefix ## _next(struct prefix##_head * h, type *item) \ +macro_pure const type *prefix ## _const_next(const struct prefix##_head *h, \ + const type *item) \ { \ - struct slist_item *sitem = &item->field.si; \ + const struct slist_item *sitem = &item->field.si; \ return container_of_null(sitem->next, type, field.si); \ } \ +TYPESAFE_FIRST_NEXT(prefix, type) \ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ { \ struct slist_item *sitem; \ @@ -232,20 +269,22 @@ macro_inline type *prefix ## _pop(struct prefix##_head *h) \ h->dh.count--; \ return container_of(ditem, type, field.di); \ } \ -macro_pure type *prefix ## _first(struct prefix##_head *h) \ +macro_pure const type *prefix ## _const_first(const struct prefix##_head *h) \ { \ - struct dlist_item *ditem = h->dh.hitem.next; \ + const struct dlist_item *ditem = h->dh.hitem.next; \ if (ditem == &h->dh.hitem) \ return NULL; \ return container_of(ditem, type, field.di); \ } \ -macro_pure type *prefix ## _next(struct prefix##_head * h, type *item) \ +macro_pure const type *prefix ## _const_next(const struct prefix##_head *h, \ + const type *item) \ { \ - struct dlist_item *ditem = &item->field.di; \ + const struct dlist_item *ditem = &item->field.di; \ if (ditem->next == &h->dh.hitem) \ return NULL; \ return container_of(ditem->next, type, field.di); \ } \ +TYPESAFE_FIRST_NEXT(prefix, type) \ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ { \ if (!item) \ @@ -338,19 +377,21 @@ macro_inline type *prefix ## _pop(struct prefix##_head *h) \ typesafe_heap_resize(&h->hh, false); \ return container_of(hitem, type, field.hi); \ } \ -macro_pure type *prefix ## _first(struct prefix##_head *h) \ +macro_pure const type *prefix ## _const_first(const struct prefix##_head *h) \ { \ if (h->hh.count == 0) \ return NULL; \ return container_of(h->hh.array[0], type, field.hi); \ } \ -macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \ +macro_pure const type *prefix ## _const_next(const struct prefix##_head *h, \ + const type *item) \ { \ uint32_t idx = item->field.hi.index + 1; \ if (idx >= h->hh.count) \ return NULL; \ return container_of(h->hh.array[idx], type, field.hi); \ } \ +TYPESAFE_FIRST_NEXT(prefix, type) \ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ { \ if (!item) \ @@ -431,26 +472,27 @@ macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \ h->sh.count++; \ return NULL; \ } \ -macro_inline type *prefix ## _find_gteq(struct prefix##_head *h, \ - const type *item) \ +macro_inline const type *prefix ## _const_find_gteq( \ + const struct prefix##_head *h, const type *item) \ { \ - struct ssort_item *sitem = h->sh.first; \ + const struct ssort_item *sitem = h->sh.first; \ int cmpval = 0; \ while (sitem && (cmpval = cmpfn_nuq( \ container_of(sitem, type, field.si), item)) < 0) \ sitem = sitem->next; \ return container_of_null(sitem, type, field.si); \ } \ -macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \ - const type *item) \ +macro_inline const type *prefix ## _const_find_lt( \ + const struct prefix##_head *h, const type *item) \ { \ - struct ssort_item *prev = NULL, *sitem = h->sh.first; \ + const struct ssort_item *prev = NULL, *sitem = h->sh.first; \ int cmpval = 0; \ while (sitem && (cmpval = cmpfn_nuq( \ container_of(sitem, type, field.si), item)) < 0) \ sitem = (prev = sitem)->next; \ return container_of_null(prev, type, field.si); \ } \ +TYPESAFE_FIND_CMP(prefix, type) \ /* TODO: del_hint */ \ macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \ { \ @@ -472,15 +514,17 @@ macro_inline type *prefix ## _pop(struct prefix##_head *h) \ h->sh.first = sitem->next; \ return container_of(sitem, type, field.si); \ } \ -macro_pure type *prefix ## _first(struct prefix##_head *h) \ +macro_pure const type *prefix ## _const_first(const struct prefix##_head *h) \ { \ return container_of_null(h->sh.first, type, field.si); \ } \ -macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \ +macro_pure const type *prefix ## _const_next(const struct prefix##_head *h, \ + const type *item) \ { \ - struct ssort_item *sitem = &item->field.si; \ + const struct ssort_item *sitem = &item->field.si; \ return container_of_null(sitem->next, type, field.si); \ } \ +TYPESAFE_FIRST_NEXT(prefix, type) \ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ { \ struct ssort_item *sitem; \ @@ -497,10 +541,11 @@ macro_pure size_t prefix ## _count(const struct prefix##_head *h) \ #define DECLARE_SORTLIST_UNIQ(prefix, type, field, cmpfn) \ _DECLARE_SORTLIST(prefix, type, field, cmpfn, cmpfn) \ - \ -macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \ + \ +macro_inline const type *prefix ## _const_find(const struct prefix##_head *h, \ + const type *item) \ { \ - struct ssort_item *sitem = h->sh.first; \ + const struct ssort_item *sitem = h->sh.first; \ int cmpval = 0; \ while (sitem && (cmpval = cmpfn( \ container_of(sitem, type, field.si), item)) < 0) \ @@ -509,6 +554,7 @@ macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \ return NULL; \ return container_of(sitem, type, field.si); \ } \ +TYPESAFE_FIND(prefix, type) \ /* ... */ #define DECLARE_SORTLIST_NONUNIQ(prefix, type, field, cmpfn) \ @@ -606,12 +652,13 @@ macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \ *np = &item->field.hi; \ return NULL; \ } \ -macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \ +macro_inline const type *prefix ## _const_find(const struct prefix##_head *h, \ + const type *item) \ { \ if (!h->hh.tabshift) \ return NULL; \ uint32_t hval = hashfn(item), hbits = HASH_KEY(h->hh, hval); \ - struct thash_item *hitem = h->hh.entries[hbits]; \ + const struct thash_item *hitem = h->hh.entries[hbits]; \ while (hitem && hitem->hashval < hval) \ hitem = hitem->next; \ while (hitem && hitem->hashval == hval) { \ @@ -621,6 +668,7 @@ macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \ } \ return NULL; \ } \ +TYPESAFE_FIND(prefix, type) \ macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \ { \ if (!h->hh.tabshift) \ @@ -655,7 +703,7 @@ macro_inline type *prefix ## _pop(struct prefix##_head *h) \ } \ return NULL; \ } \ -macro_pure type *prefix ## _first(struct prefix##_head *h) \ +macro_pure const type *prefix ## _const_first(const struct prefix##_head *h) \ { \ uint32_t i; \ for (i = 0; i < HASH_SIZE(h->hh); i++) \ @@ -663,17 +711,19 @@ macro_pure type *prefix ## _first(struct prefix##_head *h) \ return container_of(h->hh.entries[i], type, field.hi); \ return NULL; \ } \ -macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \ +macro_pure const type *prefix ## _const_next(const struct prefix##_head *h, \ + const type *item) \ { \ - struct thash_item *hitem = &item->field.hi; \ + const struct thash_item *hitem = &item->field.hi; \ if (hitem->next) \ return container_of(hitem->next, type, field.hi); \ uint32_t i = HASH_KEY(h->hh, hitem->hashval) + 1; \ - for (; i < HASH_SIZE(h->hh); i++) \ + for (; i < HASH_SIZE(h->hh); i++) \ if (h->hh.entries[i]) \ return container_of(h->hh.entries[i], type, field.hi); \ return NULL; \ } \ +TYPESAFE_FIRST_NEXT(prefix, type) \ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ { \ if (!item) \ @@ -742,20 +792,21 @@ macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \ si = typesafe_skiplist_add(&h->sh, &item->field.si, cmpfn_uq); \ return container_of_null(si, type, field.si); \ } \ -macro_inline type *prefix ## _find_gteq(struct prefix##_head *h, \ - const type *item) \ +macro_inline const type *prefix ## _const_find_gteq( \ + const struct prefix##_head *h, const type *item) \ { \ - struct sskip_item *sitem = typesafe_skiplist_find_gteq(&h->sh, \ + const struct sskip_item *sitem = typesafe_skiplist_find_gteq(&h->sh, \ &item->field.si, cmpfn_nuq); \ return container_of_null(sitem, type, field.si); \ } \ -macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \ - const type *item) \ +macro_inline const type *prefix ## _const_find_lt( \ + const struct prefix##_head *h, const type *item) \ { \ - struct sskip_item *sitem = typesafe_skiplist_find_lt(&h->sh, \ + const struct sskip_item *sitem = typesafe_skiplist_find_lt(&h->sh, \ &item->field.si, cmpfn_nuq); \ return container_of_null(sitem, type, field.si); \ } \ +TYPESAFE_FIND_CMP(prefix, type) \ macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \ { \ struct sskip_item *sitem = typesafe_skiplist_del(&h->sh, \ @@ -767,16 +818,18 @@ macro_inline type *prefix ## _pop(struct prefix##_head *h) \ struct sskip_item *sitem = typesafe_skiplist_pop(&h->sh); \ return container_of_null(sitem, type, field.si); \ } \ -macro_pure type *prefix ## _first(struct prefix##_head *h) \ +macro_pure const type *prefix ## _const_first(const struct prefix##_head *h) \ { \ - struct sskip_item *first = h->sh.hitem.next[0]; \ + const struct sskip_item *first = h->sh.hitem.next[0]; \ return container_of_null(first, type, field.si); \ } \ -macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \ +macro_pure const type *prefix ## _const_next(const struct prefix##_head *h, \ + const type *item) \ { \ - struct sskip_item *next = item->field.si.next[0]; \ + const struct sskip_item *next = item->field.si.next[0]; \ return container_of_null(next, type, field.si); \ } \ +TYPESAFE_FIRST_NEXT(prefix, type) \ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ { \ struct sskip_item *next; \ @@ -792,19 +845,21 @@ macro_pure size_t prefix ## _count(const struct prefix##_head *h) \ #define PREDECL_SKIPLIST_UNIQ(prefix) \ _PREDECL_SKIPLIST(prefix) #define DECLARE_SKIPLIST_UNIQ(prefix, type, field, cmpfn) \ - \ + \ macro_inline int prefix ## __cmp(const struct sskip_item *a, \ const struct sskip_item *b) \ { \ return cmpfn(container_of(a, type, field.si), \ container_of(b, type, field.si)); \ } \ -macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \ +macro_inline const type *prefix ## _const_find(const struct prefix##_head *h, \ + const type *item) \ { \ - struct sskip_item *sitem = typesafe_skiplist_find(&h->sh, \ + const struct sskip_item *sitem = typesafe_skiplist_find(&h->sh, \ &item->field.si, &prefix ## __cmp); \ return container_of_null(sitem, type, field.si); \ } \ +TYPESAFE_FIND(prefix, type) \ \ _DECLARE_SKIPLIST(prefix, type, field, \ prefix ## __cmp, prefix ## __cmp) \ @@ -843,15 +898,18 @@ extern struct sskip_item *typesafe_skiplist_add(struct sskip_head *head, struct sskip_item *item, int (*cmpfn)( const struct sskip_item *a, const struct sskip_item *b)); -extern struct sskip_item *typesafe_skiplist_find(struct sskip_head *head, +extern const struct sskip_item *typesafe_skiplist_find( + const struct sskip_head *head, const struct sskip_item *item, int (*cmpfn)( const struct sskip_item *a, const struct sskip_item *b)); -extern struct sskip_item *typesafe_skiplist_find_gteq(struct sskip_head *head, +extern const struct sskip_item *typesafe_skiplist_find_gteq( + const struct sskip_head *head, const struct sskip_item *item, int (*cmpfn)( const struct sskip_item *a, const struct sskip_item *b)); -extern struct sskip_item *typesafe_skiplist_find_lt(struct sskip_head *head, +extern const struct sskip_item *typesafe_skiplist_find_lt( + const struct sskip_head *head, const struct sskip_item *item, int (*cmpfn)( const struct sskip_item *a, const struct sskip_item *b)); @@ -652,7 +652,8 @@ int vrf_handler_create(struct vty *vty, const char *vrfname, } int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, char *pathname, - ns_id_t ns_id, ns_id_t internal_ns_id) + ns_id_t ns_id, ns_id_t internal_ns_id, + ns_id_t rel_def_ns_id) { struct ns *ns = NULL; @@ -700,6 +701,7 @@ int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, char *pathname, } ns = ns_get_created(ns, pathname, ns_id); ns->internal_ns_id = internal_ns_id; + ns->relative_default_ns = rel_def_ns_id; ns->vrf_ctxt = (void *)vrf; vrf->ns_ctxt = (void *)ns; /* update VRF netns NAME */ @@ -797,7 +799,9 @@ DEFUN_NOSH (vrf_netns, frr_with_privs(vrf_daemon_privs) { ret = vrf_netns_handler_create(vty, vrf, pathname, - NS_UNKNOWN, NS_UNKNOWN); + NS_UNKNOWN, + NS_UNKNOWN, + NS_UNKNOWN); } return ret; } @@ -315,7 +315,7 @@ extern int vrf_handler_create(struct vty *vty, const char *name, */ extern int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, char *pathname, ns_id_t ext_ns_id, - ns_id_t ns_id); + ns_id_t ns_id, ns_id_t rel_def_ns_id); /* used internally to enable or disable VRF. * Notify a change in the VRF ID of the VRF diff --git a/tests/lib/test_typelist.h b/tests/lib/test_typelist.h index da3530e9c0..f86cadd398 100644 --- a/tests/lib/test_typelist.h +++ b/tests/lib/test_typelist.h @@ -25,7 +25,9 @@ #define list_hash concat(TYPE, _hash) #define list_init concat(TYPE, _init) #define list_fini concat(TYPE, _fini) +#define list_const_first concat(TYPE, _const_first) #define list_first concat(TYPE, _first) +#define list_const_next concat(TYPE, _const_next) #define list_next concat(TYPE, _next) #define list_next_safe concat(TYPE, _next_safe) #define list_count concat(TYPE, _count) @@ -177,18 +179,29 @@ static void concat(test_, TYPE)(void) ts_hashx("fill", "a538546a6e6ab0484e925940aa8dd02fd934408bbaed8cb66a0721841584d838"); k = 0; - prev = NULL; - frr_each(list, &head, item) { + +#if IS_ATOMIC(REALTYPE) + struct list_head *chead = &head; + struct item *citem, *cprev = NULL; + + frr_each(list, chead, citem) { +#else + const struct list_head *chead = &head; + const struct item *citem, *cprev = NULL; + + frr_each(list_const, chead, citem) { +#endif + #if IS_HASH(REALTYPE) || IS_HEAP(REALTYPE) /* hash table doesn't give sorting */ - (void)prev; + (void)cprev; #else - assert(!prev || prev->val < item->val); + assert(!cprev || cprev->val < citem->val); #endif - prev = item; + cprev = citem; k++; } - assert(list_count(&head) == k); + assert(list_count(chead) == k); ts_ref("walk"); #if IS_UNIQ(REALTYPE) diff --git a/yang/frr-bfdd.yang b/yang/frr-bfdd.yang index a6d537f01c..19e36fdfd7 100644 --- a/yang/frr-bfdd.yang +++ b/yang/frr-bfdd.yang @@ -54,7 +54,7 @@ module frr-bfdd { "RFC 5880: Bidirectional Forwarding Detection (BFD). RFC 5881: Bidirectional Forwarding Detection (BFD) for IPv4 and IPv6 (Single Hop). - RFC 5882: Bidirectional Forwarding Detection (BFD) for Multihop Paths."; + RFC 5883: Bidirectional Forwarding Detection (BFD) for Multihop Paths."; } diff --git a/zebra/dplane_fpm_nl.c b/zebra/dplane_fpm_nl.c index 32b7faaad7..88d5ab5cec 100644 --- a/zebra/dplane_fpm_nl.c +++ b/zebra/dplane_fpm_nl.c @@ -74,6 +74,7 @@ struct fpm_nl_ctx { bool connecting; bool rib_complete; bool rmac_complete; + bool use_nhg; struct sockaddr_storage addr; /* data plane buffers. */ @@ -99,6 +100,8 @@ struct fpm_nl_ctx { struct thread *t_dequeue; /* zebra events. */ + struct thread *t_nhgreset; + struct thread *t_nhgwalk; struct thread *t_ribreset; struct thread *t_ribwalk; struct thread *t_rmacreset; @@ -144,6 +147,8 @@ enum fpm_nl_events { FNE_DISABLE, /* Reset counters. */ FNE_RESET_COUNTERS, + /* Toggle next hop group feature. */ + FNE_TOGGLE_NHG, }; /* @@ -151,6 +156,8 @@ enum fpm_nl_events { */ static int fpm_process_event(struct thread *t); static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx); +static int fpm_nhg_send(struct thread *t); +static int fpm_nhg_reset(struct thread *t); static int fpm_rib_send(struct thread *t); static int fpm_rib_reset(struct thread *t); static int fpm_rmac_send(struct thread *t); @@ -255,6 +262,37 @@ DEFUN(no_fpm_set_address, no_fpm_set_address_cmd, return CMD_SUCCESS; } +DEFUN(fpm_use_nhg, fpm_use_nhg_cmd, + "fpm use-next-hop-groups", + FPM_STR + "Use netlink next hop groups feature.\n") +{ + /* Already enabled. */ + if (gfnc->use_nhg) + return CMD_SUCCESS; + + thread_add_event(gfnc->fthread->master, fpm_process_event, gfnc, + FNE_TOGGLE_NHG, &gfnc->t_event); + + return CMD_SUCCESS; +} + +DEFUN(no_fpm_use_nhg, no_fpm_use_nhg_cmd, + "no fpm use-next-hop-groups", + NO_STR + FPM_STR + "Use netlink next hop groups feature.\n") +{ + /* Already disabled. */ + if (!gfnc->use_nhg) + return CMD_SUCCESS; + + thread_add_event(gfnc->fthread->master, fpm_process_event, gfnc, + FNE_TOGGLE_NHG, &gfnc->t_event); + + return CMD_SUCCESS; +} + DEFUN(fpm_reset_counters, fpm_reset_counters_cmd, "clear fpm counters", CLEAR_STR @@ -368,6 +406,11 @@ static int fpm_write_config(struct vty *vty) break; } + if (!gfnc->use_nhg) { + vty_out(vty, "no fpm use-next-hop-groups\n"); + written = 1; + } + return written; } @@ -399,14 +442,12 @@ static void fpm_reconnect(struct fpm_nl_ctx *fnc) THREAD_OFF(fnc->t_read); THREAD_OFF(fnc->t_write); - if (fnc->t_ribreset) - thread_cancel_async(zrouter.master, &fnc->t_ribreset, NULL); - if (fnc->t_ribwalk) - thread_cancel_async(zrouter.master, &fnc->t_ribwalk, NULL); - if (fnc->t_rmacreset) - thread_cancel_async(zrouter.master, &fnc->t_rmacreset, NULL); - if (fnc->t_rmacwalk) - thread_cancel_async(zrouter.master, &fnc->t_rmacwalk, NULL); + thread_cancel_async(zrouter.master, &fnc->t_nhgreset, NULL); + thread_cancel_async(zrouter.master, &fnc->t_nhgwalk, NULL); + thread_cancel_async(zrouter.master, &fnc->t_ribreset, NULL); + thread_cancel_async(zrouter.master, &fnc->t_ribwalk, NULL); + thread_cancel_async(zrouter.master, &fnc->t_rmacreset, NULL); + thread_cancel_async(zrouter.master, &fnc->t_rmacwalk, NULL); /* FPM is disabled, don't attempt to connect. */ if (fnc->disabled) @@ -490,9 +531,25 @@ static int fpm_write(struct thread *t) fnc->connecting = false; - /* Ask zebra main thread to start walking the RIB table. */ - thread_add_timer(zrouter.master, fpm_rib_send, fnc, 0, - &fnc->t_ribwalk); + /* + * Walk the route tables to send old information before starting + * to send updated information. + * + * NOTE 1: + * RIB table walk is called after the next group table walk + * ends. + * + * NOTE 2: + * Don't attempt to go through next hop group table if we were + * explictly told to not use it. + */ + if (fnc->use_nhg) + thread_add_timer(zrouter.master, fpm_nhg_send, fnc, 0, + &fnc->t_nhgwalk); + else + thread_add_timer(zrouter.master, fpm_rib_send, fnc, 0, + &fnc->t_ribwalk); + thread_add_timer(zrouter.master, fpm_rmac_send, fnc, 0, &fnc->t_rmacwalk); } @@ -610,6 +667,8 @@ static int fpm_connect(struct thread *t) &fnc->t_write); /* Mark all routes as unsent. */ + thread_add_timer(zrouter.master, fpm_nhg_reset, fnc, 0, + &fnc->t_nhgreset); thread_add_timer(zrouter.master, fpm_rib_reset, fnc, 0, &fnc->t_ribreset); thread_add_timer(zrouter.master, fpm_rmac_reset, fnc, 0, @@ -632,16 +691,27 @@ static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx) size_t nl_buf_len; ssize_t rv; uint64_t obytes, obytes_peak; + enum dplane_op_e op = dplane_ctx_get_op(ctx); + + /* + * If we were configured to not use next hop groups, then quit as soon + * as possible. + */ + if ((!fnc->use_nhg) + && (op == DPLANE_OP_NH_DELETE || op == DPLANE_OP_NH_INSTALL + || op == DPLANE_OP_NH_UPDATE)) + return 0; nl_buf_len = 0; frr_mutex_lock_autounlock(&fnc->obuf_mutex); - switch (dplane_ctx_get_op(ctx)) { + switch (op) { case DPLANE_OP_ROUTE_UPDATE: case DPLANE_OP_ROUTE_DELETE: rv = netlink_route_multipath(RTM_DELROUTE, ctx, nl_buf, - sizeof(nl_buf), true); + sizeof(nl_buf), true, + fnc->use_nhg); if (rv <= 0) { zlog_err("%s: netlink_route_multipath failed", __func__); @@ -651,14 +721,14 @@ static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx) nl_buf_len = (size_t)rv; /* UPDATE operations need a INSTALL, otherwise just quit. */ - if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_DELETE) + if (op == DPLANE_OP_ROUTE_DELETE) break; /* FALL THROUGH */ case DPLANE_OP_ROUTE_INSTALL: - rv = netlink_route_multipath(RTM_NEWROUTE, ctx, - &nl_buf[nl_buf_len], - sizeof(nl_buf) - nl_buf_len, true); + rv = netlink_route_multipath( + RTM_NEWROUTE, ctx, &nl_buf[nl_buf_len], + sizeof(nl_buf) - nl_buf_len, true, fnc->use_nhg); if (rv <= 0) { zlog_err("%s: netlink_route_multipath failed", __func__); @@ -680,9 +750,28 @@ static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx) nl_buf_len = (size_t)rv; break; + case DPLANE_OP_NH_DELETE: + rv = netlink_nexthop_encode(RTM_DELNEXTHOP, ctx, nl_buf, + sizeof(nl_buf)); + if (rv <= 0) { + zlog_err("%s: netlink_nexthop_encode failed", __func__); + return 0; + } + + nl_buf_len = (size_t)rv; + break; case DPLANE_OP_NH_INSTALL: case DPLANE_OP_NH_UPDATE: - case DPLANE_OP_NH_DELETE: + rv = netlink_nexthop_encode(RTM_NEWNEXTHOP, ctx, nl_buf, + sizeof(nl_buf)); + if (rv <= 0) { + zlog_err("%s: netlink_nexthop_encode failed", __func__); + return 0; + } + + nl_buf_len = (size_t)rv; + break; + case DPLANE_OP_LSP_INSTALL: case DPLANE_OP_LSP_UPDATE: case DPLANE_OP_LSP_DELETE: @@ -762,6 +851,65 @@ static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx) return 0; } +/* + * Next hop walk/send functions. + */ +struct fpm_nhg_arg { + struct zebra_dplane_ctx *ctx; + struct fpm_nl_ctx *fnc; + bool complete; +}; + +static int fpm_nhg_send_cb(struct hash_bucket *bucket, void *arg) +{ + struct nhg_hash_entry *nhe = bucket->data; + struct fpm_nhg_arg *fna = arg; + + /* This entry was already sent, skip it. */ + if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_FPM)) + return HASHWALK_CONTINUE; + + /* Reset ctx to reuse allocated memory, take a snapshot and send it. */ + dplane_ctx_reset(fna->ctx); + dplane_ctx_nexthop_init(fna->ctx, DPLANE_OP_NH_INSTALL, nhe); + if (fpm_nl_enqueue(fna->fnc, fna->ctx) == -1) { + /* Our buffers are full, lets give it some cycles. */ + fna->complete = false; + return HASHWALK_ABORT; + } + + /* Mark group as sent, so it doesn't get sent again. */ + SET_FLAG(nhe->flags, NEXTHOP_GROUP_FPM); + + return HASHWALK_CONTINUE; +} + +static int fpm_nhg_send(struct thread *t) +{ + struct fpm_nl_ctx *fnc = THREAD_ARG(t); + struct fpm_nhg_arg fna; + + fna.fnc = fnc; + fna.ctx = dplane_ctx_alloc(); + fna.complete = true; + + /* Send next hops. */ + hash_walk(zrouter.nhgs_id, fpm_nhg_send_cb, &fna); + + /* `free()` allocated memory. */ + dplane_ctx_fini(&fna.ctx); + + /* We are done sending next hops, lets install the routes now. */ + if (fna.complete) + thread_add_timer(zrouter.master, fpm_rib_send, fnc, 0, + &fnc->t_ribwalk); + else /* Otherwise reschedule next hop group again. */ + thread_add_timer(zrouter.master, fpm_nhg_send, fnc, 0, + &fnc->t_nhgwalk); + + return 0; +} + /** * Send all RIB installed routes to the connected data plane. */ @@ -876,6 +1024,23 @@ static int fpm_rmac_send(struct thread *t) return 0; } +/* + * Resets the next hop FPM flags so we send all next hops again. + */ +static void fpm_nhg_reset_cb(struct hash_bucket *bucket, void *arg) +{ + struct nhg_hash_entry *nhe = bucket->data; + + /* Unset FPM installation flag so it gets installed again. */ + UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_FPM); +} + +static int fpm_nhg_reset(struct thread *t) +{ + hash_iterate(zrouter.nhgs_id, fpm_nhg_reset_cb, NULL); + return 0; +} + /** * Resets the RIB FPM flags so we send all routes again. */ @@ -999,6 +1164,12 @@ static int fpm_process_event(struct thread *t) memset(&fnc->counters, 0, sizeof(fnc->counters)); break; + case FNE_TOGGLE_NHG: + zlog_info("%s: toggle next hop groups support", __func__); + fnc->use_nhg = !fnc->use_nhg; + fpm_reconnect(fnc); + break; + default: if (IS_ZEBRA_DEBUG_FPM) zlog_debug("%s: unhandled event %d", __func__, event); @@ -1027,12 +1198,17 @@ static int fpm_nl_start(struct zebra_dplane_provider *prov) TAILQ_INIT(&fnc->ctxqueue); pthread_mutex_init(&fnc->ctxqueue_mutex, NULL); + /* Set default values. */ + fnc->use_nhg = true; + return 0; } static int fpm_nl_finish_early(struct fpm_nl_ctx *fnc) { /* Disable all events and close socket. */ + THREAD_OFF(fnc->t_nhgreset); + THREAD_OFF(fnc->t_nhgwalk); THREAD_OFF(fnc->t_ribreset); THREAD_OFF(fnc->t_ribwalk); THREAD_OFF(fnc->t_rmacreset); @@ -1147,6 +1323,8 @@ static int fpm_nl_new(struct thread_master *tm) install_element(ENABLE_NODE, &fpm_reset_counters_cmd); install_element(CONFIG_NODE, &fpm_set_address_cmd); install_element(CONFIG_NODE, &no_fpm_set_address_cmd); + install_element(CONFIG_NODE, &fpm_use_nhg_cmd); + install_element(CONFIG_NODE, &no_fpm_use_nhg_cmd); return 0; } diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 3f14e8d6af..c743011224 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -482,6 +482,7 @@ static int netlink_extract_vxlan_info(struct rtattr *link_data, struct rtattr *attr[IFLA_VXLAN_MAX + 1]; vni_t vni_in_msg; struct in_addr vtep_ip_in_msg; + ifindex_t ifindex_link; memset(vxl_info, 0, sizeof(*vxl_info)); memset(attr, 0, sizeof(attr)); @@ -510,6 +511,15 @@ static int netlink_extract_vxlan_info(struct rtattr *link_data, *(struct in_addr *)RTA_DATA(attr[IFLA_VXLAN_GROUP]); } + if (!attr[IFLA_VXLAN_LINK]) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("IFLA_VXLAN_LINK missing " + "from VXLAN IF message"); + } else { + ifindex_link = + *(ifindex_t *)RTA_DATA(attr[IFLA_VXLAN_LINK]); + vxl_info->ifindex_link = ifindex_link; + } return 0; } @@ -519,7 +529,8 @@ static int netlink_extract_vxlan_info(struct rtattr *link_data, * its members. Likewise, for VxLAN interface. */ static void netlink_interface_update_l2info(struct interface *ifp, - struct rtattr *link_data, int add) + struct rtattr *link_data, int add, + ns_id_t link_nsid) { if (!link_data) return; @@ -538,7 +549,12 @@ static void netlink_interface_update_l2info(struct interface *ifp, struct zebra_l2info_vxlan vxlan_info; netlink_extract_vxlan_info(link_data, &vxlan_info); + vxlan_info.link_nsid = link_nsid; zebra_l2_vxlanif_add_update(ifp, &vxlan_info, add); + if (link_nsid != NS_UNKNOWN && + vxlan_info.ifindex_link) + zebra_if_update_link(ifp, vxlan_info.ifindex_link, + link_nsid); } } @@ -622,6 +638,7 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) ifindex_t link_ifindex = IFINDEX_INTERNAL; ifindex_t bond_ifindex = IFINDEX_INTERNAL; struct zebra_if *zif; + ns_id_t link_nsid = ns_id; zns = zebra_ns_lookup(ns_id); ifi = NLMSG_DATA(h); @@ -705,6 +722,11 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) if (tb[IFLA_LINK]) link_ifindex = *(ifindex_t *)RTA_DATA(tb[IFLA_LINK]); + if (tb[IFLA_LINK_NETNSID]) { + link_nsid = *(ns_id_t *)RTA_DATA(tb[IFLA_LINK_NETNSID]); + link_nsid = ns_id_get_absolute(ns_id, link_nsid); + } + /* Add interface. * We add by index first because in some cases such as the master * interface, we have the index before we have the name. Fixing @@ -747,9 +769,10 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) /* Extract and save L2 interface information, take additional actions. */ - netlink_interface_update_l2info(ifp, linkinfo[IFLA_INFO_DATA], 1); + netlink_interface_update_l2info(ifp, linkinfo[IFLA_INFO_DATA], + 1, link_nsid); if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp)) - zebra_l2if_update_bridge_slave(ifp, bridge_ifindex); + zebra_l2if_update_bridge_slave(ifp, bridge_ifindex, ns_id); else if (IS_ZEBRA_IF_BOND_SLAVE(ifp)) zebra_l2if_update_bond_slave(ifp, bond_ifindex); @@ -1166,6 +1189,7 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) ifindex_t link_ifindex = IFINDEX_INTERNAL; uint8_t old_hw_addr[INTERFACE_HWADDR_MAX]; struct zebra_if *zif; + ns_id_t link_nsid = ns_id; zns = zebra_ns_lookup(ns_id); ifi = NLMSG_DATA(h); @@ -1233,6 +1257,10 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) if (tb[IFLA_LINK]) link_ifindex = *(ifindex_t *)RTA_DATA(tb[IFLA_LINK]); + if (tb[IFLA_LINK_NETNSID]) { + link_nsid = *(ns_id_t *)RTA_DATA(tb[IFLA_LINK_NETNSID]); + link_nsid = ns_id_get_absolute(ns_id, link_nsid); + } if (tb[IFLA_IFALIAS]) { desc = (char *)RTA_DATA(tb[IFLA_IFALIAS]); } @@ -1317,10 +1345,12 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) /* Extract and save L2 interface information, take * additional actions. */ netlink_interface_update_l2info( - ifp, linkinfo[IFLA_INFO_DATA], 1); + ifp, linkinfo[IFLA_INFO_DATA], + 1, link_nsid); if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp)) zebra_l2if_update_bridge_slave(ifp, - bridge_ifindex); + bridge_ifindex, + ns_id); else if (IS_ZEBRA_IF_BOND_SLAVE(ifp)) zebra_l2if_update_bond_slave(ifp, bond_ifindex); } else if (ifp->vrf_id != vrf_id) { @@ -1419,10 +1449,12 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) /* Extract and save L2 interface information, take * additional actions. */ netlink_interface_update_l2info( - ifp, linkinfo[IFLA_INFO_DATA], 0); + ifp, linkinfo[IFLA_INFO_DATA], + 0, link_nsid); if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp) || was_bridge_slave) zebra_l2if_update_bridge_slave(ifp, - bridge_ifindex); + bridge_ifindex, + ns_id); else if (IS_ZEBRA_IF_BOND_SLAVE(ifp) || was_bond_slave) zebra_l2if_update_bond_slave(ifp, bond_ifindex); } diff --git a/zebra/interface.c b/zebra/interface.c index 9a248ba5d1..9d1f70609b 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -1236,6 +1236,23 @@ static void nbr_connected_dump_vty(struct vty *vty, vty_out(vty, "\n"); } +static const char *zebra_zifslavetype_2str(zebra_slave_iftype_t zif_slave_type) +{ + switch (zif_slave_type) { + case ZEBRA_IF_SLAVE_BRIDGE: + return "Bridge"; + case ZEBRA_IF_SLAVE_VRF: + return "Vrf"; + case ZEBRA_IF_SLAVE_BOND: + return "Bond"; + case ZEBRA_IF_SLAVE_OTHER: + return "Other"; + case ZEBRA_IF_SLAVE_NONE: + return "None"; + } + return "None"; +} + static const char *zebra_ziftype_2str(zebra_iftype_t zif_type) { switch (zif_type) { @@ -1463,6 +1480,9 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) vty_out(vty, " Interface Type %s\n", zebra_ziftype_2str(zebra_if->zif_type)); + vty_out(vty, " Interface Slave Type %s\n", + zebra_zifslavetype_2str(zebra_if->zif_slave_type)); + if (IS_ZEBRA_IF_BRIDGE(ifp)) { struct zebra_l2info_bridge *bridge_info; @@ -1488,6 +1508,17 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) if (vxlan_info->mcast_grp.s_addr != INADDR_ANY) vty_out(vty, " Mcast Group %s", inet_ntoa(vxlan_info->mcast_grp)); + if (vxlan_info->ifindex_link && + (vxlan_info->link_nsid != NS_UNKNOWN)) { + struct interface *ifp; + + ifp = if_lookup_by_index_per_ns( + zebra_ns_lookup(vxlan_info->link_nsid), + vxlan_info->ifindex_link); + vty_out(vty, " Link Interface %s", + ifp == NULL ? "Unknown" : + ifp->name); + } vty_out(vty, "\n"); } diff --git a/zebra/main.c b/zebra/main.c index e230a744f6..f447e9aa07 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -177,7 +177,7 @@ static void sigint(void) vrf_terminate(); rtadv_terminate(); - ns_walk_func(zebra_ns_early_shutdown); + ns_walk_func(zebra_ns_early_shutdown, NULL, NULL); zebra_ns_notify_close(); access_list_reset(); @@ -208,7 +208,7 @@ int zebra_finalize(struct thread *dummy) zlog_info("Zebra final shutdown"); /* Final shutdown of ns resources */ - ns_walk_func(zebra_ns_final_shutdown); + ns_walk_func(zebra_ns_final_shutdown, NULL, NULL); /* Stop dplane thread and finish any cleanup */ zebra_dplane_shutdown(); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 466e985494..2e6cc7cd06 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1552,7 +1552,8 @@ static void netlink_route_nexthop_encap(struct nlmsghdr *n, size_t nlen, * Routing table change via netlink interface, using a dataplane context object */ ssize_t netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx, - uint8_t *data, size_t datalen, bool fpm) + uint8_t *data, size_t datalen, bool fpm, + bool force_nhg) { int bytelen; struct nexthop *nexthop = NULL; @@ -1674,7 +1675,7 @@ ssize_t netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx, RTA_PAYLOAD(rta)); } - if (kernel_nexthops_supported()) { + if (kernel_nexthops_supported() || force_nhg) { /* Kernel supports nexthop objects */ if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( @@ -1954,43 +1955,42 @@ static void _netlink_nexthop_build_group(struct nlmsghdr *n, size_t req_size, } /** - * netlink_nexthop() - Nexthop change via the netlink interface + * Next hop packet encoding helper function. * - * @ctx: Dataplane ctx + * \param[in] cmd netlink command. + * \param[in] ctx dataplane context (information snapshot). + * \param[out] buf buffer to hold the packet. + * \param[in] buflen amount of buffer bytes. * - * Return: Result status + * \returns -1 on failure or the number of bytes written to buf. */ -static int netlink_nexthop(int cmd, struct zebra_dplane_ctx *ctx) +ssize_t netlink_nexthop_encode(uint16_t cmd, const struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) { struct { struct nlmsghdr n; struct nhmsg nhm; - char buf[NL_PKT_BUF_SIZE]; - } req; + char buf[]; + } *req = buf; mpls_lse_t out_lse[MPLS_MAX_LABELS]; char label_buf[256]; int num_labels = 0; - size_t req_size = sizeof(req); - - /* Nothing to do if the kernel doesn't support nexthop objects */ - if (!kernel_nexthops_supported()) - return 0; label_buf[0] = '\0'; - memset(&req, 0, req_size); + memset(req, 0, buflen); - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)); - req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; + req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)); + req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; if (cmd == RTM_NEWNEXTHOP) - req.n.nlmsg_flags |= NLM_F_REPLACE; + req->n.nlmsg_flags |= NLM_F_REPLACE; - req.n.nlmsg_type = cmd; - req.n.nlmsg_pid = dplane_ctx_get_ns(ctx)->nls.snl.nl_pid; + req->n.nlmsg_type = cmd; + req->n.nlmsg_pid = dplane_ctx_get_ns(ctx)->nls.snl.nl_pid; - req.nhm.nh_family = AF_UNSPEC; + req->nhm.nh_family = AF_UNSPEC; /* TODO: Scope? */ uint32_t id = dplane_ctx_get_nhe_id(ctx); @@ -2002,7 +2002,7 @@ static int netlink_nexthop(int cmd, struct zebra_dplane_ctx *ctx) return -1; } - addattr32(&req.n, req_size, NHA_ID, id); + addattr32(&req->n, buflen, NHA_ID, id); if (cmd == RTM_NEWNEXTHOP) { /* @@ -2013,7 +2013,7 @@ static int netlink_nexthop(int cmd, struct zebra_dplane_ctx *ctx) */ if (dplane_ctx_get_nhe_nh_grp_count(ctx)) _netlink_nexthop_build_group( - &req.n, req_size, id, + &req->n, buflen, id, dplane_ctx_get_nhe_nh_grp(ctx), dplane_ctx_get_nhe_nh_grp_count(ctx)); else { @@ -2022,23 +2022,23 @@ static int netlink_nexthop(int cmd, struct zebra_dplane_ctx *ctx) afi_t afi = dplane_ctx_get_nhe_afi(ctx); if (afi == AFI_IP) - req.nhm.nh_family = AF_INET; + req->nhm.nh_family = AF_INET; else if (afi == AFI_IP6) - req.nhm.nh_family = AF_INET6; + req->nhm.nh_family = AF_INET6; switch (nh->type) { case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: - addattr_l(&req.n, req_size, NHA_GATEWAY, + addattr_l(&req->n, buflen, NHA_GATEWAY, &nh->gate.ipv4, IPV4_MAX_BYTELEN); break; case NEXTHOP_TYPE_IPV6: case NEXTHOP_TYPE_IPV6_IFINDEX: - addattr_l(&req.n, req_size, NHA_GATEWAY, + addattr_l(&req->n, buflen, NHA_GATEWAY, &nh->gate.ipv6, IPV6_MAX_BYTELEN); break; case NEXTHOP_TYPE_BLACKHOLE: - addattr_l(&req.n, req_size, NHA_BLACKHOLE, NULL, + addattr_l(&req->n, buflen, NHA_BLACKHOLE, NULL, 0); /* Blackhole shouldn't have anymore attributes */ @@ -2055,10 +2055,10 @@ static int netlink_nexthop(int cmd, struct zebra_dplane_ctx *ctx) return -1; } - addattr32(&req.n, req_size, NHA_OIF, nh->ifindex); + addattr32(&req->n, buflen, NHA_OIF, nh->ifindex); if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_ONLINK)) - req.nhm.nh_flags |= RTNH_F_ONLINK; + req->nhm.nh_flags |= RTNH_F_ONLINK; num_labels = build_label_stack(nh->nh_label, out_lse, @@ -2072,10 +2072,10 @@ static int netlink_nexthop(int cmd, struct zebra_dplane_ctx *ctx) /* * TODO: MPLS unsupported for now in kernel. */ - if (req.nhm.nh_family == AF_MPLS) + if (req->nhm.nh_family == AF_MPLS) goto nexthop_done; #if 0 - addattr_l(&req.n, req_size, NHA_NEWDST, + addattr_l(&req->n, buflen, NHA_NEWDST, &out_lse, num_labels * sizeof(mpls_lse_t)); @@ -2084,16 +2084,16 @@ static int netlink_nexthop(int cmd, struct zebra_dplane_ctx *ctx) struct rtattr *nest; uint16_t encap = LWTUNNEL_ENCAP_MPLS; - addattr_l(&req.n, req_size, + addattr_l(&req->n, buflen, NHA_ENCAP_TYPE, &encap, sizeof(uint16_t)); - nest = addattr_nest(&req.n, req_size, + nest = addattr_nest(&req->n, buflen, NHA_ENCAP); - addattr_l(&req.n, req_size, + addattr_l(&req->n, buflen, MPLS_IPTUNNEL_DST, &out_lse, num_labels * sizeof(mpls_lse_t)); - addattr_nest_end(&req.n, nest); + addattr_nest_end(&req->n, nest); } } @@ -2106,7 +2106,8 @@ nexthop_done: nh->vrf_id, label_buf); } - req.nhm.nh_protocol = zebra2proto(dplane_ctx_get_nhe_type(ctx)); + req->nhm.nh_protocol = + zebra2proto(dplane_ctx_get_nhe_type(ctx)); } else if (cmd != RTM_DELNEXTHOP) { flog_err( @@ -2120,8 +2121,7 @@ nexthop_done: zlog_debug("%s: %s, id=%u", __func__, nl_msg_type_to_str(cmd), id); - return netlink_talk_info(netlink_talk_filter, &req.n, - dplane_ctx_get_ns(ctx), 0); + return NLMSG_ALIGN(req->n.nlmsg_len); } /** @@ -2136,6 +2136,7 @@ enum zebra_dplane_result kernel_nexthop_update(struct zebra_dplane_ctx *ctx) enum dplane_op_e op; int cmd = 0; int ret = 0; + char buf[NL_PKT_BUF_SIZE]; op = dplane_ctx_get_op(ctx); if (op == DPLANE_OP_NH_INSTALL || op == DPLANE_OP_NH_UPDATE) @@ -2149,7 +2150,15 @@ enum zebra_dplane_result kernel_nexthop_update(struct zebra_dplane_ctx *ctx) return ZEBRA_DPLANE_REQUEST_FAILURE; } - ret = netlink_nexthop(cmd, ctx); + /* Nothing to do if the kernel doesn't support nexthop objects */ + if (!kernel_nexthops_supported()) + return ZEBRA_DPLANE_REQUEST_SUCCESS; + + if (netlink_nexthop_encode(cmd, ctx, buf, sizeof(buf)) > 0) + ret = netlink_talk_info(netlink_talk_filter, (void *)&buf, + dplane_ctx_get_ns(ctx), 0); + else + ret = 0; return (ret == 0 ? ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE); @@ -2188,7 +2197,7 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx) !RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx))) { netlink_route_multipath(RTM_DELROUTE, ctx, nl_pkt, sizeof(nl_pkt), - false); + false, false); netlink_talk_info(netlink_talk_filter, (struct nlmsghdr *)nl_pkt, dplane_ctx_get_ns(ctx), 0); @@ -2209,7 +2218,7 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx) if (!RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx))) { netlink_route_multipath(RTM_DELROUTE, ctx, nl_pkt, sizeof(nl_pkt), - false); + false, false); netlink_talk_info(netlink_talk_filter, (struct nlmsghdr *)nl_pkt, dplane_ctx_get_ns(ctx), 0); @@ -2222,7 +2231,7 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx) } if (!RSYSTEM_ROUTE(dplane_ctx_get_type(ctx))) { - netlink_route_multipath(cmd, ctx, nl_pkt, sizeof(nl_pkt), + netlink_route_multipath(cmd, ctx, nl_pkt, sizeof(nl_pkt), false, false); ret = netlink_talk_info(netlink_talk_filter, (struct nlmsghdr *)nl_pkt, @@ -2552,10 +2561,15 @@ int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, /** * netlink_update_neigh_ctx_internal() - Common helper api for evpn * neighbor updates using dataplane context object. + * Here, a neighbor refers to a bridge forwarding database entry for + * either unicast forwarding or head-end replication or an IP neighbor + * entry. * @ctx: Dataplane context * @cmd: Netlink command (RTM_NEWNEIGH or RTM_DELNEIGH) * @mac: A neighbor cache link layer address * @ip: A neighbor cache n/w layer destination address + * In the case of bridge FDB, this represnts the remote + * VTEP IP. * @replace_obj: Whether NEW request should replace existing object or * add to the end of the list * @family: AF_* netlink family @@ -2975,7 +2989,6 @@ netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, uint8_t *data, int cmd; uint8_t flags; uint16_t state; - uint8_t nl_pkt[NL_PKT_BUF_SIZE]; cmd = dplane_ctx_get_op(ctx) == DPLANE_OP_MAC_INSTALL ? RTM_NEWNEIGH : RTM_DELNEIGH; @@ -3014,9 +3027,8 @@ netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, uint8_t *data, } total = netlink_update_neigh_ctx_internal( - ctx, cmd, dplane_ctx_mac_get_addr(ctx), - dplane_ctx_neigh_get_ipaddr(ctx), true, AF_BRIDGE, 0, - flags, state, nl_pkt, sizeof(nl_pkt)); + ctx, cmd, dplane_ctx_mac_get_addr(ctx), &vtep_ip, + true, AF_BRIDGE, 0, flags, state, data, datalen); return total; } diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h index a364d305c5..c09609f47c 100644 --- a/zebra/rt_netlink.h +++ b/zebra/rt_netlink.h @@ -67,8 +67,8 @@ void rt_netlink_init(void); extern int netlink_mpls_multipath(int cmd, struct zebra_dplane_ctx *ctx); extern ssize_t netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx, - uint8_t *data, size_t datalen, - bool fpm); + uint8_t *data, size_t datalen, bool fpm, + bool force_nhg); extern ssize_t netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, uint8_t *data, size_t datalen); @@ -78,6 +78,9 @@ extern int netlink_route_read(struct zebra_ns *zns); extern int netlink_nexthop_change(struct nlmsghdr *h, ns_id_t ns_id, int startup); extern int netlink_nexthop_read(struct zebra_ns *zns); +extern ssize_t netlink_nexthop_encode(uint16_t cmd, + const struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen); extern int netlink_neigh_change(struct nlmsghdr *h, ns_id_t ns_id); extern int netlink_macfdb_read(struct zebra_ns *zns); diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index cc8cab1ff5..278e894d06 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -1658,9 +1658,8 @@ done: * * Return: Result status */ -static int dplane_ctx_nexthop_init(struct zebra_dplane_ctx *ctx, - enum dplane_op_e op, - struct nhg_hash_entry *nhe) +int dplane_ctx_nexthop_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, + struct nhg_hash_entry *nhe) { struct zebra_vrf *zvrf = NULL; struct zebra_ns *zns = NULL; diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index f01ca2e84c..9ce542944d 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -499,6 +499,10 @@ enum zebra_dplane_result dplane_vtep_delete(const struct interface *ifp, int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, struct route_node *rn, struct route_entry *re); +/* Encode next hop information into data plane context. */ +int dplane_ctx_nexthop_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, + struct nhg_hash_entry *nhe); + /* Retrieve the limit on the number of pending, unprocessed updates. */ uint32_t dplane_get_in_queue_limit(void); diff --git a/zebra/zebra_l2.c b/zebra/zebra_l2.c index e67b5cddb4..4c0cc62fbf 100644 --- a/zebra/zebra_l2.c +++ b/zebra/zebra_l2.c @@ -53,7 +53,13 @@ static void map_slaves_to_bridge(struct interface *br_if, int link) { struct vrf *vrf; struct interface *ifp; + struct zebra_vrf *zvrf; + struct zebra_ns *zns; + zvrf = zebra_vrf_lookup_by_id(br_if->vrf_id); + assert(zvrf); + zns = zvrf->zns; + assert(zns); RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { FOR_ALL_INTERFACES (vrf, ifp) { struct zebra_if *zif; @@ -72,7 +78,8 @@ static void map_slaves_to_bridge(struct interface *br_if, int link) br_slave = &zif->brslave_info; if (link) { - if (br_slave->bridge_ifindex == br_if->ifindex) + if (br_slave->bridge_ifindex == br_if->ifindex && + br_slave->ns_id == zns->ns_id) br_slave->br_if = br_if; } else { if (br_slave->br_if == br_if) @@ -83,12 +90,14 @@ static void map_slaves_to_bridge(struct interface *br_if, int link) } /* Public functions */ -void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave) +void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave, + struct zebra_ns *zns) { struct interface *br_if; /* TODO: Handle change of master */ - br_if = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), + assert(zns); + br_if = if_lookup_by_index_per_ns(zebra_ns_lookup(zns->ns_id), br_slave->bridge_ifindex); if (br_if) br_slave->br_if = br_if; @@ -237,23 +246,32 @@ void zebra_l2_vxlanif_del(struct interface *ifp) * from a bridge before it can be mapped to another bridge. */ void zebra_l2if_update_bridge_slave(struct interface *ifp, - ifindex_t bridge_ifindex) + ifindex_t bridge_ifindex, + ns_id_t ns_id) { struct zebra_if *zif; ifindex_t old_bridge_ifindex; + ns_id_t old_ns_id; + struct zebra_vrf *zvrf; zif = ifp->info; assert(zif); + zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); + if (!zvrf) + return; + old_bridge_ifindex = zif->brslave_info.bridge_ifindex; - if (old_bridge_ifindex == bridge_ifindex) + old_ns_id = zif->brslave_info.ns_id; + if (old_bridge_ifindex == bridge_ifindex && + old_ns_id == zif->brslave_info.ns_id) return; + zif->brslave_info.ns_id = ns_id; zif->brslave_info.bridge_ifindex = bridge_ifindex; - /* Set up or remove link with master */ if (bridge_ifindex != IFINDEX_INTERNAL) { - zebra_l2_map_slave_to_bridge(&zif->brslave_info); + zebra_l2_map_slave_to_bridge(&zif->brslave_info, zvrf->zns); /* In the case of VxLAN, invoke the handler for EVPN. */ if (zif->zif_type == ZEBRA_IF_VXLAN) zebra_vxlan_if_update(ifp, ZEBRA_VXLIF_MASTER_CHANGE); diff --git a/zebra/zebra_l2.h b/zebra/zebra_l2.h index d9f0eec3f8..a3c780ee09 100644 --- a/zebra/zebra_l2.h +++ b/zebra/zebra_l2.h @@ -37,6 +37,7 @@ extern "C" { struct zebra_l2info_brslave { ifindex_t bridge_ifindex; /* Bridge Master */ struct interface *br_if; /* Pointer to master */ + ns_id_t ns_id; /* network namespace where bridge is */ }; /* zebra L2 interface information - bridge interface */ @@ -55,6 +56,10 @@ struct zebra_l2info_vxlan { struct in_addr vtep_ip; /* Local tunnel IP */ vlanid_t access_vlan; /* Access VLAN - for VLAN-aware bridge. */ struct in_addr mcast_grp; + ifindex_t ifindex_link; /* Interface index of interface + * linked with VXLAN + */ + ns_id_t link_nsid; }; struct zebra_l2info_bondslave { @@ -77,7 +82,8 @@ union zebra_l2if_info { #define IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(zif) ((zif)->l2info.br.vlan_aware == 1) -extern void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave); +extern void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave, + struct zebra_ns *zns); extern void zebra_l2_unmap_slave_from_bridge(struct zebra_l2info_brslave *br_slave); extern void @@ -97,7 +103,8 @@ extern void zebra_l2_vxlanif_update_access_vlan(struct interface *ifp, vlanid_t access_vlan); extern void zebra_l2_vxlanif_del(struct interface *ifp); extern void zebra_l2if_update_bridge_slave(struct interface *ifp, - ifindex_t bridge_ifindex); + ifindex_t bridge_ifindex, + ns_id_t ns_id); extern void zebra_l2if_update_bond_slave(struct interface *ifp, ifindex_t bond_ifindex); diff --git a/zebra/zebra_netns_id.c b/zebra/zebra_netns_id.c index 77a9a7c368..0d86421b9e 100644 --- a/zebra/zebra_netns_id.c +++ b/zebra/zebra_netns_id.c @@ -159,27 +159,34 @@ static ns_id_t extract_nsid(struct nlmsghdr *nlh, char *buf) return ns_id; } -ns_id_t zebra_ns_id_get(const char *netnspath) +/* fd_param = -1 is ignored. + * netnspath set to null is ignored. + * one of the 2 params is mandatory. netnspath is looked in priority + */ +ns_id_t zebra_ns_id_get(const char *netnspath, int fd_param) { int ns_id = -1; struct sockaddr_nl snl; - int fd, sock, ret; + int fd = -1, sock, ret; unsigned int seq; ns_id_t return_nsid = NS_UNKNOWN; /* netns path check */ - if (!netnspath) - return NS_UNKNOWN; - fd = open(netnspath, O_RDONLY); - if (fd == -1) + if (!netnspath && fd_param == -1) return NS_UNKNOWN; - + if (netnspath) { + fd = open(netnspath, O_RDONLY); + if (fd == -1) + return NS_UNKNOWN; + } else if (fd_param != -1) + fd = fd_param; /* netlink socket */ sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (sock < 0) { flog_err_sys(EC_LIB_SOCKET, "netlink( %u) socket() error: %s", sock, safe_strerror(errno)); - close(fd); + if (fd_param == -1) + close(fd); return NS_UNKNOWN; } memset(&snl, 0, sizeof(snl)); @@ -192,7 +199,8 @@ ns_id_t zebra_ns_id_get(const char *netnspath) "netlink( %u) socket() bind error: %s", sock, safe_strerror(errno)); close(sock); - close(fd); + if (fd_param == -1) + close(fd); return NS_UNKNOWN; } @@ -214,7 +222,8 @@ ns_id_t zebra_ns_id_get(const char *netnspath) ret = send_receive(sock, nlh, seq, buf); if (ret < 0) { close(sock); - close(fd); + if (fd_param == -1) + close(fd); return NS_UNKNOWN; } nlh = (struct nlmsghdr *)buf; @@ -258,7 +267,8 @@ ns_id_t zebra_ns_id_get(const char *netnspath) "netlink( %u) recvfrom() error 2 when reading: %s", fd, safe_strerror(errno)); close(sock); - close(fd); + if (fd_param == -1) + close(fd); if (errno == ENOTSUP) { zlog_debug("NEWNSID locally generated"); return zebra_ns_id_get_fallback(netnspath); @@ -278,7 +288,8 @@ ns_id_t zebra_ns_id_get(const char *netnspath) ret = send_receive(sock, nlh, seq, buf); if (ret < 0) { close(sock); - close(fd); + if (fd_param == -1) + close(fd); return NS_UNKNOWN; } nlh = (struct nlmsghdr *)buf; @@ -309,16 +320,18 @@ ns_id_t zebra_ns_id_get(const char *netnspath) } while (len != 0 && ret == 0); } - close(fd); + if (fd_param == -1) + close(fd); close(sock); return return_nsid; } #else -ns_id_t zebra_ns_id_get(const char *netnspath) +ns_id_t zebra_ns_id_get(const char *netnspath, int fd __attribute__ ((unused))) { return zebra_ns_id_get_fallback(netnspath); } + #endif /* ! defined(HAVE_NETLINK) */ #ifdef HAVE_NETNS @@ -354,7 +367,7 @@ ns_id_t zebra_ns_id_get_default(void) return NS_DEFAULT_INTERNAL; } close(fd); - return zebra_ns_id_get((char *)NS_DEFAULT_NAME); + return zebra_ns_id_get((char *)NS_DEFAULT_NAME, -1); #else /* HAVE_NETNS */ return NS_DEFAULT_INTERNAL; #endif /* !HAVE_NETNS */ diff --git a/zebra/zebra_netns_id.h b/zebra/zebra_netns_id.h index 7a5f6851f4..dd9eab18e0 100644 --- a/zebra/zebra_netns_id.h +++ b/zebra/zebra_netns_id.h @@ -24,7 +24,7 @@ extern "C" { #endif -extern ns_id_t zebra_ns_id_get(const char *netnspath); +extern ns_id_t zebra_ns_id_get(const char *netnspath, int fd); extern ns_id_t zebra_ns_id_get_default(void); #ifdef __cplusplus diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c index c5d11f1837..72e4fd0055 100644 --- a/zebra/zebra_netns_notify.c +++ b/zebra/zebra_netns_notify.c @@ -72,13 +72,14 @@ static void zebra_ns_notify_create_context_from_entry_name(const char *name) char *netnspath = ns_netns_pathname(NULL, name); struct vrf *vrf; int ret; - ns_id_t ns_id, ns_id_external; + ns_id_t ns_id, ns_id_external, ns_id_relative = NS_UNKNOWN; + struct ns *default_ns; if (netnspath == NULL) return; frr_with_privs(&zserv_privs) { - ns_id = zebra_ns_id_get(netnspath); + ns_id = zebra_ns_id_get(netnspath, -1); } if (ns_id == NS_UNKNOWN) return; @@ -97,9 +98,21 @@ static void zebra_ns_notify_create_context_from_entry_name(const char *name) ns_map_nsid_with_external(ns_id, false); return; } + + default_ns = ns_get_default(); + + /* force kernel ns_id creation in that new vrf */ + frr_with_privs(&zserv_privs) { + ns_switch_to_netns(netnspath); + ns_id_relative = zebra_ns_id_get(NULL, default_ns->fd); + ns_switchback_to_initial(); + } + frr_with_privs(&zserv_privs) { ret = vrf_netns_handler_create(NULL, vrf, netnspath, - ns_id_external, ns_id); + ns_id_external, + ns_id, + ns_id_relative); } if (ret != CMD_SUCCESS) { flog_warn(EC_ZEBRA_NS_VRF_CREATION_FAILED, diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index fdbeac88e1..1ac18c6fdd 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -1681,17 +1681,37 @@ static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop, /* Copy labels of the resolved route and the parent resolving to it */ if (newhop->nh_label) { - for (i = 0; i < newhop->nh_label->num_labels; i++) + for (i = 0; i < newhop->nh_label->num_labels; i++) { + /* Be a bit picky about overrunning the local array */ + if (num_labels >= MPLS_MAX_LABELS) { + if (IS_ZEBRA_DEBUG_NHG || IS_ZEBRA_DEBUG_RIB) + zlog_debug("%s: too many labels in newhop %pNHv", + __func__, newhop); + break; + } labels[num_labels++] = newhop->nh_label->label[i]; + } + /* Use the "outer" type */ label_type = newhop->nh_label_type; } if (nexthop->nh_label) { - for (i = 0; i < nexthop->nh_label->num_labels; i++) + for (i = 0; i < nexthop->nh_label->num_labels; i++) { + /* Be a bit picky about overrunning the local array */ + if (num_labels >= MPLS_MAX_LABELS) { + if (IS_ZEBRA_DEBUG_NHG || IS_ZEBRA_DEBUG_RIB) + zlog_debug("%s: too many labels in nexthop %pNHv", + __func__, nexthop); + break; + } labels[num_labels++] = nexthop->nh_label->label[i]; + } - /* If the parent has labels, use its type */ - label_type = nexthop->nh_label_type; + /* If the parent has labels, use its type if + * we don't already have one. + */ + if (label_type == ZEBRA_LSP_NONE) + label_type = nexthop->nh_label_type; } if (num_labels) diff --git a/zebra/zebra_nhg.h b/zebra/zebra_nhg.h index 5792584d0f..785ce20b75 100644 --- a/zebra/zebra_nhg.h +++ b/zebra/zebra_nhg.h @@ -117,6 +117,10 @@ struct nhg_hash_entry { */ #define NEXTHOP_GROUP_BACKUP (1 << 5) +/* + * Track FPM installation status.. + */ +#define NEXTHOP_GROUP_FPM (1 << 6) }; /* Was this one we created, either this session or previously? */ diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 4e51437337..6462daf687 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -153,20 +153,25 @@ static int zebra_ns_disable_internal(struct zebra_ns *zns, bool complete) /* During zebra shutdown, do partial cleanup while the async dataplane * is still running. */ -int zebra_ns_early_shutdown(struct ns *ns) +int zebra_ns_early_shutdown(struct ns *ns, + void *param_in __attribute__((unused)), + void **param_out __attribute__((unused))) { struct zebra_ns *zns = ns->info; if (zns == NULL) return 0; - return zebra_ns_disable_internal(zns, false); + zebra_ns_disable_internal(zns, false); + return NS_WALK_CONTINUE; } /* During zebra shutdown, do final cleanup * after all dataplane work is complete. */ -int zebra_ns_final_shutdown(struct ns *ns) +int zebra_ns_final_shutdown(struct ns *ns, + void *param_in __attribute__((unused)), + void **param_out __attribute__((unused))) { struct zebra_ns *zns = ns->info; @@ -175,7 +180,7 @@ int zebra_ns_final_shutdown(struct ns *ns) kernel_terminate(zns, true); - return 0; + return NS_WALK_CONTINUE; } int zebra_ns_init(const char *optional_default_name) @@ -183,12 +188,16 @@ int zebra_ns_init(const char *optional_default_name) struct ns *default_ns; ns_id_t ns_id; ns_id_t ns_id_external; + struct ns *ns; frr_with_privs(&zserv_privs) { ns_id = zebra_ns_id_get_default(); } ns_id_external = ns_map_nsid_with_external(ns_id, true); ns_init_management(ns_id_external, ns_id); + ns = ns_get_default(); + if (ns) + ns->relative_default_ns = ns_id; default_ns = ns_lookup(ns_get_default_id()); if (!default_ns) { diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index dc79a83db0..f7d1f40782 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -67,9 +67,12 @@ struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id); int zebra_ns_init(const char *optional_default_name); int zebra_ns_enable(ns_id_t ns_id, void **info); int zebra_ns_disabled(struct ns *ns); -int zebra_ns_early_shutdown(struct ns *ns); -int zebra_ns_final_shutdown(struct ns *ns); - +int zebra_ns_early_shutdown(struct ns *ns, + void *param_in __attribute__((unused)), + void **param_out __attribute__((unused))); +int zebra_ns_final_shutdown(struct ns *ns, + void *param_in __attribute__((unused)), + void **param_out __attribute__((unused))); int zebra_ns_config_write(struct vty *vty, struct ns *ns); #ifdef __cplusplus diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index d85f48e570..998c035656 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -1194,7 +1194,7 @@ static void zvni_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json) ifindex_t ifindex; ifindex = mac->fwd_info.local.ifindex; - zns = zebra_ns_lookup(NS_DEFAULT); + zns = zebra_ns_lookup(mac->fwd_info.local.ns_id); ifp = if_lookup_by_index_per_ns(zns, ifindex); if (!ifp) return; @@ -1276,7 +1276,7 @@ static void zvni_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json) ifindex_t ifindex; ifindex = mac->fwd_info.local.ifindex; - zns = zebra_ns_lookup(NS_DEFAULT); + zns = zebra_ns_lookup(mac->fwd_info.local.ns_id); ifp = if_lookup_by_index_per_ns(zns, ifindex); if (!ifp) return; @@ -1368,7 +1368,7 @@ static void zvni_print_mac_hash(struct hash_bucket *bucket, void *ctxt) if (wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) return; - zns = zebra_ns_lookup(NS_DEFAULT); + zns = zebra_ns_lookup(mac->fwd_info.local.ns_id); ifindex = mac->fwd_info.local.ifindex; ifp = if_lookup_by_index_per_ns(zns, ifindex); if (!ifp) // unexpected @@ -2731,7 +2731,12 @@ static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni, zebra_mac_t *mac = NULL; struct zebra_if *zif = NULL; struct zebra_l2info_vxlan *vxl = NULL; + struct zebra_vrf *zvrf; + ns_id_t local_ns_id = NS_DEFAULT; + zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); + if (zvrf && zvrf->zns) + local_ns_id = zvrf->zns->ns_id; zif = zvni->vxlan_if->info; if (!zif) return -1; @@ -2756,6 +2761,7 @@ static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni, SET_FLAG(mac->flags, ZEBRA_MAC_DEF_GW); memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); mac->fwd_info.local.ifindex = ifp->ifindex; + mac->fwd_info.local.ns_id = local_ns_id; mac->fwd_info.local.vid = vxl->access_vlan; n = zvni_neigh_lookup(zvni, ip); @@ -3522,6 +3528,68 @@ static int zvni_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr) 0 /* seq */, ZEBRA_NEIGH_ACTIVE, ZEBRA_MACIP_DEL); } +struct zvni_from_svi_param { + struct interface *br_if; + struct interface *svi_if; + struct zebra_if *zif; + uint8_t bridge_vlan_aware; + vlanid_t vid; +}; + +static int zvni_map_vlan_ns(struct ns *ns, + void *_in_param, + void **_p_zvni) +{ + struct zebra_ns *zns = ns->info; + struct route_node *rn; + struct interface *br_if; + zebra_vni_t **p_zvni = (zebra_vni_t **)_p_zvni; + zebra_vni_t *zvni; + struct interface *tmp_if = NULL; + struct zebra_if *zif; + struct zebra_l2info_vxlan *vxl = NULL; + struct zvni_from_svi_param *in_param = + (struct zvni_from_svi_param *)_in_param; + int found = 0; + + if (!in_param) + return NS_WALK_STOP; + br_if = in_param->br_if; + zif = in_param->zif; + assert(zif); + assert(br_if); + + /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ + /* TODO: Optimize with a hash. */ + for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { + tmp_if = (struct interface *)rn->info; + if (!tmp_if) + continue; + zif = tmp_if->info; + if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) + continue; + if (!if_is_operative(tmp_if)) + continue; + vxl = &zif->l2info.vxl; + + if (zif->brslave_info.br_if != br_if) + continue; + + if (!in_param->bridge_vlan_aware + || vxl->access_vlan == in_param->vid) { + found = 1; + break; + } + } + if (!found) + return NS_WALK_CONTINUE; + + zvni = zvni_lookup(vxl->vni); + if (p_zvni) + *p_zvni = zvni; + return NS_WALK_STOP; +} + /* * Map port or (port, VLAN) to a VNI. This is invoked upon getting MAC * notifications, to see if they are of interest. @@ -3529,25 +3597,51 @@ static int zvni_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr) static zebra_vni_t *zvni_map_vlan(struct interface *ifp, struct interface *br_if, vlanid_t vid) { - struct zebra_ns *zns; - struct route_node *rn; - struct interface *tmp_if = NULL; struct zebra_if *zif; struct zebra_l2info_bridge *br; - struct zebra_l2info_vxlan *vxl = NULL; - uint8_t bridge_vlan_aware; - zebra_vni_t *zvni; - int found = 0; + zebra_vni_t **p_zvni; + zebra_vni_t *zvni = NULL; + struct zvni_from_svi_param in_param; /* Determine if bridge is VLAN-aware or not */ zif = br_if->info; assert(zif); br = &zif->l2info.br; - bridge_vlan_aware = br->vlan_aware; + in_param.bridge_vlan_aware = br->vlan_aware; + in_param.vid = vid; + in_param.br_if = br_if; + in_param.zif = zif; + p_zvni = &zvni; + + ns_walk_func(zvni_map_vlan_ns, + (void *)&in_param, + (void **)p_zvni); + return zvni; +} + +static int zvni_from_svi_ns(struct ns *ns, + void *_in_param, + void **_p_zvni) +{ + struct zebra_ns *zns = ns->info; + struct route_node *rn; + struct interface *br_if; + zebra_vni_t **p_zvni = (zebra_vni_t **)_p_zvni; + zebra_vni_t *zvni; + struct interface *tmp_if = NULL; + struct zebra_if *zif; + struct zebra_l2info_vxlan *vxl = NULL; + struct zvni_from_svi_param *in_param = + (struct zvni_from_svi_param *)_in_param; + int found = 0; + + if (!in_param) + return NS_WALK_STOP; + br_if = in_param->br_if; + zif = in_param->zif; + assert(zif); - /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ /* TODO: Optimize with a hash. */ - zns = zebra_ns_lookup(NS_DEFAULT); for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { tmp_if = (struct interface *)rn->info; if (!tmp_if) @@ -3562,17 +3656,20 @@ static zebra_vni_t *zvni_map_vlan(struct interface *ifp, if (zif->brslave_info.br_if != br_if) continue; - if (!bridge_vlan_aware || vxl->access_vlan == vid) { + if (!in_param->bridge_vlan_aware + || vxl->access_vlan == !in_param->vid) { found = 1; break; } } if (!found) - return NULL; + return NS_WALK_CONTINUE; zvni = zvni_lookup(vxl->vni); - return zvni; + if (p_zvni) + *p_zvni = zvni; + return NS_WALK_STOP; } /* @@ -3582,16 +3679,11 @@ static zebra_vni_t *zvni_map_vlan(struct interface *ifp, static zebra_vni_t *zvni_from_svi(struct interface *ifp, struct interface *br_if) { - struct zebra_ns *zns; - struct route_node *rn; - struct interface *tmp_if = NULL; - struct zebra_if *zif; struct zebra_l2info_bridge *br; - struct zebra_l2info_vxlan *vxl = NULL; - uint8_t bridge_vlan_aware; - vlanid_t vid = 0; - zebra_vni_t *zvni; - int found = 0; + zebra_vni_t *zvni = NULL; + zebra_vni_t **p_zvni; + struct zebra_if *zif; + struct zvni_from_svi_param in_param; if (!br_if) return NULL; @@ -3604,8 +3696,10 @@ static zebra_vni_t *zvni_from_svi(struct interface *ifp, zif = br_if->info; assert(zif); br = &zif->l2info.br; - bridge_vlan_aware = br->vlan_aware; - if (bridge_vlan_aware) { + in_param.bridge_vlan_aware = br->vlan_aware; + in_param.vid = 0; + + if (in_param.bridge_vlan_aware) { struct zebra_l2info_vlan *vl; if (!IS_ZEBRA_IF_VLAN(ifp)) @@ -3614,37 +3708,54 @@ static zebra_vni_t *zvni_from_svi(struct interface *ifp, zif = ifp->info; assert(zif); vl = &zif->l2info.vl; - vid = vl->vid; + in_param.vid = vl->vid; } + in_param.br_if = br_if; + in_param.zif = zif; + p_zvni = &zvni; /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ + ns_walk_func(zvni_from_svi_ns, + (void *)&in_param, + (void **)p_zvni); + return zvni; +} + +static int zvni_map_to_svi_ns(struct ns *ns, + void *_in_param, + void **_p_ifp) +{ + struct zebra_ns *zns = ns->info; + struct route_node *rn; + struct zvni_from_svi_param *in_param = + (struct zvni_from_svi_param *)_in_param; + struct zebra_l2info_vlan *vl; + struct interface *tmp_if = NULL; + struct interface **p_ifp = (struct interface **)_p_ifp; + struct zebra_if *zif; + + if (!in_param) + return NS_WALK_STOP; + /* TODO: Optimize with a hash. */ - zns = zebra_ns_lookup(NS_DEFAULT); for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { tmp_if = (struct interface *)rn->info; - if (!tmp_if) + /* Check oper status of the SVI. */ + if (!tmp_if || !if_is_operative(tmp_if)) continue; zif = tmp_if->info; - if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) - continue; - if (!if_is_operative(tmp_if)) - continue; - vxl = &zif->l2info.vxl; - - if (zif->brslave_info.br_if != br_if) + if (!zif || zif->zif_type != ZEBRA_IF_VLAN + || zif->link != in_param->br_if) continue; + vl = (struct zebra_l2info_vlan *)&zif->l2info.vl; - if (!bridge_vlan_aware || vxl->access_vlan == vid) { - found = 1; - break; + if (vl->vid == in_param->vid) { + if (p_ifp) + *p_ifp = tmp_if; + return NS_WALK_STOP; } } - - if (!found) - return NULL; - - zvni = zvni_lookup(vxl->vni); - return zvni; + return NS_WALK_CONTINUE; } /* Map to SVI on bridge corresponding to specified VLAN. This can be one @@ -3656,15 +3767,11 @@ static zebra_vni_t *zvni_from_svi(struct interface *ifp, */ static struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if) { - struct zebra_ns *zns; - struct route_node *rn; struct interface *tmp_if = NULL; struct zebra_if *zif; struct zebra_l2info_bridge *br; - struct zebra_l2info_vlan *vl; - uint8_t bridge_vlan_aware; - int found = 0; - + struct zvni_from_svi_param in_param; + struct interface **p_ifp; /* Defensive check, caller expected to invoke only with valid bridge. */ if (!br_if) return NULL; @@ -3673,33 +3780,56 @@ static struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if) zif = br_if->info; assert(zif); br = &zif->l2info.br; - bridge_vlan_aware = br->vlan_aware; - + in_param.bridge_vlan_aware = br->vlan_aware; /* Check oper status of the SVI. */ - if (!bridge_vlan_aware) + if (!in_param.bridge_vlan_aware) return if_is_operative(br_if) ? br_if : NULL; + in_param.vid = vid; + in_param.br_if = br_if; + in_param.zif = NULL; + p_ifp = &tmp_if; + /* Identify corresponding VLAN interface. */ + ns_walk_func(zvni_map_to_svi_ns, + (void *)&in_param, + (void **)p_ifp); + return tmp_if; +} + +static int zvni_map_to_macvlan_ns(struct ns *ns, + void *_in_param, + void **_p_ifp) +{ + struct zebra_ns *zns = ns->info; + struct zvni_from_svi_param *in_param = + (struct zvni_from_svi_param *)_in_param; + struct interface **p_ifp = (struct interface **)_p_ifp; + struct route_node *rn; + struct interface *tmp_if = NULL; + struct zebra_if *zif; + + if (!in_param) + return NS_WALK_STOP; + /* Identify corresponding VLAN interface. */ - /* TODO: Optimize with a hash. */ - zns = zebra_ns_lookup(NS_DEFAULT); for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { tmp_if = (struct interface *)rn->info; /* Check oper status of the SVI. */ if (!tmp_if || !if_is_operative(tmp_if)) continue; zif = tmp_if->info; - if (!zif || zif->zif_type != ZEBRA_IF_VLAN - || zif->link != br_if) + + if (!zif || zif->zif_type != ZEBRA_IF_MACVLAN) continue; - vl = &zif->l2info.vl; - if (vl->vid == vid) { - found = 1; - break; + if (zif->link == in_param->svi_if) { + if (p_ifp) + *p_ifp = tmp_if; + return NS_WALK_STOP; } } - return found ? tmp_if : NULL; + return NS_WALK_CONTINUE; } /* Map to MAC-VLAN interface corresponding to specified SVI interface. @@ -3707,11 +3837,10 @@ static struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if) static struct interface *zvni_map_to_macvlan(struct interface *br_if, struct interface *svi_if) { - struct zebra_ns *zns; - struct route_node *rn; struct interface *tmp_if = NULL; struct zebra_if *zif; - int found = 0; + struct interface **p_ifp; + struct zvni_from_svi_param in_param; /* Defensive check, caller expected to invoke only with valid bridge. */ if (!br_if) @@ -3726,28 +3855,19 @@ static struct interface *zvni_map_to_macvlan(struct interface *br_if, zif = br_if->info; assert(zif); - /* Identify corresponding VLAN interface. */ - zns = zebra_ns_lookup(NS_DEFAULT); - for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { - tmp_if = (struct interface *)rn->info; - /* Check oper status of the SVI. */ - if (!tmp_if || !if_is_operative(tmp_if)) - continue; - zif = tmp_if->info; - - if (!zif || zif->zif_type != ZEBRA_IF_MACVLAN) - continue; - - if (zif->link == svi_if) { - found = 1; - break; - } - } + in_param.vid = 0; + in_param.br_if = br_if; + in_param.zif = NULL; + in_param.svi_if = svi_if; + p_ifp = &tmp_if; - return found ? tmp_if : NULL; + /* Identify corresponding VLAN interface. */ + ns_walk_func(zvni_map_to_macvlan_ns, + (void *)&in_param, + (void **)p_ifp); + return tmp_if; } - /* * Install remote MAC into the forwarding plane. */ @@ -3900,6 +4020,7 @@ static void zvni_deref_ip2mac(zebra_vni_t *zvni, zebra_mac_t *mac) static void zvni_read_mac_neigh(zebra_vni_t *zvni, struct interface *ifp) { struct zebra_ns *zns; + struct zebra_vrf *zvrf; struct zebra_if *zif; struct interface *vlan_if; struct zebra_l2info_vxlan *vxl; @@ -3907,7 +4028,10 @@ static void zvni_read_mac_neigh(zebra_vni_t *zvni, struct interface *ifp) zif = ifp->info; vxl = &zif->l2info.vxl; - zns = zebra_ns_lookup(NS_DEFAULT); + zvrf = zebra_vrf_lookup_by_id(zvni->vrf_id); + if (!zvrf || !zvrf->zns) + return; + zns = zvrf->zns; if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( @@ -4115,18 +4239,21 @@ static int zvni_send_del_to_client(vni_t vni) return zserv_send_message(client, s); } -/* - * Build the VNI hash table by going over the VxLAN interfaces. This - * is called when EVPN (advertise-all-vni) is enabled. - */ -static void zvni_build_hash_table(void) +static int zvni_build_hash_table_ns(struct ns *ns, + void *param_in __attribute__((unused)), + void **param_out __attribute__((unused))) { - struct zebra_ns *zns; + struct zebra_ns *zns = ns->info; struct route_node *rn; struct interface *ifp; + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_get_evpn(); + + if (!zvrf) + return NS_WALK_STOP; /* Walk VxLAN interfaces and create VNI hash. */ - zns = zebra_ns_lookup(NS_DEFAULT); for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { vni_t vni; zebra_vni_t *zvni = NULL; @@ -4143,7 +4270,15 @@ static void zvni_build_hash_table(void) vxl = &zif->l2info.vxl; vni = vxl->vni; - + /* link of VXLAN interface should be in zebra_evpn_vrf */ + if (zvrf->zns->ns_id != vxl->link_nsid) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Intf %s(%u) VNI %u, link not in same " + "namespace than BGP EVPN core instance ", + ifp->name, ifp->ifindex, vni); + continue; + } /* L3-VNI and L2-VNI are handled seperately */ zl3vni = zl3vni_lookup(vni); if (zl3vni) { @@ -4212,7 +4347,7 @@ static void zvni_build_hash_table(void) zlog_debug( "Failed to add VNI hash, IF %s(%u) L2-VNI %u", ifp->name, ifp->ifindex, vni); - return; + return NS_WALK_CONTINUE; } if (zvni->local_vtep_ip.s_addr != @@ -4249,6 +4384,19 @@ static void zvni_build_hash_table(void) } } } + return NS_WALK_CONTINUE; +} + +/* + * Build the VNI hash table by going over the VxLAN interfaces. This + * is called when EVPN (advertise-all-vni) is enabled. + */ + +static void zvni_build_hash_table(void) +{ + ns_walk_func(zvni_build_hash_table_ns, + (void *)NULL, + (void **)NULL); } /* @@ -5033,14 +5181,22 @@ static int zl3vni_del(zebra_l3vni_t *zl3vni) return 0; } -struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni) +static int zl3vni_map_to_vxlan_if_ns(struct ns *ns, + void *_zl3vni, + void **_pifp) { - struct zebra_ns *zns = NULL; + struct zebra_ns *zns = ns->info; + zebra_l3vni_t *zl3vni = (zebra_l3vni_t *)_zl3vni; struct route_node *rn = NULL; struct interface *ifp = NULL; + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_get_evpn(); + + if (!zvrf) + return NS_WALK_STOP; /* loop through all vxlan-interface */ - zns = zebra_ns_lookup(NS_DEFAULT); for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { struct zebra_if *zif = NULL; @@ -5055,13 +5211,39 @@ struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni) continue; vxl = &zif->l2info.vxl; - if (vxl->vni == zl3vni->vni) { - zl3vni->local_vtep_ip = vxl->vtep_ip; - return ifp; + if (vxl->vni != zl3vni->vni) + continue; + + /* link of VXLAN interface should be in zebra_evpn_vrf */ + if (zvrf->zns->ns_id != vxl->link_nsid) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Intf %s(%u) VNI %u, link not in same " + "namespace than BGP EVPN core instance ", + ifp->name, ifp->ifindex, vxl->vni); + continue; } + + + zl3vni->local_vtep_ip = vxl->vtep_ip; + if (_pifp) + *_pifp = (void *)ifp; + return NS_WALK_STOP; } - return NULL; + return NS_WALK_CONTINUE; +} + +struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni) +{ + struct interface **p_ifp; + struct interface *ifp = NULL; + + p_ifp = &ifp; + + ns_walk_func(zl3vni_map_to_vxlan_if_ns, + (void *)zl3vni, (void **)p_ifp); + return ifp; } struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni) @@ -5537,7 +5719,7 @@ static void process_remote_macip_add(vni_t vni, return; } - zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); + zvrf = zebra_vrf_get_evpn(); if (!zvrf) return; @@ -8018,6 +8200,11 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, bool upd_neigh = false; bool is_dup_detect = false; struct in_addr vtep_ip = {.s_addr = 0}; + ns_id_t local_ns_id = NS_DEFAULT; + + zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); + if (zvrf && zvrf->zns) + local_ns_id = zvrf->zns->ns_id; /* We are interested in MACs only on ports or (port, VLAN) that * map to a VNI. @@ -8041,11 +8228,10 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, return -1; } - zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); + zvrf = zebra_vrf_get_evpn(); if (!zvrf) { if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug(" No Vrf found for vrf_id: %d", - zvni->vxlan_if->vrf_id); + zlog_debug(" No Evpn Global Vrf found"); return -1; } @@ -8070,6 +8256,7 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, } SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); mac->fwd_info.local.ifindex = ifp->ifindex; + mac->fwd_info.local.ns_id = local_ns_id; mac->fwd_info.local.vid = vid; if (sticky) SET_FLAG(mac->flags, ZEBRA_MAC_STICKY); @@ -8094,6 +8281,7 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, */ if (mac_sticky == sticky && mac->fwd_info.local.ifindex == ifp->ifindex + && mac->fwd_info.local.ns_id == local_ns_id && mac->fwd_info.local.vid == vid) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( @@ -8118,6 +8306,7 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); mac->fwd_info.local.ifindex = ifp->ifindex; + mac->fwd_info.local.ns_id = local_ns_id; mac->fwd_info.local.vid = vid; } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) || @@ -8155,6 +8344,7 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); mac->fwd_info.local.ifindex = ifp->ifindex; + mac->fwd_info.local.ns_id = local_ns_id; mac->fwd_info.local.vid = vid; if (sticky) SET_FLAG(mac->flags, ZEBRA_MAC_STICKY); @@ -9620,6 +9810,25 @@ stream_failure: return; } +static int macfdb_read_ns(struct ns *ns, + void *_in_param __attribute__((unused)), + void **out_param __attribute__((unused))) +{ + struct zebra_ns *zns = ns->info; + + macfdb_read(zns); + return NS_WALK_CONTINUE; +} + +static int neigh_read_ns(struct ns *ns, + void *_in_param __attribute__((unused)), + void **out_param __attribute__((unused))) +{ + struct zebra_ns *zns = ns->info; + + neigh_read(zns); + return NS_WALK_CONTINUE; +} /* * Handle message from client to learn (or stop learning) about VNIs and MACs. @@ -9669,10 +9878,10 @@ void zebra_vxlan_advertise_all_vni(ZAPI_HANDLER_ARGS) NULL); /* Read the MAC FDB */ - macfdb_read(zvrf->zns); + ns_walk_func(macfdb_read_ns, NULL, NULL); /* Read neighbors */ - neigh_read(zvrf->zns); + ns_walk_func(neigh_read_ns, NULL, NULL); } else { /* Cleanup VTEPs for all VNIs - uninstall from * kernel and free entries. diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h index 0a46fb2075..d2b02df2ad 100644 --- a/zebra/zebra_vxlan_private.h +++ b/zebra/zebra_vxlan_private.h @@ -310,6 +310,7 @@ struct zebra_mac_t_ { union { struct { ifindex_t ifindex; + ns_id_t ns_id; vlanid_t vid; } local; |
