diff options
Diffstat (limited to 'zebra')
45 files changed, 4086 insertions, 1260 deletions
diff --git a/zebra/dplane_fpm_nl.c b/zebra/dplane_fpm_nl.c index 0a9fecc9df..6a0f02bab4 100644 --- a/zebra/dplane_fpm_nl.c +++ b/zebra/dplane_fpm_nl.c @@ -45,9 +45,10 @@ #include "zebra/zebra_dplane.h" #include "zebra/zebra_mpls.h" #include "zebra/zebra_router.h" +#include "zebra/interface.h" +#include "zebra/zebra_vxlan_private.h" #include "zebra/zebra_evpn.h" #include "zebra/zebra_evpn_mac.h" -#include "zebra/zebra_vxlan_private.h" #include "zebra/kernel_netlink.h" #include "zebra/rt_netlink.h" #include "zebra/debug.h" @@ -1189,7 +1190,7 @@ static void fpm_enqueue_rmac_table(struct hash_bucket *bucket, void *arg) struct fpm_rmac_arg *fra = arg; struct zebra_mac *zrmac = bucket->data; struct zebra_if *zif = fra->zl3vni->vxlan_if->info; - const struct zebra_l2info_vxlan *vxl = &zif->l2info.vxl; + struct zebra_vxlan_vni *vni; struct zebra_if *br_zif; vlanid_t vid; bool sticky; @@ -1201,14 +1202,15 @@ static void fpm_enqueue_rmac_table(struct hash_bucket *bucket, void *arg) sticky = !!CHECK_FLAG(zrmac->flags, (ZEBRA_MAC_STICKY | ZEBRA_MAC_REMOTE_DEF_GW)); br_zif = (struct zebra_if *)(zif->brslave_info.br_if->info); - vid = IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif) ? vxl->access_vlan : 0; + vni = zebra_vxlan_if_vni_find(zif, fra->zl3vni->vni); + vid = IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif) ? vni->access_vlan : 0; dplane_ctx_reset(fra->ctx); dplane_ctx_set_op(fra->ctx, DPLANE_OP_MAC_INSTALL); dplane_mac_init(fra->ctx, fra->zl3vni->vxlan_if, - zif->brslave_info.br_if, vid, - &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip, sticky, - 0 /*nhg*/, 0 /*update_flags*/); + zif->brslave_info.br_if, vid, &zrmac->macaddr, vni->vni, + zrmac->fwd_info.r_vtep_ip, sticky, 0 /*nhg*/, + 0 /*update_flags*/); if (fpm_nl_enqueue(fra->fnc, fra->ctx) == -1) { thread_add_timer(zrouter.master, fpm_rmac_send, fra->fnc, 1, &fra->fnc->t_rmacwalk); diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 835659332b..6e5fef3b3f 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -544,7 +544,7 @@ static int netlink_extract_bridge_info(struct rtattr *link_data, memset(bridge_info, 0, sizeof(*bridge_info)); netlink_parse_rtattr_nested(attr, IFLA_BR_MAX, link_data); if (attr[IFLA_BR_VLAN_FILTERING]) - bridge_info->vlan_aware = + bridge_info->bridge.vlan_aware = *(uint8_t *)RTA_DATA(attr[IFLA_BR_VLAN_FILTERING]); return 0; } @@ -612,6 +612,7 @@ static int netlink_extract_gre_info(struct rtattr *link_data, static int netlink_extract_vxlan_info(struct rtattr *link_data, struct zebra_l2info_vxlan *vxl_info) { + uint8_t svd = 0; struct rtattr *attr[IFLA_VXLAN_MAX + 1]; vni_t vni_in_msg; struct in_addr vtep_ip_in_msg; @@ -619,15 +620,33 @@ static int netlink_extract_vxlan_info(struct rtattr *link_data, memset(vxl_info, 0, sizeof(*vxl_info)); netlink_parse_rtattr_nested(attr, IFLA_VXLAN_MAX, link_data); - if (!attr[IFLA_VXLAN_ID]) { + if (attr[IFLA_VXLAN_COLLECT_METADATA]) { + svd = *(uint8_t *)RTA_DATA(attr[IFLA_VXLAN_COLLECT_METADATA]); if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( - "IFLA_VXLAN_ID missing from VXLAN IF message"); - return -1; + "IFLA_VXLAN_COLLECT_METADATA=%u in VXLAN IF message", + svd); + } + + if (!svd) { + /* + * In case of svd we will not get vni info directly from the + * device + */ + if (!attr[IFLA_VXLAN_ID]) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug( + "IFLA_VXLAN_ID missing from VXLAN IF message"); + return -1; + } + + vxl_info->vni_info.iftype = ZEBRA_VXLAN_IF_VNI; + vni_in_msg = *(vni_t *)RTA_DATA(attr[IFLA_VXLAN_ID]); + vxl_info->vni_info.vni.vni = vni_in_msg; + } else { + vxl_info->vni_info.iftype = ZEBRA_VXLAN_IF_SVD; } - vni_in_msg = *(vni_t *)RTA_DATA(attr[IFLA_VXLAN_ID]); - vxl_info->vni = vni_in_msg; if (!attr[IFLA_VXLAN_LOCAL]) { if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( @@ -639,8 +658,10 @@ static int netlink_extract_vxlan_info(struct rtattr *link_data, } if (attr[IFLA_VXLAN_GROUP]) { - vxl_info->mcast_grp = - *(struct in_addr *)RTA_DATA(attr[IFLA_VXLAN_GROUP]); + if (!svd) + vxl_info->vni_info.vni.mcast_grp = + *(struct in_addr *)RTA_DATA( + attr[IFLA_VXLAN_GROUP]); } if (!attr[IFLA_VXLAN_LINK]) { @@ -701,16 +722,113 @@ static void netlink_interface_update_l2info(struct interface *ifp, } } +static int netlink_bridge_vxlan_vlan_vni_map_update(struct interface *ifp, + struct rtattr *af_spec) +{ + int rem; + vni_t vni_id; + vlanid_t vid; + uint16_t flags; + struct rtattr *i; + struct zebra_vxlan_vni vni; + struct zebra_vxlan_vni *vnip; + struct hash *vni_table = NULL; + struct zebra_vxlan_vni vni_end; + struct zebra_vxlan_vni vni_start; + struct rtattr *aftb[IFLA_BRIDGE_VLAN_TUNNEL_MAX + 1]; + + memset(&vni_start, 0, sizeof(vni_start)); + memset(&vni_end, 0, sizeof(vni_end)); + + for (i = RTA_DATA(af_spec), rem = RTA_PAYLOAD(af_spec); RTA_OK(i, rem); + i = RTA_NEXT(i, rem)) { + + if (i->rta_type != IFLA_BRIDGE_VLAN_TUNNEL_INFO) + continue; + + memset(aftb, 0, sizeof(aftb)); + netlink_parse_rtattr_nested(aftb, IFLA_BRIDGE_VLAN_TUNNEL_MAX, + i); + if (!aftb[IFLA_BRIDGE_VLAN_TUNNEL_ID] || + !aftb[IFLA_BRIDGE_VLAN_TUNNEL_VID]) + /* vlan-vni info missing */ + return 0; + + flags = 0; + memset(&vni, 0, sizeof(vni)); + + vni.vni = *(vni_t *)RTA_DATA(aftb[IFLA_BRIDGE_VLAN_TUNNEL_ID]); + vni.access_vlan = *(vlanid_t *)RTA_DATA( + aftb[IFLA_BRIDGE_VLAN_TUNNEL_VID]); + + if (aftb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS]) + flags = *(uint16_t *)RTA_DATA( + aftb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS]); + + if (flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) { + vni_start = vni; + continue; + } + + if (flags & BRIDGE_VLAN_INFO_RANGE_END) + vni_end = vni; + + if (!(flags & BRIDGE_VLAN_INFO_RANGE_END)) { + vni_start = vni; + vni_end = vni; + } + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug( + "Vlan-Vni(%d:%d-%d:%d) update for VxLAN IF %s(%u)", + vni_start.access_vlan, vni_end.access_vlan, + vni_start.vni, vni_end.vni, ifp->name, + ifp->ifindex); + + if (!vni_table) { + vni_table = zebra_vxlan_vni_table_create(); + if (!vni_table) + return 0; + } + + for (vid = vni_start.access_vlan, vni_id = vni_start.vni; + vid <= vni_end.access_vlan; vid++, vni_id++) { + + memset(&vni, 0, sizeof(vni)); + vni.vni = vni_id; + vni.access_vlan = vid; + vnip = hash_get(vni_table, &vni, zebra_vxlan_vni_alloc); + if (!vnip) + return 0; + } + + memset(&vni_start, 0, sizeof(vni_start)); + memset(&vni_end, 0, sizeof(vni_end)); + } + + if (vni_table) + zebra_vxlan_if_vni_table_add_update(ifp, vni_table); + + return 0; +} + static int netlink_bridge_vxlan_update(struct interface *ifp, struct rtattr *af_spec) { struct rtattr *aftb[IFLA_BRIDGE_MAX + 1]; struct bridge_vlan_info *vinfo; + struct zebra_if *zif; vlanid_t access_vlan; if (!af_spec) return 0; + zif = (struct zebra_if *)ifp->info; + + /* Single vxlan devices has vni-vlan range to update */ + if (IS_ZEBRA_VXLAN_IF_SVD(zif)) + return netlink_bridge_vxlan_vlan_vni_map_update(ifp, af_spec); + /* There is a 1-to-1 mapping of VLAN to VxLAN - hence * only 1 access VLAN is accepted. */ @@ -2404,4 +2522,235 @@ int netlink_tunneldump_read(struct zebra_ns *zns) return 0; } + +static const char *port_state2str(uint8_t state) +{ + switch (state) { + case BR_STATE_DISABLED: + return "DISABLED"; + case BR_STATE_LISTENING: + return "LISTENING"; + case BR_STATE_LEARNING: + return "LEARNING"; + case BR_STATE_FORWARDING: + return "FORWARDING"; + case BR_STATE_BLOCKING: + return "BLOCKING"; + } + + return "UNKNOWN"; +} + +static void vxlan_vni_state_change(struct zebra_if *zif, uint16_t id, + uint8_t state) +{ + struct zebra_vxlan_vni *vnip; + + vnip = zebra_vxlan_if_vlanid_vni_find(zif, id); + + if (!vnip) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Cannot find VNI for VID (%u) IF %s for vlan state update", + id, zif->ifp->name); + + return; + } + + switch (state) { + case BR_STATE_FORWARDING: + zebra_vxlan_if_vni_up(zif->ifp, vnip); + break; + case BR_STATE_BLOCKING: + zebra_vxlan_if_vni_down(zif->ifp, vnip); + break; + case BR_STATE_DISABLED: + case BR_STATE_LISTENING: + case BR_STATE_LEARNING: + default: + /* Not used for anything at the moment */ + break; + } +} + +static void vlan_id_range_state_change(struct interface *ifp, uint16_t id_start, + uint16_t id_end, uint8_t state) +{ + struct zebra_if *zif; + + zif = (struct zebra_if *)ifp->info; + + if (!zif) + return; + + for (uint16_t i = id_start; i <= id_end; i++) + vxlan_vni_state_change(zif, i, state); +} + +/** + * netlink_vlan_change() - Read in change about vlans from the kernel + * + * @h: Netlink message header + * @ns_id: Namspace id + * @startup: Are we reading under startup conditions? + * + * Return: Result status + */ +int netlink_vlan_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) +{ + int len, rem; + struct interface *ifp; + struct br_vlan_msg *bvm; + struct bridge_vlan_info *vinfo; + struct rtattr *vtb[BRIDGE_VLANDB_ENTRY_MAX + 1] = {}; + struct rtattr *attr; + uint8_t state; + uint32_t vrange; + int type; + + /* We only care about state changes for now */ + if (!(h->nlmsg_type == RTM_NEWVLAN)) + return 0; + + len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct br_vlan_msg)); + if (len < 0) { + zlog_warn( + "%s: Message received from netlink is of a broken size %d %zu", + __func__, h->nlmsg_len, + (size_t)NLMSG_LENGTH(sizeof(struct br_vlan_msg))); + return -1; + } + + bvm = NLMSG_DATA(h); + + if (bvm->family != AF_BRIDGE) + return 0; + + ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), bvm->ifindex); + if (!ifp) { + zlog_debug("Cannot find bridge-vlan IF (%u) for vlan update", + bvm->ifindex); + return 0; + } + + if (!IS_ZEBRA_IF_VXLAN(ifp)) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("Ignoring non-vxlan IF (%s) for vlan update", + ifp->name); + + return 0; + } + + if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("%s %s IF %s NS %u", + nl_msg_type_to_str(h->nlmsg_type), + nl_family_to_str(bvm->family), ifp->name, ns_id); + + /* Loop over "ALL" BRIDGE_VLANDB_ENTRY */ + rem = len; + for (attr = BRVLAN_RTA(bvm); RTA_OK(attr, rem); + attr = RTA_NEXT(attr, rem)) { + vinfo = NULL; + vrange = 0; + + type = attr->rta_type & NLA_TYPE_MASK; + + if (type != BRIDGE_VLANDB_ENTRY) + continue; + + /* Parse nested entry data */ + netlink_parse_rtattr_nested(vtb, BRIDGE_VLANDB_ENTRY_MAX, attr); + + /* It must have info for the ID */ + if (!vtb[BRIDGE_VLANDB_ENTRY_INFO]) + continue; + + vinfo = (struct bridge_vlan_info *)RTA_DATA( + vtb[BRIDGE_VLANDB_ENTRY_INFO]); + + /* + * We only care about state info, if there is none, just ignore + * it. + */ + if (!vtb[BRIDGE_VLANDB_ENTRY_STATE]) + continue; + + state = *(uint8_t *)RTA_DATA(vtb[BRIDGE_VLANDB_ENTRY_STATE]); + + if (vtb[BRIDGE_VLANDB_ENTRY_RANGE]) + vrange = *(uint32_t *)RTA_DATA( + vtb[BRIDGE_VLANDB_ENTRY_RANGE]); + + if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_VXLAN) { + if (vrange) + zlog_debug("VLANDB_ENTRY: VID (%u-%u) state=%s", + vinfo->vid, vrange, + port_state2str(state)); + else + zlog_debug("VLANDB_ENTRY: VID (%u) state=%s", + vinfo->vid, port_state2str(state)); + } + + vlan_id_range_state_change( + ifp, vinfo->vid, (vrange ? vrange : vinfo->vid), state); + } + + return 0; +} + +/** + * netlink_request_vlan() - Request vlan information from the kernel + * @zns: Zebra namespace + * @family: AF_* netlink family + * @type: RTM_* type + * + * Return: Result status + */ +static int netlink_request_vlan(struct zebra_ns *zns, int family, int type) +{ + struct { + struct nlmsghdr n; + struct br_vlan_msg bvm; + char buf[256]; + } req; + + /* Form the request, specifying filter (rtattr) if needed. */ + memset(&req, 0, sizeof(req)); + req.n.nlmsg_type = type; + req.n.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_vlan_msg)); + req.bvm.family = family; + + nl_attr_put32(&req.n, sizeof(req), BRIDGE_VLANDB_DUMP_FLAGS, + BRIDGE_VLANDB_DUMPF_STATS); + + return netlink_request(&zns->netlink_cmd, &req); +} + +/** + * netlink_vlan_read() - Vlan read function using netlink interface + * + * @zns: Zebra name space + * + * Return: Result status + * Only called at bootstrap time. + */ +int netlink_vlan_read(struct zebra_ns *zns) +{ + int ret; + struct zebra_dplane_info dp_info; + + zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/); + + /* Get bridg vlan info */ + ret = netlink_request_vlan(zns, PF_BRIDGE, RTM_GETVLAN); + if (ret < 0) + return ret; + + ret = netlink_parse_info(netlink_vlan_change, &zns->netlink_cmd, + &dp_info, 0, 1); + + return ret; +} + #endif /* GNU_LINUX */ diff --git a/zebra/if_netlink.h b/zebra/if_netlink.h index 21ae1713be..f215968657 100644 --- a/zebra/if_netlink.h +++ b/zebra/if_netlink.h @@ -40,6 +40,9 @@ int netlink_interface_addr_dplane(struct nlmsghdr *h, ns_id_t ns_id, extern int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup); extern int interface_lookup_netlink(struct zebra_ns *zns); +extern int netlink_vlan_change(struct nlmsghdr *h, ns_id_t ns_id, int startup); +extern int netlink_vlan_read(struct zebra_ns *zns); + extern ssize_t netlink_intf_msg_encode(uint16_t cmd, const struct zebra_dplane_ctx *ctx, void *buf, size_t buflen); diff --git a/zebra/interface.c b/zebra/interface.c index a24f55dbba..5c60d355c2 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -229,6 +229,7 @@ static int if_zebra_delete_hook(struct interface *ifp) rtadv_if_fini(zebra_if); + zebra_l2_bridge_if_cleanup(ifp); zebra_evpn_if_cleanup(zebra_if); zebra_evpn_mac_ifp_del(ifp); @@ -807,12 +808,12 @@ void if_delete_update(struct interface **pifp) /* Reset some zebra interface params to default values. */ zif = ifp->info; if (zif) { + zebra_evpn_if_cleanup(zif); zif->zif_type = ZEBRA_IF_OTHER; zif->zif_slave_type = ZEBRA_IF_SLAVE_NONE; memset(&zif->l2info, 0, sizeof(union zebra_l2if_info)); memset(&zif->brslave_info, 0, sizeof(struct zebra_l2info_brslave)); - zebra_evpn_if_cleanup(zif); zebra_evpn_mac_ifp_del(ifp); } @@ -1890,6 +1891,63 @@ static inline bool if_is_protodown_applicable(struct interface *ifp) return true; } +static void zebra_vxlan_if_vni_dump_vty(struct vty *vty, + struct zebra_vxlan_vni *vni) +{ + char str[INET6_ADDRSTRLEN]; + + vty_out(vty, " VxLAN Id %u", vni->vni); + if (vni->access_vlan) + vty_out(vty, " Access VLAN Id %u\n", vni->access_vlan); + + if (vni->mcast_grp.s_addr != INADDR_ANY) + vty_out(vty, " Mcast Group %s", + inet_ntop(AF_INET, &vni->mcast_grp, str, sizeof(str))); +} + +static void zebra_vxlan_if_vni_hash_dump_vty(struct hash_bucket *bucket, + void *ctxt) +{ + struct vty *vty; + struct zebra_vxlan_vni *vni; + + vni = (struct zebra_vxlan_vni *)bucket->data; + vty = (struct vty *)ctxt; + + zebra_vxlan_if_vni_dump_vty(vty, vni); +} + +static void zebra_vxlan_if_dump_vty(struct vty *vty, struct zebra_if *zebra_if) +{ + struct zebra_l2info_vxlan *vxlan_info; + struct zebra_vxlan_vni_info *vni_info; + + vxlan_info = &zebra_if->l2info.vxl; + vni_info = &vxlan_info->vni_info; + + if (vxlan_info->vtep_ip.s_addr != INADDR_ANY) + vty_out(vty, " VTEP IP: %pI4", &vxlan_info->vtep_ip); + + 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); + } + + if (IS_ZEBRA_VXLAN_IF_VNI(zebra_if)) { + zebra_vxlan_if_vni_dump_vty(vty, &vni_info->vni); + } else { + hash_iterate(vni_info->vni_table, + zebra_vxlan_if_vni_hash_dump_vty, vty); + } + + vty_out(vty, "\n"); +} + /* Interface's information print out to vty interface. */ static void if_dump_vty(struct vty *vty, struct interface *ifp) { @@ -1998,42 +2056,15 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) zebra_zifslavetype_2str(zebra_if->zif_slave_type)); if (IS_ZEBRA_IF_BRIDGE(ifp)) { - struct zebra_l2info_bridge *bridge_info; - - bridge_info = &zebra_if->l2info.br; vty_out(vty, " Bridge VLAN-aware: %s\n", - bridge_info->vlan_aware ? "yes" : "no"); + IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(zebra_if) ? "yes" : "no"); } else if (IS_ZEBRA_IF_VLAN(ifp)) { struct zebra_l2info_vlan *vlan_info; vlan_info = &zebra_if->l2info.vl; vty_out(vty, " VLAN Id %u\n", vlan_info->vid); } else if (IS_ZEBRA_IF_VXLAN(ifp)) { - struct zebra_l2info_vxlan *vxlan_info; - - vxlan_info = &zebra_if->l2info.vxl; - vty_out(vty, " VxLAN Id %u", vxlan_info->vni); - if (vxlan_info->vtep_ip.s_addr != INADDR_ANY) - vty_out(vty, " VTEP IP: %pI4", - &vxlan_info->vtep_ip); - if (vxlan_info->access_vlan) - vty_out(vty, " Access VLAN Id %u\n", - vxlan_info->access_vlan); - if (vxlan_info->mcast_grp.s_addr != INADDR_ANY) - vty_out(vty, " Mcast Group %pI4", - &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"); + zebra_vxlan_if_dump_vty(vty, zebra_if); } else if (IS_ZEBRA_IF_GRE(ifp)) { struct zebra_l2info_gre *gre_info; @@ -2226,6 +2257,59 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) #endif /* HAVE_NET_RT_IFLIST */ } +static void zebra_vxlan_if_vni_dump_vty_json(json_object *json_if, + struct zebra_vxlan_vni *vni) +{ + json_object_int_add(json_if, "vxlanId", vni->vni); + if (vni->access_vlan) + json_object_int_add(json_if, "accessVlanId", vni->access_vlan); + if (vni->mcast_grp.s_addr != INADDR_ANY) + json_object_string_addf(json_if, "mcastGroup", "%pI4", + &vni->mcast_grp); +} + +static void zebra_vxlan_if_vni_hash_dump_vty_json(struct hash_bucket *bucket, + void *ctxt) +{ + json_object *json_if; + struct zebra_vxlan_vni *vni; + + vni = (struct zebra_vxlan_vni *)bucket->data; + json_if = (json_object *)ctxt; + + zebra_vxlan_if_vni_dump_vty_json(json_if, vni); +} + +static void zebra_vxlan_if_dump_vty_json(json_object *json_if, + struct zebra_if *zebra_if) +{ + struct zebra_l2info_vxlan *vxlan_info; + struct zebra_vxlan_vni_info *vni_info; + + vxlan_info = &zebra_if->l2info.vxl; + vni_info = &vxlan_info->vni_info; + + if (vxlan_info->vtep_ip.s_addr != INADDR_ANY) + json_object_string_addf(json_if, "vtepIp", "%pI4", + &vxlan_info->vtep_ip); + + 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); + json_object_string_add(json_if, "linkInterface", + ifp == NULL ? "Unknown" : ifp->name); + } + if (IS_ZEBRA_VXLAN_IF_VNI(zebra_if)) { + zebra_vxlan_if_vni_dump_vty_json(json_if, &vni_info->vni); + } else { + hash_iterate(vni_info->vni_table, + zebra_vxlan_if_vni_hash_dump_vty_json, json_if); + } +} + static void if_dump_vty_json(struct vty *vty, struct interface *ifp, json_object *json) { @@ -2353,37 +2437,15 @@ static void if_dump_vty_json(struct vty *vty, struct interface *ifp, bridge_info = &zebra_if->l2info.br; json_object_boolean_add(json_if, "bridgeVlanAware", - bridge_info->vlan_aware); + bridge_info->bridge.vlan_aware); } else if (IS_ZEBRA_IF_VLAN(ifp)) { struct zebra_l2info_vlan *vlan_info; vlan_info = &zebra_if->l2info.vl; json_object_int_add(json_if, "vlanId", vlan_info->vid); } else if (IS_ZEBRA_IF_VXLAN(ifp)) { - struct zebra_l2info_vxlan *vxlan_info; - - vxlan_info = &zebra_if->l2info.vxl; - json_object_int_add(json_if, "vxlanId", vxlan_info->vni); - if (vxlan_info->vtep_ip.s_addr != INADDR_ANY) - json_object_string_addf(json_if, "vtepIp", "%pI4", - &vxlan_info->vtep_ip); - if (vxlan_info->access_vlan) - json_object_int_add(json_if, "accessVlanId", - vxlan_info->access_vlan); - if (vxlan_info->mcast_grp.s_addr != INADDR_ANY) - json_object_string_addf(json_if, "mcastGroup", "%pI4", - &vxlan_info->mcast_grp); - if (vxlan_info->ifindex_link - && (vxlan_info->link_nsid != NS_UNKNOWN)) { - struct interface *ifp; + zebra_vxlan_if_dump_vty_json(json_if, zebra_if); - ifp = if_lookup_by_index_per_ns( - zebra_ns_lookup(vxlan_info->link_nsid), - vxlan_info->ifindex_link); - json_object_string_add(json_if, "linkInterface", - ifp == NULL ? "Unknown" - : ifp->name); - } } else if (IS_ZEBRA_IF_GRE(ifp)) { struct zebra_l2info_gre *gre_info; diff --git a/zebra/interface.h b/zebra/interface.h index 8e39ca8c96..7e52990002 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -28,6 +28,7 @@ #include "bitfield.h" #include "zebra/zebra_l2.h" +#include "zebra/zebra_l2_bridge_if.h" #include "zebra/zebra_nhg_private.h" #include "zebra/zebra_router.h" #include "zebra/rtadv.h" diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index 42afe61469..fac6acae0c 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -124,6 +124,9 @@ static const struct message nlmsg_str[] = {{RTM_NEWROUTE, "RTM_NEWROUTE"}, {RTM_NEWTFILTER, "RTM_NEWTFILTER"}, {RTM_DELTFILTER, "RTM_DELTFILTER"}, {RTM_GETTFILTER, "RTM_GETTFILTER"}, + {RTM_NEWVLAN, "RTM_NEWVLAN"}, + {RTM_DELVLAN, "RTM_DELVLAN"}, + {RTM_GETVLAN, "RTM_GETVLAN"}, {0}}; static const struct message rtproto_str[] = { @@ -432,6 +435,10 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, case RTM_NEWTFILTER: case RTM_DELTFILTER: return netlink_tfilter_change(h, ns_id, startup); + case RTM_NEWVLAN: + return netlink_vlan_change(h, ns_id, startup); + case RTM_DELVLAN: + return netlink_vlan_change(h, ns_id, startup); /* Messages handled in the dplane thread */ case RTM_NEWADDR: @@ -705,6 +712,12 @@ bool nl_attr_put32(struct nlmsghdr *n, unsigned int maxlen, int type, return nl_attr_put(n, maxlen, type, &data, sizeof(uint32_t)); } +bool nl_attr_put64(struct nlmsghdr *n, unsigned int maxlen, int type, + uint64_t data) +{ + return nl_attr_put(n, maxlen, type, &data, sizeof(uint64_t)); +} + struct rtattr *nl_attr_nest(struct nlmsghdr *n, unsigned int maxlen, int type) { struct rtattr *nest = NLMSG_TAIL(n); @@ -1080,7 +1093,8 @@ static int netlink_parse_error(const struct nlsock *nl, struct nlmsghdr *h, nl_msg_type_to_str(msg_type), msg_type, err->msg.nlmsg_seq, err->msg.nlmsg_pid); } else { - if ((msg_type != RTM_GETNEXTHOP) || !startup) + if ((msg_type != RTM_GETNEXTHOP && msg_type != RTM_GETVLAN) || + !startup) flog_err(EC_ZEBRA_UNEXPECTED_MESSAGE, "%s error: %s, type=%s(%u), seq=%u, pid=%u", nl->name, safe_strerror(-errnum), @@ -1761,7 +1775,7 @@ void kernel_init(struct zebra_ns *zns) { uint32_t groups, dplane_groups, ext_groups; #if defined SOL_NETLINK - int one, ret; + int one, ret, grp; #endif /* @@ -1772,6 +1786,11 @@ void kernel_init(struct zebra_ns *zns) * keeping track of all the different values would * lead to confusion, so we need to convert the * RTNLGRP_XXX to a bit position for ourself + * + * + * NOTE: If the bit is >= 32, you must use setsockopt(). Those + * groups are added further below after SOL_NETLINK is verified to + * exist. */ groups = RTMGRP_LINK | RTMGRP_IPV4_ROUTE | @@ -1851,6 +1870,18 @@ void kernel_init(struct zebra_ns *zns) * sure that we want to pull into our build system. */ #if defined SOL_NETLINK + + /* + * setsockopt multicast group subscriptions that don't fit in nl_groups + */ + grp = RTNLGRP_BRVLAN; + ret = setsockopt(zns->netlink.sock, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, + &grp, sizeof(grp)); + + if (ret < 0) + zlog_notice( + "Registration for RTNLGRP_BRVLAN Membership failed : %d %s", + errno, safe_strerror(errno)); /* * Let's tell the kernel that we want to receive extended * ACKS over our command socket(s) diff --git a/zebra/kernel_netlink.h b/zebra/kernel_netlink.h index 08cd706a9f..7d5453d544 100644 --- a/zebra/kernel_netlink.h +++ b/zebra/kernel_netlink.h @@ -31,7 +31,7 @@ extern "C" { ((struct rtattr *)(((char *)(h)) + NLMSG_ALIGN(sizeof(struct nhmsg)))) -#define NL_RCV_PKT_BUF_SIZE 32768 +#define NL_RCV_PKT_BUF_SIZE (34 * 1024) #define NL_PKT_BUF_SIZE 8192 /* @@ -48,6 +48,8 @@ extern bool nl_attr_put16(struct nlmsghdr *n, unsigned int maxlen, int type, uint16_t data); extern bool nl_attr_put32(struct nlmsghdr *n, unsigned int maxlen, int type, uint32_t data); +extern bool nl_attr_put64(struct nlmsghdr *n, unsigned int maxlen, int type, + uint64_t data); /* * nl_attr_nest - start an attribute nest. diff --git a/zebra/redistribute.c b/zebra/redistribute.c index 4a8fe938ed..32d28f8002 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -118,13 +118,15 @@ static void zebra_redistribute(struct zserv *client, int type, RNODE_FOREACH_RE (rn, newre) { if (IS_ZEBRA_DEBUG_RIB) zlog_debug( - "%s: client %s %pRN(%u:%u) checking: selected=%d, type=%d, distance=%d, metric=%d zebra_check_addr=%d", + "%s: client %s %pRN(%u:%u) checking: selected=%d, type=%s, instance=%u, distance=%d, metric=%d zebra_check_addr=%d", __func__, zebra_route_string(client->proto), rn, vrf_id, newre->instance, !!CHECK_FLAG(newre->flags, ZEBRA_FLAG_SELECTED), - newre->type, newre->distance, + zebra_route_string(newre->type), + newre->instance, + newre->distance, newre->metric, zebra_check_addr(&rn->p)); diff --git a/zebra/rib.h b/zebra/rib.h index 166500fa5c..8a3b3e657f 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -625,6 +625,9 @@ extern pid_t pid; extern bool v6_rr_semantics; +/* Name of hook calls */ +#define ZEBRA_ON_RIB_PROCESS_HOOK_CALL "on_rib_process_dplane_results" + #ifdef __cplusplus } #endif diff --git a/zebra/rt.h b/zebra/rt.h index 6f4dd48a54..4ebf479754 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -103,7 +103,9 @@ extern void kernel_init(struct zebra_ns *zns); extern void kernel_terminate(struct zebra_ns *zns, bool complete); extern void macfdb_read(struct zebra_ns *zns); extern void macfdb_read_for_bridge(struct zebra_ns *zns, struct interface *ifp, - struct interface *br_if); + struct interface *br_if, vlanid_t vid); +extern void macfdb_read_mcast_entry_for_vni(struct zebra_ns *zns, + struct interface *ifp, vni_t vni); extern void macfdb_read_specific_mac(struct zebra_ns *zns, struct interface *br_if, const struct ethaddr *mac, vlanid_t vid); @@ -127,6 +129,7 @@ extern void kernel_update_multi(struct dplane_ctx_list_head *ctx_list); * Called by the dplane pthread to read incoming OS messages and dispatch them. */ int kernel_dplane_read(struct zebra_dplane_info *info); +extern void vlan_read(struct zebra_ns *zns); #ifdef __cplusplus } diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 216b719ff8..8bf59e02c7 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -153,6 +153,22 @@ static bool is_proto_nhg(uint32_t id, int type) return false; } +/* Is vni mcast group */ +static bool is_mac_vni_mcast_group(struct ethaddr *mac, vni_t vni, + struct in_addr grp_addr) +{ + if (!vni) + return false; + + if (!is_zero_mac(mac)) + return false; + + if (!IN_MULTICAST(ntohl(grp_addr.s_addr))) + return false; + + return true; +} + /* * The ipv4_ll data structure is used for all 5549 * additions to the kernel. Let's figure out the @@ -1309,6 +1325,7 @@ static bool _netlink_route_add_gateway_info(uint8_t route_family, } static int build_label_stack(struct mpls_label_stack *nh_label, + enum lsp_types_t nh_label_type, mpls_lse_t *out_lse, char *label_buf, size_t label_buf_size) { @@ -1316,7 +1333,8 @@ static int build_label_stack(struct mpls_label_stack *nh_label, int num_labels = 0; for (int i = 0; nh_label && i < nh_label->num_labels; i++) { - if (nh_label->label[i] == MPLS_LABEL_IMPLICIT_NULL) + if (nh_label_type != ZEBRA_LSP_EVPN && + nh_label->label[i] == MPLS_LABEL_IMPLICIT_NULL) continue; if (IS_ZEBRA_DEBUG_KERNEL) { @@ -1330,15 +1348,58 @@ static int build_label_stack(struct mpls_label_stack *nh_label, } } - out_lse[num_labels] = - mpls_lse_encode(nh_label->label[i], 0, 0, 0); + if (nh_label_type == ZEBRA_LSP_EVPN) + out_lse[num_labels] = label2vni(&nh_label->label[i]); + else + out_lse[num_labels] = + mpls_lse_encode(nh_label->label[i], 0, 0, 0); num_labels++; } return num_labels; } -static bool _netlink_route_encode_label_info(struct mpls_label_stack *nh_label, +static bool _netlink_nexthop_encode_dvni_label(const struct nexthop *nexthop, + struct nlmsghdr *nlmsg, + mpls_lse_t *out_lse, + size_t buflen, char *label_buf) +{ + struct in_addr ipv4; + + if (!nl_attr_put64(nlmsg, buflen, LWTUNNEL_IP_ID, + htonll((uint64_t)out_lse[0]))) + return false; + + if (nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) { + if (!nl_attr_put(nlmsg, buflen, LWTUNNEL_IP_DST, + &nexthop->gate.ipv4, 4)) + return false; + + } else if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) { + if (IS_MAPPED_IPV6(&nexthop->gate.ipv6)) { + ipv4_mapped_ipv6_to_ipv4(&nexthop->gate.ipv6, &ipv4); + if (!nl_attr_put(nlmsg, buflen, LWTUNNEL_IP_DST, &ipv4, + 4)) + return false; + + } else { + if (!nl_attr_put(nlmsg, buflen, LWTUNNEL_IP_DST, + &nexthop->gate.ipv6, 16)) + return false; + } + } else { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug( + "%s: nexthop %pNHv %s must NEXTHOP_TYPE_IPV*_IFINDEX to be vxlan encapped", + __func__, nexthop, label_buf); + + return false; + } + + return true; +} + +static bool _netlink_route_encode_label_info(const struct nexthop *nexthop, struct nlmsghdr *nlmsg, size_t buflen, struct rtmsg *rtmsg, char *label_buf, @@ -1346,6 +1407,12 @@ static bool _netlink_route_encode_label_info(struct mpls_label_stack *nh_label, { mpls_lse_t out_lse[MPLS_MAX_LABELS]; int num_labels; + struct rtattr *nest; + struct mpls_label_stack *nh_label; + enum lsp_types_t nh_label_type; + + nh_label = nexthop->nh_label; + nh_label_type = nexthop->nh_label_type; /* * label_buf is *only* currently used within debugging. @@ -1355,10 +1422,26 @@ static bool _netlink_route_encode_label_info(struct mpls_label_stack *nh_label, */ label_buf[0] = '\0'; - num_labels = - build_label_stack(nh_label, out_lse, label_buf, label_buf_size); + num_labels = build_label_stack(nh_label, nh_label_type, out_lse, + label_buf, label_buf_size); + + if (num_labels && nh_label_type == ZEBRA_LSP_EVPN) { + if (!nl_attr_put16(nlmsg, buflen, RTA_ENCAP_TYPE, + LWTUNNEL_ENCAP_IP)) + return false; + + nest = nl_attr_nest(nlmsg, buflen, RTA_ENCAP); + if (!nest) + return false; + + if (_netlink_nexthop_encode_dvni_label(nexthop, nlmsg, out_lse, + buflen, + label_buf) == false) + return false; - if (num_labels) { + nl_attr_nest_end(nlmsg, nest); + + } else if (num_labels) { /* Set the BoS bit */ out_lse[num_labels - 1] |= htonl(1 << MPLS_LS_S_SHIFT); @@ -1367,8 +1450,6 @@ static bool _netlink_route_encode_label_info(struct mpls_label_stack *nh_label, num_labels * sizeof(mpls_lse_t))) return false; } else { - struct rtattr *nest; - if (!nl_attr_put16(nlmsg, buflen, RTA_ENCAP_TYPE, LWTUNNEL_ENCAP_MPLS)) return false; @@ -1482,9 +1563,8 @@ static bool _netlink_route_build_singlepath(const struct prefix *p, vrf = vrf_lookup_by_id(nexthop->vrf_id); - if (!_netlink_route_encode_label_info(nexthop->nh_label, nlmsg, - req_size, rtmsg, label_buf, - sizeof(label_buf))) + if (!_netlink_route_encode_label_info(nexthop, nlmsg, req_size, rtmsg, + label_buf, sizeof(label_buf))) return false; if (nexthop->nh_srv6) { @@ -1765,9 +1845,8 @@ static bool _netlink_route_build_multipath( vrf = vrf_lookup_by_id(nexthop->vrf_id); - if (!_netlink_route_encode_label_info(nexthop->nh_label, nlmsg, - req_size, rtmsg, label_buf, - sizeof(label_buf))) + if (!_netlink_route_encode_label_info(nexthop, nlmsg, req_size, rtmsg, + label_buf, sizeof(label_buf))) return false; if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) @@ -2649,11 +2728,28 @@ ssize_t netlink_nexthop_msg_encode(uint16_t cmd, if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_ONLINK)) req->nhm.nh_flags |= RTNH_F_ONLINK; - num_labels = - build_label_stack(nh->nh_label, out_lse, - label_buf, sizeof(label_buf)); + num_labels = build_label_stack( + nh->nh_label, nh->nh_label_type, out_lse, + label_buf, sizeof(label_buf)); - if (num_labels) { + if (num_labels && nh->nh_label_type == ZEBRA_LSP_EVPN) { + if (!nl_attr_put16(&req->n, buflen, + NHA_ENCAP_TYPE, + LWTUNNEL_ENCAP_IP)) + return 0; + + nest = nl_attr_nest(&req->n, buflen, NHA_ENCAP); + if (!nest) + return 0; + + if (_netlink_nexthop_encode_dvni_label( + nh, &req->n, out_lse, buflen, + label_buf) == false) + return 0; + + nl_attr_nest_end(&req->n, nest); + + } else if (num_labels) { /* Set the BoS bit */ out_lse[num_labels - 1] |= htonl(1 << MPLS_LS_S_SHIFT); @@ -3400,17 +3496,32 @@ static ssize_t netlink_neigh_update_msg_encode( if (op == DPLANE_OP_MAC_INSTALL || op == DPLANE_OP_MAC_DELETE) { vlanid_t vid = dplane_ctx_mac_get_vlan(ctx); + vni_t vni = dplane_ctx_mac_get_vni(ctx); if (vid > 0) { if (!nl_attr_put16(&req->n, datalen, NDA_VLAN, vid)) return 0; } + if (vni > 0) { + if (!nl_attr_put32(&req->n, datalen, NDA_SRC_VNI, vni)) + return 0; + } + if (!nl_attr_put32(&req->n, datalen, NDA_MASTER, dplane_ctx_mac_get_br_ifindex(ctx))) return 0; } + if (op == DPLANE_OP_VTEP_ADD || op == DPLANE_OP_VTEP_DELETE) { + vni_t vni = dplane_ctx_neigh_get_vni(ctx); + + if (vni > 0) { + if (!nl_attr_put32(&req->n, datalen, NDA_SRC_VNI, vni)) + return 0; + } + } + return NLMSG_ALIGN(req->n.nlmsg_len); } @@ -3457,7 +3568,9 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id) bool sticky; bool local_inactive = false; bool dp_static = false; + vni_t vni = 0; uint32_t nhg_id = 0; + bool vni_mcast_grp = false; ndm = NLMSG_DATA(h); @@ -3503,7 +3616,8 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id) IPV4_MAX_BYTELEN); snprintfrr(dst_buf, sizeof(dst_buf), " dst %pI4", &vtep_ip); - } + } else + memset(&vtep_ip, 0, sizeof(vtep_ip)); if (tb[NDA_NH_ID]) nhg_id = *(uint32_t *)RTA_DATA(tb[NDA_NH_ID]); @@ -3528,12 +3642,16 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id) } } + if (tb[NDA_SRC_VNI]) + vni = *(vni_t *)RTA_DATA(tb[NDA_SRC_VNI]); + if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("Rx %s AF_BRIDGE IF %u%s st 0x%x fl 0x%x MAC %pEA%s nhg %d", - nl_msg_type_to_str(h->nlmsg_type), - ndm->ndm_ifindex, vid_present ? vid_buf : "", - ndm->ndm_state, ndm->ndm_flags, &mac, - dst_present ? dst_buf : "", nhg_id); + zlog_debug( + "Rx %s AF_BRIDGE IF %u%s st 0x%x fl 0x%x MAC %pEA%s nhg %d vni %d", + nl_msg_type_to_str(h->nlmsg_type), ndm->ndm_ifindex, + vid_present ? vid_buf : "", ndm->ndm_state, + ndm->ndm_flags, &mac, dst_present ? dst_buf : "", + nhg_id, vni); /* The interface should exist. */ ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), @@ -3556,6 +3674,14 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id) return 0; } + /* For per vni device, vni comes from device itself */ + if (IS_ZEBRA_IF_VXLAN(ifp) && IS_ZEBRA_VXLAN_IF_VNI(zif)) { + struct zebra_vxlan_vni *vnip; + + vnip = zebra_vxlan_if_vni_find(zif, 0); + vni = vnip->vni; + } + sticky = !!(ndm->ndm_flags & NTF_STICKY); if (filter_vlan && vid != filter_vlan) { @@ -3565,6 +3691,11 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id) return 0; } + /* + * Check if this is a mcast group update (svd case) + */ + vni_mcast_grp = is_mac_vni_mcast_group(&mac, vni, vtep_ip); + /* If add or update, do accordingly if learnt on a "local" interface; if * the notification is over VxLAN, this has to be related to * multi-homing, @@ -3572,17 +3703,25 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id) */ if (h->nlmsg_type == RTM_NEWNEIGH) { /* Drop "permanent" entries. */ - if (ndm->ndm_state & NUD_PERMANENT) { + if (!vni_mcast_grp && (ndm->ndm_state & NUD_PERMANENT)) { if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( " Dropping entry because of NUD_PERMANENT"); return 0; } - if (IS_ZEBRA_IF_VXLAN(ifp)) + if (IS_ZEBRA_IF_VXLAN(ifp)) { + if (!dst_present) + return 0; + + if (vni_mcast_grp) + return zebra_vxlan_if_vni_mcast_group_add_update( + ifp, vni, &vtep_ip); + return zebra_vxlan_dp_network_mac_add( - ifp, br_if, &mac, vid, nhg_id, sticky, + ifp, br_if, &mac, vid, vni, nhg_id, sticky, !!(ndm->ndm_flags & NTF_EXT_LEARNED)); + } return zebra_vxlan_local_mac_add_update(ifp, br_if, &mac, vid, sticky, local_inactive, dp_static); @@ -3602,15 +3741,18 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id) return 0; if (dst_present) { - u_char zero_mac[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; + if (vni_mcast_grp) + return zebra_vxlan_if_vni_mcast_group_del(ifp, vni, + &vtep_ip); + + if (is_zero_mac(&mac) && vni) + return zebra_vxlan_check_readd_vtep(ifp, vni, vtep_ip); - if (!memcmp(zero_mac, mac.octet, ETH_ALEN)) - return zebra_vxlan_check_readd_vtep(ifp, vtep_ip); return 0; } if (IS_ZEBRA_IF_VXLAN(ifp)) - return zebra_vxlan_dp_network_mac_del(ifp, br_if, &mac, vid); + return 0; return zebra_vxlan_local_mac_del(ifp, br_if, &mac, vid); } @@ -3687,11 +3829,9 @@ int netlink_macfdb_read(struct zebra_ns *zns) * specific bridge and matching specific access VLAN (if VLAN-aware bridge). */ int netlink_macfdb_read_for_bridge(struct zebra_ns *zns, struct interface *ifp, - struct interface *br_if) + struct interface *br_if, vlanid_t vid) { struct zebra_if *br_zif; - struct zebra_if *zif; - struct zebra_l2info_vxlan *vxl; struct zebra_dplane_info dp_info; int ret = 0; @@ -3699,10 +3839,8 @@ int netlink_macfdb_read_for_bridge(struct zebra_ns *zns, struct interface *ifp, /* Save VLAN we're filtering on, if needed. */ br_zif = (struct zebra_if *)br_if->info; - zif = (struct zebra_if *)ifp->info; - vxl = &zif->l2info.vxl; if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif)) - filter_vlan = vxl->access_vlan; + filter_vlan = vid; /* Get bridge FDB table for specific bridge - we do the VLAN filtering. */ @@ -3720,40 +3858,45 @@ int netlink_macfdb_read_for_bridge(struct zebra_ns *zns, struct interface *ifp, /* Request for MAC FDB for a specific MAC address in VLAN from the kernel */ -static int netlink_request_specific_mac_in_bridge(struct zebra_ns *zns, - int family, int type, - struct interface *br_if, - const struct ethaddr *mac, - vlanid_t vid) +static int netlink_request_specific_mac(struct zebra_ns *zns, int family, + int type, struct interface *ifp, + const struct ethaddr *mac, vlanid_t vid, + vni_t vni, uint8_t flags) { struct { struct nlmsghdr n; struct ndmsg ndm; char buf[256]; } req; - struct zebra_if *br_zif; + struct zebra_if *zif; memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); req.n.nlmsg_type = type; /* RTM_GETNEIGH */ req.n.nlmsg_flags = NLM_F_REQUEST; req.ndm.ndm_family = family; /* AF_BRIDGE */ + req.ndm.ndm_flags = flags; /* req.ndm.ndm_state = NUD_REACHABLE; */ nl_attr_put(&req.n, sizeof(req), NDA_LLADDR, mac, 6); - br_zif = (struct zebra_if *)br_if->info; - if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif) && vid > 0) - nl_attr_put16(&req.n, sizeof(req), NDA_VLAN, vid); - - nl_attr_put32(&req.n, sizeof(req), NDA_MASTER, br_if->ifindex); + zif = (struct zebra_if *)ifp->info; + /* Is this a read on a VXLAN interface? */ + if (IS_ZEBRA_IF_VXLAN(ifp)) { + nl_attr_put32(&req.n, sizeof(req), NDA_VNI, vni); + /* TBD: Why is ifindex not filled in the non-vxlan case? */ + req.ndm.ndm_ifindex = ifp->ifindex; + } else { + if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(zif) && vid > 0) + nl_attr_put16(&req.n, sizeof(req), NDA_VLAN, vid); + nl_attr_put32(&req.n, sizeof(req), NDA_MASTER, ifp->ifindex); + } if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "%s: Tx family %s IF %s(%u) vrf %s(%u) MAC %pEA vid %u", - __func__, nl_family_to_str(req.ndm.ndm_family), - br_if->name, br_if->ifindex, br_if->vrf->name, - br_if->vrf->vrf_id, mac, vid); + zlog_debug("Tx %s %s IF %s(%u) MAC %pEA vid %u vni %u", + nl_msg_type_to_str(type), + nl_family_to_str(req.ndm.ndm_family), ifp->name, + ifp->ifindex, mac, vid, vni); return netlink_request(&zns->netlink_cmd, &req); } @@ -3769,9 +3912,34 @@ int netlink_macfdb_read_specific_mac(struct zebra_ns *zns, /* Get bridge FDB table for specific bridge - we do the VLAN filtering. */ - ret = netlink_request_specific_mac_in_bridge(zns, AF_BRIDGE, - RTM_GETNEIGH, - br_if, mac, vid); + ret = netlink_request_specific_mac(zns, AF_BRIDGE, RTM_GETNEIGH, br_if, + mac, vid, 0, 0); + if (ret < 0) + return ret; + + ret = netlink_parse_info(netlink_macfdb_table, &zns->netlink_cmd, + &dp_info, 1, 0); + + return ret; +} + +int netlink_macfdb_read_mcast_for_vni(struct zebra_ns *zns, + struct interface *ifp, vni_t vni) +{ + struct zebra_if *zif; + struct ethaddr mac = {.octet = {0}}; + struct zebra_dplane_info dp_info; + int ret = 0; + + zif = ifp->info; + if (IS_ZEBRA_VXLAN_IF_VNI(zif)) + return 0; + + zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/); + + /* Get specific FDB entry for BUM handling, if any */ + ret = netlink_request_specific_mac(zns, AF_BRIDGE, RTM_GETNEIGH, ifp, + &mac, 0, vni, NTF_SELF); if (ret < 0) return ret; diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h index 8506367ae4..b550cc06b7 100644 --- a/zebra/rt_netlink.h +++ b/zebra/rt_netlink.h @@ -92,7 +92,10 @@ extern int netlink_neigh_change(struct nlmsghdr *h, ns_id_t ns_id); extern int netlink_macfdb_read(struct zebra_ns *zns); extern int netlink_macfdb_read_for_bridge(struct zebra_ns *zns, struct interface *ifp, - struct interface *br_if); + struct interface *br_if, + vlanid_t vid); +extern int netlink_macfdb_read_mcast_for_vni(struct zebra_ns *zns, + struct interface *ifp, vni_t vni); extern int netlink_neigh_read(struct zebra_ns *zns); extern int netlink_neigh_read_for_vlan(struct zebra_ns *zns, struct interface *vlan_if); diff --git a/zebra/rtread_netlink.c b/zebra/rtread_netlink.c index 8990f66ef8..f9d4605562 100644 --- a/zebra/rtread_netlink.c +++ b/zebra/rtread_netlink.c @@ -28,6 +28,7 @@ #include "zebra/zebra_pbr.h" #include "zebra/zebra_tc.h" #include "zebra/rt_netlink.h" +#include "zebra/if_netlink.h" #include "zebra/rule_netlink.h" #include "zebra/tc_netlink.h" @@ -42,9 +43,15 @@ void macfdb_read(struct zebra_ns *zns) } void macfdb_read_for_bridge(struct zebra_ns *zns, struct interface *ifp, - struct interface *br_if) + struct interface *br_if, vlanid_t vid) { - netlink_macfdb_read_for_bridge(zns, ifp, br_if); + netlink_macfdb_read_for_bridge(zns, ifp, br_if, vid); +} + +void macfdb_read_mcast_entry_for_vni(struct zebra_ns *zns, + struct interface *ifp, vni_t vni) +{ + netlink_macfdb_read_mcast_for_vni(zns, ifp, vni); } void macfdb_read_specific_mac(struct zebra_ns *zns, struct interface *br_if, @@ -78,4 +85,9 @@ void kernel_read_tc_qdisc(struct zebra_ns *zns) netlink_qdisc_read(zns); } +void vlan_read(struct zebra_ns *zns) +{ + netlink_vlan_read(zns); +} + #endif /* GNU_LINUX */ diff --git a/zebra/rtread_sysctl.c b/zebra/rtread_sysctl.c index 35dde0e686..f5b918c022 100644 --- a/zebra/rtread_sysctl.c +++ b/zebra/rtread_sysctl.c @@ -84,7 +84,12 @@ void macfdb_read(struct zebra_ns *zns) } void macfdb_read_for_bridge(struct zebra_ns *zns, struct interface *ifp, - struct interface *br_if) + struct interface *br_if, vlanid_t vid) +{ +} + +void macfdb_read_mcast_entry_for_vni(struct zebra_ns *zns, + struct interface *ifp, vni_t vni) { } @@ -113,4 +118,8 @@ void kernel_read_tc_qdisc(struct zebra_ns *zns) { } +void vlan_read(struct zebra_ns *zns) +{ +} + #endif /* !defined(GNU_LINUX) */ diff --git a/zebra/subdir.am b/zebra/subdir.am index 00f7d52994..1060e38785 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -67,6 +67,7 @@ zebra_zebra_SOURCES = \ zebra/zebra_errors.c \ zebra/zebra_gr.c \ zebra/zebra_l2.c \ + zebra/zebra_l2_bridge_if.c \ zebra/zebra_evpn.c \ zebra/zebra_evpn_mac.c \ zebra/zebra_evpn_neigh.c \ @@ -106,6 +107,7 @@ zebra_zebra_SOURCES = \ zebra/zebra_vrf.c \ zebra/zebra_vty.c \ zebra/zebra_vxlan.c \ + zebra/zebra_vxlan_if.c \ zebra/zebra_evpn_mh.c \ zebra/zebra_neigh.c \ zebra/zserv.c \ @@ -185,6 +187,8 @@ noinst_HEADERS += \ zebra/zebra_vxlan_private.h \ zebra/zebra_evpn_mh.h \ zebra/zebra_neigh.h \ + zebra/zebra_l2_bridge_if.h \ + zebra/zebra_vxlan_if.h \ zebra/zserv.h \ zebra/dpdk/zebra_dplane_dpdk.h \ zebra/dpdk/zebra_dplane_dpdk_private.h \ diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index fdea766a1b..15b5790923 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -1779,13 +1779,19 @@ static bool zapi_read_nexthops(struct zserv *client, struct prefix *p, nexthop->srte_color = api_nh->srte_color; } - /* MPLS labels for BGP-LU or Segment Routing */ + /* Labels for MPLS BGP-LU or Segment Routing or EVPN */ if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL) && api_nh->type != NEXTHOP_TYPE_IFINDEX && api_nh->type != NEXTHOP_TYPE_BLACKHOLE && api_nh->label_num > 0) { - label_type = lsp_type_from_re_type(client->proto); + /* If label type was passed, use it */ + if (api_nh->label_type) + label_type = api_nh->label_type; + else + label_type = + lsp_type_from_re_type(client->proto); + nexthop_add_labels(nexthop, label_type, api_nh->label_num, &api_nh->labels[0]); @@ -1823,7 +1829,7 @@ static bool zapi_read_nexthops(struct zserv *client, struct prefix *p, mpls_label2str(nexthop->nh_label->num_labels, nexthop->nh_label->label, labelbuf, sizeof(labelbuf), - false); + nexthop->nh_label_type, false); } zlog_debug("%s: nh=%s, vrf_id=%d %s", diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index ef4cef65ca..dea5af44f8 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -222,6 +222,7 @@ struct dplane_mac_info { vlanid_t vid; ifindex_t br_ifindex; struct ethaddr mac; + vni_t vni; struct in_addr vtep_ip; bool is_sticky; uint32_t nhg_id; @@ -237,6 +238,7 @@ struct dplane_neigh_info { struct ethaddr mac; struct ipaddr ip_addr; } link; + vni_t vni; uint32_t flags; uint16_t state; uint32_t update_flags; @@ -628,17 +630,16 @@ static enum zebra_dplane_result pw_update_internal(struct zebra_pw *pw, static enum zebra_dplane_result intf_addr_update_internal( const struct interface *ifp, const struct connected *ifc, enum dplane_op_e op); -static enum zebra_dplane_result mac_update_common( - enum dplane_op_e op, const struct interface *ifp, - const struct interface *br_ifp, - vlanid_t vid, const struct ethaddr *mac, - struct in_addr vtep_ip, bool sticky, uint32_t nhg_id, - uint32_t update_flags); +static enum zebra_dplane_result +mac_update_common(enum dplane_op_e op, const struct interface *ifp, + const struct interface *br_ifp, vlanid_t vid, + const struct ethaddr *mac, vni_t vni, struct in_addr vtep_ip, + bool sticky, uint32_t nhg_id, uint32_t update_flags); static enum zebra_dplane_result neigh_update_internal(enum dplane_op_e op, const struct interface *ifp, const void *link, int link_family, - const struct ipaddr *ip, uint32_t flags, uint16_t state, - uint32_t update_flags, int protocol); + const struct ipaddr *ip, vni_t vni, uint32_t flags, + uint16_t state, uint32_t update_flags, int protocol); /* * Public APIs @@ -2229,6 +2230,12 @@ const struct ethaddr *dplane_ctx_mac_get_addr( return &(ctx->u.macinfo.mac); } +vni_t dplane_ctx_mac_get_vni(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return ctx->u.macinfo.vni; +} + const struct in_addr *dplane_ctx_mac_get_vtep_ip( const struct zebra_dplane_ctx *ctx) { @@ -2264,6 +2271,12 @@ const struct ethaddr *dplane_ctx_neigh_get_mac( return &(ctx->u.neigh.link.mac); } +vni_t dplane_ctx_neigh_get_vni(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return ctx->u.neigh.vni; +} + uint32_t dplane_ctx_neigh_get_flags(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -4649,14 +4662,11 @@ enum zebra_dplane_result dplane_intf_delete(const struct interface *ifp) /* * Enqueue vxlan/evpn mac add (or update). */ -enum zebra_dplane_result dplane_rem_mac_add(const struct interface *ifp, - const struct interface *bridge_ifp, - vlanid_t vid, - const struct ethaddr *mac, - struct in_addr vtep_ip, - bool sticky, - uint32_t nhg_id, - bool was_static) +enum zebra_dplane_result +dplane_rem_mac_add(const struct interface *ifp, + const struct interface *bridge_ifp, vlanid_t vid, + const struct ethaddr *mac, vni_t vni, struct in_addr vtep_ip, + bool sticky, uint32_t nhg_id, bool was_static) { enum zebra_dplane_result result; uint32_t update_flags = 0; @@ -4666,8 +4676,9 @@ enum zebra_dplane_result dplane_rem_mac_add(const struct interface *ifp, update_flags |= DPLANE_MAC_WAS_STATIC; /* Use common helper api */ - result = mac_update_common(DPLANE_OP_MAC_INSTALL, ifp, bridge_ifp, - vid, mac, vtep_ip, sticky, nhg_id, update_flags); + result = mac_update_common(DPLANE_OP_MAC_INSTALL, ifp, bridge_ifp, vid, + mac, vni, vtep_ip, sticky, nhg_id, + update_flags); return result; } @@ -4675,10 +4686,10 @@ enum zebra_dplane_result dplane_rem_mac_add(const struct interface *ifp, * Enqueue vxlan/evpn mac delete. */ enum zebra_dplane_result dplane_rem_mac_del(const struct interface *ifp, - const struct interface *bridge_ifp, - vlanid_t vid, - const struct ethaddr *mac, - struct in_addr vtep_ip) + const struct interface *bridge_ifp, + vlanid_t vid, + const struct ethaddr *mac, + vni_t vni, struct in_addr vtep_ip) { enum zebra_dplane_result result; uint32_t update_flags = 0; @@ -4686,8 +4697,8 @@ enum zebra_dplane_result dplane_rem_mac_del(const struct interface *ifp, update_flags |= DPLANE_MAC_REMOTE; /* Use common helper api */ - result = mac_update_common(DPLANE_OP_MAC_DELETE, ifp, bridge_ifp, - vid, mac, vtep_ip, false, 0, update_flags); + result = mac_update_common(DPLANE_OP_MAC_DELETE, ifp, bridge_ifp, vid, + mac, vni, vtep_ip, false, 0, update_flags); return result; } @@ -4716,7 +4727,7 @@ enum zebra_dplane_result dplane_neigh_ip_update(enum dplane_op_e op, update_flags = DPLANE_NEIGH_NO_EXTENSION; result = neigh_update_internal(op, ifp, (const void *)link_ip, - ipaddr_family(link_ip), ip, 0, state, + ipaddr_family(link_ip), ip, 0, 0, state, update_flags, protocol); return result; @@ -4746,9 +4757,8 @@ enum zebra_dplane_result dplane_local_mac_add(const struct interface *ifp, vtep_ip.s_addr = 0; /* Use common helper api */ - result = mac_update_common(DPLANE_OP_MAC_INSTALL, ifp, bridge_ifp, - vid, mac, vtep_ip, sticky, 0, - update_flags); + result = mac_update_common(DPLANE_OP_MAC_INSTALL, ifp, bridge_ifp, vid, + mac, 0, vtep_ip, sticky, 0, update_flags); return result; } @@ -4767,21 +4777,17 @@ dplane_local_mac_del(const struct interface *ifp, /* Use common helper api */ result = mac_update_common(DPLANE_OP_MAC_DELETE, ifp, bridge_ifp, vid, - mac, vtep_ip, false, 0, 0); + mac, 0, vtep_ip, false, 0, 0); return result; } /* * Public api to init an empty context - either newly-allocated or * reset/cleared - for a MAC update. */ -void dplane_mac_init(struct zebra_dplane_ctx *ctx, - const struct interface *ifp, - const struct interface *br_ifp, - vlanid_t vid, - const struct ethaddr *mac, - struct in_addr vtep_ip, - bool sticky, - uint32_t nhg_id, +void dplane_mac_init(struct zebra_dplane_ctx *ctx, const struct interface *ifp, + const struct interface *br_ifp, vlanid_t vid, + const struct ethaddr *mac, vni_t vni, + struct in_addr vtep_ip, bool sticky, uint32_t nhg_id, uint32_t update_flags) { struct zebra_ns *zns; @@ -4801,6 +4807,7 @@ void dplane_mac_init(struct zebra_dplane_ctx *ctx, ctx->u.macinfo.br_ifindex = br_ifp->ifindex; ctx->u.macinfo.vtep_ip = vtep_ip; ctx->u.macinfo.mac = *mac; + ctx->u.macinfo.vni = vni; ctx->u.macinfo.vid = vid; ctx->u.macinfo.is_sticky = sticky; ctx->u.macinfo.nhg_id = nhg_id; @@ -4811,15 +4818,10 @@ void dplane_mac_init(struct zebra_dplane_ctx *ctx, * Common helper api for MAC address/vxlan updates */ static enum zebra_dplane_result -mac_update_common(enum dplane_op_e op, - const struct interface *ifp, - const struct interface *br_ifp, - vlanid_t vid, - const struct ethaddr *mac, - struct in_addr vtep_ip, - bool sticky, - uint32_t nhg_id, - uint32_t update_flags) +mac_update_common(enum dplane_op_e op, const struct interface *ifp, + const struct interface *br_ifp, vlanid_t vid, + const struct ethaddr *mac, vni_t vni, struct in_addr vtep_ip, + bool sticky, uint32_t nhg_id, uint32_t update_flags) { enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; int ret; @@ -4833,7 +4835,7 @@ mac_update_common(enum dplane_op_e op, ctx->zd_op = op; /* Common init for the ctx */ - dplane_mac_init(ctx, ifp, br_ifp, vid, mac, vtep_ip, sticky, + dplane_mac_init(ctx, ifp, br_ifp, vid, mac, vni, vtep_ip, sticky, nhg_id, update_flags); /* Enqueue for processing on the dplane pthread */ @@ -4873,7 +4875,7 @@ enum zebra_dplane_result dplane_rem_neigh_add(const struct interface *ifp, result = neigh_update_internal( DPLANE_OP_NEIGH_INSTALL, ifp, (const void *)mac, AF_ETHERNET, - ip, flags, DPLANE_NUD_NOARP, update_flags, 0); + ip, 0, flags, DPLANE_NUD_NOARP, update_flags, 0); return result; } @@ -4906,8 +4908,8 @@ enum zebra_dplane_result dplane_local_neigh_add(const struct interface *ifp, ntf |= DPLANE_NTF_ROUTER; result = neigh_update_internal(DPLANE_OP_NEIGH_INSTALL, ifp, - (const void *)mac, AF_ETHERNET, ip, ntf, - state, update_flags, 0); + (const void *)mac, AF_ETHERNET, ip, 0, + ntf, state, update_flags, 0); return result; } @@ -4924,7 +4926,8 @@ enum zebra_dplane_result dplane_rem_neigh_delete(const struct interface *ifp, update_flags |= DPLANE_NEIGH_REMOTE; result = neigh_update_internal(DPLANE_OP_NEIGH_DELETE, ifp, NULL, - AF_ETHERNET, ip, 0, 0, update_flags, 0); + AF_ETHERNET, ip, 0, 0, 0, update_flags, + 0); return result; } @@ -4948,7 +4951,7 @@ enum zebra_dplane_result dplane_vtep_add(const struct interface *ifp, addr.ipaddr_v4 = *ip; result = neigh_update_internal(DPLANE_OP_VTEP_ADD, ifp, &mac, - AF_ETHERNET, &addr, 0, 0, 0, 0); + AF_ETHERNET, &addr, vni, 0, 0, 0, 0); return result; } @@ -4974,7 +4977,7 @@ enum zebra_dplane_result dplane_vtep_delete(const struct interface *ifp, result = neigh_update_internal(DPLANE_OP_VTEP_DELETE, ifp, (const void *)&mac, AF_ETHERNET, &addr, - 0, 0, 0, 0); + vni, 0, 0, 0, 0); return result; } @@ -4985,7 +4988,7 @@ enum zebra_dplane_result dplane_neigh_discover(const struct interface *ifp, enum zebra_dplane_result result; result = neigh_update_internal(DPLANE_OP_NEIGH_DISCOVER, ifp, NULL, - AF_ETHERNET, ip, DPLANE_NTF_USE, + AF_ETHERNET, ip, 0, DPLANE_NTF_USE, DPLANE_NUD_INCOMPLETE, 0, 0); return result; @@ -5053,8 +5056,8 @@ enum zebra_dplane_result dplane_neigh_table_update(const struct interface *ifp, static enum zebra_dplane_result neigh_update_internal(enum dplane_op_e op, const struct interface *ifp, const void *link, const int link_family, - const struct ipaddr *ip, uint32_t flags, uint16_t state, - uint32_t update_flags, int protocol) + const struct ipaddr *ip, vni_t vni, uint32_t flags, + uint16_t state, uint32_t update_flags, int protocol) { enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; int ret; @@ -5105,6 +5108,7 @@ neigh_update_internal(enum dplane_op_e op, const struct interface *ifp, ctx->u.neigh.link.ip_addr = *link_ip; ctx->u.neigh.flags = flags; + ctx->u.neigh.vni = vni; ctx->u.neigh.state = state; ctx->u.neigh.update_flags = update_flags; diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index ae13243a16..0c84992051 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -568,6 +568,7 @@ uint32_t dplane_ctx_mac_get_update_flags(const struct zebra_dplane_ctx *ctx); uint32_t dplane_ctx_mac_get_nhg_id(const struct zebra_dplane_ctx *ctx); const struct ethaddr *dplane_ctx_mac_get_addr( const struct zebra_dplane_ctx *ctx); +vni_t dplane_ctx_mac_get_vni(const struct zebra_dplane_ctx *ctx); const struct in_addr *dplane_ctx_mac_get_vtep_ip( const struct zebra_dplane_ctx *ctx); ifindex_t dplane_ctx_mac_get_br_ifindex(const struct zebra_dplane_ctx *ctx); @@ -577,6 +578,7 @@ const struct ipaddr *dplane_ctx_neigh_get_ipaddr( const struct zebra_dplane_ctx *ctx); const struct ethaddr *dplane_ctx_neigh_get_mac( const struct zebra_dplane_ctx *ctx); +vni_t dplane_ctx_neigh_get_vni(const struct zebra_dplane_ctx *ctx); const struct ipaddr * dplane_ctx_neigh_get_link_ip(const struct zebra_dplane_ctx *ctx); uint32_t dplane_ctx_neigh_get_flags(const struct zebra_dplane_ctx *ctx); @@ -792,14 +794,11 @@ enum zebra_dplane_result dplane_neigh_ip_update(enum dplane_op_e op, /* * Enqueue evpn mac operations for the dataplane. */ -enum zebra_dplane_result dplane_rem_mac_add(const struct interface *ifp, - const struct interface *bridge_ifp, - vlanid_t vid, - const struct ethaddr *mac, - struct in_addr vtep_ip, - bool sticky, - uint32_t nhg_id, - bool was_static); +enum zebra_dplane_result +dplane_rem_mac_add(const struct interface *ifp, + const struct interface *bridge_ifp, vlanid_t vid, + const struct ethaddr *mac, vni_t vni, struct in_addr vtep_ip, + bool sticky, uint32_t nhg_id, bool was_static); enum zebra_dplane_result dplane_local_mac_add(const struct interface *ifp, const struct interface *bridge_ifp, @@ -815,20 +814,17 @@ dplane_local_mac_del(const struct interface *ifp, const struct ethaddr *mac); enum zebra_dplane_result dplane_rem_mac_del(const struct interface *ifp, - const struct interface *bridge_ifp, - vlanid_t vid, - const struct ethaddr *mac, - struct in_addr vtep_ip); + const struct interface *bridge_ifp, + vlanid_t vid, + const struct ethaddr *mac, + vni_t vni, struct in_addr vtep_ip); /* Helper api to init an empty or new context for a MAC update */ -void dplane_mac_init(struct zebra_dplane_ctx *ctx, - const struct interface *ifp, - const struct interface *br_ifp, - vlanid_t vid, - const struct ethaddr *mac, - struct in_addr vtep_ip, - bool sticky, - uint32_t nhg_id, uint32_t update_flags); +void dplane_mac_init(struct zebra_dplane_ctx *ctx, const struct interface *ifp, + const struct interface *br_ifp, vlanid_t vid, + const struct ethaddr *mac, vni_t vni, + struct in_addr vtep_ip, bool sticky, uint32_t nhg_id, + uint32_t update_flags); /* * Enqueue evpn neighbor updates for the dataplane. diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c index ff9ca1ac25..bbe53adb2f 100644 --- a/zebra/zebra_evpn.c +++ b/zebra/zebra_evpn.c @@ -44,13 +44,14 @@ #include "zebra/rt_netlink.h" #include "zebra/zebra_errors.h" #include "zebra/zebra_l2.h" +#include "zebra/zebra_l2_bridge_if.h" #include "zebra/zebra_ns.h" #include "zebra/zebra_vrf.h" #include "zebra/zebra_vxlan.h" +#include "zebra/zebra_vxlan_private.h" #include "zebra/zebra_evpn.h" #include "zebra/zebra_evpn_mac.h" #include "zebra/zebra_evpn_neigh.h" -#include "zebra/zebra_vxlan_private.h" #include "zebra/zebra_evpn_mh.h" #include "zebra/zebra_evpn_vxlan.h" #include "zebra/zebra_router.h" @@ -117,6 +118,9 @@ void zebra_evpn_print(struct zebra_evpn *zevpn, void **ctxt) if (json == NULL) { vty_out(vty, "VNI: %u\n", zevpn->vni); vty_out(vty, " Type: %s\n", "L2"); + vty_out(vty, " Vlan: %u\n", zevpn->vid); + vty_out(vty, " Bridge: %s\n", + zevpn->bridge_if ? zevpn->bridge_if->name : "-"); vty_out(vty, " Tenant VRF: %s\n", vrf_id_to_name(zevpn->vrf_id)); } else { json_object_int_add(json, "vni", zevpn->vni); @@ -455,16 +459,16 @@ int zebra_evpn_gw_macip_add(struct interface *ifp, struct zebra_evpn *zevpn, { struct zebra_mac *mac = NULL; struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan *vxl = NULL; + struct zebra_vxlan_vni *vni; zif = zevpn->vxlan_if->info; if (!zif) return -1; - vxl = &zif->l2info.vxl; + vni = zebra_vxlan_if_vni_find(zif, zevpn->vni); zebra_evpn_mac_gw_macip_add(ifp, zevpn, ip, &mac, macaddr, - vxl->access_vlan, true); + vni->access_vlan, true); return zebra_evpn_neigh_gw_macip_add(ifp, zevpn, ip, mac); } @@ -523,7 +527,7 @@ void zebra_evpn_gw_macip_del_for_evpn_hash(struct hash_bucket *bucket, { struct zebra_evpn *zevpn = NULL; struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan zl2_info; + struct zebra_vxlan_vni *vni = NULL; struct interface *vlan_if = NULL; struct interface *vrr_if = NULL; struct interface *ifp; @@ -550,10 +554,11 @@ void zebra_evpn_gw_macip_del_for_evpn_hash(struct hash_bucket *bucket, if (!if_is_operative(ifp) || !zif->brslave_info.br_if) return; - zl2_info = zif->l2info.vxl; + vni = zebra_vxlan_if_vni_find(zif, zevpn->vni); + if (!vni) + return; - vlan_if = - zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if); + vlan_if = zvni_map_to_svi(vni->access_vlan, zif->brslave_info.br_if); if (!vlan_if) return; @@ -573,10 +578,10 @@ void zebra_evpn_gw_macip_add_for_evpn_hash(struct hash_bucket *bucket, { struct zebra_evpn *zevpn = NULL; struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan zl2_info; struct interface *vlan_if = NULL; struct interface *vrr_if = NULL; struct interface *ifp = NULL; + struct zebra_vxlan_vni *vni = NULL; zevpn = (struct zebra_evpn *)bucket->data; @@ -588,10 +593,11 @@ void zebra_evpn_gw_macip_add_for_evpn_hash(struct hash_bucket *bucket, /* If down or not mapped to a bridge, we're done. */ if (!if_is_operative(ifp) || !zif->brslave_info.br_if) return; - zl2_info = zif->l2info.vxl; + vni = zebra_vxlan_if_vni_find(zif, zevpn->vni); + if (!vni) + return; - vlan_if = - zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if); + vlan_if = zvni_map_to_svi(vni->access_vlan, zif->brslave_info.br_if); if (!vlan_if) return; @@ -615,8 +621,8 @@ void zebra_evpn_svi_macip_del_for_evpn_hash(struct hash_bucket *bucket, { struct zebra_evpn *zevpn = NULL; struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan zl2_info; struct interface *vlan_if = NULL; + struct zebra_vxlan_vni *vni = NULL; struct interface *ifp; /* Add primary SVI MAC*/ @@ -643,10 +649,11 @@ void zebra_evpn_svi_macip_del_for_evpn_hash(struct hash_bucket *bucket, if (!if_is_operative(ifp) || !zif->brslave_info.br_if) return; - zl2_info = zif->l2info.vxl; + vni = zebra_vxlan_if_vni_find(zif, zevpn->vni); + if (!vni) + return; - vlan_if = - zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if); + vlan_if = zvni_map_to_svi(vni->access_vlan, zif->brslave_info.br_if); if (!vlan_if) return; @@ -660,6 +667,7 @@ static int zebra_evpn_map_vlan_ns(struct ns *ns, void *_in_param, void **_p_zevpn) { + int found = 0; struct zebra_ns *zns = ns->info; struct route_node *rn; struct interface *br_if; @@ -667,42 +675,59 @@ static int zebra_evpn_map_vlan_ns(struct ns *ns, struct zebra_evpn *zevpn; struct interface *tmp_if = NULL; struct zebra_if *zif; - struct zebra_l2info_vxlan *vxl = NULL; struct zebra_from_svi_param *in_param = (struct zebra_from_svi_param *)_in_param; + vlanid_t vid; + vni_t vni_id = 0; + uint8_t bridge_vlan_aware; assert(p_zevpn && in_param); br_if = in_param->br_if; + assert(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; + vid = in_param->vid; + bridge_vlan_aware = in_param->bridge_vlan_aware; - if (!in_param->bridge_vlan_aware - || vxl->access_vlan == in_param->vid) { - zevpn = zebra_evpn_lookup(vxl->vni); - *p_zevpn = zevpn; - return NS_WALK_STOP; + if (bridge_vlan_aware) { + vni_id = zebra_l2_bridge_if_vni_find(zif, vid); + if (vni_id) + found = 1; + } else { + /* + * 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; + + if (zif->brslave_info.br_if != br_if) + continue; + + vni_id = + zebra_vxlan_if_access_vlan_vni_find(zif, br_if); + if (vni_id) { + found = 1; + break; + } } } - return NS_WALK_CONTINUE; + if (!found) + return NS_WALK_CONTINUE; + + zevpn = zebra_evpn_lookup(vni_id); + *p_zevpn = zevpn; + return NS_WALK_STOP; } /* @@ -713,7 +738,6 @@ struct zebra_evpn *zebra_evpn_map_vlan(struct interface *ifp, struct interface *br_if, vlanid_t vid) { struct zebra_if *zif; - struct zebra_l2info_bridge *br; struct zebra_evpn **p_zevpn; struct zebra_evpn *zevpn = NULL; struct zebra_from_svi_param in_param; @@ -721,8 +745,7 @@ struct zebra_evpn *zebra_evpn_map_vlan(struct interface *ifp, /* Determine if bridge is VLAN-aware or not */ zif = br_if->info; assert(zif); - br = &zif->l2info.br; - in_param.bridge_vlan_aware = br->vlan_aware; + in_param.bridge_vlan_aware = IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(zif); in_param.vid = vid; in_param.br_if = br_if; in_param.zif = zif; @@ -745,43 +768,60 @@ static int zebra_evpn_from_svi_ns(struct ns *ns, struct zebra_evpn *zevpn; struct interface *tmp_if = NULL; struct zebra_if *zif; - struct zebra_l2info_vxlan *vxl = NULL; + struct zebra_if *br_zif; + struct zebra_l2_bridge_vlan *bvlan; struct zebra_from_svi_param *in_param = (struct zebra_from_svi_param *)_in_param; int found = 0; + vni_t vni_id = 0; + vlanid_t vid = 0; + uint8_t bridge_vlan_aware; if (!in_param) return NS_WALK_STOP; + br_if = in_param->br_if; zif = in_param->zif; assert(zif); - - /* 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) { + bridge_vlan_aware = in_param->bridge_vlan_aware; + vid = in_param->vid; + br_zif = br_if->info; + assert(br_zif); + + if (bridge_vlan_aware) { + bvlan = zebra_l2_bridge_if_vlan_find(br_zif, vid); + if (bvlan && bvlan->access_bd && bvlan->access_bd->vni) { found = 1; - break; + vni_id = bvlan->access_bd->vni; + } + } else { + /* 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; + + if (zif->brslave_info.br_if != br_if) + continue; + + vni_id = + zebra_vxlan_if_access_vlan_vni_find(zif, br_if); + if (vni_id) { + found = 1; + break; + } } } if (!found) return NS_WALK_CONTINUE; - zevpn = zebra_evpn_lookup(vxl->vni); + zevpn = zebra_evpn_lookup(vni_id); if (p_zevpn) *p_zevpn = zevpn; return NS_WALK_STOP; @@ -794,7 +834,6 @@ static int zebra_evpn_from_svi_ns(struct ns *ns, struct zebra_evpn *zebra_evpn_from_svi(struct interface *ifp, struct interface *br_if) { - struct zebra_l2info_bridge *br; struct zebra_evpn *zevpn = NULL; struct zebra_evpn **p_zevpn; struct zebra_if *zif; @@ -810,8 +849,7 @@ struct zebra_evpn *zebra_evpn_from_svi(struct interface *ifp, /* Determine if bridge is VLAN-aware or not */ zif = br_if->info; assert(zif); - br = &zif->l2info.br; - in_param.bridge_vlan_aware = br->vlan_aware; + in_param.bridge_vlan_aware = IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(zif); in_param.vid = 0; if (in_param.bridge_vlan_aware) { @@ -906,9 +944,24 @@ struct interface *zebra_evpn_map_to_macvlan(struct interface *br_if, } /* + * Uninstall MAC hash entry - called upon access VLAN change. + */ +static void zebra_evpn_uninstall_mac_hash(struct hash_bucket *bucket, + void *ctxt) +{ + struct zebra_mac *mac; + struct mac_walk_ctx *wctx = ctxt; + + mac = (struct zebra_mac *)bucket->data; + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) + zebra_evpn_rem_mac_uninstall(wctx->zevpn, mac, false); +} + +/* * Install MAC hash entry - called upon access VLAN change. */ -void zebra_evpn_install_mac_hash(struct hash_bucket *bucket, void *ctxt) +static void zebra_evpn_install_mac_hash(struct hash_bucket *bucket, void *ctxt) { struct zebra_mac *mac; struct mac_walk_ctx *wctx = ctxt; @@ -920,6 +973,44 @@ void zebra_evpn_install_mac_hash(struct hash_bucket *bucket, void *ctxt) } /* + * Uninstall remote MAC entries for this EVPN. + */ +void zebra_evpn_rem_mac_uninstall_all(struct zebra_evpn *zevpn) +{ + struct mac_walk_ctx wctx; + + if (!zevpn->mac_table) + return; + + memset(&wctx, 0, sizeof(struct mac_walk_ctx)); + wctx.zevpn = zevpn; + wctx.uninstall = 1; + wctx.upd_client = 0; + wctx.flags = ZEBRA_MAC_REMOTE; + + hash_iterate(zevpn->mac_table, zebra_evpn_uninstall_mac_hash, &wctx); +} + +/* + * Install remote MAC entries for this EVPN. + */ +void zebra_evpn_rem_mac_install_all(struct zebra_evpn *zevpn) +{ + struct mac_walk_ctx wctx; + + if (!zevpn->mac_table) + return; + + memset(&wctx, 0, sizeof(struct mac_walk_ctx)); + wctx.zevpn = zevpn; + wctx.uninstall = 0; + wctx.upd_client = 0; + wctx.flags = ZEBRA_MAC_REMOTE; + + hash_iterate(zevpn->mac_table, zebra_evpn_install_mac_hash, &wctx); +} + +/* * Read and populate local MACs and neighbors corresponding to this EVPN. */ void zebra_evpn_read_mac_neigh(struct zebra_evpn *zevpn, struct interface *ifp) @@ -928,11 +1019,11 @@ void zebra_evpn_read_mac_neigh(struct zebra_evpn *zevpn, struct interface *ifp) struct zebra_vrf *zvrf; struct zebra_if *zif; struct interface *vlan_if; - struct zebra_l2info_vxlan *vxl; + struct zebra_vxlan_vni *vni; struct interface *vrr_if; zif = ifp->info; - vxl = &zif->l2info.vxl; + vni = zebra_vxlan_if_vni_find(zif, zevpn->vni); zvrf = zebra_vrf_lookup_by_id(zevpn->vrf_id); if (!zvrf || !zvrf->zns) return; @@ -944,8 +1035,13 @@ void zebra_evpn_read_mac_neigh(struct zebra_evpn *zevpn, struct interface *ifp) ifp->name, ifp->ifindex, zevpn->vni, zif->brslave_info.bridge_ifindex); - macfdb_read_for_bridge(zns, ifp, zif->brslave_info.br_if); - vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); + macfdb_read_for_bridge(zns, ifp, zif->brslave_info.br_if, + vni->access_vlan); + /* We need to specifically read and retrieve the entry for BUM handling + * via multicast, if any. + */ + macfdb_read_mcast_entry_for_vni(zns, ifp, zevpn->vni); + vlan_if = zvni_map_to_svi(vni->access_vlan, zif->brslave_info.br_if); if (vlan_if) { /* Add SVI MAC */ zebra_evpn_acc_bd_svi_mac_add(vlan_if); @@ -1518,7 +1614,7 @@ void zebra_evpn_rem_macip_del(vni_t vni, const struct ethaddr *macaddr, struct interface *ifp = NULL; struct zebra_if *zif = NULL; struct zebra_ns *zns; - struct zebra_l2info_vxlan *vxl; + struct zebra_vxlan_vni *vnip; struct zebra_vrf *zvrf; char buf1[INET6_ADDRSTRLEN]; @@ -1541,7 +1637,14 @@ void zebra_evpn_rem_macip_del(vni_t vni, const struct ethaddr *macaddr, return; } zns = zebra_ns_lookup(NS_DEFAULT); - vxl = &zif->l2info.vxl; + vnip = zebra_vxlan_if_vni_find(zif, vni); + if (!vnip) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "VNI %u not in interface upon remote MACIP DEL", + vni); + return; + } mac = zebra_evpn_mac_lookup(zevpn, macaddr); if (ipa_len) @@ -1596,7 +1699,7 @@ void zebra_evpn_rem_macip_del(vni_t vni, const struct ethaddr *macaddr, "%s: MAC %pEA (flags 0x%x) is remote and duplicate, read kernel for local entry", __func__, macaddr, mac->flags); macfdb_read_specific_mac(zns, zif->brslave_info.br_if, - macaddr, vxl->access_vlan); + macaddr, vnip->access_vlan); } if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { diff --git a/zebra/zebra_evpn.h b/zebra/zebra_evpn.h index 2c84d23045..8218b77213 100644 --- a/zebra/zebra_evpn.h +++ b/zebra/zebra_evpn.h @@ -33,6 +33,7 @@ #include "zebra/zebra_l2.h" #include "zebra/interface.h" +#include "zebra/zebra_vxlan.h" #ifdef __cplusplus extern "C" { @@ -83,6 +84,10 @@ struct zebra_evpn { uint32_t flags; #define ZEVPN_READY_FOR_BGP (1 << 0) /* ready to be sent to BGP */ + /* Corresponding Bridge information */ + vlanid_t vid; + struct interface *bridge_if; + /* Flag for advertising gw macip */ uint8_t advertise_gw_macip; @@ -138,7 +143,7 @@ static inline struct interface *zevpn_map_to_svi(struct zebra_evpn *zevpn) { struct interface *ifp; struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan zl2_info; + struct zebra_vxlan_vni *vni; ifp = zevpn->vxlan_if; if (!ifp) @@ -146,12 +151,15 @@ static inline struct interface *zevpn_map_to_svi(struct zebra_evpn *zevpn) zif = ifp->info; if (!zif) return NULL; + vni = zebra_vxlan_if_vni_find(zif, zevpn->vni); + if (!vni) + return NULL; /* If down or not mapped to a bridge, we're done. */ if (!if_is_operative(ifp) || !zif->brslave_info.br_if) return NULL; - zl2_info = zif->l2info.vxl; - return zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if); + + return zvni_map_to_svi(vni->access_vlan, zif->brslave_info.br_if); } int advertise_gw_macip_enabled(struct zebra_evpn *zevpn); @@ -181,7 +189,8 @@ struct zebra_evpn *zebra_evpn_from_svi(struct interface *ifp, struct interface *br_if); struct interface *zebra_evpn_map_to_macvlan(struct interface *br_if, struct interface *svi_if); -void zebra_evpn_install_mac_hash(struct hash_bucket *bucket, void *ctxt); +void zebra_evpn_rem_mac_install_all(struct zebra_evpn *zevpn); +void zebra_evpn_rem_mac_uninstall_all(struct zebra_evpn *zevpn); void zebra_evpn_read_mac_neigh(struct zebra_evpn *zevpn, struct interface *ifp); unsigned int zebra_evpn_hash_keymake(const void *p); bool zebra_evpn_hash_cmp(const void *p1, const void *p2); diff --git a/zebra/zebra_evpn_mac.c b/zebra/zebra_evpn_mac.c index 2c953eef15..801403c11c 100644 --- a/zebra/zebra_evpn_mac.c +++ b/zebra/zebra_evpn_mac.c @@ -36,6 +36,8 @@ #include "zebra/zebra_router.h" #include "zebra/zebra_errors.h" #include "zebra/zebra_vrf.h" +#include "zebra/zebra_vxlan.h" +#include "zebra/zebra_vxlan_if.h" #include "zebra/zebra_evpn.h" #include "zebra/zebra_evpn_mh.h" #include "zebra/zebra_evpn_mac.h" @@ -198,7 +200,7 @@ int zebra_evpn_rem_mac_install(struct zebra_evpn *zevpn, struct zebra_mac *mac, bool was_static) { const struct zebra_if *zif, *br_zif; - const struct zebra_l2info_vxlan *vxl; + const struct zebra_vxlan_vni *vni; bool sticky; enum zebra_dplane_result res; const struct interface *br_ifp; @@ -214,7 +216,9 @@ int zebra_evpn_rem_mac_install(struct zebra_evpn *zevpn, struct zebra_mac *mac, if (br_ifp == NULL) return -1; - vxl = &zif->l2info.vxl; + vni = zebra_vxlan_if_vni_find(zif, zevpn->vni); + if (!vni) + return -1; sticky = !!CHECK_FLAG(mac->flags, (ZEBRA_MAC_STICKY | ZEBRA_MAC_REMOTE_DEF_GW)); @@ -235,12 +239,12 @@ int zebra_evpn_rem_mac_install(struct zebra_evpn *zevpn, struct zebra_mac *mac, br_zif = (const struct zebra_if *)(br_ifp->info); if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif)) - vid = vxl->access_vlan; + vid = vni->access_vlan; else vid = 0; res = dplane_rem_mac_add(zevpn->vxlan_if, br_ifp, vid, &mac->macaddr, - vtep_ip, sticky, nhg_id, was_static); + vni->vni, vtep_ip, sticky, nhg_id, was_static); if (res != ZEBRA_DPLANE_REQUEST_FAILURE) return 0; else @@ -254,7 +258,7 @@ int zebra_evpn_rem_mac_uninstall(struct zebra_evpn *zevpn, struct zebra_mac *mac, bool force) { const struct zebra_if *zif, *br_zif; - const struct zebra_l2info_vxlan *vxl; + struct zebra_vxlan_vni *vni; struct in_addr vtep_ip; const struct interface *ifp, *br_ifp; vlanid_t vid; @@ -280,19 +284,22 @@ int zebra_evpn_rem_mac_uninstall(struct zebra_evpn *zevpn, if (br_ifp == NULL) return -1; - vxl = &zif->l2info.vxl; + vni = zebra_vxlan_if_vni_find(zif, zevpn->vni); + if (!vni) + return -1; br_zif = (const struct zebra_if *)br_ifp->info; if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif)) - vid = vxl->access_vlan; + vid = vni->access_vlan; else vid = 0; ifp = zevpn->vxlan_if; vtep_ip = mac->fwd_info.r_vtep_ip; - res = dplane_rem_mac_del(ifp, br_ifp, vid, &mac->macaddr, vtep_ip); + res = dplane_rem_mac_del(ifp, br_ifp, vid, &mac->macaddr, vni->vni, + vtep_ip); if (res != ZEBRA_DPLANE_REQUEST_FAILURE) return 0; else @@ -327,6 +334,8 @@ static void zebra_evpn_mac_get_access_info(struct zebra_mac *mac, struct interface **p_ifp, vlanid_t *vid) { + struct zebra_vxlan_vni *vni; + /* if the mac is associated with an ES we must get the access * info from the ES */ @@ -338,7 +347,8 @@ static void zebra_evpn_mac_get_access_info(struct zebra_mac *mac, /* get the vlan from the EVPN */ if (mac->zevpn->vxlan_if) { zif = mac->zevpn->vxlan_if->info; - *vid = zif->l2info.vxl.access_vlan; + vni = zebra_vxlan_if_vni_find(zif, mac->zevpn->vni); + *vid = vni->access_vlan; } else { *vid = 0; } diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c index 01ea9c5b9c..15def89e97 100644 --- a/zebra/zebra_evpn_mh.c +++ b/zebra/zebra_evpn_mh.c @@ -41,12 +41,13 @@ #include "zebra/if_netlink.h" #include "zebra/zebra_errors.h" #include "zebra/zebra_l2.h" +#include "zebra/zebra_l2_bridge_if.h" #include "zebra/zebra_ns.h" #include "zebra/zebra_vrf.h" #include "zebra/zebra_vxlan.h" +#include "zebra/zebra_vxlan_private.h" #include "zebra/zebra_evpn.h" #include "zebra/zebra_evpn_mac.h" -#include "zebra/zebra_vxlan_private.h" #include "zebra/zebra_router.h" #include "zebra/zebra_evpn_mh.h" #include "zebra/zebra_nhg.h" @@ -481,6 +482,7 @@ void zebra_evpn_update_all_es(struct zebra_evpn *zevpn) struct interface *vlan_if; struct interface *vxlan_if; struct zebra_if *vxlan_zif; + struct zebra_vxlan_vni *vni; /* the EVPN is now elgible as a base for EVPN-MH */ if (zebra_evpn_send_to_client_ok(zevpn)) @@ -497,11 +499,15 @@ void zebra_evpn_update_all_es(struct zebra_evpn *zevpn) vxlan_zif = vxlan_if->info; if (if_is_operative(vxlan_if) && vxlan_zif->brslave_info.br_if) { - vlan_if = zvni_map_to_svi( - vxlan_zif->l2info.vxl.access_vlan, - vxlan_zif->brslave_info.br_if); - if (vlan_if) - zebra_evpn_acc_bd_svi_mac_add(vlan_if); + vni = zebra_vxlan_if_vni_find(vxlan_zif, zevpn->vni); + /* VLAN-VNI mappings may not exist */ + if (vni) { + vlan_if = zvni_map_to_svi( + vni->access_vlan, + vxlan_zif->brslave_info.br_if); + if (vlan_if) + zebra_evpn_acc_bd_svi_mac_add(vlan_if); + } } } } @@ -520,7 +526,7 @@ static unsigned int zebra_evpn_acc_vl_hash_keymake(const void *p) { const struct zebra_evpn_access_bd *acc_bd = p; - return jhash_1word(acc_bd->vid, 0); + return jhash_2words(acc_bd->vid, acc_bd->bridge_ifindex, 0); } /* Compare two VLAN based broadcast domains */ @@ -535,16 +541,19 @@ static bool zebra_evpn_acc_vl_cmp(const void *p1, const void *p2) if (acc_bd1 == NULL || acc_bd2 == NULL) return false; - return (acc_bd1->vid == acc_bd2->vid); + return ((acc_bd1->vid == acc_bd2->vid) && + (acc_bd1->bridge_ifindex == acc_bd2->bridge_ifindex)); } /* Lookup VLAN based broadcast domain */ -static struct zebra_evpn_access_bd *zebra_evpn_acc_vl_find(vlanid_t vid) +struct zebra_evpn_access_bd *zebra_evpn_acc_vl_find(vlanid_t vid, + struct interface *br_if) { struct zebra_evpn_access_bd *acc_bd; struct zebra_evpn_access_bd tmp; tmp.vid = vid; + tmp.bridge_ifindex = br_if->ifindex; acc_bd = hash_lookup(zmh_info->evpn_vlan_table, &tmp); return acc_bd; @@ -560,11 +569,13 @@ zebra_evpn_acc_vl_new(vlanid_t vid, struct interface *br_if) struct interface *vlan_if; if (IS_ZEBRA_DEBUG_EVPN_MH_ES) - zlog_debug("access vlan %d add", vid); + zlog_debug("access vlan %d bridge %s add", vid, br_if->name); acc_bd = XCALLOC(MTYPE_ZACC_BD, sizeof(struct zebra_evpn_access_bd)); acc_bd->vid = vid; + acc_bd->bridge_ifindex = br_if->ifindex; + acc_bd->bridge_zif = (struct zebra_if *)br_if->info; /* Initialize the mbr list */ acc_bd->mbr_zifs = list_new(); @@ -573,14 +584,12 @@ zebra_evpn_acc_vl_new(vlanid_t vid, struct interface *br_if) (void)hash_get(zmh_info->evpn_vlan_table, acc_bd, hash_alloc_intern); /* check if an svi exists for the vlan */ - if (br_if) { - vlan_if = zvni_map_to_svi(vid, br_if); - if (vlan_if) { - if (IS_ZEBRA_DEBUG_EVPN_MH_ES) - zlog_debug("vlan %d SVI %s set", vid, - vlan_if->name); - acc_bd->vlan_zif = vlan_if->info; - } + vlan_if = zvni_map_to_svi(vid, br_if); + if (vlan_if) { + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("vlan %d bridge %s SVI %s set", vid, + br_if->name, vlan_if->name); + acc_bd->vlan_zif = vlan_if->info; } return acc_bd; } @@ -621,16 +630,32 @@ static void zebra_evpn_acc_bd_free_on_deref(struct zebra_evpn_access_bd *acc_bd) if (!list_isempty(acc_bd->mbr_zifs) || acc_bd->vxlan_zif) return; + /* Remove this access_bd from bridge hash table */ + zebra_l2_bridge_if_vlan_access_bd_deref(acc_bd); + /* if there are no references free the EVI */ zebra_evpn_acc_vl_free(acc_bd); } +static struct zebra_evpn_access_bd * +zebra_evpn_acc_bd_alloc_on_ref(vlanid_t vid, struct interface *br_if) +{ + struct zebra_evpn_access_bd *acc_bd = NULL; + + assert(br_if && br_if->info); + acc_bd = zebra_evpn_acc_vl_new(vid, br_if); + if (acc_bd) + /* Add this access_bd to bridge hash table */ + zebra_l2_bridge_if_vlan_access_bd_ref(acc_bd); + + return acc_bd; +} + /* called when a SVI is goes up/down */ void zebra_evpn_acc_bd_svi_set(struct zebra_if *vlan_zif, struct zebra_if *br_zif, bool is_up) { struct zebra_evpn_access_bd *acc_bd; - struct zebra_l2info_bridge *br; uint16_t vid; struct zebra_if *tmp_br_zif = br_zif; @@ -641,20 +666,19 @@ void zebra_evpn_acc_bd_svi_set(struct zebra_if *vlan_zif, tmp_br_zif = vlan_zif->link->info; } - br = &tmp_br_zif->l2info.br; /* ignore vlan unaware bridges */ - if (!br->vlan_aware) + if (!IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(tmp_br_zif)) return; vid = vlan_zif->l2info.vl.vid; - acc_bd = zebra_evpn_acc_vl_find(vid); + acc_bd = zebra_evpn_acc_vl_find(vid, tmp_br_zif->ifp); if (!acc_bd) return; if (is_up) { if (IS_ZEBRA_DEBUG_EVPN_MH_ES) - zlog_debug("vlan %d SVI %s set", vid, - vlan_zif->ifp->name); + zlog_debug("vlan %d bridge %s SVI %s set", vid, + tmp_br_zif->ifp->name, vlan_zif->ifp->name); acc_bd->vlan_zif = vlan_zif; if (acc_bd->zevpn) @@ -662,7 +686,8 @@ void zebra_evpn_acc_bd_svi_set(struct zebra_if *vlan_zif, acc_bd->zevpn); } else if (acc_bd->vlan_zif) { if (IS_ZEBRA_DEBUG_EVPN_MH_ES) - zlog_debug("vlan %d SVI clear", vid); + zlog_debug("vlan %d bridge %s SVI clear", vid, + tmp_br_zif->ifp->name); acc_bd->vlan_zif = NULL; if (acc_bd->zevpn && acc_bd->zevpn->mac_table) zebra_evpn_mac_svi_del(vlan_zif->ifp, acc_bd->zevpn); @@ -687,8 +712,9 @@ static void zebra_evpn_acc_bd_evpn_set(struct zebra_evpn_access_bd *acc_bd, struct listnode *node; if (IS_ZEBRA_DEBUG_EVPN_MH_ES) - zlog_debug("access vlan %d l2-vni %u set", - acc_bd->vid, zevpn ? zevpn->vni : 0); + zlog_debug("access vlan %d bridge %s l2-vni %u set", + acc_bd->vid, acc_bd->bridge_zif->ifp->name, + zevpn ? zevpn->vni : 0); for (ALL_LIST_ELEMENTS_RO(acc_bd->mbr_zifs, node, zif)) { if (!zif->es_info.es) @@ -711,33 +737,44 @@ static void zebra_evpn_acc_bd_evpn_set(struct zebra_evpn_access_bd *acc_bd, } /* handle VLAN->VxLAN_IF association */ -void zebra_evpn_vl_vxl_ref(uint16_t vid, struct zebra_if *vxlan_zif) +void zebra_evpn_vl_vxl_ref(uint16_t vid, vni_t vni_id, + struct zebra_if *vxlan_zif) { + vni_t old_vni; struct zebra_evpn_access_bd *acc_bd; - struct zebra_if *old_vxlan_zif; struct zebra_evpn *old_zevpn; + struct interface *br_if; if (!vid) return; - acc_bd = zebra_evpn_acc_vl_find(vid); + if (!vni_id) + return; + + br_if = vxlan_zif->brslave_info.br_if; + + if (!br_if) + return; + + acc_bd = zebra_evpn_acc_vl_find(vid, br_if); if (!acc_bd) - acc_bd = zebra_evpn_acc_vl_new(vid, - vxlan_zif->brslave_info.br_if); + acc_bd = zebra_evpn_acc_bd_alloc_on_ref(vid, br_if); - old_vxlan_zif = acc_bd->vxlan_zif; - acc_bd->vxlan_zif = vxlan_zif; - if (vxlan_zif == old_vxlan_zif) + old_vni = acc_bd->vni; + + if (vni_id == old_vni) return; + acc_bd->vni = vni_id; + acc_bd->vxlan_zif = vxlan_zif; + old_zevpn = acc_bd->zevpn; - acc_bd->zevpn = zebra_evpn_lookup(vxlan_zif->l2info.vxl.vni); + acc_bd->zevpn = zebra_evpn_lookup(vni_id); if (acc_bd->zevpn == old_zevpn) return; if (IS_ZEBRA_DEBUG_EVPN_MH_ES) - zlog_debug("access vlan %d vni %u ref", - acc_bd->vid, vxlan_zif->l2info.vxl.vni); + zlog_debug("access vlan %d vni %u ref", acc_bd->vid, vni_id); if (old_zevpn) zebra_evpn_acc_bd_evpn_set(acc_bd, NULL, old_zevpn); @@ -747,30 +784,66 @@ void zebra_evpn_vl_vxl_ref(uint16_t vid, struct zebra_if *vxlan_zif) } /* handle VLAN->VxLAN_IF deref */ -void zebra_evpn_vl_vxl_deref(uint16_t vid, struct zebra_if *vxlan_zif) +void zebra_evpn_vl_vxl_deref(uint16_t vid, vni_t vni_id, + struct zebra_if *vxlan_zif) { + struct interface *br_if; struct zebra_evpn_access_bd *acc_bd; if (!vid) return; - acc_bd = zebra_evpn_acc_vl_find(vid); + if (!vni_id) + return; + + br_if = vxlan_zif->brslave_info.br_if; + if (!br_if) + return; + + acc_bd = zebra_evpn_acc_vl_find(vid, br_if); if (!acc_bd) return; /* clear vxlan_if only if it matches */ - if (acc_bd->vxlan_zif != vxlan_zif) + if (acc_bd->vni != vni_id) return; if (IS_ZEBRA_DEBUG_EVPN_MH_ES) - zlog_debug("access vlan %d vni %u deref", - acc_bd->vid, vxlan_zif->l2info.vxl.vni); + zlog_debug("access vlan %d bridge %s vni %u deref", acc_bd->vid, + br_if->name, vni_id); if (acc_bd->zevpn) zebra_evpn_acc_bd_evpn_set(acc_bd, NULL, acc_bd->zevpn); acc_bd->zevpn = NULL; acc_bd->vxlan_zif = NULL; + acc_bd->vni = 0; + + /* if there are no other references the access_bd can be freed */ + zebra_evpn_acc_bd_free_on_deref(acc_bd); +} + +/* handle BridgeIf<->AccessBD cleanup */ +void zebra_evpn_access_bd_bridge_cleanup(vlanid_t vid, struct interface *br_if, + struct zebra_evpn_access_bd *acc_bd) +{ + struct zebra_evpn *zevpn; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("access bd vlan %d bridge %s cleanup", acc_bd->vid, + br_if->name); + + zevpn = acc_bd->zevpn; + if (zevpn) + zebra_evpn_acc_bd_evpn_set(acc_bd, NULL, zevpn); + + /* cleanup resources maintained against the ES */ + list_delete_all_node(acc_bd->mbr_zifs); + + acc_bd->zevpn = NULL; + acc_bd->vxlan_zif = NULL; + acc_bd->vni = 0; + acc_bd->bridge_zif = NULL; /* if there are no other references the access_bd can be freed */ zebra_evpn_acc_bd_free_on_deref(acc_bd); @@ -780,15 +853,23 @@ void zebra_evpn_vl_vxl_deref(uint16_t vid, struct zebra_if *vxlan_zif) void zebra_evpn_vxl_evpn_set(struct zebra_if *zif, struct zebra_evpn *zevpn, bool set) { - struct zebra_l2info_vxlan *vxl; + struct interface *br_if; + struct zebra_vxlan_vni *vni; struct zebra_evpn_access_bd *acc_bd; if (!zif) return; /* locate access_bd associated with the vxlan device */ - vxl = &zif->l2info.vxl; - acc_bd = zebra_evpn_acc_vl_find(vxl->access_vlan); + vni = zebra_vxlan_if_vni_find(zif, zevpn->vni); + if (!vni) + return; + + br_if = zif->brslave_info.br_if; + if (!br_if) + return; + + acc_bd = zebra_evpn_acc_vl_find(vni->access_vlan, br_if); if (!acc_bd) return; @@ -810,21 +891,26 @@ void zebra_evpn_vxl_evpn_set(struct zebra_if *zif, struct zebra_evpn *zevpn, /* handle addition of new VLAN members */ void zebra_evpn_vl_mbr_ref(uint16_t vid, struct zebra_if *zif) { + struct interface *br_if; struct zebra_evpn_access_bd *acc_bd; if (!vid) return; - acc_bd = zebra_evpn_acc_vl_find(vid); + br_if = zif->brslave_info.br_if; + if (!br_if) + return; + + acc_bd = zebra_evpn_acc_vl_find(vid, br_if); if (!acc_bd) - acc_bd = zebra_evpn_acc_vl_new(vid, zif->brslave_info.br_if); + acc_bd = zebra_evpn_acc_bd_alloc_on_ref(vid, br_if); if (listnode_lookup(acc_bd->mbr_zifs, zif)) return; if (IS_ZEBRA_DEBUG_EVPN_MH_ES) - zlog_debug("access vlan %d mbr %s ref", - vid, zif->ifp->name); + zlog_debug("access vlan %d bridge %s mbr %s ref", vid, + br_if->name, zif->ifp->name); listnode_add(acc_bd->mbr_zifs, zif); if (acc_bd->zevpn && zif->es_info.es) @@ -834,13 +920,18 @@ void zebra_evpn_vl_mbr_ref(uint16_t vid, struct zebra_if *zif) /* handle deletion of VLAN members */ void zebra_evpn_vl_mbr_deref(uint16_t vid, struct zebra_if *zif) { + struct interface *br_if; struct zebra_evpn_access_bd *acc_bd; struct listnode *node; if (!vid) return; - acc_bd = zebra_evpn_acc_vl_find(vid); + br_if = zif->brslave_info.br_if; + if (!br_if) + return; + + acc_bd = zebra_evpn_acc_vl_find(vid, br_if); if (!acc_bd) return; @@ -849,8 +940,8 @@ void zebra_evpn_vl_mbr_deref(uint16_t vid, struct zebra_if *zif) return; if (IS_ZEBRA_DEBUG_EVPN_MH_ES) - zlog_debug("access vlan %d mbr %s deref", - vid, zif->ifp->name); + zlog_debug("access vlan %d bridge %s mbr %s deref", vid, + br_if->name, zif->ifp->name); list_delete_node(acc_bd->mbr_zifs, node); @@ -917,14 +1008,19 @@ static void zebra_evpn_acc_vl_show_entry_detail(struct vty *vty, if (json) { zebra_evpn_acc_vl_json_fill(acc_bd, json, true); } else { - vty_out(vty, "VLAN: %u\n", acc_bd->vid); + vty_out(vty, "VLAN: %s.%u\n", acc_bd->bridge_zif->ifp->name, + acc_bd->vid); vty_out(vty, " VxLAN Interface: %s\n", acc_bd->vxlan_zif ? acc_bd->vxlan_zif->ifp->name : "-"); vty_out(vty, " SVI: %s\n", acc_bd->vlan_zif ? acc_bd->vlan_zif->ifp->name : "-"); - vty_out(vty, " L2-VNI: %d\n", - acc_bd->zevpn ? acc_bd->zevpn->vni : 0); + if (acc_bd->zevpn) + vty_out(vty, " L2-VNI: %d\n", acc_bd->zevpn->vni); + else { + vty_out(vty, " L2-VNI: 0\n"); + vty_out(vty, " L3-VNI: %d\n", acc_bd->vni); + } vty_out(vty, " Member Count: %d\n", listcount(acc_bd->mbr_zifs)); vty_out(vty, " Members: \n"); @@ -940,7 +1036,8 @@ static void zebra_evpn_acc_vl_show_entry(struct vty *vty, if (json) { zebra_evpn_acc_vl_json_fill(acc_bd, json, false); } else { - vty_out(vty, "%-5u %-15s %-8d %-15s %u\n", acc_bd->vid, + vty_out(vty, "%-5s.%-5u %-15s %-8d %-15s %u\n", + acc_bd->bridge_zif->ifp->name, acc_bd->vid, acc_bd->vlan_zif ? acc_bd->vlan_zif->ifp->name : "-", acc_bd->zevpn ? acc_bd->zevpn->vni : 0, acc_bd->vxlan_zif ? acc_bd->vxlan_zif->ifp->name : "-", @@ -978,7 +1075,7 @@ void zebra_evpn_acc_vl_show(struct vty *vty, bool uj) wctx.detail = false; if (!uj) - vty_out(vty, "%-5s %-15s %-8s %-15s %s\n", "VLAN", "SVI", + vty_out(vty, "%-12s %-15s %-8s %-15s %s\n", "VLAN", "SVI", "L2-VNI", "VXLAN-IF", "# Members"); hash_iterate(zmh_info->evpn_vlan_table, zebra_evpn_acc_vl_show_hash, @@ -1007,7 +1104,8 @@ void zebra_evpn_acc_vl_show_detail(struct vty *vty, bool uj) vty_json(vty, json_array); } -void zebra_evpn_acc_vl_show_vid(struct vty *vty, bool uj, vlanid_t vid) +void zebra_evpn_acc_vl_show_vid(struct vty *vty, bool uj, vlanid_t vid, + struct interface *br_if) { json_object *json = NULL; struct zebra_evpn_access_bd *acc_bd; @@ -1015,12 +1113,13 @@ void zebra_evpn_acc_vl_show_vid(struct vty *vty, bool uj, vlanid_t vid) if (uj) json = json_object_new_object(); - acc_bd = zebra_evpn_acc_vl_find(vid); + acc_bd = zebra_evpn_acc_vl_find(vid, br_if); if (acc_bd) { zebra_evpn_acc_vl_show_entry_detail(vty, acc_bd, json); } else { if (!json) - vty_out(vty, "VLAN %u not present\n", vid); + vty_out(vty, "VLAN %s.%u not present\n", br_if->name, + vid); } if (uj) @@ -1977,7 +2076,7 @@ static void zebra_evpn_es_setup_evis(struct zebra_evpn_es *es) return; bf_for_each_set_bit(zif->vlan_bitmap, vid, IF_VLAN_BITMAP_MAX) { - acc_bd = zebra_evpn_acc_vl_find(vid); + acc_bd = zebra_evpn_acc_vl_find(vid, zif->brslave_info.br_if); if (acc_bd->zevpn) zebra_evpn_local_es_evi_add(es, acc_bd->zevpn); } @@ -1986,9 +2085,10 @@ static void zebra_evpn_es_setup_evis(struct zebra_evpn_es *es) static void zebra_evpn_flush_local_mac(struct zebra_mac *mac, struct interface *ifp) { + vlanid_t vid; struct zebra_if *zif; struct interface *br_ifp; - vlanid_t vid; + struct zebra_vxlan_vni *vni; zif = ifp->info; br_ifp = zif->brslave_info.br_if; @@ -1997,7 +2097,8 @@ static void zebra_evpn_flush_local_mac(struct zebra_mac *mac, if (mac->zevpn->vxlan_if) { zif = mac->zevpn->vxlan_if->info; - vid = zif->l2info.vxl.access_vlan; + vni = zebra_vxlan_if_vni_find(zif, mac->zevpn->vni); + vid = vni->access_vlan; } else { vid = 0; } diff --git a/zebra/zebra_evpn_mh.h b/zebra/zebra_evpn_mh.h index 037648311c..12a8569504 100644 --- a/zebra/zebra_evpn_mh.h +++ b/zebra/zebra_evpn_mh.h @@ -179,6 +179,10 @@ struct zebra_evpn_es_vtep { struct zebra_evpn_access_bd { vlanid_t vid; + ifindex_t bridge_ifindex; + struct zebra_if *bridge_zif; /* associated bridge */ + + vni_t vni; /* vni associated with the vxlan device */ struct zebra_if *vxlan_zif; /* vxlan device */ /* list of members associated with the BD i.e. (potential) ESs */ struct list *mbr_zifs; @@ -319,8 +323,10 @@ extern void zebra_evpn_vxl_evpn_set(struct zebra_if *zif, struct zebra_evpn *zevpn, bool set); extern void zebra_evpn_es_set_base_evpn(struct zebra_evpn *zevpn); extern void zebra_evpn_es_clear_base_evpn(struct zebra_evpn *zevpn); -extern void zebra_evpn_vl_vxl_ref(uint16_t vid, struct zebra_if *vxlan_zif); -extern void zebra_evpn_vl_vxl_deref(uint16_t vid, struct zebra_if *vxlan_zif); +extern void zebra_evpn_vl_vxl_ref(uint16_t vid, vni_t vni_id, + struct zebra_if *vxlan_zif); +extern void zebra_evpn_vl_vxl_deref(uint16_t vid, vni_t vni_id, + struct zebra_if *vxlan_zif); extern void zebra_evpn_vl_mbr_ref(uint16_t vid, struct zebra_if *zif); extern void zebra_evpn_vl_mbr_deref(uint16_t vid, struct zebra_if *zif); extern void zebra_evpn_es_send_all_to_client(bool add); @@ -345,9 +351,12 @@ extern void zebra_evpn_interface_init(void); extern int zebra_evpn_mh_if_write(struct vty *vty, struct interface *ifp); extern void zebra_evpn_acc_vl_show(struct vty *vty, bool uj); extern void zebra_evpn_acc_vl_show_detail(struct vty *vty, bool uj); -extern void zebra_evpn_acc_vl_show_vid(struct vty *vty, bool uj, vlanid_t vid); extern void zebra_evpn_if_es_print(struct vty *vty, json_object *json, struct zebra_if *zif); +extern struct zebra_evpn_access_bd * +zebra_evpn_acc_vl_find(vlanid_t vid, struct interface *br_if); +extern void zebra_evpn_acc_vl_show_vid(struct vty *vty, bool uj, vlanid_t vid, + struct interface *br_if); extern void zebra_evpn_es_cleanup(void); extern int zebra_evpn_mh_mac_holdtime_update(struct vty *vty, uint32_t duration, bool set_default); @@ -373,6 +382,9 @@ extern void zebra_evpn_l2_nh_show(struct vty *vty, bool uj); extern void zebra_evpn_acc_bd_svi_set(struct zebra_if *vlan_zif, struct zebra_if *br_zif, bool is_up); extern void zebra_evpn_acc_bd_svi_mac_add(struct interface *vlan_if); +extern void +zebra_evpn_access_bd_bridge_cleanup(vlanid_t vid, struct interface *br_if, + struct zebra_evpn_access_bd *acc_bd); extern void zebra_evpn_es_bypass_update(struct zebra_evpn_es *es, struct interface *ifp, bool bypass); extern void zebra_evpn_proc_remote_nh(ZAPI_HANDLER_ARGS); diff --git a/zebra/zebra_evpn_neigh.c b/zebra/zebra_evpn_neigh.c index 684720bb4d..4c7f393fa1 100644 --- a/zebra/zebra_evpn_neigh.c +++ b/zebra/zebra_evpn_neigh.c @@ -36,6 +36,8 @@ #include "zebra/rt.h" #include "zebra/zebra_errors.h" #include "zebra/zebra_vrf.h" +#include "zebra/zebra_vxlan.h" +#include "zebra/zebra_vxlan_if.h" #include "zebra/zebra_evpn.h" #include "zebra/zebra_evpn_mh.h" #include "zebra/zebra_evpn_neigh.h" diff --git a/zebra/zebra_evpn_neigh.h b/zebra/zebra_evpn_neigh.h index 9271817440..6119cb8e72 100644 --- a/zebra/zebra_evpn_neigh.h +++ b/zebra/zebra_evpn_neigh.h @@ -63,6 +63,9 @@ struct zebra_neigh { struct zebra_evpn *zevpn; + /* Refcnt - Only used by SVD neighs currently */ + uint32_t refcnt; + uint32_t flags; #define ZEBRA_NEIGH_LOCAL 0x01 #define ZEBRA_NEIGH_REMOTE 0x02 diff --git a/zebra/zebra_evpn_vxlan.h b/zebra/zebra_evpn_vxlan.h index 3884a1e7ea..71df08a7a7 100644 --- a/zebra/zebra_evpn_vxlan.h +++ b/zebra/zebra_evpn_vxlan.h @@ -69,3 +69,33 @@ static inline void zevpn_vxlan_if_set(struct zebra_evpn *zevpn, zebra_evpn_vxl_evpn_set(zif, zevpn, set); } + +/* EVPN<=>Bridge interface association */ +static inline void zevpn_bridge_if_set(struct zebra_evpn *zevpn, + struct interface *ifp, bool set) +{ + if (set) { + if (zevpn->bridge_if == ifp) + return; + zevpn->bridge_if = ifp; + } else { + if (!zevpn->bridge_if) + return; + zevpn->bridge_if = NULL; + } +} + +/* EVPN<=>Bridge interface association */ +static inline void zl3vni_bridge_if_set(struct zebra_l3vni *zl3vni, + struct interface *ifp, bool set) +{ + if (set) { + if (zl3vni->bridge_if == ifp) + return; + zl3vni->bridge_if = ifp; + } else { + if (!zl3vni->bridge_if) + return; + zl3vni->bridge_if = NULL; + } +} diff --git a/zebra/zebra_l2.c b/zebra/zebra_l2.c index 8a9f3dffe3..c5d4c58852 100644 --- a/zebra/zebra_l2.c +++ b/zebra/zebra_l2.c @@ -42,7 +42,9 @@ #include "zebra/rt_netlink.h" #include "zebra/interface.h" #include "zebra/zebra_l2.h" +#include "zebra/zebra_l2_bridge_if.h" #include "zebra/zebra_vxlan.h" +#include "zebra/zebra_vxlan_if.h" #include "zebra/zebra_evpn_mh.h" /* definitions */ @@ -262,12 +264,14 @@ void zebra_l2_bridge_add_update(struct interface *ifp, int add) { struct zebra_if *zif; + struct zebra_l2_bridge_if *br; zif = ifp->info; assert(zif); - /* Copy over the L2 information. */ - memcpy(&zif->l2info.br, bridge_info, sizeof(*bridge_info)); + br = BRIDGE_FROM_ZEBRA_IF(zif); + br->vlan_aware = bridge_info->bridge.vlan_aware; + zebra_l2_bridge_if_add(ifp); /* Link all slaves to this bridge */ map_slaves_to_bridge(ifp, 1, false, ZEBRA_BRIDGE_NO_ACTION); @@ -278,6 +282,8 @@ void zebra_l2_bridge_add_update(struct interface *ifp, */ void zebra_l2_bridge_del(struct interface *ifp) { + zebra_l2_bridge_if_del(ifp); + /* Unlink all slaves to this bridge */ map_slaves_to_bridge(ifp, 0, false, ZEBRA_BRIDGE_NO_ACTION); } @@ -341,34 +347,40 @@ void zebra_l2_vxlanif_add_update(struct interface *ifp, struct zebra_l2info_vxlan *vxlan_info, int add) { struct zebra_if *zif; - struct in_addr old_vtep_ip; uint16_t chgflags = 0; + struct zebra_vxlan_if_update_ctx ctx; zif = ifp->info; assert(zif); if (add) { memcpy(&zif->l2info.vxl, vxlan_info, sizeof(*vxlan_info)); - zebra_evpn_vl_vxl_ref(zif->l2info.vxl.access_vlan, zif); zebra_vxlan_if_add(ifp); return; } - old_vtep_ip = zif->l2info.vxl.vtep_ip; + memset(&ctx, 0, sizeof(ctx)); + ctx.old_vtep_ip = zif->l2info.vxl.vtep_ip; - if (!IPV4_ADDR_SAME(&old_vtep_ip, &vxlan_info->vtep_ip)) { + if (!IPV4_ADDR_SAME(&ctx.old_vtep_ip, &vxlan_info->vtep_ip)) { chgflags |= ZEBRA_VXLIF_LOCAL_IP_CHANGE; zif->l2info.vxl.vtep_ip = vxlan_info->vtep_ip; } - if (!IPV4_ADDR_SAME(&zif->l2info.vxl.mcast_grp, - &vxlan_info->mcast_grp)) { - chgflags |= ZEBRA_VXLIF_MCAST_GRP_CHANGE; - zif->l2info.vxl.mcast_grp = vxlan_info->mcast_grp; + if (IS_ZEBRA_VXLAN_IF_VNI(zif)) { + ctx.old_vni = vxlan_info->vni_info.vni; + if (!IPV4_ADDR_SAME(&zif->l2info.vxl.vni_info.vni.mcast_grp, + &vxlan_info->vni_info.vni.mcast_grp)) { + chgflags |= ZEBRA_VXLIF_MCAST_GRP_CHANGE; + zif->l2info.vxl.vni_info.vni.mcast_grp = + vxlan_info->vni_info.vni.mcast_grp; + } } - if (chgflags) - zebra_vxlan_if_update(ifp, chgflags); + if (chgflags) { + ctx.chgflags = chgflags; + zebra_vxlan_if_update(ifp, &ctx); + } } /* @@ -379,19 +391,30 @@ void zebra_l2_vxlanif_update_access_vlan(struct interface *ifp, { struct zebra_if *zif; vlanid_t old_access_vlan; + struct zebra_vxlan_vni *vni; + struct zebra_vxlan_if_update_ctx ctx; + zif = ifp->info; assert(zif); - old_access_vlan = zif->l2info.vxl.access_vlan; + /* This would be called only in non svd case */ + assert(IS_ZEBRA_VXLAN_IF_VNI(zif)); + + old_access_vlan = zif->l2info.vxl.vni_info.vni.access_vlan; + ; if (old_access_vlan == access_vlan) return; - zif->l2info.vxl.access_vlan = access_vlan; + memset(&ctx, 0, sizeof(ctx)); + vni = zebra_vxlan_if_vni_find(zif, 0); + ctx.old_vni = *vni; + ctx.chgflags = ZEBRA_VXLIF_VLAN_CHANGE; + vni->access_vlan = access_vlan; - zebra_evpn_vl_vxl_deref(old_access_vlan, zif); - zebra_evpn_vl_vxl_ref(zif->l2info.vxl.access_vlan, zif); - zebra_vxlan_if_update(ifp, ZEBRA_VXLIF_VLAN_CHANGE); + zebra_evpn_vl_vxl_deref(old_access_vlan, vni->vni, zif); + zebra_evpn_vl_vxl_ref(access_vlan, vni->vni, zif); + zebra_vxlan_if_update(ifp, &ctx); } /* @@ -404,7 +427,6 @@ void zebra_l2_vxlanif_del(struct interface *ifp) zif = ifp->info; assert(zif); - zebra_evpn_vl_vxl_deref(zif->l2info.vxl.access_vlan, zif); zebra_vxlan_if_del(ifp); } @@ -421,6 +443,9 @@ void zebra_l2if_update_bridge_slave(struct interface *ifp, ifindex_t old_bridge_ifindex; ns_id_t old_ns_id; struct zebra_vrf *zvrf; + struct zebra_vxlan_if_update_ctx ctx; + + memset(&ctx, 0, sizeof(ctx)); zif = ifp->info; assert(zif); @@ -431,11 +456,14 @@ void zebra_l2if_update_bridge_slave(struct interface *ifp, if (zif->zif_type == ZEBRA_IF_VXLAN && chgflags != ZEBRA_BRIDGE_NO_ACTION) { - if (chgflags & ZEBRA_BRIDGE_MASTER_MAC_CHANGE) - zebra_vxlan_if_update(ifp, - ZEBRA_VXLIF_MASTER_MAC_CHANGE); - if (chgflags & ZEBRA_BRIDGE_MASTER_UP) - zebra_vxlan_if_update(ifp, ZEBRA_VXLIF_MASTER_CHANGE); + if (chgflags & ZEBRA_BRIDGE_MASTER_MAC_CHANGE) { + ctx.chgflags = ZEBRA_VXLIF_MASTER_MAC_CHANGE; + zebra_vxlan_if_update(ifp, &ctx); + } + if (chgflags & ZEBRA_BRIDGE_MASTER_UP) { + ctx.chgflags = ZEBRA_VXLIF_MASTER_CHANGE; + zebra_vxlan_if_update(ifp, &ctx); + } } old_bridge_ifindex = zif->brslave_info.bridge_ifindex; old_ns_id = zif->brslave_info.ns_id; @@ -443,6 +471,9 @@ void zebra_l2if_update_bridge_slave(struct interface *ifp, old_ns_id == zif->brslave_info.ns_id) return; + ctx.chgflags = ZEBRA_VXLIF_MASTER_CHANGE; + + zif->brslave_info.ns_id = ns_id; zif->brslave_info.bridge_ifindex = bridge_ifindex; /* Set up or remove link with master */ @@ -450,7 +481,7 @@ void zebra_l2if_update_bridge_slave(struct interface *ifp, 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); + zebra_vxlan_if_update(ifp, &ctx); if (zif->es_info.es) zebra_evpn_es_local_br_port_update(zif); } else if (old_bridge_ifindex != IFINDEX_INTERNAL) { @@ -460,7 +491,7 @@ void zebra_l2if_update_bridge_slave(struct interface *ifp, * to unmapping the interface from the bridge. */ if (zif->zif_type == ZEBRA_IF_VXLAN) - zebra_vxlan_if_update(ifp, ZEBRA_VXLIF_MASTER_CHANGE); + zebra_vxlan_if_update(ifp, &ctx); if (zif->es_info.es) zebra_evpn_es_local_br_port_update(zif); zebra_l2_unmap_slave_from_bridge(&zif->brslave_info); diff --git a/zebra/zebra_l2.h b/zebra/zebra_l2.h index 1c3e98158d..e1ce13b163 100644 --- a/zebra/zebra_l2.h +++ b/zebra/zebra_l2.h @@ -49,9 +49,30 @@ struct zebra_l2info_bond { struct list *mbr_zifs; /* slaves using this bond as a master */ }; +struct zebra_l2_bridge_vlan { + vlanid_t vid; + struct zebra_evpn_access_bd *access_bd; +}; + +struct zebra_l2_bridge_if_ctx { + /* input */ + struct zebra_if *zif; + int (*func)(struct zebra_if *zif, struct zebra_l2_bridge_vlan *vlan, + void *arg); + + /* input-output */ + void *arg; +}; + +struct zebra_l2_bridge_if { + uint8_t vlan_aware; + struct zebra_if *br_zif; + struct hash *vlan_table; +}; + /* zebra L2 interface information - bridge interface */ struct zebra_l2info_bridge { - uint8_t vlan_aware; /* VLAN-aware bridge? */ + struct zebra_l2_bridge_if bridge; }; /* zebra L2 interface information - VLAN interface */ @@ -71,12 +92,52 @@ struct zebra_l2info_gre { ns_id_t link_nsid; }; +struct zebra_vxlan_vni { + vni_t vni; /* VNI */ + vlanid_t access_vlan; /* Access VLAN - for VLAN-aware bridge. */ + struct in_addr mcast_grp; +}; + +enum { + ZEBRA_VXLAN_IF_VNI = 0, /* per vni vxlan if */ + ZEBRA_VXLAN_IF_SVD /* single vxlan device */ +}; + +struct zebra_vxlan_if_vlan_ctx { + vlanid_t vid; + struct zebra_vxlan_vni *vni; +}; + +struct zebra_vxlan_if_update_ctx { + uint16_t chgflags; + struct in_addr old_vtep_ip; + struct zebra_vxlan_vni old_vni; + struct hash *old_vni_table; +}; + +struct zebra_vxlan_if_ctx { + /* input */ + struct zebra_if *zif; + int (*func)(struct zebra_if *zif, struct zebra_vxlan_vni *vni, + void *arg); + + /* input-output */ + void *arg; +}; + +struct zebra_vxlan_vni_info { + int iftype; + union { + struct zebra_vxlan_vni vni; /* per vni vxlan device vni info */ + struct hash + *vni_table; /* table of vni's assocated with this if */ + }; +}; + /* zebra L2 interface information - VXLAN interface */ struct zebra_l2info_vxlan { - vni_t vni; /* VNI */ + struct zebra_vxlan_vni_info vni_info; 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 */ @@ -99,10 +160,16 @@ union zebra_l2if_info { * IOW, the macro VNI_FROM_ZEBRA_IF() will assume the interface is * of type ZEBRA_IF_VXLAN. */ -#define VNI_FROM_ZEBRA_IF(zif) (zif)->l2info.vxl.vni +#define VNI_INFO_FROM_ZEBRA_IF(zif) (&((zif)->l2info.vxl.vni_info)) +#define IS_ZEBRA_VXLAN_IF_SVD(zif) \ + ((zif)->l2info.vxl.vni_info.iftype == ZEBRA_VXLAN_IF_SVD) +#define IS_ZEBRA_VXLAN_IF_VNI(zif) \ + ((zif)->l2info.vxl.vni_info.iftype == ZEBRA_VXLAN_IF_VNI) #define VLAN_ID_FROM_ZEBRA_IF(zif) (zif)->l2info.vl.vid -#define IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(zif) ((zif)->l2info.br.vlan_aware == 1) +#define BRIDGE_FROM_ZEBRA_IF(zif) (&((zif)->l2info.br.bridge)) +#define IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(zif) \ + ((zif)->l2info.br.bridge.vlan_aware == 1) extern void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave, struct zebra_ns *zns); diff --git a/zebra/zebra_l2_bridge_if.c b/zebra/zebra_l2_bridge_if.c new file mode 100644 index 0000000000..6574899927 --- /dev/null +++ b/zebra/zebra_l2_bridge_if.c @@ -0,0 +1,383 @@ +/* + * Zebra L2 bridge interface handling + * + * Copyright (C) 2021 Cumulus Networks, Inc. + * Sharath Ramamurthy + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <zebra.h> + +#include "hash.h" +#include "if.h" +#include "jhash.h" +#include "linklist.h" +#include "log.h" +#include "memory.h" +#include "prefix.h" +#include "stream.h" +#include "table.h" +#include "vlan.h" +#include "vxlan.h" +#ifdef GNU_LINUX +#include <linux/neighbour.h> +#endif + +#include "zebra/zebra_router.h" +#include "zebra/debug.h" +#include "zebra/interface.h" +#include "zebra/rib.h" +#include "zebra/rt.h" +#include "zebra/rt_netlink.h" +#include "zebra/zebra_errors.h" +#include "zebra/zebra_l2.h" +#include "zebra/zebra_l2_bridge_if.h" +#include "zebra/zebra_ns.h" +#include "zebra/zebra_vrf.h" +#include "zebra/zebra_vxlan.h" +#include "zebra/zebra_vxlan_if.h" +#include "zebra/zebra_evpn.h" +#include "zebra/zebra_evpn_mac.h" +#include "zebra/zebra_evpn_neigh.h" +#include "zebra/zebra_vxlan_private.h" +#include "zebra/zebra_evpn_mh.h" +#include "zebra/zebra_evpn_vxlan.h" +#include "zebra/zebra_router.h" + +static unsigned int zebra_l2_bridge_vlan_hash_keymake(const void *p) +{ + const struct zebra_l2_bridge_vlan *bvlan; + + bvlan = (const struct zebra_l2_bridge_vlan *)p; + return jhash(&bvlan->vid, sizeof(bvlan->vid), 0); +} + +static bool zebra_l2_bridge_vlan_hash_cmp(const void *p1, const void *p2) +{ + const struct zebra_l2_bridge_vlan *bv1; + const struct zebra_l2_bridge_vlan *bv2; + + bv1 = (const struct zebra_l2_bridge_vlan *)p1; + bv2 = (const struct zebra_l2_bridge_vlan *)p2; + + return (bv1->vid == bv2->vid); +} + +static int zebra_l2_bridge_if_vlan_walk_callback(struct hash_bucket *bucket, + void *ctxt) +{ + int ret; + struct zebra_l2_bridge_vlan *bvlan; + struct zebra_l2_bridge_if_ctx *ctx; + + bvlan = (struct zebra_l2_bridge_vlan *)bucket->data; + ctx = (struct zebra_l2_bridge_if_ctx *)ctxt; + + ret = ctx->func(ctx->zif, bvlan, ctx->arg); + return ret; +} + +static void zebra_l2_bridge_if_vlan_iterate_callback(struct hash_bucket *bucket, + void *ctxt) +{ + struct zebra_l2_bridge_vlan *bvlan; + struct zebra_l2_bridge_if_ctx *ctx; + + bvlan = (struct zebra_l2_bridge_vlan *)bucket->data; + ctx = (struct zebra_l2_bridge_if_ctx *)ctxt; + + ctx->func(ctx->zif, bvlan, ctx->arg); +} + +static int zebra_l2_bridge_if_vlan_clean(struct zebra_if *zif, + struct zebra_l2_bridge_vlan *bvlan, + void *ctxt) +{ + struct zebra_evpn_access_bd *acc_bd; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("access vlan %d bridge %s cleanup", bvlan->vid, + zif->ifp->name); + + acc_bd = zebra_evpn_acc_vl_find(bvlan->vid, zif->ifp); + if (acc_bd) + zebra_evpn_access_bd_bridge_cleanup(bvlan->vid, zif->ifp, + acc_bd); + + bvlan->access_bd = NULL; + return 0; +} + +static void zebra_l2_bridge_vlan_free(void *arg) +{ + struct zebra_l2_bridge_vlan *bvl; + + bvl = (struct zebra_l2_bridge_vlan *)arg; + XFREE(MTYPE_TMP, bvl); +} + +static void *zebra_l2_bridge_vlan_alloc(void *p) +{ + struct zebra_l2_bridge_vlan *bvlan; + const struct zebra_l2_bridge_vlan *bvl; + + bvl = (const struct zebra_l2_bridge_vlan *)p; + bvlan = XCALLOC(MTYPE_TMP, sizeof(*bvlan)); + bvlan->vid = bvl->vid; + bvlan->access_bd = bvl->access_bd; + + return (void *)bvlan; +} + +static void zebra_l2_bridge_vlan_table_destroy(struct hash *vlan_table) +{ + if (vlan_table) { + hash_clean(vlan_table, zebra_l2_bridge_vlan_free); + hash_free(vlan_table); + } +} + +static struct hash *zebra_l2_bridge_vlan_table_create(void) +{ + return hash_create(zebra_l2_bridge_vlan_hash_keymake, + zebra_l2_bridge_vlan_hash_cmp, + "Zebra L2 Bridge Vlan Table"); +} + +static void zebra_l2_bridge_if_vlan_table_destroy(struct zebra_if *zif) +{ + struct zebra_l2_bridge_if *br; + + br = BRIDGE_FROM_ZEBRA_IF(zif); + zebra_l2_bridge_if_vlan_iterate(zif, zebra_l2_bridge_if_vlan_clean, + NULL); + zebra_l2_bridge_vlan_table_destroy(br->vlan_table); + br->vlan_table = NULL; +} + +static int zebra_l2_bridge_if_vlan_table_create(struct zebra_if *zif) +{ + struct zebra_l2_bridge_if *br; + + br = BRIDGE_FROM_ZEBRA_IF(zif); + if (!br->vlan_table) { + br->vlan_table = zebra_l2_bridge_vlan_table_create(); + if (!br->vlan_table) + return -ENOMEM; + } + + return 0; +} + +static int zebra_l2_bridge_if_vlan_del(struct interface *ifp, vlanid_t vid) +{ + struct zebra_if *zif; + struct zebra_l2_bridge_if *br; + struct zebra_l2_bridge_vlan bvl; + struct zebra_l2_bridge_vlan *bvlan; + + zif = (struct zebra_if *)ifp->info; + memset(&bvl, 0, sizeof(bvl)); + bvl.vid = vid; + + br = BRIDGE_FROM_ZEBRA_IF(zif); + bvlan = hash_release(br->vlan_table, &bvl); + + if (bvlan) + zebra_l2_bridge_vlan_free(bvlan); + + return 0; +} + +static int zebra_l2_bridge_if_vlan_update(struct interface *ifp, + struct zebra_l2_bridge_vlan *bvl, + int chgflags) +{ + struct zebra_if *zif; + struct zebra_l2_bridge_vlan *bvlan; + + zif = (struct zebra_if *)ifp->info; + bvlan = zebra_l2_bridge_if_vlan_find(zif, bvl->vid); + + if (chgflags & ZEBRA_BRIDGEIF_ACCESS_BD_CHANGE) + bvlan->access_bd = bvl->access_bd; + + if (!bvlan->access_bd) + return zebra_l2_bridge_if_vlan_del(ifp, bvl->vid); + + return 0; +} + +static int zebra_l2_bridge_if_vlan_add(struct interface *ifp, + struct zebra_l2_bridge_vlan *bvlan) +{ + struct zebra_if *zif; + struct zebra_l2_bridge_if *br; + + zif = (struct zebra_if *)ifp->info; + br = BRIDGE_FROM_ZEBRA_IF(zif); + hash_get(br->vlan_table, (void *)bvlan, zebra_l2_bridge_vlan_alloc); + + return 0; +} + +struct zebra_l2_bridge_vlan * +zebra_l2_bridge_if_vlan_find(const struct zebra_if *zif, vlanid_t vid) +{ + const struct zebra_l2_bridge_if *br; + struct zebra_l2_bridge_vlan *bvl; + struct zebra_l2_bridge_vlan bvlan; + + br = BRIDGE_FROM_ZEBRA_IF(zif); + memset(&bvlan, 0, sizeof(bvlan)); + bvlan.vid = vid; + bvl = (struct zebra_l2_bridge_vlan *)hash_lookup(br->vlan_table, + (void *)&bvlan); + + /* TODO: For debugging. Remove later */ + if (bvl) + assert(bvl->vid == vid); + + return bvl; +} + +vni_t zebra_l2_bridge_if_vni_find(const struct zebra_if *zif, vlanid_t vid) +{ + vni_t vni_id = 0; + struct zebra_l2_bridge_vlan *bvlan; + + bvlan = zebra_l2_bridge_if_vlan_find(zif, vid); + if (bvlan && bvlan->access_bd && bvlan->access_bd->vni) + vni_id = bvlan->access_bd->vni; + + return vni_id; +} + +void zebra_l2_bridge_if_vlan_iterate(struct zebra_if *zif, + int (*func)(struct zebra_if *zif, + struct zebra_l2_bridge_vlan *, + void *), + void *arg) +{ + struct zebra_l2_bridge_if *br; + struct zebra_l2_bridge_if_ctx ctx; + + br = BRIDGE_FROM_ZEBRA_IF(zif); + memset(&ctx, 0, sizeof(ctx)); + ctx.zif = zif; + ctx.func = func; + ctx.arg = arg; + hash_iterate(br->vlan_table, zebra_l2_bridge_if_vlan_iterate_callback, + &ctx); +} + +void zebra_l2_bridge_if_vlan_walk(struct zebra_if *zif, + int (*func)(struct zebra_if *zif, + struct zebra_l2_bridge_vlan *, + void *), + void *arg) +{ + struct zebra_l2_bridge_if *br; + struct zebra_l2_bridge_if_ctx ctx; + + br = BRIDGE_FROM_ZEBRA_IF(zif); + memset(&ctx, 0, sizeof(ctx)); + ctx.zif = zif; + ctx.func = func; + ctx.arg = arg; + hash_walk(br->vlan_table, zebra_l2_bridge_if_vlan_walk_callback, &ctx); +} + +int zebra_l2_bridge_if_vlan_access_bd_deref(struct zebra_evpn_access_bd *bd) +{ + int chgflags = 0; + struct zebra_if *zif; + struct zebra_l2_bridge_vlan bvl; + struct zebra_l2_bridge_vlan *bvlan; + + zif = bd->bridge_zif; + if (!zif) + return -1; + + bvlan = zebra_l2_bridge_if_vlan_find(zif, bd->vid); + if (!bvlan) + return 0; + + memset(&bvl, 0, sizeof(bvl)); + bvl.vid = bd->vid; + bvl.access_bd = NULL; + chgflags = ZEBRA_BRIDGEIF_ACCESS_BD_CHANGE; + return zebra_l2_bridge_if_vlan_update(zif->ifp, &bvl, chgflags); +} + +int zebra_l2_bridge_if_vlan_access_bd_ref(struct zebra_evpn_access_bd *bd) +{ + int chgflags = 0; + struct zebra_if *zif; + struct zebra_l2_bridge_vlan bvl; + struct zebra_l2_bridge_vlan *bvlan; + + zif = bd->bridge_zif; + if (!zif) + return -1; + + if (!bd->vid) + return -1; + + memset(&bvl, 0, sizeof(bvl)); + bvl.vid = bd->vid; + bvl.access_bd = bd; + + bvlan = zebra_l2_bridge_if_vlan_find(zif, bd->vid); + if (!bvlan) + return zebra_l2_bridge_if_vlan_add(zif->ifp, &bvl); + + chgflags = ZEBRA_BRIDGEIF_ACCESS_BD_CHANGE; + return zebra_l2_bridge_if_vlan_update(zif->ifp, &bvl, chgflags); +} + +int zebra_l2_bridge_if_cleanup(struct interface *ifp) +{ + struct zebra_if *zif; + + if (!IS_ZEBRA_IF_BRIDGE(ifp)) + return 0; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("bridge %s cleanup", ifp->name); + + zif = (struct zebra_if *)ifp->info; + zebra_l2_bridge_if_vlan_table_destroy(zif); + return 0; +} + +int zebra_l2_bridge_if_del(struct interface *ifp) +{ + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("bridge %s delete", ifp->name); + + return zebra_l2_bridge_if_cleanup(ifp); +} + +int zebra_l2_bridge_if_add(struct interface *ifp) +{ + struct zebra_if *zif; + struct zebra_l2_bridge_if *br; + + zif = (struct zebra_if *)ifp->info; + br = BRIDGE_FROM_ZEBRA_IF(zif); + br->br_zif = (struct zebra_if *)ifp->info; + zebra_l2_bridge_if_vlan_table_create(zif); + return 0; +} diff --git a/zebra/zebra_l2_bridge_if.h b/zebra/zebra_l2_bridge_if.h new file mode 100644 index 0000000000..734ecfdcf1 --- /dev/null +++ b/zebra/zebra_l2_bridge_if.h @@ -0,0 +1,75 @@ +/* + * Zebra L2 bridge interface data structures and definitions + * These are public definitions referenced by other files. + * Copyright (C) 2021 Cumulus Networks, Inc. + * Sharath Ramamurthy + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _ZEBRA_L2_BRIDGE_IF_H +#define _ZEBRA_L2_BRIDGE_IF_H + +#include <zebra.h> +#include <zebra/zebra_router.h> + +#include "linklist.h" +#include "if.h" +#include "vlan.h" +#include "vxlan.h" + +#include "lib/json.h" +#include "zebra/zebra_vrf.h" +#include "zebra/zserv.h" + +#include "zebra/zebra_dplane.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Bridge interface change flags of interest. */ +#define ZEBRA_BRIDGEIF_ACCESS_BD_CHANGE (1 << 0) + +extern struct zebra_l2_bridge_vlan * +zebra_l2_bridge_if_vlan_find(const struct zebra_if *zif, vlanid_t vid); +extern vni_t zebra_l2_bridge_if_vni_find(const struct zebra_if *zif, + vlanid_t vid); +extern void zebra_l2_bridge_if_vlan_iterate( + struct zebra_if *zif, + int (*func)(struct zebra_if *zif, struct zebra_l2_bridge_vlan *, + void *), + void *arg); +extern void +zebra_l2_bridge_if_vlan_walk(struct zebra_if *zif, + int (*func)(struct zebra_if *zif, + struct zebra_l2_bridge_vlan *, void *), + void *arg); +extern int +zebra_l2_bridge_if_vlan_access_bd_deref(struct zebra_evpn_access_bd *bd); +extern int +zebra_l2_bridge_if_vlan_access_bd_ref(struct zebra_evpn_access_bd *bd); +extern int zebra_l2_bridge_if_del(struct interface *ifp); +extern int zebra_l2_bridge_if_add(struct interface *ifp); +extern int zebra_l2_bridge_if_cleanup(struct interface *ifp); + +#ifdef __cplusplus +} +#endif + +#endif /* _ZEBRA_L2_BRIDGE_IF_H */ diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 13963d9cc3..aec4961126 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -492,7 +492,7 @@ static void fec_print(struct zebra_fec *fec, struct vty *vty) rn = fec->rn; vty_out(vty, "%pRN\n", rn); - vty_out(vty, " Label: %s", label2str(fec->label, buf, BUFSIZ)); + vty_out(vty, " Label: %s", label2str(fec->label, 0, buf, BUFSIZ)); if (fec->label_index != MPLS_INVALID_LABEL_INDEX) vty_out(vty, ", Label Index: %u", fec->label_index); vty_out(vty, "\n"); @@ -1597,8 +1597,8 @@ static void nhlfe_print(struct zebra_nhlfe *nhlfe, struct vty *vty, vty_out(vty, " type: %s remote label: %s distance: %d\n", nhlfe_type2str(nhlfe->type), mpls_label2str(nexthop->nh_label->num_labels, - nexthop->nh_label->label, - buf, sizeof(buf), 0), + nexthop->nh_label->label, buf, sizeof(buf), + nexthop->nh_label_type, 0), nhlfe->distance); if (indent) @@ -2719,7 +2719,7 @@ int zebra_mpls_write_fec_config(struct vty *vty, struct zebra_vrf *zvrf) write = 1; vty_out(vty, "mpls label bind %pFX %s\n", &rn->p, - label2str(fec->label, lstr, BUFSIZ)); + label2str(fec->label, 0, lstr, BUFSIZ)); } } @@ -3164,10 +3164,10 @@ lsp_add_nhlfe(struct zebra_lsp *lsp, enum lsp_types_t type, nhlfe2str(nhlfe, buf, sizeof(buf)); mpls_label2str(num_out_labels, out_labels, buf2, - sizeof(buf2), 0); + sizeof(buf2), 0, 0); mpls_label2str(nh->nh_label->num_labels, nh->nh_label->label, buf3, sizeof(buf3), - 0); + nh->nh_label_type, 0); zlog_debug("LSP in-label %u type %d %snexthop %s out-label(s) changed to %s (old %s)", lsp->ile.in_label, type, backup_str, buf, @@ -3195,7 +3195,7 @@ lsp_add_nhlfe(struct zebra_lsp *lsp, enum lsp_types_t type, nhlfe2str(nhlfe, buf, sizeof(buf)); mpls_label2str(num_out_labels, out_labels, buf2, - sizeof(buf2), 0); + sizeof(buf2), 0, 0); zlog_debug("Add LSP in-label %u type %d %snexthop %s out-label(s) %s", lsp->ile.in_label, type, backup_str, buf, @@ -3778,10 +3778,10 @@ void zebra_mpls_print_lsp_table(struct vty *vty, struct zebra_vrf *zvrf, json = json_object_new_object(); for (ALL_LIST_ELEMENTS_RO(lsp_list, node, lsp)) - json_object_object_add( - json, label2str(lsp->ile.in_label, buf, - sizeof(buf)), - lsp_json(lsp)); + json_object_object_add(json, + label2str(lsp->ile.in_label, 0, + buf, sizeof(buf)), + lsp_json(lsp)); vty_json(vty, json); } else { @@ -3833,7 +3833,8 @@ void zebra_mpls_print_lsp_table(struct vty *vty, struct zebra_vrf *zvrf, out_label_str = mpls_label2str( nexthop->nh_label->num_labels, &nexthop->nh_label->label[0], - buf, sizeof(buf), 1); + buf, sizeof(buf), + nexthop->nh_label_type, 1); else out_label_str = "-"; diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h index cf247861f8..1e63a59576 100644 --- a/zebra/zebra_mpls.h +++ b/zebra/zebra_mpls.h @@ -446,6 +446,7 @@ static inline uint8_t lsp_distance(enum lsp_types_t type) return (route_distance(ZEBRA_ROUTE_BGP)); case ZEBRA_LSP_NONE: case ZEBRA_LSP_SHARP: + case ZEBRA_LSP_EVPN: case ZEBRA_LSP_OSPF_SR: case ZEBRA_LSP_ISIS_SR: case ZEBRA_LSP_SRTE: @@ -498,6 +499,7 @@ static inline int re_type_from_lsp_type(enum lsp_types_t lsp_type) case ZEBRA_LSP_LDP: return ZEBRA_ROUTE_LDP; case ZEBRA_LSP_BGP: + case ZEBRA_LSP_EVPN: return ZEBRA_ROUTE_BGP; case ZEBRA_LSP_OSPF_SR: return ZEBRA_ROUTE_OSPF; @@ -538,6 +540,8 @@ static inline const char *nhlfe_type2str(enum lsp_types_t lsp_type) return "SHARP"; case ZEBRA_LSP_SRTE: return "SR-TE"; + case ZEBRA_LSP_EVPN: + return "EVPN"; case ZEBRA_LSP_NONE: return "Unknown"; } diff --git a/zebra/zebra_nb_state.c b/zebra/zebra_nb_state.c index 0f3d56f214..45b9d440a6 100644 --- a/zebra/zebra_nb_state.c +++ b/zebra/zebra_nb_state.c @@ -25,6 +25,8 @@ #include "zebra/zebra_router.h" #include "zebra/debug.h" #include "printfrr.h" +#include "zebra/zebra_vxlan.h" +#include "zebra/zebra_vxlan_if.h" /* * XPath: /frr-interface:lib/interface/frr-zebra:zebra/state/up-count @@ -101,15 +103,18 @@ lib_interface_zebra_state_vni_id_get_elem(struct nb_cb_get_elem_args *args) { const struct interface *ifp = args->list_entry; struct zebra_if *zebra_if; - struct zebra_l2info_vxlan *vxlan_info; + struct zebra_vxlan_vni *vni; if (!IS_ZEBRA_IF_VXLAN(ifp)) return NULL; zebra_if = ifp->info; - vxlan_info = &zebra_if->l2info.vxl; - return yang_data_new_uint32(args->xpath, vxlan_info->vni); + if (!IS_ZEBRA_VXLAN_IF_VNI(zebra_if)) + return NULL; + + vni = zebra_vxlan_if_vni_find(zebra_if, 0); + return yang_data_new_uint32(args->xpath, vni->vni); } /* @@ -139,15 +144,18 @@ lib_interface_zebra_state_mcast_group_get_elem(struct nb_cb_get_elem_args *args) { const struct interface *ifp = args->list_entry; struct zebra_if *zebra_if; - struct zebra_l2info_vxlan *vxlan_info; + struct zebra_vxlan_vni *vni; if (!IS_ZEBRA_IF_VXLAN(ifp)) return NULL; zebra_if = ifp->info; - vxlan_info = &zebra_if->l2info.vxl; - return yang_data_new_ipv4(args->xpath, &vxlan_info->mcast_grp); + if (!IS_ZEBRA_VXLAN_IF_VNI(zebra_if)) + return NULL; + + vni = zebra_vxlan_if_vni_find(zebra_if, 0); + return yang_data_new_ipv4(args->xpath, &vni->mcast_grp); } const void *lib_vrf_zebra_ribs_rib_get_next(struct nb_cb_get_next_args *args) diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 0bca00ced3..d321ab4949 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -44,6 +44,7 @@ #include "zebra/interface.h" #include "zebra/zapi_msg.h" #include "zebra/rib.h" +#include "zebra/zebra_vxlan.h" DEFINE_MTYPE_STATIC(ZEBRA, NHG, "Nexthop Group Entry"); DEFINE_MTYPE_STATIC(ZEBRA, NHG_CONNECTED, "Nexthop Group Connected"); @@ -1922,10 +1923,42 @@ static bool nexthop_valid_resolve(const struct nexthop *nexthop, } /* - * When resolving a recursive nexthop, capture backup nexthop(s) also - * so they can be conveyed through the dataplane to the FIB. We'll look - * at the backups in the resolving nh 'nexthop' and its nhe, and copy them - * into the route's resolved nh 'resolved' and its nhe 'nhe'. + * Downstream VNI and Single VXlan device check. + * + * If it has nexthop VNI labels at this point it must be D-VNI allocated + * and all the nexthops have to be on an SVD. + * + * If SVD is not available, mark as inactive. + */ +static bool nexthop_set_evpn_dvni_svd(vrf_id_t re_vrf_id, + struct nexthop *nexthop) +{ + if (!is_vrf_l3vni_svd_backed(re_vrf_id)) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) { + struct vrf *vrf = vrf_lookup_by_id(re_vrf_id); + + zlog_debug( + "nexthop %pNHv D-VNI but route's vrf %s(%u) doesn't use SVD", + nexthop, VRF_LOGNAME(vrf), re_vrf_id); + } + + return false; + } + + nexthop->ifindex = get_l3vni_vxlan_ifindex(re_vrf_id); + nexthop->vrf_id = 0; + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("nexthop %pNHv using SVD", nexthop); + + return true; +} + +/* + * Given a nexthop we need to properly recursively resolve + * the route. As such, do a table lookup to find and match + * if at all possible. Set the nexthop->ifindex and resolved_id + * as appropriate */ static int resolve_backup_nexthops(const struct nexthop *nexthop, const struct nhg_hash_entry *nhe, @@ -2195,6 +2228,12 @@ static int nexthop_active(struct nexthop *nexthop, struct nhg_hash_entry *nhe, * sure the nexthop's interface is known and is operational. */ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) { + /* DVNI/SVD Checks for EVPN routes */ + if (nexthop->nh_label && + nexthop->nh_label_type == ZEBRA_LSP_EVPN && + !nexthop_set_evpn_dvni_svd(vrf_id, nexthop)) + return 0; + ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id); if (!ifp) { if (IS_ZEBRA_DEBUG_RIB_DETAILED) @@ -2706,6 +2745,51 @@ done: return valid; } +/* Checks if the first nexthop is EVPN. If not, early return. + * + * This is used to determine if there is a mismatch between l3VNI + * of the route's vrf and the nexthops in use's VNI labels. + * + * If there is a mismatch, we keep the labels as these MUST be DVNI nexthops. + * + * IF there is no mismatch, we remove the labels and handle the routes as + * we have traditionally with evpn. + */ +static bool nexthop_list_set_evpn_dvni(struct route_entry *re, + struct nexthop_group *nhg) +{ + struct nexthop *nexthop; + vni_t re_vrf_vni; + vni_t nh_vni; + bool use_dvni = false; + + nexthop = nhg->nexthop; + + if (!nexthop->nh_label || nexthop->nh_label_type != ZEBRA_LSP_EVPN) + return false; + + re_vrf_vni = get_l3vni_vni(re->vrf_id); + + for (; nexthop; nexthop = nexthop->next) { + if (!nexthop->nh_label || + nexthop->nh_label_type != ZEBRA_LSP_EVPN) + continue; + + nh_vni = label2vni(&nexthop->nh_label->label[0]); + + if (nh_vni != re_vrf_vni) + use_dvni = true; + } + + /* Using traditional way, no VNI encap - remove labels */ + if (!use_dvni) { + for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) + nexthop_del_labels(nexthop); + } + + return use_dvni; +} + /* * Process a list of nexthops, given an nhe, determining * whether each one is ACTIVE/installable at this time. @@ -2721,12 +2805,16 @@ static uint32_t nexthop_list_active_update(struct route_node *rn, uint32_t counter = 0; struct nexthop *nexthop; struct nexthop_group *nhg = &nhe->nhg; + bool vni_removed = false; nexthop = nhg->nexthop; /* Init recursive nh mtu */ re->nexthop_mtu = 0; + /* Handler for dvni evpn nexthops. Has to be done at nhg level */ + vni_removed = !nexthop_list_set_evpn_dvni(re, nhg); + /* Process nexthops one-by-one */ for ( ; nexthop; nexthop = nexthop->next) { @@ -2764,16 +2852,17 @@ static uint32_t nexthop_list_active_update(struct route_node *rn, counter++; /* Check for changes to the nexthop - set ROUTE_ENTRY_CHANGED */ - if (prev_active != new_active || prev_index != nexthop->ifindex - || ((nexthop->type >= NEXTHOP_TYPE_IFINDEX - && nexthop->type < NEXTHOP_TYPE_IPV6) - && prev_src.ipv4.s_addr - != nexthop->rmap_src.ipv4.s_addr) - || ((nexthop->type >= NEXTHOP_TYPE_IPV6 - && nexthop->type < NEXTHOP_TYPE_BLACKHOLE) - && !(IPV6_ADDR_SAME(&prev_src.ipv6, - &nexthop->rmap_src.ipv6))) - || CHECK_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED)) + if (prev_active != new_active || + prev_index != nexthop->ifindex || + ((nexthop->type >= NEXTHOP_TYPE_IFINDEX && + nexthop->type < NEXTHOP_TYPE_IPV6) && + prev_src.ipv4.s_addr != nexthop->rmap_src.ipv4.s_addr) || + ((nexthop->type >= NEXTHOP_TYPE_IPV6 && + nexthop->type < NEXTHOP_TYPE_BLACKHOLE) && + !(IPV6_ADDR_SAME(&prev_src.ipv6, + &nexthop->rmap_src.ipv6))) || + CHECK_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED) || + vni_removed) SET_FLAG(re->status, ROUTE_ENTRY_CHANGED); } diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 7b076b8a4a..457a4955b4 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -127,6 +127,8 @@ int zebra_ns_enable(ns_id_t ns_id, void **info) zebra_dplane_ns_enable(zns, true); interface_list(zns); route_read(zns); + + vlan_read(zns); kernel_read_pbr_rules(zns); kernel_read_tc_qdisc(zns); diff --git a/zebra/zebra_ptm.c b/zebra/zebra_ptm.c index 4a18eb021e..937dc090a4 100644 --- a/zebra/zebra_ptm.c +++ b/zebra/zebra_ptm.c @@ -31,7 +31,6 @@ #include "ptm_lib.h" #include "rib.h" #include "stream.h" -#include "lib/version.h" #include "vrf.h" #include "vty.h" #include "lib_errors.h" diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index ba8073defe..415fc78142 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -99,42 +99,79 @@ static const struct { uint8_t distance; enum meta_queue_indexes meta_q_map; } route_info[ZEBRA_ROUTE_MAX] = { - [ZEBRA_ROUTE_NHG] = {ZEBRA_ROUTE_NHG, 255 /* Unneeded for nhg's */, - META_QUEUE_NHG}, - [ZEBRA_ROUTE_SYSTEM] = {ZEBRA_ROUTE_SYSTEM, 0, META_QUEUE_KERNEL}, - [ZEBRA_ROUTE_KERNEL] = {ZEBRA_ROUTE_KERNEL, 0, META_QUEUE_KERNEL}, - [ZEBRA_ROUTE_CONNECT] = {ZEBRA_ROUTE_CONNECT, 0, META_QUEUE_CONNECTED}, - [ZEBRA_ROUTE_STATIC] = {ZEBRA_ROUTE_STATIC, 1, META_QUEUE_STATIC}, - [ZEBRA_ROUTE_RIP] = {ZEBRA_ROUTE_RIP, 120, META_QUEUE_NOTBGP}, - [ZEBRA_ROUTE_RIPNG] = {ZEBRA_ROUTE_RIPNG, 120, META_QUEUE_NOTBGP}, - [ZEBRA_ROUTE_OSPF] = {ZEBRA_ROUTE_OSPF, 110, META_QUEUE_NOTBGP}, - [ZEBRA_ROUTE_OSPF6] = {ZEBRA_ROUTE_OSPF6, 110, META_QUEUE_NOTBGP}, - [ZEBRA_ROUTE_ISIS] = {ZEBRA_ROUTE_ISIS, 115, META_QUEUE_NOTBGP}, - [ZEBRA_ROUTE_BGP] = {ZEBRA_ROUTE_BGP, 20 /* IBGP is 200. */, + [ZEBRA_ROUTE_NHG] = + {ZEBRA_ROUTE_NHG, + ZEBRA_MAX_DISTANCE_DEFAULT /* Unneeded for nhg's */, + META_QUEUE_NHG}, + [ZEBRA_ROUTE_SYSTEM] = {ZEBRA_ROUTE_SYSTEM, + ZEBRA_KERNEL_DISTANCE_DEFAULT, + META_QUEUE_KERNEL}, + [ZEBRA_ROUTE_KERNEL] = {ZEBRA_ROUTE_KERNEL, + ZEBRA_KERNEL_DISTANCE_DEFAULT, + META_QUEUE_KERNEL}, + [ZEBRA_ROUTE_CONNECT] = {ZEBRA_ROUTE_CONNECT, + ZEBRA_CONNECT_DISTANCE_DEFAULT, + META_QUEUE_CONNECTED}, + [ZEBRA_ROUTE_STATIC] = {ZEBRA_ROUTE_STATIC, + ZEBRA_STATIC_DISTANCE_DEFAULT, + META_QUEUE_STATIC}, + [ZEBRA_ROUTE_RIP] = {ZEBRA_ROUTE_RIP, ZEBRA_RIP_DISTANCE_DEFAULT, + META_QUEUE_NOTBGP}, + [ZEBRA_ROUTE_RIPNG] = {ZEBRA_ROUTE_RIPNG, ZEBRA_RIP_DISTANCE_DEFAULT, + META_QUEUE_NOTBGP}, + [ZEBRA_ROUTE_OSPF] = {ZEBRA_ROUTE_OSPF, ZEBRA_OSPF_DISTANCE_DEFAULT, + META_QUEUE_NOTBGP}, + [ZEBRA_ROUTE_OSPF6] = {ZEBRA_ROUTE_OSPF6, ZEBRA_OSPF6_DISTANCE_DEFAULT, + META_QUEUE_NOTBGP}, + [ZEBRA_ROUTE_ISIS] = {ZEBRA_ROUTE_ISIS, ZEBRA_ISIS_DISTANCE_DEFAULT, + META_QUEUE_NOTBGP}, + [ZEBRA_ROUTE_BGP] = {ZEBRA_ROUTE_BGP, + ZEBRA_EBGP_DISTANCE_DEFAULT /* IBGP is 200. */, META_QUEUE_BGP}, - [ZEBRA_ROUTE_PIM] = {ZEBRA_ROUTE_PIM, 255, META_QUEUE_OTHER}, - [ZEBRA_ROUTE_EIGRP] = {ZEBRA_ROUTE_EIGRP, 90, META_QUEUE_NOTBGP}, - [ZEBRA_ROUTE_NHRP] = {ZEBRA_ROUTE_NHRP, 10, META_QUEUE_NOTBGP}, - [ZEBRA_ROUTE_HSLS] = {ZEBRA_ROUTE_HSLS, 255, META_QUEUE_OTHER}, - [ZEBRA_ROUTE_OLSR] = {ZEBRA_ROUTE_OLSR, 255, META_QUEUE_OTHER}, - [ZEBRA_ROUTE_TABLE] = {ZEBRA_ROUTE_TABLE, 150, META_QUEUE_STATIC}, - [ZEBRA_ROUTE_LDP] = {ZEBRA_ROUTE_LDP, 150, META_QUEUE_OTHER}, - [ZEBRA_ROUTE_VNC] = {ZEBRA_ROUTE_VNC, 20, META_QUEUE_BGP}, - [ZEBRA_ROUTE_VNC_DIRECT] = {ZEBRA_ROUTE_VNC_DIRECT, 20, META_QUEUE_BGP}, - [ZEBRA_ROUTE_VNC_DIRECT_RH] = {ZEBRA_ROUTE_VNC_DIRECT_RH, 20, + [ZEBRA_ROUTE_PIM] = {ZEBRA_ROUTE_PIM, ZEBRA_MAX_DISTANCE_DEFAULT, + META_QUEUE_OTHER}, + [ZEBRA_ROUTE_EIGRP] = {ZEBRA_ROUTE_EIGRP, ZEBRA_EIGRP_DISTANCE_DEFAULT, + META_QUEUE_NOTBGP}, + [ZEBRA_ROUTE_NHRP] = {ZEBRA_ROUTE_NHRP, ZEBRA_NHRP_DISTANCE_DEFAULT, + META_QUEUE_NOTBGP}, + [ZEBRA_ROUTE_HSLS] = {ZEBRA_ROUTE_HSLS, ZEBRA_MAX_DISTANCE_DEFAULT, + META_QUEUE_OTHER}, + [ZEBRA_ROUTE_OLSR] = {ZEBRA_ROUTE_OLSR, ZEBRA_MAX_DISTANCE_DEFAULT, + META_QUEUE_OTHER}, + [ZEBRA_ROUTE_TABLE] = {ZEBRA_ROUTE_TABLE, ZEBRA_TABLE_DISTANCE_DEFAULT, META_QUEUE_STATIC}, + [ZEBRA_ROUTE_LDP] = {ZEBRA_ROUTE_LDP, ZEBRA_LDP_DISTANCE_DEFAULT, + META_QUEUE_OTHER}, + [ZEBRA_ROUTE_VNC] = {ZEBRA_ROUTE_VNC, ZEBRA_EBGP_DISTANCE_DEFAULT, + META_QUEUE_BGP}, + [ZEBRA_ROUTE_VNC_DIRECT] = {ZEBRA_ROUTE_VNC_DIRECT, + ZEBRA_EBGP_DISTANCE_DEFAULT, + META_QUEUE_BGP}, + [ZEBRA_ROUTE_VNC_DIRECT_RH] = {ZEBRA_ROUTE_VNC_DIRECT_RH, + ZEBRA_EBGP_DISTANCE_DEFAULT, META_QUEUE_BGP}, - [ZEBRA_ROUTE_BGP_DIRECT] = {ZEBRA_ROUTE_BGP_DIRECT, 20, META_QUEUE_BGP}, - [ZEBRA_ROUTE_BGP_DIRECT_EXT] = {ZEBRA_ROUTE_BGP_DIRECT_EXT, 20, + [ZEBRA_ROUTE_BGP_DIRECT] = {ZEBRA_ROUTE_BGP_DIRECT, + ZEBRA_EBGP_DISTANCE_DEFAULT, + META_QUEUE_BGP}, + [ZEBRA_ROUTE_BGP_DIRECT_EXT] = {ZEBRA_ROUTE_BGP_DIRECT_EXT, + ZEBRA_EBGP_DISTANCE_DEFAULT, META_QUEUE_BGP}, - [ZEBRA_ROUTE_BABEL] = {ZEBRA_ROUTE_BABEL, 100, META_QUEUE_NOTBGP}, - [ZEBRA_ROUTE_SHARP] = {ZEBRA_ROUTE_SHARP, 150, META_QUEUE_OTHER}, - [ZEBRA_ROUTE_PBR] = {ZEBRA_ROUTE_PBR, 200, META_QUEUE_OTHER}, - [ZEBRA_ROUTE_BFD] = {ZEBRA_ROUTE_BFD, 255, META_QUEUE_OTHER}, - [ZEBRA_ROUTE_OPENFABRIC] = {ZEBRA_ROUTE_OPENFABRIC, 115, + [ZEBRA_ROUTE_BABEL] = {ZEBRA_ROUTE_BABEL, ZEBRA_BABEL_DISTANCE_DEFAULT, + META_QUEUE_NOTBGP}, + [ZEBRA_ROUTE_SHARP] = {ZEBRA_ROUTE_SHARP, ZEBRA_SHARP_DISTANCE_DEFAULT, + META_QUEUE_OTHER}, + [ZEBRA_ROUTE_PBR] = {ZEBRA_ROUTE_PBR, ZEBRA_PBR_DISTANCE_DEFAULT, + META_QUEUE_OTHER}, + [ZEBRA_ROUTE_BFD] = {ZEBRA_ROUTE_BFD, ZEBRA_MAX_DISTANCE_DEFAULT, + META_QUEUE_OTHER}, + [ZEBRA_ROUTE_OPENFABRIC] = {ZEBRA_ROUTE_OPENFABRIC, + ZEBRA_OPENFABRIC_DISTANCE_DEFAULT, META_QUEUE_NOTBGP}, - [ZEBRA_ROUTE_VRRP] = {ZEBRA_ROUTE_VRRP, 255, META_QUEUE_OTHER}, - [ZEBRA_ROUTE_SRTE] = {ZEBRA_ROUTE_SRTE, 255, META_QUEUE_OTHER}, - [ZEBRA_ROUTE_ALL] = {ZEBRA_ROUTE_ALL, 255, META_QUEUE_OTHER}, + [ZEBRA_ROUTE_VRRP] = {ZEBRA_ROUTE_VRRP, ZEBRA_MAX_DISTANCE_DEFAULT, + META_QUEUE_OTHER}, + [ZEBRA_ROUTE_SRTE] = {ZEBRA_ROUTE_SRTE, ZEBRA_MAX_DISTANCE_DEFAULT, + META_QUEUE_OTHER}, + [ZEBRA_ROUTE_ALL] = {ZEBRA_ROUTE_ALL, ZEBRA_MAX_DISTANCE_DEFAULT, + META_QUEUE_OTHER}, /* Any new route type added to zebra, should be mirrored here */ /* no entry/default: 150 */ @@ -4025,7 +4062,8 @@ static void _route_entry_dump_nh(const struct route_entry *re, if (nexthop->nh_label && nexthop->nh_label->num_labels > 0) { mpls_label2str(nexthop->nh_label->num_labels, nexthop->nh_label->label, label_str, - sizeof(label_str), 0 /*pretty*/); + sizeof(label_str), nexthop->nh_label_type, + 0 /*pretty*/); strlcat(label_str, ", ", sizeof(label_str)); } diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 342a8d0f96..3bbb1f2325 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -1413,8 +1413,14 @@ void show_nexthop_json_helper(json_object *json_nexthop, label_index < nexthop->nh_label->num_labels; label_index++) json_object_array_add( json_labels, - json_object_new_int( - nexthop->nh_label->label[label_index])); + json_object_new_int(( + (nexthop->nh_label_type == + ZEBRA_LSP_EVPN) + ? label2vni( + &nexthop->nh_label->label + [label_index]) + : nexthop->nh_label->label + [label_index]))); json_object_object_add(json_nexthop, "labels", json_labels); } @@ -1534,7 +1540,7 @@ void show_route_nexthop_helper(struct vty *vty, const struct route_entry *re, vty_out(vty, ", label %s", mpls_label2str(nexthop->nh_label->num_labels, nexthop->nh_label->label, buf, - sizeof(buf), 1)); + sizeof(buf), nexthop->nh_label_type, 1)); } if (nexthop->nh_srv6) { diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c index 5f307f95e3..8f24404a44 100644 --- a/zebra/zebra_routemap.c +++ b/zebra/zebra_routemap.c @@ -1055,8 +1055,13 @@ route_match_ip_next_hop(void *rule, const struct prefix *prefix, void *object) return RMAP_NOMATCH; } alist = access_list_lookup(AFI_IP, (char *)rule); - if (alist == NULL) + if (alist == NULL) { + if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + zlog_debug( + "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH", + __func__, (char *)rule); return RMAP_NOMATCH; + } return (access_list_apply(alist, &p) == FILTER_DENY ? RMAP_NOMATCH : RMAP_MATCH); @@ -1113,8 +1118,13 @@ route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix, return RMAP_NOMATCH; } plist = prefix_list_lookup(AFI_IP, (char *)rule); - if (plist == NULL) + if (plist == NULL) { + if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + zlog_debug( + "%s: Prefix List %s specified does not exist defaulting to NO_MATCH", + __func__, (char *)rule); return RMAP_NOMATCH; + } return (prefix_list_apply(plist, &p) == PREFIX_DENY ? RMAP_NOMATCH : RMAP_MATCH); @@ -1149,8 +1159,13 @@ route_match_address(afi_t afi, void *rule, const struct prefix *prefix, struct access_list *alist; alist = access_list_lookup(afi, (char *)rule); - if (alist == NULL) + if (alist == NULL) { + if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + zlog_debug( + "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH", + __func__, (char *)rule); return RMAP_NOMATCH; + } return (access_list_apply(alist, prefix) == FILTER_DENY ? RMAP_NOMATCH : RMAP_MATCH); @@ -1206,8 +1221,13 @@ route_match_address_prefix_list(void *rule, const struct prefix *prefix, struct prefix_list *plist; plist = prefix_list_lookup(afi, (char *)rule); - if (plist == NULL) + if (plist == NULL) { + if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + zlog_debug( + "%s: Prefix List %s specified does not exist defaulting to NO_MATCH", + __func__, (char *)rule); return RMAP_NOMATCH; + } return (prefix_list_apply(plist, prefix) == PREFIX_DENY ? RMAP_NOMATCH : RMAP_MATCH); diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index b013e59015..3b63821d37 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -420,7 +420,8 @@ static void show_nexthop_detail_helper(struct vty *vty, vty_out(vty, ", label %s", mpls_label2str(nexthop->nh_label->num_labels, nexthop->nh_label->label, buf, - sizeof(buf), 1 /*pretty*/)); + sizeof(buf), nexthop->nh_label_type, + 1 /*pretty*/)); } if (nexthop->weight) @@ -3018,20 +3019,37 @@ DEFPY(show_evpn_es_evi, return CMD_SUCCESS; } -DEFPY(show_evpn_access_vlan, - show_evpn_access_vlan_cmd, - "show evpn access-vlan [(1-4094)$vid | detail$detail] [json$json]", +DEFPY(show_evpn_access_vlan, show_evpn_access_vlan_cmd, + "show evpn access-vlan [IFNAME$if_name (1-4094)$vid | detail$detail] [json$json]", SHOW_STR "EVPN\n" "Access VLANs\n" + "Interface Name\n" "VLAN ID\n" - "Detailed information\n" - JSON_STR) + "Detailed information\n" JSON_STR) { bool uj = !!json; - if (vid) { - zebra_evpn_acc_vl_show_vid(vty, uj, vid); + if (if_name && vid) { + bool found = false; + struct vrf *vrf; + struct interface *ifp; + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + if (if_name) { + ifp = if_lookup_by_name(if_name, vrf->vrf_id); + if (ifp) { + zebra_evpn_acc_vl_show_vid(vty, uj, vid, + ifp); + found = true; + break; + } + } + } + if (!found) { + vty_out(vty, "%% Can't find interface %s\n", if_name); + return CMD_WARNING; + } } else { if (detail) zebra_evpn_acc_vl_show_detail(vty, uj); @@ -3181,6 +3199,30 @@ DEFUN (show_evpn_nh_vni_ip, return CMD_SUCCESS; } +DEFUN_HIDDEN (show_evpn_nh_svd_ip, + show_evpn_nh_svd_ip_cmd, + "show evpn next-hops svd ip WORD [json]", + SHOW_STR + "EVPN\n" + "Remote Vteps\n" + "Single Vxlan Device\n" + "Ip address\n" + "Host address (ipv4 or ipv6)\n" + JSON_STR) +{ + struct ipaddr ip; + bool uj = use_json(argc, argv); + + if (str2ipaddr(argv[5]->arg, &ip) != 0) { + if (!uj) + vty_out(vty, "%% Malformed Neighbor address\n"); + return CMD_WARNING; + } + zebra_vxlan_print_specific_nh_l3vni(vty, 0, &ip, uj); + + return CMD_SUCCESS; +} + DEFUN (show_evpn_nh_vni, show_evpn_nh_vni_cmd, "show evpn next-hops vni " CMD_VNI_RANGE "[json]", @@ -3200,6 +3242,22 @@ DEFUN (show_evpn_nh_vni, return CMD_SUCCESS; } +DEFUN_HIDDEN (show_evpn_nh_svd, + show_evpn_nh_svd_cmd, + "show evpn next-hops svd [json]", + SHOW_STR + "EVPN\n" + "Remote VTEPs\n" + "Single Vxlan Device\n" + JSON_STR) +{ + bool uj = use_json(argc, argv); + + zebra_vxlan_print_nh_svd(vty, uj); + + return CMD_SUCCESS; +} + DEFUN (show_evpn_nh_vni_all, show_evpn_nh_vni_all_cmd, "show evpn next-hops vni all [json]", @@ -4557,7 +4615,9 @@ void zebra_vty_init(void) install_element(VIEW_NODE, &show_evpn_rmac_vni_cmd); install_element(VIEW_NODE, &show_evpn_rmac_vni_all_cmd); install_element(VIEW_NODE, &show_evpn_nh_vni_ip_cmd); + install_element(VIEW_NODE, &show_evpn_nh_svd_ip_cmd); install_element(VIEW_NODE, &show_evpn_nh_vni_cmd); + install_element(VIEW_NODE, &show_evpn_nh_svd_cmd); install_element(VIEW_NODE, &show_evpn_nh_vni_all_cmd); install_element(VIEW_NODE, &show_evpn_mac_vni_cmd); install_element(VIEW_NODE, &show_evpn_mac_vni_all_cmd); diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 0bbc811324..4baa3d64af 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -46,13 +46,14 @@ #include "zebra/rt_netlink.h" #include "zebra/zebra_errors.h" #include "zebra/zebra_l2.h" +#include "zebra/zebra_l2_bridge_if.h" #include "zebra/zebra_ns.h" #include "zebra/zebra_vrf.h" #include "zebra/zebra_vxlan.h" +#include "zebra/zebra_vxlan_private.h" #include "zebra/zebra_evpn.h" #include "zebra/zebra_evpn_mac.h" #include "zebra/zebra_evpn_neigh.h" -#include "zebra/zebra_vxlan_private.h" #include "zebra/zebra_evpn_mh.h" #include "zebra/zebra_evpn_vxlan.h" #include "zebra/zebra_router.h" @@ -72,6 +73,9 @@ DEFINE_HOOK(zebra_rmac_update, /* config knobs */ static bool accept_bgp_seq = true; +/* Single VXlan Device Global Neigh Table */ +struct hash *svd_nh_table; + /* static function declarations */ static void zevpn_print_neigh_hash_all_evpn(struct hash_bucket *bucket, void **args); @@ -92,6 +96,11 @@ static int zl3vni_nh_del(struct zebra_l3vni *zl3vni, struct zebra_neigh *n); static int zl3vni_nh_install(struct zebra_l3vni *zl3vni, struct zebra_neigh *n); static int zl3vni_nh_uninstall(struct zebra_l3vni *zl3vni, struct zebra_neigh *n); +static struct zebra_neigh *svd_nh_add(const struct ipaddr *vtep_ip, + const struct ethaddr *rmac); +static int svd_nh_del(struct zebra_neigh *n); +static int svd_nh_install(struct zebra_l3vni *zl3vni, struct zebra_neigh *n); +static int svd_nh_uninstall(struct zebra_l3vni *zl3vni, struct zebra_neigh *n); /* l3-vni rmac related APIs */ static void zl3vni_print_rmac_hash(struct hash_bucket *, void *); @@ -110,8 +119,6 @@ static int zl3vni_rmac_uninstall(struct zebra_l3vni *zl3vni, static void *zl3vni_alloc(void *p); static struct zebra_l3vni *zl3vni_add(vni_t vni, vrf_id_t vrf_id); static int zl3vni_del(struct zebra_l3vni *zl3vni); -static void zebra_vxlan_process_l3vni_oper_up(struct zebra_l3vni *zl3vni); -static void zebra_vxlan_process_l3vni_oper_down(struct zebra_l3vni *zl3vni); static void zevpn_build_hash_table(void); static unsigned int zebra_vxlan_sg_hash_key_make(const void *p); @@ -121,10 +128,6 @@ static void zebra_vxlan_sg_do_deref(struct zebra_vrf *zvrf, static struct zebra_vxlan_sg *zebra_vxlan_sg_do_ref(struct zebra_vrf *vrf, struct in_addr sip, struct in_addr mcast_grp); -static void zebra_vxlan_sg_deref(struct in_addr local_vtep_ip, - struct in_addr mcast_grp); -static void zebra_vxlan_sg_ref(struct in_addr local_vtep_ip, - struct in_addr mcast_grp); static void zebra_vxlan_cleanup_sg_table(struct zebra_vrf *zvrf); bool zebra_evpn_do_dup_addr_detect(struct zebra_vrf *zvrf) @@ -375,11 +378,16 @@ static void zl3vni_print_nh(struct zebra_neigh *n, struct vty *vty, ipaddr2str(&n->ip, buf2, sizeof(buf2))); vty_out(vty, " RMAC: %s\n", prefix_mac2str(&n->emac, buf1, sizeof(buf1))); - vty_out(vty, " Refcount: %d\n", - rb_host_count(&n->host_rb)); - vty_out(vty, " Prefixes:\n"); - RB_FOREACH (hle, host_rb_tree_entry, &n->host_rb) - vty_out(vty, " %pFX\n", &hle->p); + if (n->refcnt) + /* SVD neigh */ + vty_out(vty, " Refcount: %u\n", n->refcnt); + else { + vty_out(vty, " Refcount: %d\n", + rb_host_count(&n->host_rb)); + vty_out(vty, " Prefixes:\n"); + RB_FOREACH (hle, host_rb_tree_entry, &n->host_rb) + vty_out(vty, " %pFX\n", &hle->p); + } } else { json_hosts = json_object_new_array(); json_object_string_add( @@ -387,13 +395,19 @@ static void zl3vni_print_nh(struct zebra_neigh *n, struct vty *vty, json_object_string_add( json, "routerMac", prefix_mac2str(&n->emac, buf2, sizeof(buf2))); - json_object_int_add(json, "refCount", - rb_host_count(&n->host_rb)); - RB_FOREACH (hle, host_rb_tree_entry, &n->host_rb) - json_object_array_add(json_hosts, - json_object_new_string(prefix2str( - &hle->p, buf2, sizeof(buf2)))); - json_object_object_add(json, "prefixList", json_hosts); + if (n->refcnt) + /* SVD neigh */ + json_object_int_add(json, "refCount", n->refcnt); + else { + json_object_int_add(json, "refCount", + rb_host_count(&n->host_rb)); + RB_FOREACH (hle, host_rb_tree_entry, &n->host_rb) + json_object_array_add( + json_hosts, + json_object_new_string(prefix2str( + &hle->p, buf2, sizeof(buf2)))); + json_object_object_add(json, "prefixList", json_hosts); + } } } @@ -597,33 +611,36 @@ static void zl3vni_print_nh_hash(struct hash_bucket *bucket, void *ctx) } } -static void zl3vni_print_nh_hash_all_vni(struct hash_bucket *bucket, - void **args) +static void zl3vni_print_nh_all_table(struct hash *nh_table, vni_t vni, + struct vty *vty, json_object *json) { - struct vty *vty = NULL; - json_object *json = NULL; - json_object *json_evpn = NULL; - struct zebra_l3vni *zl3vni = NULL; uint32_t num_nh = 0; struct nh_walk_ctx wctx; char vni_str[VNI_STR_LEN]; + json_object *json_evpn = NULL; + bool is_svd = false; + const char *svd_str = "Global SVD Table"; - vty = (struct vty *)args[0]; - json = (struct json_object *)args[1]; + if (vni == 0) + is_svd = true; - zl3vni = (struct zebra_l3vni *)bucket->data; + num_nh = hashcount(nh_table); - num_nh = hashcount(zl3vni->nh_table); if (!num_nh) return; if (json) { json_evpn = json_object_new_object(); - snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni); + + snprintf(vni_str, VNI_STR_LEN, "%u", vni); } if (json == NULL) { - vty_out(vty, "\nVNI %u #Next-Hops %u\n\n", zl3vni->vni, num_nh); + if (is_svd) + vty_out(vty, "\n%s #Next-Hops %u\n\n", svd_str, num_nh); + else + vty_out(vty, "\nVNI %u #Next-Hops %u\n\n", vni, num_nh); + vty_out(vty, "%-15s %-17s\n", "IP", "RMAC"); } else json_object_int_add(json_evpn, "numNextHops", num_nh); @@ -631,11 +648,26 @@ static void zl3vni_print_nh_hash_all_vni(struct hash_bucket *bucket, memset(&wctx, 0, sizeof(wctx)); wctx.vty = vty; wctx.json = json_evpn; - hash_iterate(zl3vni->nh_table, zl3vni_print_nh_hash, &wctx); + hash_iterate(nh_table, zl3vni_print_nh_hash, &wctx); if (json) json_object_object_add(json, vni_str, json_evpn); } +static void zl3vni_print_nh_hash_all_vni(struct hash_bucket *bucket, + void **args) +{ + struct vty *vty = NULL; + json_object *json = NULL; + struct zebra_l3vni *zl3vni = NULL; + + vty = (struct vty *)args[0]; + json = (struct json_object *)args[1]; + + zl3vni = (struct zebra_l3vni *)bucket->data; + + zl3vni_print_nh_all_table(zl3vni->nh_table, zl3vni->vni, vty, json); +} + static void zl3vni_print_rmac_hash_all_vni(struct hash_bucket *bucket, void **args) { @@ -728,6 +760,9 @@ static void zl3vni_print(struct zebra_l3vni *zl3vni, void **ctx) vty_out(vty, "VNI: %u\n", zl3vni->vni); vty_out(vty, " Type: %s\n", "L3"); vty_out(vty, " Tenant VRF: %s\n", zl3vni_vrf_name(zl3vni)); + vty_out(vty, " Vlan: %u\n", zl3vni->vid); + vty_out(vty, " Bridge: %s\n", + zl3vni->bridge_if ? zl3vni->bridge_if->name : "-"); vty_out(vty, " Local Vtep Ip: %pI4\n", &zl3vni->local_vtep_ip); vty_out(vty, " Vxlan-Intf: %s\n", @@ -889,7 +924,6 @@ struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if) { struct interface *tmp_if = NULL; struct zebra_if *zif; - struct zebra_l2info_bridge *br; struct zebra_from_svi_param in_param; struct interface **p_ifp; /* Defensive check, caller expected to invoke only with valid bridge. */ @@ -899,8 +933,7 @@ struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if) /* Determine if bridge is VLAN-aware or not */ zif = br_if->info; assert(zif); - br = &zif->l2info.br; - in_param.bridge_vlan_aware = br->vlan_aware; + in_param.bridge_vlan_aware = IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(zif); /* Check oper status of the SVI. */ if (!in_param.bridge_vlan_aware) return if_is_operative(br_if) ? br_if : NULL; @@ -915,9 +948,11 @@ struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if) return tmp_if; } -static int zebra_evpn_vxlan_del(struct zebra_evpn *zevpn) +int zebra_evpn_vxlan_del(struct zebra_evpn *zevpn) { + zevpn->vid = 0; zevpn_vxlan_if_set(zevpn, zevpn->vxlan_if, false /* set */); + zevpn_bridge_if_set(zevpn, zevpn->bridge_if, false /* set */); /* Remove references to the BUM mcast grp */ zebra_vxlan_sg_deref(zevpn->local_vtep_ip, zevpn->mcast_grp); @@ -925,6 +960,136 @@ static int zebra_evpn_vxlan_del(struct zebra_evpn *zevpn) return zebra_evpn_del(zevpn); } +static int zevpn_build_vni_hash_table(struct zebra_if *zif, + struct zebra_vxlan_vni *vnip, void *arg) +{ + vni_t vni; + struct zebra_evpn *zevpn; + struct zebra_l3vni *zl3vni; + struct interface *ifp; + struct zebra_l2info_vxlan *vxl; + struct interface *br_if; + + ifp = zif->ifp; + vxl = &zif->l2info.vxl; + vni = vnip->vni; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Build vni table for vni %u for Intf %s", vni, + ifp->name); + + /* L3-VNI and L2-VNI are handled seperately */ + zl3vni = zl3vni_lookup(vni); + if (zl3vni) { + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "create L3-VNI hash for Intf %s(%u) L3-VNI %u", + ifp->name, ifp->ifindex, vni); + + /* associate with vxlan_if */ + zl3vni->local_vtep_ip = vxl->vtep_ip; + zl3vni->vxlan_if = ifp; + + /* + * we need to associate with SVI. + * we can associate with svi-if only after association + * with vxlan-intf is complete + */ + zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni); + + /* Associate l3vni to mac-vlan and extract VRR MAC */ + zl3vni->mac_vlan_if = zl3vni_map_to_mac_vlan_if(zl3vni); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "create l3vni %u svi_if %s mac_vlan_if %s", vni, + zl3vni->svi_if ? zl3vni->svi_if->name : "NIL", + zl3vni->mac_vlan_if ? zl3vni->mac_vlan_if->name + : "NIL"); + + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up(zl3vni); + + } else { + struct interface *vlan_if = NULL; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Create L2-VNI hash for intf %s(%u) L2-VNI %u local IP %pI4", + ifp->name, ifp->ifindex, vni, &vxl->vtep_ip); + + /* + * EVPN hash entry is expected to exist, if the BGP process is + * killed + */ + zevpn = zebra_evpn_lookup(vni); + if (zevpn) { + zlog_debug( + "EVPN hash already present for IF %s(%u) L2-VNI %u", + ifp->name, ifp->ifindex, vni); + + /* + * Inform BGP if intf is up and mapped to + * bridge. + */ + if (if_is_operative(ifp) && zif->brslave_info.br_if) + zebra_evpn_send_add_to_client(zevpn); + + /* Send Local MAC-entries to client */ + zebra_evpn_send_mac_list_to_client(zevpn); + + /* Send Loval Neighbor entries to client */ + zebra_evpn_send_neigh_to_client(zevpn); + } else { + zevpn = zebra_evpn_add(vni); + if (!zevpn) { + zlog_debug( + "Failed to add EVPN hash, IF %s(%u) L2-VNI %u", + ifp->name, ifp->ifindex, vni); + return 0; + } + + if (zevpn->local_vtep_ip.s_addr != + vxl->vtep_ip.s_addr || + zevpn->mcast_grp.s_addr != vnip->mcast_grp.s_addr) { + zebra_vxlan_sg_deref(zevpn->local_vtep_ip, + zevpn->mcast_grp); + zebra_vxlan_sg_ref(vxl->vtep_ip, + vnip->mcast_grp); + zevpn->local_vtep_ip = vxl->vtep_ip; + zevpn->mcast_grp = vnip->mcast_grp; + /* on local vtep-ip check if ES + * orig-ip needs to be updated + */ + zebra_evpn_es_set_base_evpn(zevpn); + } + zevpn_vxlan_if_set(zevpn, ifp, true /* set */); + br_if = zif->brslave_info.br_if; + zevpn_bridge_if_set(zevpn, br_if, true /* set */); + vlan_if = zvni_map_to_svi(vnip->access_vlan, br_if); + if (vlan_if) { + zevpn->vid = vnip->access_vlan; + zevpn->svi_if = vlan_if; + zevpn->vrf_id = vlan_if->vrf->vrf_id; + zl3vni = zl3vni_from_vrf(vlan_if->vrf->vrf_id); + if (zl3vni) + listnode_add_sort(zl3vni->l2vnis, + zevpn); + } + + /* + * Inform BGP if intf is up and mapped to + * bridge. + */ + if (if_is_operative(ifp) && zif->brslave_info.br_if) + zebra_evpn_send_add_to_client(zevpn); + } + } + + return 0; +} + static int zevpn_build_hash_table_zns(struct ns *ns, void *param_in __attribute__((unused)), void **param_out __attribute__((unused))) @@ -938,9 +1103,6 @@ static int zevpn_build_hash_table_zns(struct ns *ns, /* Walk VxLAN interfaces and create EVPN hash. */ for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { - vni_t vni; - struct zebra_evpn *zevpn = NULL; - struct zebra_l3vni *zl3vni = NULL; struct zebra_if *zif; struct zebra_l2info_vxlan *vxl; @@ -952,126 +1114,23 @@ static int zevpn_build_hash_table_zns(struct ns *ns, continue; 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 " + "Intf %s(%u) link not in same " "namespace than BGP EVPN core instance ", - ifp->name, ifp->ifindex, vni); + ifp->name, ifp->ifindex); continue; } - /* L3-VNI and L2-VNI are handled seperately */ - zl3vni = zl3vni_lookup(vni); - if (zl3vni) { - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "create L3-VNI hash for Intf %s(%u) L3-VNI %u", - ifp->name, ifp->ifindex, vni); - - /* associate with vxlan_if */ - zl3vni->local_vtep_ip = vxl->vtep_ip; - zl3vni->vxlan_if = ifp; - - /* - * we need to associate with SVI. - * we can associate with svi-if only after association - * with vxlan-intf is complete - */ - zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni); - - /* Associate l3vni to mac-vlan and extract VRR MAC */ - zl3vni->mac_vlan_if = zl3vni_map_to_mac_vlan_if(zl3vni); - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("create l3vni %u svi_if %s mac_vlan_if %s", - vni, zl3vni->svi_if ? zl3vni->svi_if->name - : "NIL", - zl3vni->mac_vlan_if ? - zl3vni->mac_vlan_if->name : "NIL"); - - if (is_l3vni_oper_up(zl3vni)) - zebra_vxlan_process_l3vni_oper_up(zl3vni); - } else { - struct interface *vlan_if = NULL; - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Create L2-VNI hash for intf %s(%u) L2-VNI %u local IP %pI4", - ifp->name, ifp->ifindex, vni, - &vxl->vtep_ip); - - /* EVPN hash entry is expected to exist, if the BGP process is killed */ - zevpn = zebra_evpn_lookup(vni); - if (zevpn) { - zlog_debug( - "EVPN hash already present for IF %s(%u) L2-VNI %u", - ifp->name, ifp->ifindex, vni); + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Building vni table for %s-if %s", + IS_ZEBRA_VXLAN_IF_VNI(zif) ? "vni" : "svd", + ifp->name); - /* - * Inform BGP if intf is up and mapped to - * bridge. - */ - if (if_is_operative(ifp) && - zif->brslave_info.br_if) - zebra_evpn_send_add_to_client(zevpn); - - /* Send Local MAC-entries to client */ - zebra_evpn_send_mac_list_to_client(zevpn); - - /* Send Loval Neighbor entries to client */ - zebra_evpn_send_neigh_to_client(zevpn); - } else { - zevpn = zebra_evpn_add(vni); - if (!zevpn) { - zlog_debug( - "Failed to add EVPN hash, IF %s(%u) L2-VNI %u", - ifp->name, ifp->ifindex, vni); - return NS_WALK_CONTINUE; - } - - if (zevpn->local_vtep_ip.s_addr != - vxl->vtep_ip.s_addr || - zevpn->mcast_grp.s_addr != - vxl->mcast_grp.s_addr) { - zebra_vxlan_sg_deref( - zevpn->local_vtep_ip, - zevpn->mcast_grp); - zebra_vxlan_sg_ref(vxl->vtep_ip, - vxl->mcast_grp); - zevpn->local_vtep_ip = vxl->vtep_ip; - zevpn->mcast_grp = vxl->mcast_grp; - /* on local vtep-ip check if ES - * orig-ip needs to be updated - */ - zebra_evpn_es_set_base_evpn(zevpn); - } - zevpn_vxlan_if_set(zevpn, ifp, true /* set */); - vlan_if = zvni_map_to_svi( - vxl->access_vlan, - zif->brslave_info.br_if); - if (vlan_if) { - zevpn->svi_if = vlan_if; - zevpn->vrf_id = vlan_if->vrf->vrf_id; - zl3vni = zl3vni_from_vrf( - vlan_if->vrf->vrf_id); - if (zl3vni) - listnode_add_sort( - zl3vni->l2vnis, zevpn); - } - - /* - * Inform BGP if intf is up and mapped to - * bridge. - */ - if (if_is_operative(ifp) && - zif->brslave_info.br_if) - zebra_evpn_send_add_to_client(zevpn); - } - } + zebra_vxlan_if_vni_iterate(zif, zevpn_build_vni_hash_table, + NULL); } return NS_WALK_CONTINUE; } @@ -1225,7 +1284,7 @@ static int zl3vni_rmac_install(struct zebra_l3vni *zl3vni, struct zebra_mac *zrmac) { const struct zebra_if *zif = NULL, *br_zif = NULL; - const struct zebra_l2info_vxlan *vxl = NULL; + const struct zebra_vxlan_vni *vni; const struct interface *br_ifp; enum zebra_dplane_result res; vlanid_t vid; @@ -1242,17 +1301,17 @@ static int zl3vni_rmac_install(struct zebra_l3vni *zl3vni, if (br_ifp == NULL) return -1; - vxl = &zif->l2info.vxl; + vni = zebra_vxlan_if_vni_find(zif, zl3vni->vni); br_zif = (const struct zebra_if *)br_ifp->info; if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif)) - vid = vxl->access_vlan; + vid = vni->access_vlan; else vid = 0; - res = dplane_rem_mac_add(zl3vni->vxlan_if, br_ifp, vid, - &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip, 0, 0, + res = dplane_rem_mac_add(zl3vni->vxlan_if, br_ifp, vid, &zrmac->macaddr, + vni->vni, zrmac->fwd_info.r_vtep_ip, 0, 0, false /*was_static*/); if (res != ZEBRA_DPLANE_REQUEST_FAILURE) return 0; @@ -1267,7 +1326,7 @@ static int zl3vni_rmac_uninstall(struct zebra_l3vni *zl3vni, struct zebra_mac *zrmac) { const struct zebra_if *zif = NULL, *br_zif; - const struct zebra_l2info_vxlan *vxl = NULL; + const struct zebra_vxlan_vni *vni; const struct interface *br_ifp; vlanid_t vid; enum zebra_dplane_result res; @@ -1292,16 +1351,16 @@ static int zl3vni_rmac_uninstall(struct zebra_l3vni *zl3vni, if (br_ifp == NULL) return -1; - vxl = &zif->l2info.vxl; + vni = zebra_vxlan_if_vni_find(zif, zl3vni->vni); br_zif = (const struct zebra_if *)br_ifp->info; if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif)) - vid = vxl->access_vlan; + vid = vni->access_vlan; else vid = 0; - res = dplane_rem_mac_del(zl3vni->vxlan_if, br_ifp, vid, - &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip); + res = dplane_rem_mac_del(zl3vni->vxlan_if, br_ifp, vid, &zrmac->macaddr, + vni->vni, zrmac->fwd_info.r_vtep_ip); if (res != ZEBRA_DPLANE_REQUEST_FAILURE) return 0; else @@ -1424,21 +1483,41 @@ static void zl3vni_remote_rmac_del(struct zebra_l3vni *zl3vni, } /* - * Look up nh hash entry on a l3-vni. + * Common code for look up of nh hash entry. */ -static struct zebra_neigh *zl3vni_nh_lookup(struct zebra_l3vni *zl3vni, - const struct ipaddr *ip) +static struct zebra_neigh *_nh_lookup(struct zebra_l3vni *zl3vni, + const struct ipaddr *ip) { struct zebra_neigh tmp; struct zebra_neigh *n; memset(&tmp, 0, sizeof(tmp)); memcpy(&tmp.ip, ip, sizeof(struct ipaddr)); - n = hash_lookup(zl3vni->nh_table, &tmp); + + if (zl3vni) + n = hash_lookup(zl3vni->nh_table, &tmp); + else + n = hash_lookup(svd_nh_table, &tmp); return n; } +/* + * Look up nh hash entry on a l3-vni. + */ +static struct zebra_neigh *zl3vni_nh_lookup(struct zebra_l3vni *zl3vni, + const struct ipaddr *ip) +{ + return _nh_lookup(zl3vni, ip); +} + +/* + * Look up nh hash entry on a SVD. + */ +static struct zebra_neigh *svd_nh_lookup(const struct ipaddr *ip) +{ + return _nh_lookup(NULL, ip); +} /* * Callback to allocate NH hash entry on L3-VNI. @@ -1455,18 +1534,24 @@ static void *zl3vni_nh_alloc(void *p) } /* - * Add neighbor entry. + * Common code for neigh add. */ -static struct zebra_neigh *zl3vni_nh_add(struct zebra_l3vni *zl3vni, - const struct ipaddr *ip, - const struct ethaddr *mac) +static struct zebra_neigh *_nh_add(struct zebra_l3vni *zl3vni, + const struct ipaddr *ip, + const struct ethaddr *mac) { struct zebra_neigh tmp_n; struct zebra_neigh *n = NULL; memset(&tmp_n, 0, sizeof(tmp_n)); memcpy(&tmp_n.ip, ip, sizeof(struct ipaddr)); - n = hash_get(zl3vni->nh_table, &tmp_n, zl3vni_nh_alloc); + + if (zl3vni) + n = hash_get(zl3vni->nh_table, &tmp_n, zl3vni_nh_alloc); + else + n = hash_get(svd_nh_table, &tmp_n, zl3vni_nh_alloc); + + assert(n); RB_INIT(host_rb_tree_entry, &n->host_rb); @@ -1478,6 +1563,16 @@ static struct zebra_neigh *zl3vni_nh_add(struct zebra_l3vni *zl3vni, } /* + * Add neighbor entry. + */ +static struct zebra_neigh *zl3vni_nh_add(struct zebra_l3vni *zl3vni, + const struct ipaddr *ip, + const struct ethaddr *mac) +{ + return _nh_add(zl3vni, ip, mac); +} + +/* * Delete neighbor entry. */ static int zl3vni_nh_del(struct zebra_l3vni *zl3vni, struct zebra_neigh *n) @@ -1499,14 +1594,38 @@ static int zl3vni_nh_del(struct zebra_l3vni *zl3vni, struct zebra_neigh *n) } /* - * Install remote nh as neigh into the kernel. + * Add Single VXlan Device neighbor entry. */ -static int zl3vni_nh_install(struct zebra_l3vni *zl3vni, struct zebra_neigh *n) +static struct zebra_neigh *svd_nh_add(const struct ipaddr *ip, + const struct ethaddr *mac) +{ + return _nh_add(NULL, ip, mac); +} + +/* + * Del Single VXlan Device neighbor entry. + */ +static int svd_nh_del(struct zebra_neigh *n) +{ + if (n->refcnt > 0) + return -1; + + hash_release(svd_nh_table, n); + XFREE(MTYPE_L3NEIGH, n); + + return 0; +} + +/* + * Common code to install remote nh as neigh into the kernel. + */ +static int _nh_install(struct zebra_l3vni *zl3vni, struct interface *ifp, + struct zebra_neigh *n) { uint8_t flags; int ret = 0; - if (!is_l3vni_oper_up(zl3vni)) + if (zl3vni && !is_l3vni_oper_up(zl3vni)) return -1; if (!(n->flags & ZEBRA_NEIGH_REMOTE) @@ -1517,31 +1636,63 @@ static int zl3vni_nh_install(struct zebra_l3vni *zl3vni, struct zebra_neigh *n) if (n->flags & ZEBRA_NEIGH_ROUTER_FLAG) flags |= DPLANE_NTF_ROUTER; - dplane_rem_neigh_add(zl3vni->svi_if, &n->ip, &n->emac, flags, - false /*was_static*/); + dplane_rem_neigh_add(ifp, &n->ip, &n->emac, flags, + false /*was_static*/); return ret; } /* - * Uninstall remote nh from the kernel. + * Common code to uninstall remote nh from the kernel. */ -static int zl3vni_nh_uninstall(struct zebra_l3vni *zl3vni, - struct zebra_neigh *n) +static int _nh_uninstall(struct interface *ifp, struct zebra_neigh *n) { if (!(n->flags & ZEBRA_NEIGH_REMOTE) || !(n->flags & ZEBRA_NEIGH_REMOTE_NH)) return 0; - if (!zl3vni->svi_if || !if_is_operative(zl3vni->svi_if)) + if (!ifp || !if_is_operative(ifp)) return 0; - dplane_rem_neigh_delete(zl3vni->svi_if, &n->ip); + dplane_rem_neigh_delete(ifp, &n->ip); return 0; } -/* add remote vtep as a neigh entry */ +/* + * Install remote nh as neigh into the kernel. + */ +static int zl3vni_nh_install(struct zebra_l3vni *zl3vni, struct zebra_neigh *n) +{ + return _nh_install(zl3vni, zl3vni->svi_if, n); +} + +/* + * Uninstall remote nh from the kernel. + */ +static int zl3vni_nh_uninstall(struct zebra_l3vni *zl3vni, + struct zebra_neigh *n) +{ + return _nh_uninstall(zl3vni->svi_if, n); +} + +/* + * Install SVD remote nh as neigh into the kernel. + */ +static int svd_nh_install(struct zebra_l3vni *zl3vni, struct zebra_neigh *n) +{ + return _nh_install(zl3vni, zl3vni->vxlan_if, n); +} + +/* + * Uninstall SVD remote nh from the kernel. + */ +static int svd_nh_uninstall(struct zebra_l3vni *zl3vni, struct zebra_neigh *n) +{ + return _nh_uninstall(zl3vni->vxlan_if, n); +} + +/* Add remote vtep as a neigh entry */ static int zl3vni_remote_nh_add(struct zebra_l3vni *zl3vni, const struct ipaddr *vtep_ip, const struct ethaddr *rmac, @@ -1579,7 +1730,7 @@ static int zl3vni_remote_nh_add(struct zebra_l3vni *zl3vni, return 0; } -/* handle nh neigh delete */ +/* Del remote vtep as a neigh entry */ static void zl3vni_remote_nh_del(struct zebra_l3vni *zl3vni, struct zebra_neigh *nh, struct prefix *host_prefix) @@ -1595,6 +1746,91 @@ static void zl3vni_remote_nh_del(struct zebra_l3vni *zl3vni, } } +/* Add remote vtep as a SVD neigh entry */ +static int svd_remote_nh_add(struct zebra_l3vni *zl3vni, + const struct ipaddr *vtep_ip, + const struct ethaddr *rmac, + const struct prefix *host_prefix) +{ + struct zebra_neigh *nh = NULL; + + /* SVD backed VNI check */ + if (!IS_ZL3VNI_SVD_BACKED(zl3vni)) + return 0; + + /* Create the SVD next hop entry, or update its mac, if necessary. */ + nh = svd_nh_lookup(vtep_ip); + if (!nh) { + nh = svd_nh_add(vtep_ip, rmac); + if (!nh) { + zlog_debug( + "Failed to add NH %pIA as SVD Neigh (RMAC %pEA prefix %pFX)", + vtep_ip, rmac, host_prefix); + return -1; + } + + } else if (memcmp(&nh->emac, rmac, ETH_ALEN) != 0) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "SVD RMAC change(%pEA --> %pEA) for nexthop %pIA, prefix %pFX", + &nh->emac, rmac, vtep_ip, host_prefix); + + memcpy(&nh->emac, rmac, ETH_ALEN); + /* install (update) the nh neigh in kernel */ + svd_nh_install(zl3vni, nh); + + /* Don't increment refcnt change */ + return 0; + } + + nh->refcnt++; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("SVD NH ADD refcnt (%u) for nexthop %pIA", + nh->refcnt, vtep_ip); + + /* + * Install the nh neigh in kernel if this is the first time we + * have seen it. + */ + if (nh->refcnt == 1) + svd_nh_install(zl3vni, nh); + + return 0; +} + +/* Del remote vtep as a SVD neigh entry */ +static int svd_remote_nh_del(struct zebra_l3vni *zl3vni, + const struct ipaddr *vtep_ip) +{ + struct zebra_neigh *nh; + + /* SVD backed VNI check */ + if (!IS_ZL3VNI_SVD_BACKED(zl3vni)) + return 0; + + nh = svd_nh_lookup(vtep_ip); + if (!nh) { + zlog_debug("Failed to del NH %pIA as SVD Neigh", vtep_ip); + + return -1; + } + + nh->refcnt--; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("SVD NH Del refcnt (%u) for nexthop %pIA", + nh->refcnt, vtep_ip); + + /* Last refcnt on NH, remove it completely. */ + if (nh->refcnt == 0) { + svd_nh_uninstall(zl3vni, nh); + svd_nh_del(nh); + } + + return 0; +} + /* handle neigh update from kernel - the only thing of interest is to * readd stale entries. */ @@ -1756,7 +1992,8 @@ static int zl3vni_map_to_vxlan_if_ns(struct ns *ns, for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan *vxl = NULL; + struct zebra_l2info_vxlan *vxl; + struct zebra_vxlan_vni *vni = NULL; ifp = (struct interface *)rn->info; if (!ifp) @@ -1767,7 +2004,8 @@ static int zl3vni_map_to_vxlan_if_ns(struct ns *ns, continue; vxl = &zif->l2info.vxl; - if (vxl->vni != zl3vni->vni) + vni = zebra_vxlan_if_vni_find(zif, zl3vni->vni); + if (!vni || vni->vni != zl3vni->vni) continue; /* link of VXLAN interface should be in zebra_evpn_vrf */ @@ -1776,12 +2014,12 @@ static int zl3vni_map_to_vxlan_if_ns(struct ns *ns, zlog_debug( "Intf %s(%u) VNI %u, link not in same " "namespace than BGP EVPN core instance ", - ifp->name, ifp->ifindex, vxl->vni); + ifp->name, ifp->ifindex, vni->vni); continue; } - zl3vni->local_vtep_ip = vxl->vtep_ip; + zl3vni->local_vtep_ip = zif->l2info.vxl.vtep_ip; *_pifp = (void *)ifp; return NS_WALK_STOP; } @@ -1804,7 +2042,7 @@ struct interface *zl3vni_map_to_vxlan_if(struct zebra_l3vni *zl3vni) struct interface *zl3vni_map_to_svi_if(struct zebra_l3vni *zl3vni) { struct zebra_if *zif = NULL; /* zebra_if for vxlan_if */ - struct zebra_l2info_vxlan *vxl = NULL; /* l2 info for vxlan_if */ + struct zebra_vxlan_vni *vni = NULL; /* vni info in vxlan_if */ if (!zl3vni) return NULL; @@ -1816,9 +2054,11 @@ struct interface *zl3vni_map_to_svi_if(struct zebra_l3vni *zl3vni) if (!zif) return NULL; - vxl = &zif->l2info.vxl; + vni = zebra_vxlan_if_vni_find(zif, zl3vni->vni); + if (!vni) + return NULL; - return zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); + return zvni_map_to_svi(vni->access_vlan, zif->brslave_info.br_if); } struct interface *zl3vni_map_to_mac_vlan_if(struct zebra_l3vni *zl3vni) @@ -1853,6 +2093,8 @@ struct zebra_l3vni *zl3vni_from_vrf(vrf_id_t vrf_id) static int zl3vni_from_svi_ns(struct ns *ns, void *_in_param, void **_p_zl3vni) { + int found = 0; + vni_t vni_id = 0; struct zebra_ns *zns = ns->info; struct zebra_l3vni **p_zl3vni = (struct zebra_l3vni **)_p_zl3vni; struct zebra_from_svi_param *in_param = @@ -1860,33 +2102,46 @@ static int zl3vni_from_svi_ns(struct ns *ns, void *_in_param, void **_p_zl3vni) struct route_node *rn = NULL; struct interface *tmp_if = NULL; struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan *vxl = NULL; + struct zebra_if *br_zif = NULL; assert(in_param && p_zl3vni); - /* loop through all vxlan-interface */ - 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; + br_zif = in_param->br_if->info; + assert(br_zif); - if (zif->brslave_info.br_if != in_param->br_if) - continue; + if (in_param->bridge_vlan_aware) { + vni_id = zebra_l2_bridge_if_vni_find(br_zif, in_param->vid); + if (vni_id) + found = 1; + } else { + /* loop through all vxlan-interface */ + 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; - if (!in_param->bridge_vlan_aware - || vxl->access_vlan == in_param->vid) { - *p_zl3vni = zl3vni_lookup(vxl->vni); - return NS_WALK_STOP; + if (zif->brslave_info.br_if != in_param->br_if) + continue; + + vni_id = zebra_vxlan_if_access_vlan_vni_find( + zif, in_param->br_if); + if (vni_id) { + found = 1; + break; + } } } - return NS_WALK_CONTINUE; + if (!found) + return NS_WALK_CONTINUE; + + *p_zl3vni = zl3vni_lookup(vni_id); + return NS_WALK_STOP; } /* @@ -1898,7 +2153,6 @@ static struct zebra_l3vni *zl3vni_from_svi(struct interface *ifp, { struct zebra_l3vni *zl3vni = NULL; struct zebra_if *zif = NULL; - struct zebra_l2info_bridge *br = NULL; struct zebra_from_svi_param in_param = {}; struct zebra_l3vni **p_zl3vni; @@ -1913,8 +2167,7 @@ static struct zebra_l3vni *zl3vni_from_svi(struct interface *ifp, /* Determine if bridge is VLAN-aware or not */ zif = br_if->info; assert(zif); - br = &zif->l2info.br; - in_param.bridge_vlan_aware = br->vlan_aware; + in_param.bridge_vlan_aware = IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(zif); if (in_param.bridge_vlan_aware) { struct zebra_l2info_vlan *vl; @@ -2061,7 +2314,7 @@ static int zl3vni_send_del_to_client(struct zebra_l3vni *zl3vni) return zserv_send_message(client, s); } -static void zebra_vxlan_process_l3vni_oper_up(struct zebra_l3vni *zl3vni) +void zebra_vxlan_process_l3vni_oper_up(struct zebra_l3vni *zl3vni) { if (!zl3vni) return; @@ -2070,7 +2323,7 @@ static void zebra_vxlan_process_l3vni_oper_up(struct zebra_l3vni *zl3vni) zl3vni_send_add_to_client(zl3vni); } -static void zebra_vxlan_process_l3vni_oper_down(struct zebra_l3vni *zl3vni) +void zebra_vxlan_process_l3vni_oper_down(struct zebra_l3vni *zl3vni) { if (!zl3vni) return; @@ -2142,6 +2395,7 @@ static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf, vni_t vni, struct route_node *rn; struct interface *ifp; struct zebra_if *zif; + struct zebra_vxlan_vni *vnip; struct zebra_l2info_vxlan *vxl; struct interface *vlan_if; bool found = false; @@ -2161,7 +2415,8 @@ static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf, vni_t vni, continue; vxl = &zif->l2info.vxl; - if (vxl->vni == vni) { + vnip = zebra_vxlan_if_vni_find(zif, vni); + if (vnip) { found = true; break; } @@ -2183,7 +2438,7 @@ static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf, vni_t vni, zevpn = zebra_evpn_add(vni); /* Find bridge interface for the VNI */ - vlan_if = zvni_map_to_svi(vxl->access_vlan, + vlan_if = zvni_map_to_svi(vnip->access_vlan, zif->brslave_info.br_if); if (vlan_if) { zevpn->vrf_id = vlan_if->vrf->vrf_id; @@ -2282,6 +2537,9 @@ void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id, const struct ethaddr *rmac, */ zl3vni_remote_nh_add(zl3vni, vtep_ip, rmac, host_prefix); + /* Add SVD next hop neighbor */ + svd_remote_nh_add(zl3vni, vtep_ip, rmac, host_prefix); + /* * if the remote vtep is a ipv4 mapped ipv6 address convert it to ipv4 * address. Rmac is programmed against the ipv4 vtep because we only @@ -2325,6 +2583,9 @@ void zebra_vxlan_evpn_vrf_route_del(vrf_id_t vrf_id, /* delete the next hop entry */ zl3vni_remote_nh_del(zl3vni, nh, host_prefix); + /* Delete SVD next hop entry */ + svd_remote_nh_del(zl3vni, vtep_ip); + /* delete the rmac entry */ if (zrmac) zl3vni_remote_rmac_del(zl3vni, zrmac, vtep_ip); @@ -2454,22 +2715,29 @@ void zebra_vxlan_print_specific_nh_l3vni(struct vty *vty, vni_t l3vni, if (use_json) json = json_object_new_object(); - zl3vni = zl3vni_lookup(l3vni); - if (!zl3vni) { - if (use_json) - vty_out(vty, "{}\n"); - else - vty_out(vty, "%% L3-VNI %u does not exist\n", l3vni); - return; + /* If vni=0 passed, assume svd lookup */ + if (!l3vni) + n = svd_nh_lookup(ip); + else { + zl3vni = zl3vni_lookup(l3vni); + if (!zl3vni) { + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, "%% L3-VNI %u does not exist\n", + l3vni); + return; + } + + n = zl3vni_nh_lookup(zl3vni, ip); } - n = zl3vni_nh_lookup(zl3vni, ip); if (!n) { if (use_json) vty_out(vty, "{}\n"); else vty_out(vty, - "%% Requested next-hop not present for L3-VNI %u", + "%% Requested next-hop not present for L3-VNI %u\n", l3vni); return; } @@ -2480,26 +2748,14 @@ void zebra_vxlan_print_specific_nh_l3vni(struct vty *vty, vni_t l3vni, vty_json(vty, json); } -void zebra_vxlan_print_nh_l3vni(struct vty *vty, vni_t l3vni, bool use_json) +static void l3vni_print_nh_table(struct hash *nh_table, struct vty *vty, + bool use_json) { uint32_t num_nh; struct nh_walk_ctx wctx; json_object *json = NULL; - struct zebra_l3vni *zl3vni = NULL; - - if (!is_evpn_enabled()) - return; - - zl3vni = zl3vni_lookup(l3vni); - if (!zl3vni) { - if (use_json) - vty_out(vty, "{}\n"); - else - vty_out(vty, "%% L3-VNI %u does not exist\n", l3vni); - return; - } - num_nh = hashcount(zl3vni->nh_table); + num_nh = hashcount(nh_table); if (!num_nh) return; @@ -2515,12 +2771,45 @@ void zebra_vxlan_print_nh_l3vni(struct vty *vty, vni_t l3vni, bool use_json) } else json_object_int_add(json, "numNextHops", num_nh); - hash_iterate(zl3vni->nh_table, zl3vni_print_nh_hash, &wctx); + hash_iterate(nh_table, zl3vni_print_nh_hash, &wctx); if (use_json) vty_json(vty, json); } +void zebra_vxlan_print_nh_l3vni(struct vty *vty, vni_t l3vni, bool use_json) +{ + struct zebra_l3vni *zl3vni = NULL; + + if (!is_evpn_enabled()) { + if (use_json) + vty_out(vty, "{}\n"); + return; + } + + zl3vni = zl3vni_lookup(l3vni); + if (!zl3vni) { + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, "%% L3-VNI %u does not exist\n", l3vni); + return; + } + + l3vni_print_nh_table(zl3vni->nh_table, vty, use_json); +} + +void zebra_vxlan_print_nh_svd(struct vty *vty, bool use_json) +{ + if (!is_evpn_enabled()) { + if (use_json) + vty_out(vty, "{}\n"); + return; + } + + l3vni_print_nh_table(svd_nh_table, vty, use_json); +} + void zebra_vxlan_print_nh_all_l3vni(struct vty *vty, bool use_json) { json_object *json = NULL; @@ -3953,20 +4242,17 @@ stream_failure: /* * Handle remote vtep delete by kernel; re-add the vtep if we have it */ -int zebra_vxlan_check_readd_vtep(struct interface *ifp, +int zebra_vxlan_check_readd_vtep(struct interface *ifp, vni_t vni, struct in_addr vtep_ip) { struct zebra_if *zif; struct zebra_vrf *zvrf = NULL; - struct zebra_l2info_vxlan *vxl; - vni_t vni; struct zebra_evpn *zevpn = NULL; struct zebra_vtep *zvtep = NULL; + struct zebra_vxlan_vni *vnip; zif = ifp->info; assert(zif); - vxl = &zif->l2info.vxl; - vni = vxl->vni; /* If EVPN is not enabled, nothing to do. */ if (!is_evpn_enabled()) @@ -3977,6 +4263,10 @@ int zebra_vxlan_check_readd_vtep(struct interface *ifp, if (!zvrf) return -1; + vnip = zebra_vxlan_if_vni_find(zif, vni); + if (!vnip) + return 0; + /* Locate hash entry; it is expected to exist. */ zevpn = zebra_evpn_lookup(vni); if (!zevpn) @@ -4004,18 +4294,14 @@ int zebra_vxlan_check_readd_vtep(struct interface *ifp, static int zebra_vxlan_check_del_local_mac(struct interface *ifp, struct interface *br_if, struct ethaddr *macaddr, - vlanid_t vid) + vlanid_t vid, vni_t vni) { struct zebra_if *zif; - struct zebra_l2info_vxlan *vxl; - vni_t vni; struct zebra_evpn *zevpn; struct zebra_mac *mac; zif = ifp->info; assert(zif); - vxl = &zif->l2info.vxl; - vni = vxl->vni; /* Check if EVPN is enabled. */ if (!is_evpn_enabled()) @@ -4067,7 +4353,8 @@ static int zebra_vxlan_check_del_local_mac(struct interface *ifp, int zebra_vxlan_dp_network_mac_add(struct interface *ifp, struct interface *br_if, struct ethaddr *macaddr, vlanid_t vid, - uint32_t nhg_id, bool sticky, bool dp_static) + vni_t vni, uint32_t nhg_id, bool sticky, + bool dp_static) { struct zebra_evpn_es *es; struct interface *acc_ifp; @@ -4083,7 +4370,8 @@ int zebra_vxlan_dp_network_mac_add(struct interface *ifp, } /* Get vxlan's vid for netlink message has no it. */ - vid = ((struct zebra_if *)ifp->info)->l2info.vxl.access_vlan; + vid = ((struct zebra_if *)ifp->info) + ->l2info.vxl.vni_info.vni.access_vlan; /* if remote mac delete the local entry */ if (!nhg_id || !zebra_evpn_nhg_is_local_es(nhg_id, &es) @@ -4091,8 +4379,8 @@ int zebra_vxlan_dp_network_mac_add(struct interface *ifp, if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) zlog_debug("dpAdd remote MAC %pEA VID %u", macaddr, vid); - return zebra_vxlan_check_del_local_mac(ifp, br_if, macaddr, - vid); + return zebra_vxlan_check_del_local_mac(ifp, br_if, macaddr, vid, + vni); } /* If local MAC on a down local ES translate the network-mac-add @@ -4113,19 +4401,16 @@ int zebra_vxlan_dp_network_mac_add(struct interface *ifp, */ int zebra_vxlan_dp_network_mac_del(struct interface *ifp, struct interface *br_if, - struct ethaddr *macaddr, vlanid_t vid) + struct ethaddr *macaddr, vlanid_t vid, + vni_t vni) { struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan *vxl = NULL; - vni_t vni; struct zebra_evpn *zevpn = NULL; struct zebra_l3vni *zl3vni = NULL; struct zebra_mac *mac = NULL; zif = ifp->info; assert(zif); - vxl = &zif->l2info.vxl; - vni = vxl->vni; /* Check if EVPN is enabled. */ if (!is_evpn_enabled()) @@ -4804,507 +5089,6 @@ void zebra_vxlan_macvlan_up(struct interface *ifp) } } -/* - * Handle VxLAN interface down - */ -int zebra_vxlan_if_down(struct interface *ifp) -{ - vni_t vni; - struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan *vxl = NULL; - struct zebra_l3vni *zl3vni = NULL; - struct zebra_evpn *zevpn; - - /* Check if EVPN is enabled. */ - if (!is_evpn_enabled()) - return 0; - - zif = ifp->info; - assert(zif); - vxl = &zif->l2info.vxl; - vni = vxl->vni; - - zl3vni = zl3vni_lookup(vni); - if (zl3vni) { - /* process-if-down for l3-vni */ - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Intf %s(%u) L3-VNI %u is DOWN", ifp->name, - ifp->ifindex, vni); - - zebra_vxlan_process_l3vni_oper_down(zl3vni); - } else { - /* process if-down for l2-vni */ - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Intf %s(%u) L2-VNI %u is DOWN", ifp->name, - ifp->ifindex, vni); - - /* Locate hash entry; it is expected to exist. */ - zevpn = zebra_evpn_lookup(vni); - if (!zevpn) { - zlog_debug( - "Failed to locate VNI hash at DOWN, IF %s(%u) VNI %u", - ifp->name, ifp->ifindex, vni); - return -1; - } - - assert(zevpn->vxlan_if == ifp); - - /* remove from l3-vni list */ - zl3vni = zl3vni_from_vrf(zevpn->vrf_id); - if (zl3vni) - listnode_delete(zl3vni->l2vnis, zevpn); - - /* Delete this VNI from BGP. */ - zebra_evpn_send_del_to_client(zevpn); - - /* Free up all neighbors and MACs, if any. */ - zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH); - zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_ALL_MAC); - - /* Free up all remote VTEPs, if any. */ - zebra_evpn_vtep_del_all(zevpn, 1); - } - return 0; -} - -/* - * Handle VxLAN interface up - update BGP if required. - */ -int zebra_vxlan_if_up(struct interface *ifp) -{ - vni_t vni; - struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan *vxl = NULL; - struct zebra_evpn *zevpn = NULL; - struct zebra_l3vni *zl3vni = NULL; - - /* Check if EVPN is enabled. */ - if (!is_evpn_enabled()) - return 0; - - zif = ifp->info; - assert(zif); - vxl = &zif->l2info.vxl; - vni = vxl->vni; - - zl3vni = zl3vni_lookup(vni); - if (zl3vni) { - /* we need to associate with SVI, if any, we can associate with - * svi-if only after association with vxlan-intf is complete - */ - zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni); - zl3vni->mac_vlan_if = zl3vni_map_to_mac_vlan_if(zl3vni); - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Intf %s(%u) L3-VNI %u is UP svi_if %s mac_vlan_if %s" - , ifp->name, ifp->ifindex, vni, - zl3vni->svi_if ? zl3vni->svi_if->name : "NIL", - zl3vni->mac_vlan_if ? - zl3vni->mac_vlan_if->name : "NIL"); - - if (is_l3vni_oper_up(zl3vni)) - zebra_vxlan_process_l3vni_oper_up(zl3vni); - } else { - /* Handle L2-VNI add */ - struct interface *vlan_if = NULL; - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Intf %s(%u) L2-VNI %u is UP", ifp->name, - ifp->ifindex, vni); - - /* Locate hash entry; it is expected to exist. */ - zevpn = zebra_evpn_lookup(vni); - if (!zevpn) { - zlog_debug( - "Failed to locate EVPN hash at UP, IF %s(%u) VNI %u", - ifp->name, ifp->ifindex, vni); - return -1; - } - - assert(zevpn->vxlan_if == ifp); - vlan_if = zvni_map_to_svi(vxl->access_vlan, - zif->brslave_info.br_if); - if (vlan_if) { - zevpn->svi_if = vlan_if; - zevpn->vrf_id = vlan_if->vrf->vrf_id; - zl3vni = zl3vni_from_vrf(vlan_if->vrf->vrf_id); - if (zl3vni) - listnode_add_sort_nodup(zl3vni->l2vnis, zevpn); - } - - /* If part of a bridge, inform BGP about this VNI. */ - /* Also, read and populate local MACs and neighbors. */ - if (zif->brslave_info.br_if) { - zebra_evpn_send_add_to_client(zevpn); - zebra_evpn_read_mac_neigh(zevpn, ifp); - } - } - - return 0; -} - -/* - * Handle VxLAN interface delete. Locate and remove entry in hash table - * and update BGP, if required. - */ -int zebra_vxlan_if_del(struct interface *ifp) -{ - vni_t vni; - struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan *vxl = NULL; - struct zebra_evpn *zevpn = NULL; - struct zebra_l3vni *zl3vni = NULL; - - /* Check if EVPN is enabled. */ - if (!is_evpn_enabled()) - return 0; - - zif = ifp->info; - assert(zif); - vxl = &zif->l2info.vxl; - vni = vxl->vni; - - zl3vni = zl3vni_lookup(vni); - if (zl3vni) { - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Del L3-VNI %u intf %s(%u)", vni, ifp->name, - ifp->ifindex); - - /* process oper-down for l3-vni */ - zebra_vxlan_process_l3vni_oper_down(zl3vni); - - /* remove the association with vxlan_if */ - memset(&zl3vni->local_vtep_ip, 0, sizeof(struct in_addr)); - zl3vni->vxlan_if = NULL; - } else { - - /* process if-del for l2-vni*/ - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Del L2-VNI %u intf %s(%u)", vni, ifp->name, - ifp->ifindex); - - /* Locate hash entry; it is expected to exist. */ - zevpn = zebra_evpn_lookup(vni); - if (!zevpn) { - zlog_debug( - "Failed to locate VNI hash at del, IF %s(%u) VNI %u", - ifp->name, ifp->ifindex, vni); - return 0; - } - - /* remove from l3-vni list */ - zl3vni = zl3vni_from_vrf(zevpn->vrf_id); - if (zl3vni) - listnode_delete(zl3vni->l2vnis, zevpn); - /* Delete VNI from BGP. */ - zebra_evpn_send_del_to_client(zevpn); - - /* Free up all neighbors and MAC, if any. */ - zebra_evpn_neigh_del_all(zevpn, 0, 0, DEL_ALL_NEIGH); - zebra_evpn_mac_del_all(zevpn, 0, 0, DEL_ALL_MAC); - - /* Free up all remote VTEPs, if any. */ - zebra_evpn_vtep_del_all(zevpn, 0); - - /* Delete the hash entry. */ - if (zebra_evpn_vxlan_del(zevpn)) { - flog_err(EC_ZEBRA_VNI_DEL_FAILED, - "Failed to del EVPN hash %p, IF %s(%u) VNI %u", - zevpn, ifp->name, ifp->ifindex, zevpn->vni); - return -1; - } - } - return 0; -} - -/* - * Handle VxLAN interface update - change to tunnel IP, master or VLAN. - */ -int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) -{ - vni_t vni; - struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan *vxl = NULL; - struct zebra_evpn *zevpn = NULL; - struct zebra_l3vni *zl3vni = NULL; - struct interface *vlan_if = NULL; - - /* Check if EVPN is enabled. */ - if (!is_evpn_enabled()) - return 0; - - zif = ifp->info; - assert(zif); - vxl = &zif->l2info.vxl; - vni = vxl->vni; - - zl3vni = zl3vni_lookup(vni); - if (zl3vni) { - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Update L3-VNI %u intf %s(%u) VLAN %u local IP %pI4 master %u chg 0x%x", - vni, ifp->name, ifp->ifindex, vxl->access_vlan, - &vxl->vtep_ip, - zif->brslave_info.bridge_ifindex, chgflags); - - /* Removed from bridge? Cleanup and return */ - if ((chgflags & ZEBRA_VXLIF_MASTER_CHANGE) - && (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) { - zebra_vxlan_process_l3vni_oper_down(zl3vni); - return 0; - } - - if ((chgflags & ZEBRA_VXLIF_MASTER_MAC_CHANGE) - && if_is_operative(ifp) && is_l3vni_oper_up(zl3vni)) { - zebra_vxlan_process_l3vni_oper_down(zl3vni); - zebra_vxlan_process_l3vni_oper_up(zl3vni); - return 0; - } - - /* access-vlan change - process oper down, associate with new - * svi_if and then process oper up again - */ - if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { - if (if_is_operative(ifp)) { - zebra_vxlan_process_l3vni_oper_down(zl3vni); - zl3vni->svi_if = NULL; - zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni); - zl3vni->mac_vlan_if = - zl3vni_map_to_mac_vlan_if(zl3vni); - zl3vni->local_vtep_ip = vxl->vtep_ip; - if (is_l3vni_oper_up(zl3vni)) - zebra_vxlan_process_l3vni_oper_up( - zl3vni); - } - } - - /* - * local-ip change - process oper down, associate with new - * local-ip and then process oper up again - */ - if (chgflags & ZEBRA_VXLIF_LOCAL_IP_CHANGE) { - if (if_is_operative(ifp)) { - zebra_vxlan_process_l3vni_oper_down(zl3vni); - zl3vni->local_vtep_ip = vxl->vtep_ip; - if (is_l3vni_oper_up(zl3vni)) - zebra_vxlan_process_l3vni_oper_up( - zl3vni); - } - } - - /* Update local tunnel IP. */ - zl3vni->local_vtep_ip = vxl->vtep_ip; - - /* if we have a valid new master, process l3-vni oper up */ - if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE) { - if (if_is_operative(ifp) && is_l3vni_oper_up(zl3vni)) - zebra_vxlan_process_l3vni_oper_up(zl3vni); - } - } else { - - /* Update VNI hash. */ - zevpn = zebra_evpn_lookup(vni); - if (!zevpn) { - zlog_debug( - "Failed to find EVPN hash on update, IF %s(%u) VNI %u", - ifp->name, ifp->ifindex, vni); - return -1; - } - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Update L2-VNI %u intf %s(%u) VLAN %u local IP %pI4 master %u chg 0x%x", - vni, ifp->name, ifp->ifindex, vxl->access_vlan, - &vxl->vtep_ip, - zif->brslave_info.bridge_ifindex, chgflags); - - /* Removed from bridge? Cleanup and return */ - if ((chgflags & ZEBRA_VXLIF_MASTER_CHANGE) - && (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) { - /* Delete from client, remove all remote VTEPs */ - /* Also, free up all MACs and neighbors. */ - zevpn->svi_if = NULL; - zebra_evpn_send_del_to_client(zevpn); - zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH); - zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_ALL_MAC); - zebra_evpn_vtep_del_all(zevpn, 1); - return 0; - } - - /* Handle other changes. */ - if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { - /* Remove all existing local neigh and MACs for this VNI - * (including from BGP) - */ - zebra_evpn_neigh_del_all(zevpn, 0, 1, DEL_LOCAL_MAC); - zebra_evpn_mac_del_all(zevpn, 0, 1, DEL_LOCAL_MAC); - } - - if (zevpn->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr || - zevpn->mcast_grp.s_addr != vxl->mcast_grp.s_addr) { - zebra_vxlan_sg_deref(zevpn->local_vtep_ip, - zevpn->mcast_grp); - zebra_vxlan_sg_ref(vxl->vtep_ip, vxl->mcast_grp); - zevpn->local_vtep_ip = vxl->vtep_ip; - zevpn->mcast_grp = vxl->mcast_grp; - /* on local vtep-ip check if ES orig-ip - * needs to be updated - */ - zebra_evpn_es_set_base_evpn(zevpn); - } - zevpn_vxlan_if_set(zevpn, ifp, true /* set */); - vlan_if = zvni_map_to_svi(vxl->access_vlan, - zif->brslave_info.br_if); - if (vlan_if) { - zevpn->svi_if = vlan_if; - zevpn->vrf_id = vlan_if->vrf->vrf_id; - zl3vni = zl3vni_from_vrf(vlan_if->vrf->vrf_id); - if (zl3vni) - listnode_add_sort_nodup(zl3vni->l2vnis, zevpn); - } - - /* Take further actions needed. - * Note that if we are here, there is a change of interest. - */ - /* If down or not mapped to a bridge, we're done. */ - if (!if_is_operative(ifp) || !zif->brslave_info.br_if) - return 0; - - /* Inform BGP, if there is a change of interest. */ - if (chgflags & - (ZEBRA_VXLIF_MASTER_CHANGE | ZEBRA_VXLIF_LOCAL_IP_CHANGE | - ZEBRA_VXLIF_MCAST_GRP_CHANGE | ZEBRA_VXLIF_VLAN_CHANGE)) - zebra_evpn_send_add_to_client(zevpn); - - /* If there is a valid new master or a VLAN mapping change, - * read and populate local MACs and neighbors. - * Also, reinstall any remote MACs and neighbors - * for this VNI (based on new VLAN). - */ - if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE) - zebra_evpn_read_mac_neigh(zevpn, ifp); - else if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { - struct mac_walk_ctx m_wctx; - struct neigh_walk_ctx n_wctx; - - zebra_evpn_read_mac_neigh(zevpn, ifp); - - memset(&m_wctx, 0, sizeof(m_wctx)); - m_wctx.zevpn = zevpn; - hash_iterate(zevpn->mac_table, - zebra_evpn_install_mac_hash, &m_wctx); - - memset(&n_wctx, 0, sizeof(n_wctx)); - n_wctx.zevpn = zevpn; - hash_iterate(zevpn->neigh_table, - zebra_evpn_install_neigh_hash, &n_wctx); - } - } - - return 0; -} - -/* - * Handle VxLAN interface add. - */ -int zebra_vxlan_if_add(struct interface *ifp) -{ - vni_t vni; - struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan *vxl = NULL; - struct zebra_evpn *zevpn = NULL; - struct zebra_l3vni *zl3vni = NULL; - - /* Check if EVPN is enabled. */ - if (!is_evpn_enabled()) - return 0; - - zif = ifp->info; - assert(zif); - vxl = &zif->l2info.vxl; - vni = vxl->vni; - - zl3vni = zl3vni_lookup(vni); - if (zl3vni) { - - /* process if-add for l3-vni*/ - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Add L3-VNI %u intf %s(%u) VLAN %u local IP %pI4 master %u", - vni, ifp->name, ifp->ifindex, vxl->access_vlan, - &vxl->vtep_ip, - zif->brslave_info.bridge_ifindex); - - /* associate with vxlan_if */ - zl3vni->local_vtep_ip = vxl->vtep_ip; - zl3vni->vxlan_if = ifp; - - /* Associate with SVI, if any. We can associate with svi-if only - * after association with vxlan_if is complete */ - zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni); - - zl3vni->mac_vlan_if = zl3vni_map_to_mac_vlan_if(zl3vni); - - if (is_l3vni_oper_up(zl3vni)) - zebra_vxlan_process_l3vni_oper_up(zl3vni); - } else { - - /* process if-add for l2-vni */ - struct interface *vlan_if = NULL; - - /* Create or update EVPN hash. */ - zevpn = zebra_evpn_lookup(vni); - if (!zevpn) - zevpn = zebra_evpn_add(vni); - - if (zevpn->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr || - zevpn->mcast_grp.s_addr != vxl->mcast_grp.s_addr) { - zebra_vxlan_sg_deref(zevpn->local_vtep_ip, - zevpn->mcast_grp); - zebra_vxlan_sg_ref(vxl->vtep_ip, vxl->mcast_grp); - zevpn->local_vtep_ip = vxl->vtep_ip; - zevpn->mcast_grp = vxl->mcast_grp; - /* on local vtep-ip check if ES orig-ip - * needs to be updated - */ - zebra_evpn_es_set_base_evpn(zevpn); - } - zevpn_vxlan_if_set(zevpn, ifp, true /* set */); - vlan_if = zvni_map_to_svi(vxl->access_vlan, - zif->brslave_info.br_if); - if (vlan_if) { - zevpn->svi_if = vlan_if; - zevpn->vrf_id = vlan_if->vrf->vrf_id; - zl3vni = zl3vni_from_vrf(vlan_if->vrf->vrf_id); - if (zl3vni) - listnode_add_sort_nodup(zl3vni->l2vnis, zevpn); - } - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Add L2-VNI %u VRF %s intf %s(%u) VLAN %u local IP %pI4 mcast_grp %pI4 master %u", - vni, - vlan_if ? vlan_if->vrf->name : VRF_DEFAULT_NAME, - ifp->name, ifp->ifindex, vxl->access_vlan, - &vxl->vtep_ip, &vxl->mcast_grp, - zif->brslave_info.bridge_ifindex); - - /* If down or not mapped to a bridge, we're done. */ - if (!if_is_operative(ifp) || !zif->brslave_info.br_if) - return 0; - - /* Inform BGP */ - zebra_evpn_send_add_to_client(zevpn); - - /* Read and populate local MACs and neighbors */ - zebra_evpn_read_mac_neigh(zevpn, ifp); - } - - return 0; -} - int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni, char *err, int err_str_sz, int filter, int add) @@ -5557,8 +5341,8 @@ void zebra_vxlan_advertise_svi_macip(ZAPI_HANDLER_ARGS) } else { struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan zl2_info; struct interface *vlan_if = NULL; + struct zebra_vxlan_vni *zl2_info_vni; int old_advertise; zevpn = zebra_evpn_lookup(vni); @@ -5592,8 +5376,11 @@ void zebra_vxlan_advertise_svi_macip(ZAPI_HANDLER_ARGS) if (!if_is_operative(ifp) || !zif->brslave_info.br_if) return; - zl2_info = zif->l2info.vxl; - vlan_if = zvni_map_to_svi(zl2_info.access_vlan, + zl2_info_vni = zebra_vxlan_if_vni_find(zif, vni); + if (!zl2_info_vni) + return; + + vlan_if = zvni_map_to_svi(zl2_info_vni->access_vlan, zif->brslave_info.br_if); if (!vlan_if) return; @@ -5623,8 +5410,8 @@ void zebra_vxlan_advertise_subnet(ZAPI_HANDLER_ARGS) struct zebra_evpn *zevpn = NULL; struct interface *ifp = NULL; struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan zl2_info; struct interface *vlan_if = NULL; + struct zebra_vxlan_vni *zl2_info_vni = NULL; if (!EVPN_ENABLED(zvrf)) { zlog_debug("EVPN GW-MACIP Adv for non-EVPN VRF %u", @@ -5661,10 +5448,12 @@ void zebra_vxlan_advertise_subnet(ZAPI_HANDLER_ARGS) if (!if_is_operative(ifp) || !zif->brslave_info.br_if) return; - zl2_info = zif->l2info.vxl; + zl2_info_vni = zebra_vxlan_if_vni_find(zif, vni); + if (!zl2_info_vni) + return; - vlan_if = - zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if); + vlan_if = zvni_map_to_svi(zl2_info_vni->access_vlan, + zif->brslave_info.br_if); if (!vlan_if) return; @@ -5723,9 +5512,9 @@ void zebra_vxlan_advertise_gw_macip(ZAPI_HANDLER_ARGS) } else { struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan zl2_info; struct interface *vlan_if = NULL; struct interface *vrr_if = NULL; + struct zebra_vxlan_vni *zl2_info_vni = NULL; int old_advertise; zevpn = zebra_evpn_lookup(vni); @@ -5755,9 +5544,11 @@ void zebra_vxlan_advertise_gw_macip(ZAPI_HANDLER_ARGS) if (!if_is_operative(ifp) || !zif->brslave_info.br_if) return; - zl2_info = zif->l2info.vxl; + zl2_info_vni = zebra_vxlan_if_vni_find(zif, vni); + if (!zl2_info_vni) + return; - vlan_if = zvni_map_to_svi(zl2_info.access_vlan, + vlan_if = zvni_map_to_svi(zl2_info_vni->access_vlan, zif->brslave_info.br_if); if (!vlan_if) return; @@ -5934,6 +5725,9 @@ void zebra_vxlan_init(void) { zrouter.l3vni_table = hash_create(l3vni_hash_keymake, l3vni_hash_cmp, "Zebra VRF L3 VNI table"); + + svd_nh_table = zebra_neigh_db_create("Zebra SVD next-hop table"); + zrouter.evpn_vrf = NULL; zebra_evpn_mh_init(); } @@ -5957,6 +5751,42 @@ ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id) return zl3vni->svi_if->ifindex; } +/* get the l3vni vxlan ifindex */ +ifindex_t get_l3vni_vxlan_ifindex(vrf_id_t vrf_id) +{ + struct zebra_l3vni *zl3vni = NULL; + + zl3vni = zl3vni_from_vrf(vrf_id); + if (!zl3vni || !is_l3vni_oper_up(zl3vni)) + return 0; + + return zl3vni->vxlan_if->ifindex; +} + +/* get the l3vni vni */ +vni_t get_l3vni_vni(vrf_id_t vrf_id) +{ + struct zebra_l3vni *zl3vni = NULL; + + zl3vni = zl3vni_from_vrf(vrf_id); + if (!zl3vni || !is_l3vni_oper_up(zl3vni)) + return 0; + + return zl3vni->vni; +} + +/* is the vrf l3vni SVD backed? */ +bool is_vrf_l3vni_svd_backed(vrf_id_t vrf_id) +{ + struct zebra_l3vni *zl3vni = NULL; + + zl3vni = zl3vni_from_vrf(vrf_id); + if (!zl3vni || !is_l3vni_oper_up(zl3vni)) + return false; + + return IS_ZL3VNI_SVD_BACKED(zl3vni); +} + /************************** vxlan SG cache management ************************/ /* Inform PIM about the mcast group */ static int zebra_vxlan_sg_send(struct zebra_vrf *zvrf, @@ -6140,8 +5970,8 @@ static struct zebra_vxlan_sg *zebra_vxlan_sg_do_ref(struct zebra_vrf *zvrf, return vxlan_sg; } -static void zebra_vxlan_sg_deref(struct in_addr local_vtep_ip, - struct in_addr mcast_grp) +void zebra_vxlan_sg_deref(struct in_addr local_vtep_ip, + struct in_addr mcast_grp) { struct zebra_vrf *zvrf; @@ -6156,8 +5986,7 @@ static void zebra_vxlan_sg_deref(struct in_addr local_vtep_ip, zebra_vxlan_sg_do_deref(zvrf, local_vtep_ip, mcast_grp); } -static void zebra_vxlan_sg_ref(struct in_addr local_vtep_ip, - struct in_addr mcast_grp) +void zebra_vxlan_sg_ref(struct in_addr local_vtep_ip, struct in_addr mcast_grp) { struct zebra_vrf *zvrf; diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h index 16c5bc0a1a..b33c215c73 100644 --- a/zebra/zebra_vxlan.h +++ b/zebra/zebra_vxlan.h @@ -36,6 +36,7 @@ #include "zebra/zebra_vrf.h" #include "zebra/zserv.h" #include "zebra/zebra_dplane.h" +#include "zebra/interface.h" #ifdef __cplusplus extern "C" { @@ -62,6 +63,10 @@ is_vxlan_flooding_head_end(void) #define ZEBRA_VXLIF_MCAST_GRP_CHANGE (1 << 3) #define ZEBRA_VXLIF_MASTER_MAC_CHANGE (1 << 4) +#define ZEBRA_VXLIF_VNI_UPDATE(__flags) \ + ((__flags) & (ZEBRA_VXLIF_VLAN_CHANGE | ZEBRA_VXLIF_MCAST_GRP_CHANGE)) +#define ZEBRA_VXLIF_UPDATE(__flags) \ + ((__flags) & (ZEBRA_VXLIF_LOCAL_IP_CHANGE | ZEBRA_VXLIF_MASTER_CHANGE)) #define VNI_STR_LEN 32 @@ -84,6 +89,9 @@ extern void zebra_vxlan_sg_replay(ZAPI_HANDLER_ARGS); extern int is_l3vni_for_prefix_routes_only(vni_t vni); extern ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id); +extern ifindex_t get_l3vni_vxlan_ifindex(vrf_id_t vrf_id); +extern vni_t get_l3vni_vni(vrf_id_t vrf_id); +extern bool is_vrf_l3vni_svd_backed(vrf_id_t vrf_id); extern int zebra_vxlan_vrf_delete(struct zebra_vrf *zvrf); extern int zebra_vxlan_vrf_enable(struct zebra_vrf *zvrf); extern int zebra_vxlan_vrf_disable(struct zebra_vrf *zvrf); @@ -153,6 +161,7 @@ extern void zebra_vxlan_print_rmacs_l3vni(struct vty *vty, vni_t vni, extern void zebra_vxlan_print_rmacs_all_l3vni(struct vty *vty, bool use_json); extern void zebra_vxlan_print_nh_l3vni(struct vty *vty, vni_t vni, bool use_json); +extern void zebra_vxlan_print_nh_svd(struct vty *vty, bool use_json); extern void zebra_vxlan_print_nh_all_l3vni(struct vty *vty, bool use_json); extern void zebra_vxlan_print_l3vni(struct vty *vty, vni_t vni, bool use_json); extern void zebra_vxlan_print_vrf_vni(struct vty *vty, struct zebra_vrf *zvrf, @@ -177,12 +186,13 @@ extern int zebra_vxlan_local_mac_add_update(struct interface *ifp, extern int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if, struct ethaddr *mac, vlanid_t vid); -extern int zebra_vxlan_check_readd_vtep(struct interface *ifp, +extern int zebra_vxlan_check_readd_vtep(struct interface *ifp, vni_t vni, struct in_addr vtep_ip); extern int zebra_vxlan_if_up(struct interface *ifp); extern int zebra_vxlan_if_down(struct interface *ifp); extern int zebra_vxlan_if_add(struct interface *ifp); -extern int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags); +extern int zebra_vxlan_if_update(struct interface *ifp, + struct zebra_vxlan_if_update_ctx *ctx); extern int zebra_vxlan_if_del(struct interface *ifp); extern int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni, char *err, int err_str_sz, @@ -218,12 +228,12 @@ extern int vni_list_cmp(void *p1, void *p2); extern int zebra_vxlan_dp_network_mac_add(struct interface *ifp, struct interface *br_if, struct ethaddr *macaddr, vlanid_t vid, - uint32_t nhg_id, bool sticky, - bool dp_static); + vni_t vni, uint32_t nhg_id, + bool sticky, bool dp_static); extern int zebra_vxlan_dp_network_mac_del(struct interface *ifp, struct interface *br_if, - struct ethaddr *macaddr, - vlanid_t vid); + struct ethaddr *macaddr, vlanid_t vid, + vni_t vni); extern void zebra_vxlan_set_accept_bgp_seq(bool set); extern bool zebra_vxlan_get_accept_bgp_seq(void); diff --git a/zebra/zebra_vxlan_if.c b/zebra/zebra_vxlan_if.c new file mode 100644 index 0000000000..08e07b60a2 --- /dev/null +++ b/zebra/zebra_vxlan_if.c @@ -0,0 +1,1162 @@ +/* + * Zebra EVPN for VxLAN interface handling + * + * Copyright (C) 2021 Cumulus Networks, Inc. + * Vivek Venkatraman, Stephen Worley, Sharath Ramamurthy + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <zebra.h> + +#include "hash.h" +#include "if.h" +#include "jhash.h" +#include "linklist.h" +#include "log.h" +#include "memory.h" +#include "prefix.h" +#include "stream.h" +#include "table.h" +#include "vlan.h" +#include "vxlan.h" +#ifdef GNU_LINUX +#include <linux/neighbour.h> +#endif + +#include "zebra/zebra_router.h" +#include "zebra/debug.h" +#include "zebra/interface.h" +#include "zebra/rib.h" +#include "zebra/rt.h" +#include "zebra/rt_netlink.h" +#include "zebra/zebra_errors.h" +#include "zebra/zebra_l2.h" +#include "zebra/zebra_ns.h" +#include "zebra/zebra_vrf.h" +#include "zebra/zebra_vxlan.h" +#include "zebra/zebra_vxlan_if.h" +#include "zebra/zebra_evpn.h" +#include "zebra/zebra_evpn_mac.h" +#include "zebra/zebra_evpn_neigh.h" +#include "zebra/zebra_vxlan_private.h" +#include "zebra/zebra_evpn_mh.h" +#include "zebra/zebra_evpn_vxlan.h" +#include "zebra/zebra_router.h" + +static unsigned int zebra_vxlan_vni_hash_keymake(const void *p) +{ + const struct zebra_vxlan_vni *vni; + + vni = (const struct zebra_vxlan_vni *)p; + return jhash_1word(vni->vni, 0); +} + +static bool zebra_vxlan_vni_hash_cmp(const void *p1, const void *p2) +{ + const struct zebra_vxlan_vni *vni1; + const struct zebra_vxlan_vni *vni2; + + vni1 = (const struct zebra_vxlan_vni *)p1; + vni2 = (const struct zebra_vxlan_vni *)p2; + + return (vni1->vni == vni2->vni); +} + +static int zebra_vxlan_if_vni_walk_callback(struct hash_bucket *bucket, + void *ctxt) +{ + int ret; + struct zebra_vxlan_vni *vni; + struct zebra_vxlan_if_ctx *ctx; + + vni = (struct zebra_vxlan_vni *)bucket->data; + ctx = (struct zebra_vxlan_if_ctx *)ctxt; + + ret = ctx->func(ctx->zif, vni, ctx->arg); + return ret; +} + +static void zebra_vxlan_if_vni_iterate_callback(struct hash_bucket *bucket, + void *ctxt) +{ + struct zebra_vxlan_vni *vni; + struct zebra_vxlan_if_ctx *ctx; + + vni = (struct zebra_vxlan_vni *)bucket->data; + ctx = (struct zebra_vxlan_if_ctx *)ctxt; + + ctx->func(ctx->zif, vni, ctx->arg); +} + +static int zebra_vxlan_if_del_vni(struct interface *ifp, + struct zebra_vxlan_vni *vnip) +{ + vni_t vni; + struct zebra_if *zif; + struct zebra_evpn *zevpn; + struct zebra_l3vni *zl3vni; + struct interface *br_if; + + /* Check if EVPN is enabled. */ + if (!is_evpn_enabled()) + return 0; + + zif = ifp->info; + assert(zif); + vni = vnip->vni; + + zl3vni = zl3vni_lookup(vni); + if (zl3vni) { + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Del L3-VNI %u intf %s(%u)", vni, ifp->name, + ifp->ifindex); + + /* process oper-down for l3-vni */ + zebra_vxlan_process_l3vni_oper_down(zl3vni); + + /* remove the association with vxlan_if */ + memset(&zl3vni->local_vtep_ip, 0, sizeof(struct in_addr)); + zl3vni->vxlan_if = NULL; + zl3vni->vid = 0; + br_if = zif->brslave_info.br_if; + zl3vni_bridge_if_set(zl3vni, br_if, false /* unset */); + } else { + + /* process if-del for l2-vni*/ + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Del L2-VNI %u intf %s(%u)", vni, ifp->name, + ifp->ifindex); + + /* Locate hash entry; it is expected to exist. */ + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { + zlog_debug( + "Failed to locate VNI hash at del, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return 0; + } + + /* remove from l3-vni list */ + zl3vni = zl3vni_from_vrf(zevpn->vrf_id); + if (zl3vni) + listnode_delete(zl3vni->l2vnis, zevpn); + /* Delete VNI from BGP. */ + zebra_evpn_send_del_to_client(zevpn); + + /* Free up all neighbors and MAC, if any. */ + zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH); + zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_ALL_MAC); + + /* Free up all remote VTEPs, if any. */ + zebra_evpn_vtep_del_all(zevpn, 1); + + /* Delete the hash entry. */ + if (zebra_evpn_vxlan_del(zevpn)) { + flog_err(EC_ZEBRA_VNI_DEL_FAILED, + "Failed to del EVPN hash %p, IF %s(%u) VNI %u", + zevpn, ifp->name, ifp->ifindex, zevpn->vni); + return -1; + } + } + return 0; +} + +static int zebra_vxlan_if_update_vni(struct interface *ifp, + struct zebra_vxlan_vni *vnip, + struct zebra_vxlan_if_update_ctx *ctx) +{ + vni_t vni; + uint16_t chgflags; + vlanid_t access_vlan; + struct zebra_if *zif; + struct zebra_l2info_vxlan *vxl; + struct zebra_evpn *zevpn; + struct zebra_l3vni *zl3vni; + struct interface *vlan_if; + struct interface *br_if; + + /* Check if EVPN is enabled. */ + if (!is_evpn_enabled()) + return 0; + + zif = ifp->info; + assert(zif); + vxl = &zif->l2info.vxl; + vni = vnip->vni; + chgflags = ctx->chgflags; + + zl3vni = zl3vni_lookup(vni); + if (zl3vni) { + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Update L3-VNI %u intf %s(%u) VLAN %u local IP %pI4 master %u chg 0x%x", + vni, ifp->name, ifp->ifindex, vnip->access_vlan, + &vxl->vtep_ip, zif->brslave_info.bridge_ifindex, + chgflags); + + /* Removed from bridge? Cleanup and return */ + if ((chgflags & ZEBRA_VXLIF_MASTER_CHANGE) && + (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) { + zebra_vxlan_process_l3vni_oper_down(zl3vni); + return 0; + } + + if ((chgflags & ZEBRA_VXLIF_MASTER_MAC_CHANGE) && + if_is_operative(ifp) && is_l3vni_oper_up(zl3vni)) { + zebra_vxlan_process_l3vni_oper_down(zl3vni); + zebra_vxlan_process_l3vni_oper_up(zl3vni); + return 0; + } + + /* access-vlan change - process oper down, associate with new + * svi_if and then process oper up again + */ + if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { + if (if_is_operative(ifp)) { + zebra_vxlan_process_l3vni_oper_down(zl3vni); + zl3vni->svi_if = NULL; + zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni); + zl3vni->mac_vlan_if = + zl3vni_map_to_mac_vlan_if(zl3vni); + zl3vni->local_vtep_ip = vxl->vtep_ip; + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up( + zl3vni); + } + } + + /* + * local-ip change - process oper down, associate with new + * local-ip and then process oper up again + */ + if (chgflags & ZEBRA_VXLIF_LOCAL_IP_CHANGE) { + if (if_is_operative(ifp)) { + zebra_vxlan_process_l3vni_oper_down(zl3vni); + zl3vni->local_vtep_ip = vxl->vtep_ip; + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up( + zl3vni); + } + } + + /* Update local tunnel IP. */ + zl3vni->local_vtep_ip = vxl->vtep_ip; + + zl3vni->vid = (zl3vni->vid != vnip->access_vlan) + ? vnip->access_vlan + : zl3vni->vid; + br_if = zif->brslave_info.br_if; + zl3vni_bridge_if_set(zl3vni, br_if, true /* set */); + + /* if we have a valid new master, process l3-vni oper up */ + if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE) { + if (if_is_operative(ifp) && is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up(zl3vni); + } + } else { + + /* Update VNI hash. */ + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { + zlog_debug( + "Failed to find EVPN hash on update, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return -1; + } + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Update L2-VNI %u intf %s(%u) VLAN %u local IP %pI4 master %u chg 0x%x", + vni, ifp->name, ifp->ifindex, vnip->access_vlan, + &vxl->vtep_ip, zif->brslave_info.bridge_ifindex, + chgflags); + + /* Removed from bridge? Cleanup and return */ + if ((chgflags & ZEBRA_VXLIF_MASTER_CHANGE) && + (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) { + /* Delete from client, remove all remote VTEPs */ + /* Also, free up all MACs and neighbors. */ + zevpn->svi_if = NULL; + zebra_evpn_send_del_to_client(zevpn); + zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH); + zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_ALL_MAC); + zebra_evpn_vtep_del_all(zevpn, 1); + return 0; + } + + /* Handle other changes. */ + if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { + /* Remove all existing local neigh and MACs for this VNI + * (including from BGP) + */ + access_vlan = vnip->access_vlan; + vnip->access_vlan = ctx->old_vni.access_vlan; + zebra_evpn_neigh_del_all(zevpn, 0, 1, DEL_LOCAL_MAC); + zebra_evpn_mac_del_all(zevpn, 0, 1, DEL_LOCAL_MAC); + zebra_evpn_rem_mac_uninstall_all(zevpn); + vnip->access_vlan = access_vlan; + } + + if (zevpn->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr || + zevpn->mcast_grp.s_addr != vnip->mcast_grp.s_addr) { + zebra_vxlan_sg_deref(zevpn->local_vtep_ip, + zevpn->mcast_grp); + zebra_vxlan_sg_ref(vxl->vtep_ip, vnip->mcast_grp); + zevpn->local_vtep_ip = vxl->vtep_ip; + zevpn->mcast_grp = vnip->mcast_grp; + /* on local vtep-ip check if ES orig-ip + * needs to be updated + */ + zebra_evpn_es_set_base_evpn(zevpn); + } + zevpn_vxlan_if_set(zevpn, ifp, true /* set */); + zevpn->vid = (zevpn->vid != vnip->access_vlan) + ? vnip->access_vlan + : zevpn->vid; + br_if = zif->brslave_info.br_if; + zevpn_bridge_if_set(zevpn, br_if, true /* set */); + + vlan_if = zvni_map_to_svi(vnip->access_vlan, br_if); + if (vlan_if) + zevpn->svi_if = vlan_if; + + /* Take further actions needed. + * Note that if we are here, there is a change of interest. + */ + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + return 0; + + /* Inform BGP, if there is a change of interest. */ + if (chgflags & + (ZEBRA_VXLIF_MASTER_CHANGE | ZEBRA_VXLIF_LOCAL_IP_CHANGE | + ZEBRA_VXLIF_MCAST_GRP_CHANGE | ZEBRA_VXLIF_VLAN_CHANGE)) + zebra_evpn_send_add_to_client(zevpn); + + /* If there is a valid new master or a VLAN mapping change, + * read and populate local MACs and neighbors. + * Also, reinstall any remote MACs and neighbors + * for this VNI (based on new VLAN). + */ + if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE) + zebra_evpn_read_mac_neigh(zevpn, ifp); + else if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { + struct neigh_walk_ctx n_wctx; + + zebra_evpn_read_mac_neigh(zevpn, ifp); + + zebra_evpn_rem_mac_install_all(zevpn); + + memset(&n_wctx, 0, sizeof(n_wctx)); + n_wctx.zevpn = zevpn; + hash_iterate(zevpn->neigh_table, + zebra_evpn_install_neigh_hash, &n_wctx); + } + } + + return 0; +} + +static int zebra_vxlan_if_add_vni(struct interface *ifp, + struct zebra_vxlan_vni *vnip) +{ + vni_t vni; + struct zebra_if *zif; + struct zebra_l2info_vxlan *vxl; + struct zebra_evpn *zevpn; + struct zebra_l3vni *zl3vni; + struct interface *br_if; + + /* Check if EVPN is enabled. */ + if (!is_evpn_enabled()) + return 0; + + zif = ifp->info; + assert(zif); + vxl = &zif->l2info.vxl; + vni = vnip->vni; + + zl3vni = zl3vni_lookup(vni); + if (zl3vni) { + + /* process if-add for l3-vni*/ + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Add L3-VNI %u intf %s(%u) VLAN %u local IP %pI4 master %u", + vni, ifp->name, ifp->ifindex, vnip->access_vlan, + &vxl->vtep_ip, + zif->brslave_info.bridge_ifindex); + + /* associate with vxlan_if */ + zl3vni->local_vtep_ip = vxl->vtep_ip; + zl3vni->vxlan_if = ifp; + + /* + * Associate with SVI, if any. We can associate with svi-if only + * after association with vxlan_if is complete + */ + zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni); + + zl3vni->mac_vlan_if = zl3vni_map_to_mac_vlan_if(zl3vni); + + zl3vni->vid = vnip->access_vlan; + br_if = zif->brslave_info.br_if; + zl3vni_bridge_if_set(zl3vni, br_if, true /* set */); + + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up(zl3vni); + } else { + + /* process if-add for l2-vni */ + struct interface *vlan_if = NULL; + + /* Create or update EVPN hash. */ + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) + zevpn = zebra_evpn_add(vni); + + if (zevpn->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr || + zevpn->mcast_grp.s_addr != vnip->mcast_grp.s_addr) { + zebra_vxlan_sg_deref(zevpn->local_vtep_ip, + zevpn->mcast_grp); + zebra_vxlan_sg_ref(vxl->vtep_ip, vnip->mcast_grp); + zevpn->local_vtep_ip = vxl->vtep_ip; + zevpn->mcast_grp = vnip->mcast_grp; + /* on local vtep-ip check if ES orig-ip + * needs to be updated + */ + zebra_evpn_es_set_base_evpn(zevpn); + } + zevpn_vxlan_if_set(zevpn, ifp, true /* set */); + br_if = zif->brslave_info.br_if; + zevpn_bridge_if_set(zevpn, br_if, true /* set */); + vlan_if = zvni_map_to_svi(vnip->access_vlan, br_if); + if (vlan_if) { + zevpn->vid = vnip->access_vlan; + zevpn->svi_if = vlan_if; + zevpn->vrf_id = vlan_if->vrf->vrf_id; + zl3vni = zl3vni_from_vrf(vlan_if->vrf->vrf_id); + if (zl3vni) + listnode_add_sort_nodup(zl3vni->l2vnis, zevpn); + } + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Add L2-VNI %u VRF %s intf %s(%u) VLAN %u local IP %pI4 mcast_grp %pI4 master %u", + vni, + vlan_if ? vlan_if->vrf->name : VRF_DEFAULT_NAME, + ifp->name, ifp->ifindex, vnip->access_vlan, + &vxl->vtep_ip, &vnip->mcast_grp, + zif->brslave_info.bridge_ifindex); + + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + return 0; + + /* Inform BGP */ + zebra_evpn_send_add_to_client(zevpn); + + /* Read and populate local MACs and neighbors */ + zebra_evpn_read_mac_neigh(zevpn, ifp); + } + + return 0; +} + +static void zebra_vxlan_if_vni_entry_del(struct zebra_if *zif, + struct zebra_vxlan_vni *vni) +{ + if (vni) { + zebra_evpn_vl_vxl_deref(vni->access_vlan, vni->vni, zif); + zebra_vxlan_if_del_vni(zif->ifp, vni); + } +} + +static int zebra_vxlan_if_vni_entry_add(struct zebra_if *zif, + struct zebra_vxlan_vni *vni) +{ + zebra_evpn_vl_vxl_ref(vni->access_vlan, vni->vni, zif); + return zebra_vxlan_if_add_vni(zif->ifp, vni); +} + +static int zebra_vxlan_if_add_update_vni(struct zebra_if *zif, + struct zebra_vxlan_vni *vni, + void *ctxt) +{ + struct zebra_vxlan_vni vni_tmp; + struct zebra_vxlan_if_update_ctx *ctx; + struct zebra_vxlan_vni *old_vni = NULL; + + ctx = (struct zebra_vxlan_if_update_ctx *)ctxt; + memcpy(&vni_tmp, vni, sizeof(*vni)); + + if ((hashcount(ctx->old_vni_table) == 0) || + !(old_vni = hash_release(ctx->old_vni_table, &vni_tmp))) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("vxlan %s adding vni(%d, %d)", + zif->ifp->name, vni->vni, vni->access_vlan); + + zebra_vxlan_if_vni_entry_add(zif, &vni_tmp); + return 0; + } + + ctx->old_vni = *old_vni; + ctx->chgflags = ZEBRA_VXLIF_VLAN_CHANGE; + + /* copy mcast group from old_vni as thats not being changed here */ + vni->mcast_grp = old_vni->mcast_grp; + + if (old_vni->access_vlan != vni->access_vlan) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "vxlan %s updating vni(%d, %d) -> vni(%d, %d)", + zif->ifp->name, old_vni->vni, + old_vni->access_vlan, vni->vni, + vni->access_vlan); + + zebra_evpn_vl_vxl_deref(old_vni->access_vlan, old_vni->vni, + zif); + zebra_evpn_vl_vxl_ref(vni->access_vlan, vni->vni, zif); + zebra_vxlan_if_update_vni(zif->ifp, vni, ctx); + zebra_vxlan_vni_free(old_vni); + } + + return 0; +} + +static int zebra_vxlan_if_vni_entry_update_callback(struct zebra_if *zif, + struct zebra_vxlan_vni *vni, + void *ctxt) +{ + struct zebra_vxlan_if_update_ctx *ctx; + + ctx = (struct zebra_vxlan_if_update_ctx *)ctxt; + return zebra_vxlan_if_update_vni(zif->ifp, vni, ctx); +} + +static int zebra_vxlan_if_vni_entry_del_callback(struct zebra_if *zif, + struct zebra_vxlan_vni *vni, + void *ctxt) +{ + zebra_vxlan_if_vni_entry_del(zif, vni); + return 0; +} + +static int zebra_vxlan_if_vni_entry_down_callback(struct zebra_if *zif, + struct zebra_vxlan_vni *vni, + void *ctxt) +{ + return zebra_vxlan_if_vni_down(zif->ifp, vni); +} + +static int zebra_vxlan_if_vni_entry_up_callback(struct zebra_if *zif, + struct zebra_vxlan_vni *vni, + void *ctxt) +{ + return zebra_vxlan_if_vni_up(zif->ifp, vni); +} + +static void zebra_vxlan_if_vni_clean(struct hash_bucket *bucket, void *arg) +{ + struct zebra_if *zif; + struct zebra_vxlan_vni *vni; + + zif = (struct zebra_if *)arg; + vni = (struct zebra_vxlan_vni *)bucket->data; + zebra_vxlan_if_vni_entry_del(zif, vni); +} + +void zebra_vxlan_vni_free(void *arg) +{ + struct zebra_vxlan_vni *vni; + + vni = (struct zebra_vxlan_vni *)arg; + + XFREE(MTYPE_TMP, vni); +} + +void *zebra_vxlan_vni_alloc(void *p) +{ + struct zebra_vxlan_vni *vni; + const struct zebra_vxlan_vni *vnip; + + vnip = (const struct zebra_vxlan_vni *)p; + vni = XCALLOC(MTYPE_TMP, sizeof(*vni)); + vni->vni = vnip->vni; + vni->access_vlan = vnip->access_vlan; + vni->mcast_grp = vnip->mcast_grp; + + return (void *)vni; +} + +struct hash *zebra_vxlan_vni_table_create(void) +{ + return hash_create(zebra_vxlan_vni_hash_keymake, + zebra_vxlan_vni_hash_cmp, "Zebra Vxlan VNI Table"); +} + +void zebra_vxlan_vni_table_destroy(struct hash *vni_table) +{ + if (vni_table) { + hash_clean(vni_table, zebra_vxlan_vni_free); + hash_free(vni_table); + } +} + +int zebra_vxlan_if_vni_table_destroy(struct zebra_if *zif) +{ + struct zebra_vxlan_vni_info *vni_info; + + vni_info = VNI_INFO_FROM_ZEBRA_IF(zif); + if (vni_info->vni_table) { + zebra_vxlan_if_vni_iterate( + zif, zebra_vxlan_if_vni_entry_del_callback, NULL); + zebra_vxlan_vni_table_destroy(vni_info->vni_table); + vni_info->vni_table = NULL; + } + return 0; +} + +int zebra_vxlan_if_vni_table_create(struct zebra_if *zif) +{ + struct zebra_vxlan_vni_info *vni_info; + + if (!IS_ZEBRA_VXLAN_IF_SVD(zif)) + return 0; + + vni_info = VNI_INFO_FROM_ZEBRA_IF(zif); + vni_info->vni_table = zebra_vxlan_vni_table_create(); + if (!vni_info->vni_table) + return -ENOMEM; + + return 0; +} + +struct zebra_vxlan_vni *zebra_vxlan_if_vni_find(const struct zebra_if *zif, + vni_t vni) +{ + struct zebra_vxlan_vni *vnip = NULL; + const struct zebra_vxlan_vni_info *vni_info; + struct zebra_vxlan_vni vni_tmp; + + vni_info = VNI_INFO_FROM_ZEBRA_IF(zif); + if (IS_ZEBRA_VXLAN_IF_VNI(zif)) { + vnip = (struct zebra_vxlan_vni *)&vni_info->vni; + assert(vnip); + if (vni && (vnip->vni != vni)) + vnip = NULL; + + return vnip; + } + + /* For SVD, the VNI value is a required parameter. */ + assert(vni); + + memset(&vni_tmp, 0, sizeof(vni_tmp)); + vni_tmp.vni = vni; + vnip = (struct zebra_vxlan_vni *)hash_lookup(vni_info->vni_table, + (void *)&vni_tmp); + + /* TODO: For debugging. Remove later */ + if (vnip) + assert(vnip->vni == vni); + + return vnip; +} + +static int zif_vlanid_vni_walker(struct zebra_if *zif, + struct zebra_vxlan_vni *vnip, void *arg) +{ + struct zebra_vxlan_if_vlan_ctx *ctx; + + ctx = (struct zebra_vxlan_if_vlan_ctx *)arg; + + if (vnip->access_vlan == ctx->vid) { + ctx->vni = vnip; + return HASHWALK_ABORT; + } + + return HASHWALK_CONTINUE; +} + +struct zebra_vxlan_vni *zebra_vxlan_if_vlanid_vni_find(struct zebra_if *zif, + vlanid_t vid) +{ + struct zebra_vxlan_if_vlan_ctx ctx = {}; + + if (!IS_ZEBRA_VXLAN_IF_SVD(zif)) + return NULL; + + ctx.vid = vid; + + zebra_vxlan_if_vni_walk(zif, zif_vlanid_vni_walker, &ctx); + + return ctx.vni; +} + +void zebra_vxlan_if_vni_iterate(struct zebra_if *zif, + int (*func)(struct zebra_if *zif, + struct zebra_vxlan_vni *, void *), + void *arg) +{ + struct zebra_vxlan_vni_info *vni_info; + struct zebra_vxlan_vni *vni = NULL; + struct zebra_vxlan_if_ctx ctx; + + vni_info = VNI_INFO_FROM_ZEBRA_IF(zif); + if (IS_ZEBRA_VXLAN_IF_VNI(zif)) { + vni = zebra_vxlan_if_vni_find(zif, 0); + func(zif, vni, arg); + return; + } + + memset(&ctx, 0, sizeof(ctx)); + ctx.zif = zif; + ctx.func = func; + ctx.arg = arg; + hash_iterate(vni_info->vni_table, zebra_vxlan_if_vni_iterate_callback, + &ctx); +} + +void zebra_vxlan_if_vni_walk(struct zebra_if *zif, + int (*func)(struct zebra_if *zif, + struct zebra_vxlan_vni *, void *), + void *arg) +{ + struct zebra_vxlan_vni_info *vni_info; + struct zebra_vxlan_vni *vni = NULL; + struct zebra_vxlan_if_ctx ctx; + + vni_info = VNI_INFO_FROM_ZEBRA_IF(zif); + if (IS_ZEBRA_VXLAN_IF_VNI(zif)) { + vni = zebra_vxlan_if_vni_find(zif, 0); + func(zif, vni, arg); + return; + } + + memset(&ctx, 0, sizeof(ctx)); + ctx.zif = zif; + ctx.func = func; + ctx.arg = arg; + hash_walk(vni_info->vni_table, zebra_vxlan_if_vni_walk_callback, &ctx); +} + +vni_t zebra_vxlan_if_access_vlan_vni_find(struct zebra_if *zif, + struct interface *br_if) +{ + struct zebra_vxlan_vni *vni = NULL; + + /* Expected to be called only for vlan-unware bridges. In this case, + * we only support a per-VNI VXLAN interface model. + */ + if (!IS_ZEBRA_VXLAN_IF_VNI(zif)) + return 0; + + vni = zebra_vxlan_if_vni_find(zif, 0); + assert(vni); + + return vni->vni; +} + +int zebra_vxlan_if_vni_table_add_update(struct interface *ifp, + struct hash *vni_table) +{ + struct zebra_if *zif; + struct zebra_vxlan_vni_info *vni_info; + struct zebra_vxlan_if_update_ctx ctx; + + zif = (struct zebra_if *)ifp->info; + + vni_info = VNI_INFO_FROM_ZEBRA_IF(zif); + + memset(&ctx, 0, sizeof(ctx)); + ctx.old_vni_table = vni_info->vni_table; + vni_info->vni_table = vni_table; + + zebra_vxlan_if_vni_iterate(zif, zebra_vxlan_if_add_update_vni, &ctx); + + /* release kernel deleted vnis */ + if (ctx.old_vni_table) { + if (hashcount(ctx.old_vni_table)) { + /* UGLY HACK: Put back the old table so that delete of + * MACs goes through and then flip back. + */ + vni_info->vni_table = ctx.old_vni_table; + hash_iterate(ctx.old_vni_table, + zebra_vxlan_if_vni_clean, zif); + vni_info->vni_table = vni_table; + } + zebra_vxlan_vni_table_destroy(ctx.old_vni_table); + ctx.old_vni_table = NULL; + } + + return 0; +} + +int zebra_vxlan_if_vni_mcast_group_add_update(struct interface *ifp, + vni_t vni_id, + struct in_addr *mcast_group) +{ + struct zebra_if *zif; + struct zebra_vxlan_vni *vni; + struct zebra_vxlan_if_update_ctx ctx; + + zif = (struct zebra_if *)ifp->info; + + if (!IS_ZEBRA_VXLAN_IF_SVD(zif)) + return 0; + + vni = zebra_vxlan_if_vni_find(zif, vni_id); + if (!vni) + return 0; + + memset(&ctx, 0, sizeof(ctx)); + ctx.old_vni.mcast_grp = vni->mcast_grp; + ctx.chgflags = ZEBRA_VXLIF_MCAST_GRP_CHANGE; + + vni->mcast_grp = *mcast_group; + + return zebra_vxlan_if_update_vni(ifp, vni, &ctx); +} + +int zebra_vxlan_if_vni_mcast_group_del(struct interface *ifp, vni_t vni_id, + struct in_addr *mcast_group) +{ + struct zebra_if *zif = NULL; + struct zebra_vxlan_vni *vni; + struct zebra_vxlan_if_update_ctx ctx; + + zif = (struct zebra_if *)ifp->info; + + if (!IS_ZEBRA_VXLAN_IF_SVD(zif)) + return 0; + + vni = zebra_vxlan_if_vni_find(zif, vni_id); + if (!vni) + return 0; + + if (memcmp(mcast_group, &vni->mcast_grp, sizeof(*mcast_group))) + return 0; + + memset(&ctx, 0, sizeof(ctx)); + ctx.old_vni.mcast_grp = vni->mcast_grp; + ctx.chgflags = ZEBRA_VXLIF_MCAST_GRP_CHANGE; + + memset(&vni->mcast_grp, 0, sizeof(vni->mcast_grp)); + + return zebra_vxlan_if_update_vni(ifp, vni, &ctx); +} + +int zebra_vxlan_if_vni_down(struct interface *ifp, struct zebra_vxlan_vni *vnip) +{ + vni_t vni; + struct zebra_if *zif; + struct zebra_l3vni *zl3vni; + struct zebra_evpn *zevpn; + + /* Check if EVPN is enabled. */ + if (!is_evpn_enabled()) + return 0; + + zif = ifp->info; + assert(zif); + vni = vnip->vni; + + zl3vni = zl3vni_lookup(vni); + if (zl3vni) { + /* process-if-down for l3-vni */ + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Intf %s(%u) L3-VNI %u is DOWN", ifp->name, + ifp->ifindex, vni); + + zebra_vxlan_process_l3vni_oper_down(zl3vni); + } else { + /* process if-down for l2-vni */ + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Intf %s(%u) L2-VNI %u is DOWN", ifp->name, + ifp->ifindex, vni); + + /* Locate hash entry; it is expected to exist. */ + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { + zlog_debug( + "Failed to locate VNI hash at DOWN, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return -1; + } + + assert(zevpn->vxlan_if == ifp); + + /* remove from l3-vni list */ + zl3vni = zl3vni_from_vrf(zevpn->vrf_id); + if (zl3vni) + listnode_delete(zl3vni->l2vnis, zevpn); + + zebra_evpn_vl_vxl_deref(vnip->access_vlan, vnip->vni, zif); + + /* Delete this VNI from BGP. */ + zebra_evpn_send_del_to_client(zevpn); + + /* Free up all neighbors and MACs, if any. */ + zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH); + zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_ALL_MAC); + + /* Free up all remote VTEPs, if any. */ + zebra_evpn_vtep_del_all(zevpn, 1); + } + return 0; +} + +/* + * Handle VxLAN interface down + */ +int zebra_vxlan_if_down(struct interface *ifp) +{ + struct zebra_if *zif; + struct zebra_vxlan_vni_info *vni_info; + + /* Check if EVPN is enabled. */ + if (!is_evpn_enabled()) + return 0; + + zif = ifp->info; + assert(zif); + + if (IS_ZEBRA_VXLAN_IF_VNI(zif)) { + vni_info = VNI_INFO_FROM_ZEBRA_IF(zif); + return zebra_vxlan_if_vni_down(ifp, &vni_info->vni); + } + + zebra_vxlan_if_vni_iterate(zif, zebra_vxlan_if_vni_entry_down_callback, + NULL); + + return 0; +} + +int zebra_vxlan_if_vni_up(struct interface *ifp, struct zebra_vxlan_vni *vnip) +{ + vni_t vni; + struct zebra_if *zif; + struct zebra_evpn *zevpn; + struct zebra_l3vni *zl3vni; + + /* Check if EVPN is enabled. */ + if (!is_evpn_enabled()) + return 0; + + zif = ifp->info; + assert(zif); + vni = vnip->vni; + + zl3vni = zl3vni_lookup(vni); + if (zl3vni) { + /* we need to associate with SVI, if any, we can associate with + * svi-if only after association with vxlan-intf is complete + */ + zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni); + zl3vni->mac_vlan_if = zl3vni_map_to_mac_vlan_if(zl3vni); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Intf %s(%u) L3-VNI %u is UP svi_if %s mac_vlan_if %s", + ifp->name, ifp->ifindex, vni, + zl3vni->svi_if ? zl3vni->svi_if->name : "NIL", + zl3vni->mac_vlan_if ? zl3vni->mac_vlan_if->name + : "NIL"); + + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up(zl3vni); + } else { + /* Handle L2-VNI add */ + struct interface *vlan_if = NULL; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Intf %s(%u) L2-VNI %u is UP", ifp->name, + ifp->ifindex, vni); + + /* Locate hash entry; it is expected to exist. */ + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { + zlog_debug( + "Failed to locate EVPN hash at UP, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return -1; + } + + assert(zevpn->vxlan_if == ifp); + zebra_evpn_vl_vxl_ref(vnip->access_vlan, vnip->vni, zif); + vlan_if = zvni_map_to_svi(vnip->access_vlan, + zif->brslave_info.br_if); + if (vlan_if) { + zevpn->svi_if = vlan_if; + zevpn->vrf_id = vlan_if->vrf->vrf_id; + zl3vni = zl3vni_from_vrf(vlan_if->vrf->vrf_id); + if (zl3vni) + listnode_add_sort_nodup(zl3vni->l2vnis, zevpn); + } + + /* If part of a bridge, inform BGP about this VNI. */ + /* Also, read and populate local MACs and neighbors. */ + if (zif->brslave_info.br_if) { + zebra_evpn_send_add_to_client(zevpn); + zebra_evpn_read_mac_neigh(zevpn, ifp); + } + } + + return 0; +} + +/* + * Handle VxLAN interface up - update BGP if required. + */ +int zebra_vxlan_if_up(struct interface *ifp) +{ + struct zebra_if *zif; + struct zebra_vxlan_vni_info *vni_info; + + /* Check if EVPN is enabled. */ + if (!is_evpn_enabled()) + return 0; + + zif = ifp->info; + assert(zif); + + if (IS_ZEBRA_VXLAN_IF_VNI(zif)) { + vni_info = VNI_INFO_FROM_ZEBRA_IF(zif); + return zebra_vxlan_if_vni_up(ifp, &vni_info->vni); + } + + zebra_vxlan_if_vni_iterate(zif, zebra_vxlan_if_vni_entry_up_callback, + NULL); + + return 0; +} + +int zebra_vxlan_if_vni_del(struct interface *ifp, vni_t vni) +{ + struct zebra_if *zif; + struct zebra_vxlan_vni *vnip; + struct zebra_vxlan_vni vni_tmp; + struct zebra_vxlan_vni_info *vni_info; + + zif = ifp->info; + assert(zif); + + /* This should be called in SVD context only */ + assert(IS_ZEBRA_VXLAN_IF_SVD(zif)); + + vni_info = VNI_INFO_FROM_ZEBRA_IF(zif); + memset(&vni_tmp, 0, sizeof(vni_tmp)); + vni_tmp.vni = vni; + + vnip = hash_release(vni_info->vni_table, &vni_tmp); + if (vnip) { + zebra_vxlan_if_vni_entry_del(zif, vnip); + zebra_vxlan_vni_free(vnip); + } + return 0; +} + +/* + * Handle VxLAN interface delete. Locate and remove entry in hash table + * and update BGP, if required. + */ +int zebra_vxlan_if_del(struct interface *ifp) +{ + struct zebra_if *zif; + struct zebra_vxlan_vni_info *vni_info; + + zif = ifp->info; + assert(zif); + + if (IS_ZEBRA_VXLAN_IF_VNI(zif)) { + vni_info = VNI_INFO_FROM_ZEBRA_IF(zif); + zebra_evpn_vl_vxl_deref(vni_info->vni.access_vlan, + vni_info->vni.vni, zif); + return zebra_vxlan_if_del_vni(ifp, &vni_info->vni); + } + + zebra_vxlan_if_vni_table_destroy(zif); + + return 0; +} + +/* + * Handle VxLAN interface update - change to tunnel IP, master or VLAN. + */ +int zebra_vxlan_if_update(struct interface *ifp, + struct zebra_vxlan_if_update_ctx *ctx) +{ + struct zebra_if *zif; + struct zebra_vxlan_vni_info *vni_info; + + zif = ifp->info; + assert(zif); + + if (IS_ZEBRA_VXLAN_IF_VNI(zif)) { + vni_info = VNI_INFO_FROM_ZEBRA_IF(zif); + return zebra_vxlan_if_update_vni(ifp, &vni_info->vni, ctx); + } + + zebra_vxlan_if_vni_iterate( + zif, zebra_vxlan_if_vni_entry_update_callback, ctx); + + return 0; +} + +int zebra_vxlan_if_vni_add(struct interface *ifp, struct zebra_vxlan_vni *vni) +{ + struct zebra_if *zif; + struct zebra_vxlan_vni_info *vni_info; + + zif = ifp->info; + assert(zif); + + /* This should be called in SVD context only */ + assert(IS_ZEBRA_VXLAN_IF_SVD(zif)); + + /* First insert into the table */ + vni_info = VNI_INFO_FROM_ZEBRA_IF(zif); + hash_get(vni_info->vni_table, (void *)vni, zebra_vxlan_vni_alloc); + + return zebra_vxlan_if_vni_entry_add(zif, vni); +} + +/* + * Handle VxLAN interface add. + */ +int zebra_vxlan_if_add(struct interface *ifp) +{ + int ret; + struct zebra_if *zif; + struct zebra_vxlan_vni_info *vni_info; + + zif = ifp->info; + assert(zif); + + if (IS_ZEBRA_VXLAN_IF_VNI(zif)) { + vni_info = VNI_INFO_FROM_ZEBRA_IF(zif); + zebra_evpn_vl_vxl_ref(vni_info->vni.access_vlan, + vni_info->vni.vni, zif); + return zebra_vxlan_if_add_vni(ifp, &vni_info->vni); + } + + ret = zebra_vxlan_if_vni_table_create(zif); + if (ret < 0) + return ret; + + return 0; +} diff --git a/zebra/zebra_vxlan_if.h b/zebra/zebra_vxlan_if.h new file mode 100644 index 0000000000..8a03acbf1e --- /dev/null +++ b/zebra/zebra_vxlan_if.h @@ -0,0 +1,96 @@ +/* + * Zebra VxLAN (EVPN) interface data structures and definitions + * These are public definitions referenced by other files. + * Copyright (C) 2021 Cumulus Networks, Inc. + * Sharath Ramamurthy + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _ZEBRA_VXLAN_IF_H +#define _ZEBRA_VXLAN_IF_H + +#include <zebra.h> +#include <zebra/zebra_router.h> + +#include "linklist.h" +#include "if.h" +#include "vlan.h" +#include "vxlan.h" + +#include "lib/json.h" +#include "zebra/zebra_vrf.h" +#include "zebra/zserv.h" +#include "zebra/zebra_dplane.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern void *zebra_vxlan_vni_alloc(void *p); +extern void zebra_vxlan_vni_free(void *arg); +extern struct hash *zebra_vxlan_vni_table_create(void); +extern void zebra_vxlan_vni_table_destroy(struct hash *vni_table); +extern int zebra_vxlan_if_vni_table_create(struct zebra_if *zif); +extern int zebra_vxlan_if_vni_table_destroy(struct zebra_if *zif); +extern struct zebra_vxlan_vni * +zebra_vxlan_if_vni_find(const struct zebra_if *zif, vni_t vni); +extern struct zebra_vxlan_vni * +zebra_vxlan_if_vlanid_vni_find(struct zebra_if *zif, vlanid_t vni); +extern void zebra_vxlan_if_vni_iterate(struct zebra_if *zif, + int (*func)(struct zebra_if *zif, + struct zebra_vxlan_vni *, + void *), + void *arg); +extern void zebra_vxlan_if_vni_walk(struct zebra_if *zif, + int (*func)(struct zebra_if *zif, + struct zebra_vxlan_vni *, + void *), + void *arg); +extern vni_t zebra_vxlan_if_access_vlan_vni_find(struct zebra_if *zif, + struct interface *br_if); +extern int +zebra_vxlan_if_vni_mcast_group_add_update(struct interface *ifp, vni_t vni_id, + struct in_addr *mcast_group); +extern int zebra_vxlan_if_vni_mcast_group_del(struct interface *ifp, + vni_t vni_id, + struct in_addr *mcast_group); +extern int zebra_vxlan_if_vni_down(struct interface *ifp, + struct zebra_vxlan_vni *vni); +extern int zebra_vxlan_if_down(struct interface *ifp); +extern int zebra_vxlan_if_vni_up(struct interface *ifp, + struct zebra_vxlan_vni *vni); +extern int zebra_vxlan_if_up(struct interface *ifp); +extern int zebra_vxlan_if_vni_del(struct interface *ifp, vni_t vni); +extern int zebra_vxlan_if_del(struct interface *ifp); +extern int zebra_vxlan_if_vni_table_add_update(struct interface *ifp, + struct hash *vni_table); +extern int zebra_vxlan_if_vni_update(struct interface *ifp, + struct zebra_vxlan_vni *vni, + uint16_t chgflags); +extern int zebra_vxlan_if_update(struct interface *ifp, + struct zebra_vxlan_if_update_ctx *ctx); +extern int zebra_vxlan_if_vni_add(struct interface *ifp, + struct zebra_vxlan_vni *vni); +extern int zebra_vxlan_if_add(struct interface *ifp); + +#ifdef __cplusplus +} +#endif + +#endif /* _ZEBRA_VXLAN_IF_H */ diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h index fb17dac23e..7b8cdef409 100644 --- a/zebra/zebra_vxlan_private.h +++ b/zebra/zebra_vxlan_private.h @@ -29,6 +29,7 @@ #include "if.h" #include "linklist.h" #include "zebra_vxlan.h" +#include "zebra_vxlan_if.h" #include "zebra_evpn.h" #include "zebra_evpn_mac.h" @@ -50,6 +51,10 @@ struct zebra_l3vni { uint32_t filter; #define PREFIX_ROUTES_ONLY (1 << 0) /* l3-vni used for prefix routes only */ + /* Corresponding Bridge information */ + vlanid_t vid; + struct interface *bridge_if; + /* Local IP */ struct in_addr local_vtep_ip; @@ -71,6 +76,10 @@ struct zebra_l3vni { struct hash *nh_table; }; +#define IS_ZL3VNI_SVD_BACKED(zl3vni) \ + (zl3vni->vxlan_if && zl3vni->vxlan_if->info && \ + IS_ZEBRA_VXLAN_IF_SVD((struct zebra_if *)zl3vni->vxlan_if->info)) + /* get the vx-intf name for l3vni */ static inline const char *zl3vni_vxlan_if_name(struct zebra_l3vni *zl3vni) { @@ -260,5 +269,12 @@ extern void zebra_vxlan_sync_mac_dp_install(struct zebra_mac *mac, bool force_clear_static, const char *caller); extern bool zebra_evpn_do_dup_addr_detect(struct zebra_vrf *zvrf); +extern void zebra_vxlan_sg_ref(struct in_addr local_vtep_ip, + struct in_addr mcast_grp); +extern void zebra_vxlan_sg_deref(struct in_addr local_vtep_ip, + struct in_addr mcast_grp); +extern void zebra_vxlan_process_l3vni_oper_up(struct zebra_l3vni *zl3vni); +extern void zebra_vxlan_process_l3vni_oper_down(struct zebra_l3vni *zl3vni); +extern int zebra_evpn_vxlan_del(struct zebra_evpn *zevpn); #endif /* _ZEBRA_VXLAN_PRIVATE_H */ |
