/* * Zebra EVPN for VxLAN code * Copyright (C) 2016, 2017 Cumulus Networks, Inc. * * 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. */ #include #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 #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_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" DEFINE_MTYPE_STATIC(ZEBRA, ZEVPN, "VNI hash"); DEFINE_MTYPE_STATIC(ZEBRA, ZEVPN_VTEP, "VNI remote VTEP"); /* PMSI strings. */ #define VXLAN_FLOOD_STR_NO_INFO "-" #define VXLAN_FLOOD_STR_DEFAULT VXLAN_FLOOD_STR_NO_INFO static const struct message zvtep_flood_str[] = { {VXLAN_FLOOD_DISABLED, VXLAN_FLOOD_STR_NO_INFO}, {VXLAN_FLOOD_PIM_SM, "PIM-SM"}, {VXLAN_FLOOD_HEAD_END_REPL, "HER"}, {0} }; int advertise_gw_macip_enabled(struct zebra_evpn *zevpn) { struct zebra_vrf *zvrf; zvrf = zebra_vrf_get_evpn(); if (zvrf->advertise_gw_macip) return 1; if (zevpn && zevpn->advertise_gw_macip) return 1; return 0; } int advertise_svi_macip_enabled(struct zebra_evpn *zevpn) { struct zebra_vrf *zvrf; zvrf = zebra_vrf_get_evpn(); if (zvrf->advertise_svi_macip) return 1; if (zevpn && zevpn->advertise_svi_macip) return 1; return 0; } /* * Print a specific EVPN entry. */ void zebra_evpn_print(struct zebra_evpn *zevpn, void **ctxt) { struct vty *vty; struct zebra_vtep *zvtep; uint32_t num_macs; uint32_t num_neigh; json_object *json = NULL; json_object *json_vtep_list = NULL; json_object *json_ip_str = NULL; char buf[PREFIX_STRLEN]; vty = ctxt[0]; json = ctxt[1]; if (json == NULL) { vty_out(vty, "VNI: %u\n", zevpn->vni); vty_out(vty, " Type: %s\n", "L2"); vty_out(vty, " Tenant VRF: %s\n", vrf_id_to_name(zevpn->vrf_id)); } else { json_object_int_add(json, "vni", zevpn->vni); json_object_string_add(json, "type", "L2"); json_object_string_add(json, "vrf", vrf_id_to_name(zevpn->vrf_id)); } if (!zevpn->vxlan_if) { // unexpected if (json == NULL) vty_out(vty, " VxLAN interface: unknown\n"); return; } num_macs = num_valid_macs(zevpn); num_neigh = hashcount(zevpn->neigh_table); if (json == NULL) { vty_out(vty, " VxLAN interface: %s\n", zevpn->vxlan_if->name); vty_out(vty, " VxLAN ifIndex: %u\n", zevpn->vxlan_if->ifindex); vty_out(vty, " SVI interface: %s\n", (zevpn->svi_if ? zevpn->svi_if->name : "")); vty_out(vty, " SVI ifIndex: %u\n", (zevpn->svi_if ? zevpn->svi_if->ifindex : 0)); vty_out(vty, " Local VTEP IP: %pI4\n", &zevpn->local_vtep_ip); vty_out(vty, " Mcast group: %pI4\n", &zevpn->mcast_grp); } else { json_object_string_add(json, "vxlanInterface", zevpn->vxlan_if->name); json_object_int_add(json, "ifindex", zevpn->vxlan_if->ifindex); if (zevpn->svi_if) { json_object_string_add(json, "sviInterface", zevpn->svi_if->name); json_object_int_add(json, "sviIfindex", zevpn->svi_if->ifindex); } json_object_string_addf(json, "vtepIp", "%pI4", &zevpn->local_vtep_ip); json_object_string_addf(json, "mcastGroup", "%pI4", &zevpn->mcast_grp); json_object_string_add(json, "advertiseGatewayMacip", zevpn->advertise_gw_macip ? "Yes" : "No"); json_object_string_add(json, "advertiseSviMacip", zevpn->advertise_svi_macip ? "Yes" : "No"); json_object_int_add(json, "numMacs", num_macs); json_object_int_add(json, "numArpNd", num_neigh); } if (!zevpn->vteps) { if (json == NULL) vty_out(vty, " No remote VTEPs known for this VNI\n"); } else { if (json == NULL) vty_out(vty, " Remote VTEPs for this VNI:\n"); else json_vtep_list = json_object_new_array(); for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep->next) { const char *flood_str = lookup_msg(zvtep_flood_str, zvtep->flood_control, VXLAN_FLOOD_STR_DEFAULT); if (json == NULL) { vty_out(vty, " %pI4 flood: %s\n", &zvtep->vtep_ip, flood_str); } else { json_ip_str = json_object_new_string( inet_ntop(AF_INET, &zvtep->vtep_ip, buf, sizeof(buf))); json_object_array_add(json_vtep_list, json_ip_str); } } if (json) json_object_object_add(json, "numRemoteVteps", json_vtep_list); } if (json == NULL) { vty_out(vty, " Number of MACs (local and remote) known for this VNI: %u\n", num_macs); vty_out(vty, " Number of ARPs (IPv4 and IPv6, local and remote) " "known for this VNI: %u\n", num_neigh); vty_out(vty, " Advertise-gw-macip: %s\n", zevpn->advertise_gw_macip ? "Yes" : "No"); vty_out(vty, " Advertise-svi-macip: %s\n", zevpn->advertise_svi_macip ? "Yes" : "No"); } } /* * Print an EVPN hash entry - called for display of all VNIs. */ void zebra_evpn_print_hash(struct hash_bucket *bucket, void *ctxt[]) { struct vty *vty; struct zebra_evpn *zevpn; struct zebra_vtep *zvtep; uint32_t num_vteps = 0; uint32_t num_macs = 0; uint32_t num_neigh = 0; json_object *json = NULL; json_object *json_evpn = NULL; json_object *json_ip_str = NULL; json_object *json_vtep_list = NULL; char buf[PREFIX_STRLEN]; vty = ctxt[0]; json = ctxt[1]; zevpn = (struct zebra_evpn *)bucket->data; zvtep = zevpn->vteps; while (zvtep) { num_vteps++; zvtep = zvtep->next; } num_macs = num_valid_macs(zevpn); num_neigh = hashcount(zevpn->neigh_table); if (json == NULL) vty_out(vty, "%-10u %-4s %-21s %-8u %-8u %-15u %-37s\n", zevpn->vni, "L2", zevpn->vxlan_if ? zevpn->vxlan_if->name : "unknown", num_macs, num_neigh, num_vteps, vrf_id_to_name(zevpn->vrf_id)); else { char vni_str[VNI_STR_LEN]; snprintf(vni_str, VNI_STR_LEN, "%u", zevpn->vni); json_evpn = json_object_new_object(); json_object_int_add(json_evpn, "vni", zevpn->vni); json_object_string_add(json_evpn, "type", "L2"); json_object_string_add(json_evpn, "vxlanIf", zevpn->vxlan_if ? zevpn->vxlan_if->name : "unknown"); json_object_int_add(json_evpn, "numMacs", num_macs); json_object_int_add(json_evpn, "numArpNd", num_neigh); json_object_int_add(json_evpn, "numRemoteVteps", num_vteps); json_object_string_add(json_evpn, "tenantVrf", vrf_id_to_name(zevpn->vrf_id)); if (num_vteps) { json_vtep_list = json_object_new_array(); for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep->next) { json_ip_str = json_object_new_string( inet_ntop(AF_INET, &zvtep->vtep_ip, buf, sizeof(buf))); json_object_array_add(json_vtep_list, json_ip_str); } json_object_object_add(json_evpn, "remoteVteps", json_vtep_list); } json_object_object_add(json, vni_str, json_evpn); } } /* * Print an EVPN hash entry in detail - called for display of all EVPNs. */ void zebra_evpn_print_hash_detail(struct hash_bucket *bucket, void *data) { struct vty *vty; struct zebra_evpn *zevpn; json_object *json_array = NULL; bool use_json = false; struct zebra_evpn_show *zes = data; vty = zes->vty; json_array = zes->json; use_json = zes->use_json; zevpn = (struct zebra_evpn *)bucket->data; zebra_vxlan_print_vni(vty, zes->zvrf, zevpn->vni, use_json, json_array); if (!use_json) vty_out(vty, "\n"); } int zebra_evpn_del_macip_for_intf(struct interface *ifp, struct zebra_evpn *zevpn) { struct listnode *cnode = NULL, *cnnode = NULL; struct connected *c = NULL; struct ethaddr macaddr; memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) { struct ipaddr ip; memset(&ip, 0, sizeof(struct ipaddr)); if (!CHECK_FLAG(c->conf, ZEBRA_IFC_REAL)) continue; if (c->address->family == AF_INET) { ip.ipa_type = IPADDR_V4; memcpy(&(ip.ipaddr_v4), &(c->address->u.prefix4), sizeof(struct in_addr)); } else if (c->address->family == AF_INET6) { ip.ipa_type = IPADDR_V6; memcpy(&(ip.ipaddr_v6), &(c->address->u.prefix6), sizeof(struct in6_addr)); } else { continue; } zebra_evpn_gw_macip_del(ifp, zevpn, &ip); } return 0; } int zebra_evpn_add_macip_for_intf(struct interface *ifp, struct zebra_evpn *zevpn) { struct listnode *cnode = NULL, *cnnode = NULL; struct connected *c = NULL; struct ethaddr macaddr; memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) { struct ipaddr ip; if (!CHECK_FLAG(c->conf, ZEBRA_IFC_REAL)) continue; memset(&ip, 0, sizeof(struct ipaddr)); if (c->address->family == AF_INET) { ip.ipa_type = IPADDR_V4; memcpy(&(ip.ipaddr_v4), &(c->address->u.prefix4), sizeof(struct in_addr)); } else if (c->address->family == AF_INET6) { ip.ipa_type = IPADDR_V6; memcpy(&(ip.ipaddr_v6), &(c->address->u.prefix6), sizeof(struct in6_addr)); } else { continue; } zebra_evpn_gw_macip_add(ifp, zevpn, &macaddr, &ip); } return 0; } static int ip_prefix_send_to_client(vrf_id_t vrf_id, struct prefix *p, uint16_t cmd) { struct zserv *client = NULL; struct stream *s = NULL; client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); /* BGP may not be running. */ if (!client) return 0; s = stream_new(ZEBRA_MAX_PACKET_SIZ); zclient_create_header(s, cmd, vrf_id); stream_put(s, p, sizeof(struct prefix)); /* Write packet size. */ stream_putw_at(s, 0, stream_get_endp(s)); if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug("Send ip prefix %pFX %s on vrf %s", p, (cmd == ZEBRA_IP_PREFIX_ROUTE_ADD) ? "ADD" : "DEL", vrf_id_to_name(vrf_id)); if (cmd == ZEBRA_IP_PREFIX_ROUTE_ADD) client->prefixadd_cnt++; else client->prefixdel_cnt++; return zserv_send_message(client, s); } int zebra_evpn_advertise_subnet(struct zebra_evpn *zevpn, struct interface *ifp, int advertise) { struct listnode *cnode = NULL, *cnnode = NULL; struct connected *c = NULL; struct ethaddr macaddr; memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) { struct prefix p; memcpy(&p, c->address, sizeof(struct prefix)); /* skip link local address */ if (IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6)) continue; apply_mask(&p); if (advertise) ip_prefix_send_to_client(ifp->vrf->vrf_id, &p, ZEBRA_IP_PREFIX_ROUTE_ADD); else ip_prefix_send_to_client(ifp->vrf->vrf_id, &p, ZEBRA_IP_PREFIX_ROUTE_DEL); } return 0; } /* * zebra_evpn_gw_macip_add_to_client */ int zebra_evpn_gw_macip_add(struct interface *ifp, struct zebra_evpn *zevpn, struct ethaddr *macaddr, struct ipaddr *ip) { struct zebra_mac *mac = NULL; struct zebra_if *zif = NULL; struct zebra_l2info_vxlan *vxl = NULL; zif = zevpn->vxlan_if->info; if (!zif) return -1; vxl = &zif->l2info.vxl; zebra_evpn_mac_gw_macip_add(ifp, zevpn, ip, &mac, macaddr, vxl->access_vlan, true); return zebra_evpn_neigh_gw_macip_add(ifp, zevpn, ip, mac); } /* * zebra_evpn_gw_macip_del_from_client */ int zebra_evpn_gw_macip_del(struct interface *ifp, struct zebra_evpn *zevpn, struct ipaddr *ip) { struct zebra_neigh *n = NULL; struct zebra_mac *mac = NULL; /* If the neigh entry is not present nothing to do*/ n = zebra_evpn_neigh_lookup(zevpn, ip); if (!n) return 0; /* mac entry should be present */ mac = zebra_evpn_mac_lookup(zevpn, &n->emac); if (!mac) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug("MAC %pEA doesn't exist for neigh %pIA on VNI %u", &n->emac, ip, zevpn->vni); return -1; } /* If the entry is not local nothing to do*/ if (!CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) return -1; /* only need to delete the entry from bgp if we sent it before */ if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( "%u:SVI %s(%u) VNI %u, sending GW MAC %pEA IP %pIA del to BGP", ifp->vrf->vrf_id, ifp->name, ifp->ifindex, zevpn->vni, &n->emac, ip); /* Remove neighbor from BGP. */ zebra_evpn_neigh_send_del_to_client(zevpn->vni, &n->ip, &n->emac, n->flags, ZEBRA_NEIGH_ACTIVE, false /*force*/); /* Delete this neighbor entry. */ zebra_evpn_neigh_del(zevpn, n); /* see if the mac needs to be deleted as well*/ if (mac) zebra_evpn_deref_ip2mac(zevpn, mac); return 0; } void zebra_evpn_gw_macip_del_for_evpn_hash(struct hash_bucket *bucket, void *ctxt) { 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; /* Add primary SVI MAC*/ zevpn = (struct zebra_evpn *)bucket->data; /* Global (Zvrf) advertise-default-gw is disabled, * but zevpn advertise-default-gw is enabled */ if (zevpn->advertise_gw_macip) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug("VNI: %u GW-MACIP enabled, retain gw-macip", zevpn->vni); return; } ifp = zevpn->vxlan_if; if (!ifp) return; zif = ifp->info; /* 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; vlan_if = zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if); if (!vlan_if) return; /* Del primary MAC-IP */ zebra_evpn_del_macip_for_intf(vlan_if, zevpn); /* Del VRR MAC-IP - if any*/ vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); if (vrr_if) zebra_evpn_del_macip_for_intf(vrr_if, zevpn); return; } void zebra_evpn_gw_macip_add_for_evpn_hash(struct hash_bucket *bucket, void *ctxt) { 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; zevpn = (struct zebra_evpn *)bucket->data; ifp = zevpn->vxlan_if; if (!ifp) return; zif = ifp->info; /* 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; vlan_if = zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if); if (!vlan_if) return; /* Add primary SVI MAC-IP */ if (advertise_svi_macip_enabled(zevpn) || advertise_gw_macip_enabled(zevpn)) zebra_evpn_add_macip_for_intf(vlan_if, zevpn); if (advertise_gw_macip_enabled(zevpn)) { /* Add VRR MAC-IP - if any*/ vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); if (vrr_if) zebra_evpn_add_macip_for_intf(vrr_if, zevpn); } return; } void zebra_evpn_svi_macip_del_for_evpn_hash(struct hash_bucket *bucket, void *ctxt) { struct zebra_evpn *zevpn = NULL; struct zebra_if *zif = NULL; struct zebra_l2info_vxlan zl2_info; struct interface *vlan_if = NULL; struct interface *ifp; /* Add primary SVI MAC*/ zevpn = (struct zebra_evpn *)bucket->data; if (!zevpn) return; /* Global(vrf) advertise-svi-ip disabled, but zevpn advertise-svi-ip * enabled */ if (zevpn->advertise_svi_macip) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug("VNI: %u SVI-MACIP enabled, retain svi-macip", zevpn->vni); return; } ifp = zevpn->vxlan_if; if (!ifp) return; zif = ifp->info; /* 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; vlan_if = zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if); if (!vlan_if) return; /* Del primary MAC-IP */ zebra_evpn_del_macip_for_intf(vlan_if, zevpn); return; } static int zebra_evpn_map_vlan_ns(struct ns *ns, void *_in_param, void **_p_zevpn) { struct zebra_ns *zns = ns->info; struct route_node *rn; struct interface *br_if; struct zebra_evpn **p_zevpn = (struct zebra_evpn **)_p_zevpn; 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; assert(p_zevpn && in_param); br_if = in_param->br_if; zif = in_param->zif; assert(zif); assert(br_if); /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ /* TODO: Optimize with a hash. */ for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { tmp_if = (struct interface *)rn->info; if (!tmp_if) continue; zif = tmp_if->info; if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) continue; if (!if_is_operative(tmp_if)) continue; vxl = &zif->l2info.vxl; if (zif->brslave_info.br_if != br_if) continue; if (!in_param->bridge_vlan_aware || vxl->access_vlan == in_param->vid) { zevpn = zebra_evpn_lookup(vxl->vni); *p_zevpn = zevpn; return NS_WALK_STOP; } } return NS_WALK_CONTINUE; } /* * Map port or (port, VLAN) to an EVPN. This is invoked upon getting MAC * notifications, to see if they are of interest. */ 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; /* 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.vid = vid; in_param.br_if = br_if; in_param.zif = zif; p_zevpn = &zevpn; ns_walk_func(zebra_evpn_map_vlan_ns, (void *)&in_param, (void **)p_zevpn); return zevpn; } static int zebra_evpn_from_svi_ns(struct ns *ns, void *_in_param, void **_p_zevpn) { struct zebra_ns *zns = ns->info; struct route_node *rn; struct interface *br_if; struct zebra_evpn **p_zevpn = (struct zebra_evpn **)_p_zevpn; 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; int found = 0; 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) { found = 1; break; } } if (!found) return NS_WALK_CONTINUE; zevpn = zebra_evpn_lookup(vxl->vni); if (p_zevpn) *p_zevpn = zevpn; return NS_WALK_STOP; } /* * Map SVI and associated bridge to an EVPN. This is invoked upon getting * neighbor notifications, to see if they are of interest. */ 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; struct zebra_from_svi_param in_param; if (!br_if) return NULL; /* Make sure the linked interface is a bridge. */ if (!IS_ZEBRA_IF_BRIDGE(br_if)) return NULL; /* 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.vid = 0; if (in_param.bridge_vlan_aware) { struct zebra_l2info_vlan *vl; if (!IS_ZEBRA_IF_VLAN(ifp)) return NULL; zif = ifp->info; assert(zif); vl = &zif->l2info.vl; in_param.vid = vl->vid; } in_param.br_if = br_if; in_param.zif = zif; p_zevpn = &zevpn; /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ ns_walk_func(zebra_evpn_from_svi_ns, (void *)&in_param, (void **)p_zevpn); return zevpn; } static int zvni_map_to_macvlan_ns(struct ns *ns, void *_in_param, void **_p_ifp) { struct zebra_ns *zns = ns->info; struct zebra_from_svi_param *in_param = (struct zebra_from_svi_param *)_in_param; struct interface **p_ifp = (struct interface **)_p_ifp; struct route_node *rn; struct interface *tmp_if = NULL; struct zebra_if *zif; assert(in_param && p_ifp); /* Identify corresponding VLAN interface. */ for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { tmp_if = (struct interface *)rn->info; /* Check oper status of the SVI. */ if (!tmp_if || !if_is_operative(tmp_if)) continue; zif = tmp_if->info; if (!zif || zif->zif_type != ZEBRA_IF_MACVLAN) continue; if (zif->link == in_param->svi_if) { *p_ifp = tmp_if; return NS_WALK_STOP; } } return NS_WALK_CONTINUE; } /* Map to MAC-VLAN interface corresponding to specified SVI interface. */ struct interface *zebra_evpn_map_to_macvlan(struct interface *br_if, struct interface *svi_if) { struct interface *tmp_if = NULL; struct zebra_if *zif; struct interface **p_ifp; struct zebra_from_svi_param in_param; /* Defensive check, caller expected to invoke only with valid bridge. */ if (!br_if) return NULL; if (!svi_if) { zlog_debug("svi_if is not passed."); return NULL; } /* Determine if bridge is VLAN-aware or not */ zif = br_if->info; assert(zif); in_param.vid = 0; in_param.br_if = br_if; in_param.zif = NULL; in_param.svi_if = svi_if; p_ifp = &tmp_if; /* Identify corresponding VLAN interface. */ ns_walk_func(zvni_map_to_macvlan_ns, (void *)&in_param, (void **)p_ifp); return tmp_if; } /* * Install MAC hash entry - called upon access VLAN change. */ void zebra_evpn_install_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_install(wctx->zevpn, mac, false); } /* * Read and populate local MACs and neighbors corresponding to this EVPN. */ void zebra_evpn_read_mac_neigh(struct zebra_evpn *zevpn, struct interface *ifp) { struct zebra_ns *zns; struct zebra_vrf *zvrf; struct zebra_if *zif; struct interface *vlan_if; struct zebra_l2info_vxlan *vxl; struct interface *vrr_if; zif = ifp->info; vxl = &zif->l2info.vxl; zvrf = zebra_vrf_lookup_by_id(zevpn->vrf_id); if (!zvrf || !zvrf->zns) return; zns = zvrf->zns; if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( "Reading MAC FDB and Neighbors for intf %s(%u) VNI %u master %u", 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); if (vlan_if) { /* Add SVI MAC */ zebra_evpn_acc_bd_svi_mac_add(vlan_if); /* Add SVI MAC-IP */ if (advertise_svi_macip_enabled(zevpn) || advertise_gw_macip_enabled(zevpn)) zebra_evpn_add_macip_for_intf(vlan_if, zevpn); /* Add VRR MAC-IP - if any*/ if (advertise_gw_macip_enabled(zevpn)) { vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); if (vrr_if) zebra_evpn_add_macip_for_intf(vrr_if, zevpn); } neigh_read_for_vlan(zns, vlan_if); } } /* * Hash function for EVPN. */ unsigned int zebra_evpn_hash_keymake(const void *p) { const struct zebra_evpn *zevpn = p; return (jhash_1word(zevpn->vni, 0)); } /* * Compare 2 evpn hash entries. */ bool zebra_evpn_hash_cmp(const void *p1, const void *p2) { const struct zebra_evpn *zevpn1 = p1; const struct zebra_evpn *zevpn2 = p2; return (zevpn1->vni == zevpn2->vni); } int zebra_evpn_list_cmp(void *p1, void *p2) { const struct zebra_evpn *zevpn1 = p1; const struct zebra_evpn *zevpn2 = p2; if (zevpn1->vni == zevpn2->vni) return 0; return (zevpn1->vni < zevpn2->vni) ? -1 : 1; } /* * Callback to allocate VNI hash entry. */ void *zebra_evpn_alloc(void *p) { const struct zebra_evpn *tmp_vni = p; struct zebra_evpn *zevpn; zevpn = XCALLOC(MTYPE_ZEVPN, sizeof(struct zebra_evpn)); zevpn->vni = tmp_vni->vni; return ((void *)zevpn); } /* * Look up EVPN hash entry. */ struct zebra_evpn *zebra_evpn_lookup(vni_t vni) { struct zebra_vrf *zvrf; struct zebra_evpn tmp_vni; struct zebra_evpn *zevpn = NULL; zvrf = zebra_vrf_get_evpn(); memset(&tmp_vni, 0, sizeof(tmp_vni)); tmp_vni.vni = vni; zevpn = hash_lookup(zvrf->evpn_table, &tmp_vni); return zevpn; } /* * Add EVPN hash entry. */ struct zebra_evpn *zebra_evpn_add(vni_t vni) { char buffer[80]; struct zebra_vrf *zvrf; struct zebra_evpn tmp_zevpn; struct zebra_evpn *zevpn = NULL; zvrf = zebra_vrf_get_evpn(); memset(&tmp_zevpn, 0, sizeof(tmp_zevpn)); tmp_zevpn.vni = vni; zevpn = hash_get(zvrf->evpn_table, &tmp_zevpn, zebra_evpn_alloc); zebra_evpn_es_evi_init(zevpn); snprintf(buffer, sizeof(buffer), "Zebra EVPN MAC Table vni: %u", vni); /* Create hash table for MAC */ zevpn->mac_table = zebra_mac_db_create(buffer); snprintf(buffer, sizeof(buffer), "Zebra EVPN Neighbor Table vni: %u", vni); /* Create hash table for neighbors */ zevpn->neigh_table = zebra_neigh_db_create(buffer); return zevpn; } /* * Delete EVPN hash entry. */ int zebra_evpn_del(struct zebra_evpn *zevpn) { struct zebra_vrf *zvrf; struct zebra_evpn *tmp_zevpn; zvrf = zebra_vrf_get_evpn(); zevpn->svi_if = NULL; /* Free the neighbor hash table. */ hash_free(zevpn->neigh_table); zevpn->neigh_table = NULL; /* Free the MAC hash table. */ hash_free(zevpn->mac_table); zevpn->mac_table = NULL; /* Remove references to the zevpn in the MH databases */ if (zevpn->vxlan_if) zebra_evpn_vxl_evpn_set(zevpn->vxlan_if->info, zevpn, false); zebra_evpn_es_evi_cleanup(zevpn); /* Free the EVPN hash entry and allocated memory. */ tmp_zevpn = hash_release(zvrf->evpn_table, zevpn); XFREE(MTYPE_ZEVPN, tmp_zevpn); return 0; } /* * Inform BGP about local EVPN addition. */ int zebra_evpn_send_add_to_client(struct zebra_evpn *zevpn) { struct zserv *client; struct stream *s; ifindex_t svi_index; int rc; client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); /* BGP may not be running. */ if (!client) return 0; svi_index = zevpn->svi_if ? zevpn->svi_if->ifindex : 0; s = stream_new(ZEBRA_MAX_PACKET_SIZ); zclient_create_header(s, ZEBRA_VNI_ADD, zebra_vrf_get_evpn_id()); stream_putl(s, zevpn->vni); stream_put_in_addr(s, &zevpn->local_vtep_ip); stream_put(s, &zevpn->vrf_id, sizeof(vrf_id_t)); /* tenant vrf */ stream_put_in_addr(s, &zevpn->mcast_grp); stream_put(s, &svi_index, sizeof(ifindex_t)); /* Write packet size. */ stream_putw_at(s, 0, stream_get_endp(s)); if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( "Send EVPN_ADD %u %pI4 tenant vrf %s(%u) SVI index %u to %s", zevpn->vni, &zevpn->local_vtep_ip, vrf_id_to_name(zevpn->vrf_id), zevpn->vrf_id, (zevpn->svi_if ? zevpn->svi_if->ifindex : 0), zebra_route_string(client->proto)); client->vniadd_cnt++; rc = zserv_send_message(client, s); if (!(zevpn->flags & ZEVPN_READY_FOR_BGP)) { zevpn->flags |= ZEVPN_READY_FOR_BGP; /* once the EVPN is sent the ES-EVIs can also be replayed * to BGP */ zebra_evpn_update_all_es(zevpn); } return rc; } /* * Inform BGP about local EVPN deletion. */ int zebra_evpn_send_del_to_client(struct zebra_evpn *zevpn) { struct zserv *client; struct stream *s; client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); /* BGP may not be running. */ if (!client) return 0; if (zevpn->flags & ZEVPN_READY_FOR_BGP) { zevpn->flags &= ~ZEVPN_READY_FOR_BGP; /* the ES-EVIs must be removed from BGP before the EVPN is */ zebra_evpn_update_all_es(zevpn); } s = stream_new(ZEBRA_MAX_PACKET_SIZ); stream_reset(s); zclient_create_header(s, ZEBRA_VNI_DEL, zebra_vrf_get_evpn_id()); stream_putl(s, zevpn->vni); /* Write packet size. */ stream_putw_at(s, 0, stream_get_endp(s)); if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug("Send EVPN_DEL %u to %s", zevpn->vni, zebra_route_string(client->proto)); client->vnidel_cnt++; return zserv_send_message(client, s); } /* * See if remote VTEP matches with prefix. */ static int zebra_evpn_vtep_match(struct in_addr *vtep_ip, struct zebra_vtep *zvtep) { return (IPV4_ADDR_SAME(vtep_ip, &zvtep->vtep_ip)); } /* * Locate remote VTEP in EVPN hash table. */ struct zebra_vtep *zebra_evpn_vtep_find(struct zebra_evpn *zevpn, struct in_addr *vtep_ip) { struct zebra_vtep *zvtep; if (!zevpn) return NULL; for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep->next) { if (zebra_evpn_vtep_match(vtep_ip, zvtep)) break; } return zvtep; } /* * Add remote VTEP to EVPN hash table. */ struct zebra_vtep *zebra_evpn_vtep_add(struct zebra_evpn *zevpn, struct in_addr *vtep_ip, int flood_control) { struct zebra_vtep *zvtep; zvtep = XCALLOC(MTYPE_ZEVPN_VTEP, sizeof(struct zebra_vtep)); zvtep->vtep_ip = *vtep_ip; zvtep->flood_control = flood_control; if (zevpn->vteps) zevpn->vteps->prev = zvtep; zvtep->next = zevpn->vteps; zevpn->vteps = zvtep; return zvtep; } /* * Remove remote VTEP from EVPN hash table. */ int zebra_evpn_vtep_del(struct zebra_evpn *zevpn, struct zebra_vtep *zvtep) { if (zvtep->next) zvtep->next->prev = zvtep->prev; if (zvtep->prev) zvtep->prev->next = zvtep->next; else zevpn->vteps = zvtep->next; zvtep->prev = zvtep->next = NULL; XFREE(MTYPE_ZEVPN_VTEP, zvtep); return 0; } /* * Delete all remote VTEPs for this EVPN (upon VNI delete). Also * uninstall from kernel if asked to. */ int zebra_evpn_vtep_del_all(struct zebra_evpn *zevpn, int uninstall) { struct zebra_vtep *zvtep, *zvtep_next; if (!zevpn) return -1; for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep_next) { zvtep_next = zvtep->next; if (uninstall) zebra_evpn_vtep_uninstall(zevpn, &zvtep->vtep_ip); zebra_evpn_vtep_del(zevpn, zvtep); } return 0; } /* * Install remote VTEP into the kernel if the remote VTEP has asked * for head-end-replication. */ int zebra_evpn_vtep_install(struct zebra_evpn *zevpn, struct zebra_vtep *zvtep) { if (is_vxlan_flooding_head_end() && (zvtep->flood_control == VXLAN_FLOOD_HEAD_END_REPL)) { if (ZEBRA_DPLANE_REQUEST_FAILURE == dplane_vtep_add(zevpn->vxlan_if, &zvtep->vtep_ip, zevpn->vni)) return -1; } return 0; } /* * Uninstall remote VTEP from the kernel. */ int zebra_evpn_vtep_uninstall(struct zebra_evpn *zevpn, struct in_addr *vtep_ip) { if (!zevpn->vxlan_if) { zlog_debug("VNI %u hash %p couldn't be uninstalled - no intf", zevpn->vni, zevpn); return -1; } if (ZEBRA_DPLANE_REQUEST_FAILURE == dplane_vtep_delete(zevpn->vxlan_if, vtep_ip, zevpn->vni)) return -1; return 0; } /* * Install or uninstall flood entries in the kernel corresponding to * remote VTEPs. This is invoked upon change to BUM handling. */ void zebra_evpn_handle_flooding_remote_vteps(struct hash_bucket *bucket, void *zvrf) { struct zebra_evpn *zevpn; struct zebra_vtep *zvtep; zevpn = (struct zebra_evpn *)bucket->data; if (!zevpn) return; for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep->next) { if (is_vxlan_flooding_head_end()) zebra_evpn_vtep_install(zevpn, zvtep); else zebra_evpn_vtep_uninstall(zevpn, &zvtep->vtep_ip); } } /* * Cleanup EVPN/VTEP and update kernel */ void zebra_evpn_cleanup_all(struct hash_bucket *bucket, void *arg) { struct zebra_evpn *zevpn = NULL; zevpn = (struct zebra_evpn *)bucket->data; /* 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); /* Delete the hash entry. */ zebra_evpn_del(zevpn); } static void zebra_evpn_process_sync_macip_add(struct zebra_evpn *zevpn, const struct ethaddr *macaddr, uint16_t ipa_len, const struct ipaddr *ipaddr, uint8_t flags, uint32_t seq, const esi_t *esi) { struct sync_mac_ip_ctx ctx; char ipbuf[INET6_ADDRSTRLEN]; bool sticky; bool remote_gw; struct zebra_neigh *n = NULL; sticky = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY); remote_gw = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW); /* if sticky or remote-gw ignore updates from the peer */ if (sticky || remote_gw) { if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_NEIGH || IS_ZEBRA_DEBUG_EVPN_MH_MAC) zlog_debug( "Ignore sync-macip vni %u mac %pEA%s%s%s%s", zevpn->vni, macaddr, ipa_len ? " IP " : "", ipa_len ? ipaddr2str(ipaddr, ipbuf, sizeof(ipbuf)) : "", sticky ? " sticky" : "", remote_gw ? " remote_gw" : ""); return; } if (ipa_len) { n = zebra_evpn_neigh_lookup(zevpn, ipaddr); if (n && !zebra_evpn_neigh_is_bgp_seq_ok(zevpn, n, macaddr, seq, true)) return; } memset(&ctx, 0, sizeof(ctx)); ctx.mac = zebra_evpn_proc_sync_mac_update( zevpn, macaddr, ipa_len, ipaddr, flags, seq, esi, &ctx); if (ctx.ignore_macip || !ctx.mac || !ipa_len) return; zebra_evpn_proc_sync_neigh_update(zevpn, n, ipa_len, ipaddr, flags, seq, esi, &ctx); } /************************** remote mac-ip handling **************************/ /* Process a remote MACIP add from BGP. */ void zebra_evpn_rem_macip_add(vni_t vni, const struct ethaddr *macaddr, uint16_t ipa_len, const struct ipaddr *ipaddr, uint8_t flags, uint32_t seq, struct in_addr vtep_ip, const esi_t *esi) { struct zebra_evpn *zevpn; struct zebra_vtep *zvtep; struct zebra_mac *mac = NULL; struct interface *ifp = NULL; struct zebra_if *zif = NULL; struct zebra_vrf *zvrf; /* Locate EVPN hash entry - expected to exist. */ zevpn = zebra_evpn_lookup(vni); if (!zevpn) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug("Unknown VNI %u upon remote MACIP ADD", vni); return; } ifp = zevpn->vxlan_if; if (ifp) zif = ifp->info; if (!ifp || !if_is_operative(ifp) || !zif || !zif->brslave_info.br_if) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( "Ignoring remote MACIP ADD VNI %u, invalid interface state or info", vni); return; } /* Type-2 routes from another PE can be interpreted as remote or * SYNC based on the destination ES - * SYNC - if ES is local * REMOTE - if ES is not local */ if (flags & ZEBRA_MACIP_TYPE_SYNC_PATH) { struct zebra_evpn_es *es; es = zebra_evpn_es_find(esi); if (es && (es->flags & ZEBRA_EVPNES_READY_FOR_BGP)) { zebra_evpn_process_sync_macip_add(zevpn, macaddr, ipa_len, ipaddr, flags, seq, esi); } else { if (IS_ZEBRA_DEBUG_EVPN_MH_ES) { char esi_str[ESI_STR_LEN]; esi_to_str(esi, esi_str, sizeof(esi_str)); zlog_debug( "Ignore sync-macip add; ES %s is not ready", esi_str); } } return; } /* The remote VTEP specified should normally exist, but it is * possible that when peering comes up, peer may advertise MACIP * routes before advertising type-3 routes. */ if (vtep_ip.s_addr) { zvtep = zebra_evpn_vtep_find(zevpn, &vtep_ip); if (!zvtep) { zvtep = zebra_evpn_vtep_add(zevpn, &vtep_ip, VXLAN_FLOOD_DISABLED); if (!zvtep) { flog_err( EC_ZEBRA_VTEP_ADD_FAILED, "Failed to add remote VTEP, VNI %u zevpn %p upon remote MACIP ADD", vni, zevpn); return; } zebra_evpn_vtep_install(zevpn, zvtep); } } zvrf = zebra_vrf_get_evpn(); if (zebra_evpn_mac_remote_macip_add(zevpn, zvrf, macaddr, ipa_len, ipaddr, &mac, vtep_ip, flags, seq, esi) != 0) return; zebra_evpn_neigh_remote_macip_add(zevpn, zvrf, ipaddr, mac, vtep_ip, flags, seq); } /* Process a remote MACIP delete from BGP. */ void zebra_evpn_rem_macip_del(vni_t vni, const struct ethaddr *macaddr, uint16_t ipa_len, const struct ipaddr *ipaddr, struct in_addr vtep_ip) { struct zebra_evpn *zevpn; struct zebra_mac *mac = NULL; struct zebra_neigh *n = NULL; struct interface *ifp = NULL; struct zebra_if *zif = NULL; struct zebra_ns *zns; struct zebra_l2info_vxlan *vxl; struct zebra_vrf *zvrf; char buf1[INET6_ADDRSTRLEN]; /* Locate EVPN hash entry - expected to exist. */ zevpn = zebra_evpn_lookup(vni); if (!zevpn) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug("Unknown VNI %u upon remote MACIP DEL", vni); return; } ifp = zevpn->vxlan_if; if (ifp) zif = ifp->info; if (!ifp || !if_is_operative(ifp) || !zif || !zif->brslave_info.br_if) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( "Ignoring remote MACIP DEL VNI %u, invalid interface state or info", vni); return; } zns = zebra_ns_lookup(NS_DEFAULT); vxl = &zif->l2info.vxl; mac = zebra_evpn_mac_lookup(zevpn, macaddr); if (ipa_len) n = zebra_evpn_neigh_lookup(zevpn, ipaddr); if (n && !mac) { zlog_warn( "Failed to locate MAC %pEA for neigh %pIA VNI %u upon remote MACIP DEL", macaddr, ipaddr, vni); return; } /* If the remote mac or neighbor doesn't exist there is nothing * more to do. Otherwise, uninstall the entry and then remove it. */ if (!mac && !n) return; zvrf = zevpn->vxlan_if->vrf->info; /* Ignore the delete if this mac is a gateway mac-ip */ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) && CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)) { zlog_warn( "Ignore remote MACIP DEL VNI %u MAC %pEA%s%s as MAC is already configured as gateway MAC", vni, macaddr, ipa_len ? " IP " : "", ipa_len ? ipaddr2str(ipaddr, buf1, sizeof(buf1)) : ""); return; } /* Uninstall remote neighbor or MAC. */ if (n) zebra_evpn_neigh_remote_uninstall(zevpn, zvrf, n, mac, ipaddr); else { /* DAD: when MAC is freeze state as remote learn event, * remote mac-ip delete event is received will result in freeze * entry removal, first fetch kernel for the same entry present * as LOCAL and reachable, avoid deleting this entry instead * use kerenel local entry to update during unfreeze time. */ if (zvrf->dad_freeze && CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE) && CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( "%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); } if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { if (!ipa_len) zebra_evpn_sync_mac_del(mac); } else if (CHECK_FLAG(mac->flags, ZEBRA_NEIGH_REMOTE)) { zebra_evpn_rem_mac_del(zevpn, mac); } } } /************************** EVPN BGP config management ************************/ void zebra_evpn_cfg_cleanup(struct hash_bucket *bucket, void *ctxt) { struct zebra_evpn *zevpn = NULL; zevpn = (struct zebra_evpn *)bucket->data; zevpn->advertise_gw_macip = 0; zevpn->advertise_svi_macip = 0; zevpn->advertise_subnet = 0; zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_REMOTE_NEIGH | DEL_REMOTE_NEIGH_FROM_VTEP); zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_REMOTE_MAC | DEL_REMOTE_MAC_FROM_VTEP); zebra_evpn_vtep_del_all(zevpn, 1); }