diff options
Diffstat (limited to 'zebra')
| -rw-r--r-- | zebra/connected.c | 4 | ||||
| -rw-r--r-- | zebra/kernel_socket.c | 8 | ||||
| -rw-r--r-- | zebra/main.c | 7 | ||||
| -rw-r--r-- | zebra/redistribute.c | 2 | ||||
| -rw-r--r-- | zebra/rib.h | 3 | ||||
| -rw-r--r-- | zebra/rt_netlink.c | 4 | ||||
| -rw-r--r-- | zebra/zebra_ns.c | 3 | ||||
| -rw-r--r-- | zebra/zebra_ns.h | 3 | ||||
| -rw-r--r-- | zebra/zebra_rib.c | 27 | ||||
| -rw-r--r-- | zebra/zebra_vrf.c | 6 | ||||
| -rw-r--r-- | zebra/zebra_vrf.h | 4 | ||||
| -rw-r--r-- | zebra/zebra_vty.c | 282 | ||||
| -rw-r--r-- | zebra/zebra_vxlan.c | 2656 | ||||
| -rw-r--r-- | zebra/zebra_vxlan.h | 30 | ||||
| -rw-r--r-- | zebra/zebra_vxlan_null.c | 24 | ||||
| -rw-r--r-- | zebra/zebra_vxlan_private.h | 156 | ||||
| -rw-r--r-- | zebra/zserv.c | 49 | ||||
| -rw-r--r-- | zebra/zserv.h | 2 |
18 files changed, 2996 insertions, 274 deletions
diff --git a/zebra/connected.c b/zebra/connected.c index 9c0a3af8e3..7b949c5041 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -396,10 +396,10 @@ void connected_down(struct interface *ifp, struct connected *ifc) * head. */ rib_delete(afi, SAFI_UNICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0, - &p, NULL, &nh, 0, 0, false); + &p, NULL, &nh, 0, 0, false, NULL); rib_delete(afi, SAFI_MULTICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, - 0, &p, NULL, &nh, 0, 0, false); + 0, &p, NULL, &nh, 0, 0, false, NULL); if (IS_ZEBRA_DEBUG_RIB_DETAILED) { char buf[PREFIX_STRLEN]; diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index d45a502543..9fd7bb1c24 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -1039,7 +1039,7 @@ void rtm_read(struct rt_msghdr *rtm) if (rtm->rtm_type == RTM_CHANGE) rib_delete(AFI_IP, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL, - NULL, 0, 0, true); + NULL, 0, 0, true, NULL); if (!nh.type) { nh.type = NEXTHOP_TYPE_IPV4; @@ -1054,7 +1054,7 @@ void rtm_read(struct rt_msghdr *rtm) else rib_delete(AFI_IP, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL, - &nh, 0, 0, true); + &nh, 0, 0, true, NULL); } if (dest.sa.sa_family == AF_INET6) { /* One day we might have a debug section here like one in the @@ -1085,7 +1085,7 @@ void rtm_read(struct rt_msghdr *rtm) if (rtm->rtm_type == RTM_CHANGE) rib_delete(AFI_IP6, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL, - NULL, 0, 0, true); + NULL, 0, 0, true, NULL); if (!nh.type) { nh.type = ifindex ? NEXTHOP_TYPE_IPV6_IFINDEX @@ -1102,7 +1102,7 @@ void rtm_read(struct rt_msghdr *rtm) else rib_delete(AFI_IP6, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL, - &nh, 0, 0, true); + &nh, 0, 0, true, NULL); } } diff --git a/zebra/main.c b/zebra/main.c index 5eb8e75b87..e26c8e3d69 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -300,7 +300,6 @@ int main(int argc, char **argv) zebra_if_init(); zebra_debug_init(); router_id_cmd_init(); - zebra_vty_init(); access_list_init(); prefix_list_init(); #if defined(HAVE_RTADV) @@ -322,6 +321,12 @@ int main(int argc, char **argv) * routing socket. */ zebra_ns_init(); + /* + * Initialize show/config command after the vrf initialization is + * complete + */ + zebra_vty_init(); + #if defined(HANDLE_ZAPI_FUZZING) if (fuzzing) { zserv_read_file(fuzzing); diff --git a/zebra/redistribute.c b/zebra/redistribute.c index cbf77765a3..3c6a2a7daf 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -570,7 +570,7 @@ int zebra_del_import_table_entry(struct route_node *rn, struct route_entry *re) rib_delete(afi, SAFI_UNICAST, re->vrf_id, ZEBRA_ROUTE_TABLE, re->table, re->flags, &p, NULL, re->nexthop, - zebrad.rtm_table_default, re->metric, false); + zebrad.rtm_table_default, re->metric, false, NULL); return 0; } diff --git a/zebra/rib.h b/zebra/rib.h index 804372f18e..c7e83480ca 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -305,7 +305,8 @@ extern int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *, extern void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, u_short instance, int flags, struct prefix *p, struct prefix_ipv6 *src_p, const struct nexthop *nh, - u_int32_t table_id, u_int32_t metric, bool fromkernel); + u_int32_t table_id, u_int32_t metric, bool fromkernel, + struct ethaddr *rmac); extern struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t, union g_addr *, diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 910f9b3d93..a77814668d 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -543,13 +543,13 @@ static int netlink_route_change_read_unicast(struct sockaddr_nl *snl, memcpy(&nh.gate, gate, sz); rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, flags, &p, NULL, &nh, - table, metric, true); + table, metric, true, NULL); } else { /* XXX: need to compare the entire list of nexthops * here for NLM_F_APPEND stupidity */ rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, flags, &p, NULL, NULL, - table, metric, true); + table, metric, true, NULL); } } diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 602dc5ea41..5ede948e0a 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -30,6 +30,7 @@ #include "zebra_vrf.h" #include "zebra_memory.h" #include "rt.h" +#include "zebra_vxlan.h" DEFINE_MTYPE(ZEBRA, ZEBRA_NS, "Zebra Name Space") @@ -49,6 +50,7 @@ int zebra_ns_enable(ns_id_t ns_id, void **info) #endif zns->if_table = route_table_init(); + zebra_vxlan_ns_init(zns); kernel_init(zns); interface_list(zns); route_read(zns); @@ -61,6 +63,7 @@ int zebra_ns_disable(ns_id_t ns_id, void **info) struct zebra_ns *zns = (struct zebra_ns *)(*info); route_table_finish(zns->if_table); + zebra_vxlan_ns_disable(zns); #if defined(HAVE_RTADV) rtadv_terminate(zns); #endif diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index 6cfba93e50..0c340d8d59 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -49,6 +49,9 @@ struct zebra_ns { struct route_table *if_table; + /* L3-VNI hash table (for EVPN). Only in default instance */ + struct hash *l3vni_table; + #if defined(HAVE_RTADV) struct rtadv rtadv; #endif /* HAVE_RTADV */ diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 83834899c8..41e14459b1 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -50,6 +50,7 @@ #include "zebra/zebra_rnh.h" #include "zebra/interface.h" #include "zebra/connected.h" +#include "zebra/zebra_vxlan.h" DEFINE_HOOK(rib_update, (struct route_node * rn, const char *reason), (rn, reason)) @@ -259,7 +260,8 @@ struct nexthop *route_entry_nexthop_ipv4_ifindex_add(struct route_entry *re, /*Pending: need to think if null ifp here is ok during bootup? There was a crash because ifp here was coming to be NULL */ if (ifp) - if (connected_is_unnumbered(ifp)) { + if (connected_is_unnumbered(ifp) || + CHECK_FLAG(re->flags, ZEBRA_FLAG_EVPN_ROUTE)) { SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK); } @@ -839,7 +841,9 @@ static unsigned nexthop_active_check(struct route_node *rn, case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: family = AFI_IP; - if (nexthop_active(AFI_IP, re, nexthop, set, rn)) + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_EVPN_RVTEP)) + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else if (nexthop_active(AFI_IP, re, nexthop, set, rn)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); else UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); @@ -2314,7 +2318,8 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, u_short instance, int flags, struct prefix *p, struct prefix_ipv6 *src_p, const struct nexthop *nh, - u_int32_t table_id, u_int32_t metric, bool fromkernel) + u_int32_t table_id, u_int32_t metric, bool fromkernel, + struct ethaddr *rmac) { struct route_table *table; struct route_node *rn; @@ -2467,6 +2472,22 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, return; } + + if (CHECK_FLAG(flags, ZEBRA_FLAG_EVPN_ROUTE)) { + struct nexthop *tmp_nh; + + for (ALL_NEXTHOPS(re->nexthop, tmp_nh)) { + struct ipaddr vtep_ip; + + memset(&vtep_ip, 0, sizeof(struct ipaddr)); + vtep_ip.ipa_type = IPADDR_V4; + memcpy(&(vtep_ip.ipaddr_v4), + &(tmp_nh->gate.ipv4), + sizeof(struct in_addr)); + zebra_vxlan_evpn_vrf_route_del(re->vrf_id, rmac, + &vtep_ip, p); + } + } rib_delnode(rn, same); } diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 0d0a8dd747..1ae9eac61f 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -250,6 +250,10 @@ static int zebra_vrf_delete(struct vrf *vrf) route_table_finish(zvrf->rnh_table[afi]); route_table_finish(zvrf->import_check_table[afi]); } + + /* cleanup evpn states for vrf */ + zebra_vxlan_vrf_delete(zvrf); + list_delete_all_node(zvrf->rid_all_sorted_list); list_delete_all_node(zvrf->rid_lo_sorted_list); XFREE(MTYPE_ZEBRA_VRF, zvrf); @@ -474,6 +478,8 @@ static int vrf_config_write(struct vty *vty) if (strcmp(zvrf_name(zvrf), VRF_DEFAULT_NAME)) { vty_out(vty, "vrf %s\n", zvrf_name(zvrf)); + if (zvrf->l3vni) + vty_out(vty, " vni %u\n", zvrf->l3vni); vty_out(vty, "!\n"); } } diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index 1dfc0b3eb8..c7a0717ee8 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -24,6 +24,7 @@ #include <zebra/zebra_ns.h> #include <zebra/zebra_pw.h> +#include <lib/vxlan.h> /* MPLS (Segment Routing) global block */ typedef struct mpls_srgb_t_ { @@ -114,6 +115,9 @@ struct zebra_vrf { */ int advertise_gw_macip; + /* l3-vni info */ + vni_t l3vni; + /* Route Installs */ uint64_t installs; uint64_t removals; diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index ccd0c703d4..82b0157ad3 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -49,6 +49,7 @@ #include "zebra/zserv.h" #include "zebra/router-id.h" #include "zebra/ipforward.h" +#include "zebra/zebra_vxlan_private.h" extern int allow_delete; @@ -65,6 +66,14 @@ static void vty_show_ip_route_summary(struct vty *vty, static void vty_show_ip_route_summary_prefix(struct vty *vty, struct route_table *table); +/* + * special macro to allow us to get the correct zebra_vrf + */ +#define ZEBRA_DECLVAR_CONTEXT(A, B) \ + struct vrf *A = VTY_GET_CONTEXT(vrf); \ + struct zebra_vrf *B = \ + (vrf) ? vrf->info : NULL; \ + /* VNI range as per RFC 7432 */ #define CMD_VNI_RANGE "(1-16777215)" @@ -1896,6 +1905,110 @@ DEFUN (show_vrf, return CMD_SUCCESS; } +DEFUN (vrf_vni_mapping, + vrf_vni_mapping_cmd, + "vni " CMD_VNI_RANGE, + "VNI\n" + "VNI-ID\n") +{ + int ret = 0; + + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); + vni_t vni = strtoul(argv[1]->arg, NULL, 10); + char err[ERR_STR_SZ]; + + assert(vrf); + assert(zvrf); + + ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 1); + if (ret != 0) { + vty_out(vty, "%s\n", err); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (no_vrf_vni_mapping, + no_vrf_vni_mapping_cmd, + "no vni " CMD_VNI_RANGE, + NO_STR + "VNI\n" + "VNI-ID") +{ + int ret = 0; + char err[ERR_STR_SZ]; + vni_t vni = strtoul(argv[2]->arg, NULL, 10); + + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); + + assert(vrf); + assert(zvrf); + + ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0); + if (ret != 0) { + vty_out(vty, "%s\n", err); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +/* show vrf */ +DEFUN (show_vrf_vni, + show_vrf_vni_cmd, + "show vrf vni [json]", + SHOW_STR + "VRF\n" + "VNI\n" + JSON_STR) +{ + struct vrf *vrf; + struct zebra_vrf *zvrf; + json_object *json = NULL; + json_object *json_vrfs = NULL; + u_char uj = use_json(argc, argv); + + if (uj) { + json = json_object_new_object(); + json_vrfs = json_object_new_array(); + } + + RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) { + zvrf = vrf->info; + if (!zvrf) + continue; + + if (!zvrf->l3vni) + continue; + + if (!uj) { + vty_out(vty, "vrf: %s VNI: %u", + zvrf_name(zvrf), + zvrf->l3vni); + vty_out(vty, "\n"); + } else { + json_object *json_vrf = NULL; + + json_vrf = json_object_new_object(); + json_object_string_add(json_vrf, "vrf", + zvrf_name(zvrf)); + json_object_int_add(json_vrf, "l3vni", + zvrf->l3vni); + json_object_array_add(json_vrfs, json_vrf); + } + } + + if (uj) { + json_object_object_add(json, "vrfs", json_vrfs); + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + + return CMD_SUCCESS; +} + DEFUN (show_evpn_vni, show_evpn_vni_cmd, "show evpn vni [json]", @@ -1931,6 +2044,161 @@ DEFUN (show_evpn_vni_vni, return CMD_SUCCESS; } +DEFUN (show_evpn_l3vni, + show_evpn_l3vni_cmd, + "show evpn l3vni [json]", + SHOW_STR + "EVPN\n" + "L3 VNI\n" + JSON_STR) +{ + u_char uj = use_json(argc, argv); + + zebra_vxlan_print_l3vnis(vty, uj); + return CMD_SUCCESS; +} + +DEFUN (show_evpn_l3vni_vni, + show_evpn_l3vni_vni_cmd, + "show evpn l3vni " CMD_VNI_RANGE "[json]", + SHOW_STR + "EVPN\n" + "L3 VxLAN Network Identifier\n" + "VNI number\n" + JSON_STR) +{ + vni_t vni; + u_char uj = use_json(argc, argv); + + vni = strtoul(argv[3]->arg, NULL, 10); + zebra_vxlan_print_l3vni(vty, vni, uj); + return CMD_SUCCESS; +} + +DEFUN (show_evpn_rmac_l3vni_mac, + show_evpn_rmac_l3vni_mac_cmd, + "show evpn rmac l3vni " CMD_VNI_RANGE " mac WORD [json]", + SHOW_STR + "EVPN\n" + "RMAC\n" + "L3-VNI\n" + "VNI number\n" + "MAC\n" + "mac-address (e.g. 0a:0a:0a:0a:0a:0a)\n" + JSON_STR) +{ + vni_t l3vni = 0; + struct ethaddr mac; + u_char uj = use_json(argc, argv); + + l3vni = strtoul(argv[4]->arg, NULL, 10); + if (!prefix_str2mac(argv[6]->arg, &mac)) { + vty_out(vty, "%% Malformed MAC address\n"); + return CMD_WARNING; + } + zebra_vxlan_print_specific_rmac_l3vni(vty, l3vni, &mac, uj); + return CMD_SUCCESS; +} + +DEFUN (show_evpn_rmac_l3vni, + show_evpn_rmac_l3vni_cmd, + "show evpn rmac l3vni " CMD_VNI_RANGE "[json]", + SHOW_STR + "EVPN\n" + "RMAC\n" + "L3-VNI\n" + "VNI number\n" + JSON_STR) +{ + vni_t l3vni = 0; + u_char uj = use_json(argc, argv); + + l3vni = strtoul(argv[4]->arg, NULL, 10); + zebra_vxlan_print_rmacs_l3vni(vty, l3vni, uj); + + return CMD_SUCCESS; +} + +DEFUN (show_evpn_rmac_l3vni_all, + show_evpn_rmac_l3vni_all_cmd, + "show evpn rmac l3vni all [json]", + SHOW_STR + "EVPN\n" + "RMAC addresses\n" + "L3-VNI\n" + "All VNIs\n" + JSON_STR) +{ + u_char uj = use_json(argc, argv); + + zebra_vxlan_print_rmacs_all_l3vni(vty, uj); + + return CMD_SUCCESS; +} + +DEFUN (show_evpn_nh_l3vni_ip, + show_evpn_nh_l3vni_ip_cmd, + "show evpn next-hops l3vni " CMD_VNI_RANGE " ip WORD [json]", + SHOW_STR + "EVPN\n" + "Remote Vteps\n" + "L3-VNI\n" + "VNI number\n" + "Ip address\n" + "Host address (ipv4 or ipv6)\n" + JSON_STR) +{ + vni_t l3vni; + struct ipaddr ip; + u_char uj = use_json(argc, argv); + + l3vni = strtoul(argv[4]->arg, NULL, 10); + if (str2ipaddr(argv[6]->arg, &ip) != 0) { + if (!uj) + vty_out(vty, "%% Malformed Neighbor address\n"); + return CMD_WARNING; + } + zebra_vxlan_print_specific_nh_l3vni(vty, l3vni, &ip, uj); + + return CMD_SUCCESS; +} + +DEFUN (show_evpn_nh_l3vni, + show_evpn_nh_l3vni_cmd, + "show evpn next-hops l3vni " CMD_VNI_RANGE "[json]", + SHOW_STR + "EVPN\n" + "Remote Vteps\n" + "L3-VNI\n" + "VNI number\n" + JSON_STR) +{ + vni_t l3vni; + u_char uj = use_json(argc, argv); + + l3vni = strtoul(argv[4]->arg, NULL, 10); + zebra_vxlan_print_nh_l3vni(vty, l3vni, uj); + + return CMD_SUCCESS; +} + +DEFUN (show_evpn_nh_l3vni_all, + show_evpn_nh_l3vni_all_cmd, + "show evpn next-hops l3vni all [json]", + SHOW_STR + "EVPN\n" + "Remote VTEPs\n" + "L3-VNI\n" + "All VNIs\n" + JSON_STR) +{ + u_char uj = use_json(argc, argv); + + zebra_vxlan_print_nh_all_l3vni(vty, uj); + + return CMD_SUCCESS; +} + DEFUN (show_evpn_mac_vni, show_evpn_mac_vni_cmd, "show evpn mac vni " CMD_VNI_RANGE "[json]", @@ -2601,6 +2869,7 @@ void zebra_vty_init(void) install_element(CONFIG_NODE, &no_zebra_packet_process_cmd); install_element(VIEW_NODE, &show_vrf_cmd); + install_element(VIEW_NODE, &show_vrf_vni_cmd); install_element(VIEW_NODE, &show_route_cmd); install_element(VIEW_NODE, &show_route_detail_cmd); install_element(VIEW_NODE, &show_route_summary_cmd); @@ -2626,6 +2895,14 @@ void zebra_vty_init(void) install_element(VIEW_NODE, &show_evpn_vni_cmd); install_element(VIEW_NODE, &show_evpn_vni_vni_cmd); + install_element(VIEW_NODE, &show_evpn_l3vni_cmd); + install_element(VIEW_NODE, &show_evpn_l3vni_vni_cmd); + install_element(VIEW_NODE, &show_evpn_rmac_l3vni_mac_cmd); + install_element(VIEW_NODE, &show_evpn_rmac_l3vni_cmd); + install_element(VIEW_NODE, &show_evpn_rmac_l3vni_all_cmd); + install_element(VIEW_NODE, &show_evpn_nh_l3vni_ip_cmd); + install_element(VIEW_NODE, &show_evpn_nh_l3vni_cmd); + install_element(VIEW_NODE, &show_evpn_nh_l3vni_all_cmd); install_element(VIEW_NODE, &show_evpn_mac_vni_cmd); install_element(VIEW_NODE, &show_evpn_mac_vni_all_cmd); install_element(VIEW_NODE, &show_evpn_mac_vni_all_vtep_cmd); @@ -2635,4 +2912,9 @@ void zebra_vty_init(void) install_element(VIEW_NODE, &show_evpn_neigh_vni_all_cmd); install_element(VIEW_NODE, &show_evpn_neigh_vni_neigh_cmd); install_element(VIEW_NODE, &show_evpn_neigh_vni_vtep_cmd); + + install_element(CONFIG_NODE, &no_vrf_vni_mapping_cmd); + install_element(VRF_NODE, &vrf_vni_mapping_cmd); + install_element(VRF_NODE, &no_vrf_vni_mapping_cmd); + } diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 9c70b55a1a..0ef1802367 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -48,7 +48,9 @@ #include "zebra/zebra_l2.h" #include "lib/json.h" +DEFINE_MTYPE_STATIC(ZEBRA, HOST_PREFIX, "host prefix"); DEFINE_MTYPE_STATIC(ZEBRA, ZVNI, "VNI hash"); +DEFINE_MTYPE_STATIC(ZEBRA, ZL3VNI, "L3 VNI hash"); DEFINE_MTYPE_STATIC(ZEBRA, ZVNI_VTEP, "VNI remote VTEP"); DEFINE_MTYPE_STATIC(ZEBRA, MAC, "VNI MAC"); DEFINE_MTYPE_STATIC(ZEBRA, NEIGH, "VNI Neighbor"); @@ -61,6 +63,10 @@ static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json); static void zvni_print_neigh_hash(struct hash_backet *backet, void *ctxt); static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet, void **args); +static void zl3vni_print_nh(zebra_neigh_t *n, struct vty *vty, + json_object *json); +static void zl3vni_print_rmac(zebra_mac_t *zrmac, struct vty *vty, + json_object *json); static void zvni_print_mac(zebra_mac_t *mac, void *ctxt); static void zvni_print_mac_hash(struct hash_backet *backet, void *ctxt); static void zvni_print_mac_hash_all_vni(struct hash_backet *backet, void *ctxt); @@ -91,11 +97,46 @@ static int zvni_neigh_send_del_to_client(vni_t vni, struct ethaddr *macaddr, u_char flags); static int zvni_neigh_install(zebra_vni_t *zvni, zebra_neigh_t *n); static int zvni_neigh_uninstall(zebra_vni_t *zvni, zebra_neigh_t *n); -static zebra_vni_t *zvni_map_svi(struct interface *ifp, +static zebra_vni_t *zvni_from_svi(struct interface *ifp, struct interface *br_if); static struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if); +/* l3-vni next-hop neigh related APIs */ +static zebra_neigh_t *zl3vni_nh_lookup(zebra_l3vni_t *zl3vni, + struct ipaddr *ip); +static void *zl3vni_nh_alloc(void *p); +static zebra_neigh_t *zl3vni_nh_add(zebra_l3vni_t *zl3vni, + struct ipaddr *vtep_ip, + struct ethaddr *rmac); +static int zl3vni_nh_del(zebra_l3vni_t *zl3vni, zebra_neigh_t *n); +static int zl3vni_nh_install(zebra_l3vni_t *zl3vni, zebra_neigh_t *n); +static int zl3vni_nh_uninstall(zebra_l3vni_t *zl3vni, zebra_neigh_t *n); + +/* l3-vni rmac related APIs */ +static void zl3vni_print_rmac_hash(struct hash_backet *, void *); +static zebra_mac_t *zl3vni_rmac_lookup(zebra_l3vni_t *zl3vni, + struct ethaddr *rmac); +static void *zl3vni_rmac_alloc(void *p); +static zebra_mac_t *zl3vni_rmac_add(zebra_l3vni_t *zl3vni, + struct ethaddr *rmac); +static int zl3vni_rmac_del(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac); +static int zl3vni_rmac_install(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac); +static int zl3vni_rmac_uninstall(zebra_l3vni_t *zl3vni, + zebra_mac_t *zrmac); + +/* l3-vni related APIs*/ +static int is_vni_l3(vni_t); +static zebra_l3vni_t *zl3vni_lookup(vni_t vni); +static void *zl3vni_alloc(void *p); +static zebra_l3vni_t *zl3vni_add(vni_t vni, vrf_id_t vrf_id); +static int zl3vni_del(zebra_l3vni_t *zl3vni); +static zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t); +static struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni); +static struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni); +static void zebra_vxlan_process_l3vni_oper_up(zebra_l3vni_t *zl3vni); +static void zebra_vxlan_process_l3vni_oper_down(zebra_l3vni_t *zl3vni); + static unsigned int mac_hash_keymake(void *p); static int mac_cmp(const void *p1, const void *p2); static void *zvni_mac_alloc(void *p); @@ -385,6 +426,80 @@ static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet, json_object_object_add(json, vni_str, json_vni); } +/* print a specific next hop for an l3vni */ +static void zl3vni_print_nh(zebra_neigh_t *n, + struct vty *vty, + json_object *json) +{ + char buf1[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + struct listnode *node = NULL; + struct prefix *p = NULL; + json_object *json_hosts = NULL; + + if (!json) { + vty_out(vty, "Ip: %s\n", + ipaddr2str(&n->ip, buf2, sizeof(buf2))); + vty_out(vty, " RMAC: %s\n", + prefix_mac2str(&n->emac, buf1, sizeof(buf1))); + vty_out(vty, " Host-List:\n"); + for (ALL_LIST_ELEMENTS_RO(n->host_list, node, p)) + vty_out(vty, " %s\n", + prefix2str(p, buf2, sizeof(buf2))); + } else { + json_hosts = json_object_new_array(); + json_object_string_add(json, "ip", + ipaddr2str(&(n->ip), buf2, + sizeof(buf2))); + json_object_string_add(json, "rmac", + prefix_mac2str(&n->emac, buf2, + sizeof(buf2))); + for (ALL_LIST_ELEMENTS_RO(n->host_list, node, p)) + json_object_array_add(json_hosts, + json_object_new_string( + prefix2str(p, buf2, + sizeof(buf2)))); + json_object_object_add(json, "hosts", json_hosts); + } +} + +/* Print a specific RMAC entry */ +static void zl3vni_print_rmac(zebra_mac_t *zrmac, + struct vty *vty, + json_object *json) +{ + char buf1[ETHER_ADDR_STRLEN]; + char buf2[PREFIX_STRLEN]; + struct listnode *node = NULL; + struct prefix *p = NULL; + json_object *json_hosts = NULL; + + if (!json) { + vty_out(vty, "MAC: %s\n", + prefix_mac2str(&zrmac->macaddr, buf1, sizeof(buf1))); + vty_out(vty, " Remote VTEP: %s\n", + inet_ntoa(zrmac->fwd_info.r_vtep_ip)); + vty_out(vty, " Host-List:\n"); + for (ALL_LIST_ELEMENTS_RO(zrmac->host_list, node, p)) + vty_out(vty, " %s\n", + prefix2str(p, buf2, sizeof(buf2))); + } else { + json_hosts = json_object_new_array(); + json_object_string_add(json, "Rmac", + prefix_mac2str(&zrmac->macaddr, + buf1, + sizeof(buf1))); + json_object_string_add(json, "vtep-ip", + inet_ntoa(zrmac->fwd_info.r_vtep_ip)); + for (ALL_LIST_ELEMENTS_RO(zrmac->host_list, node, p)) + json_object_array_add(json_hosts, + json_object_new_string( + prefix2str(p, buf2, + sizeof(buf2)))); + json_object_object_add(json, "hosts", json_hosts); + } +} + /* * Print a specific MAC entry. */ @@ -603,6 +718,234 @@ static void zvni_print_mac_hash_all_vni(struct hash_backet *backet, void *ctxt) } } +static void zl3vni_print_nh_hash(struct hash_backet *backet, + void *ctx) +{ + struct nh_walk_ctx *wctx = NULL; + struct vty *vty = NULL; + struct json_object *json_vni = NULL; + struct json_object *json_nh = NULL; + zebra_neigh_t *n = NULL; + char buf1[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + + wctx = (struct nh_walk_ctx *)ctx; + vty = wctx->vty; + json_vni = wctx->json; + if (json_vni) + json_nh = json_object_new_object(); + n = (zebra_neigh_t *)backet->data; + if (!n) + return; + + if (!json_vni) { + vty_out(vty, "%-15s %-17s %6d\n", + ipaddr2str(&(n->ip), buf2, sizeof(buf2)), + prefix_mac2str(&n->emac, buf1, sizeof(buf1)), + listcount(n->host_list)); + } else { + json_object_string_add(json_nh, "nexthop-ip", + ipaddr2str(&n->ip, buf2, sizeof(buf2))); + json_object_string_add(json_nh, "rmac", + prefix_mac2str(&n->emac, buf1, + sizeof(buf1))); + json_object_int_add(json_nh, "refCnt", listcount(n->host_list)); + json_object_object_add(json_vni, + ipaddr2str(&(n->ip), buf2, sizeof(buf2)), + json_nh); + } +} + +static void zl3vni_print_nh_hash_all_vni(struct hash_backet *backet, + void **args) +{ + struct vty *vty = NULL; + json_object *json = NULL; + json_object *json_vni = NULL; + zebra_l3vni_t *zl3vni = NULL; + uint32_t num_nh = 0; + struct nh_walk_ctx wctx; + char vni_str[VNI_STR_LEN]; + + vty = (struct vty *)args[0]; + json = (struct json_object *)args[1]; + + zl3vni = (zebra_l3vni_t *)backet->data; + if (!zl3vni) { + if (json) + vty_out(vty, "{}\n"); + return; + } + + num_nh = hashcount(zl3vni->nh_table); + if (!num_nh) + return; + + if (json) { + json_vni = json_object_new_object(); + snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni); + } + + if (json == NULL) { + vty_out(vty, "\nVNI %u #Next-Hops %u\n\n", + zl3vni->vni, num_nh); + vty_out(vty, "%-15s %-17s %6s\n", "IP", + "RMAC", "Refcnt"); + } else + json_object_int_add(json_vni, "numNh", num_nh); + + memset(&wctx, 0, sizeof(struct nh_walk_ctx)); + wctx.vty = vty; + wctx.json = json_vni; + hash_iterate(zl3vni->nh_table, zl3vni_print_nh_hash, &wctx); + if (json) + json_object_object_add(json, vni_str, json_vni); +} + +static void zl3vni_print_rmac_hash_all_vni(struct hash_backet *backet, + void **args) +{ + struct vty *vty = NULL; + json_object *json = NULL; + json_object *json_vni = NULL; + zebra_l3vni_t *zl3vni = NULL; + u_int32_t num_rmacs; + struct rmac_walk_ctx wctx; + char vni_str[VNI_STR_LEN]; + + vty = (struct vty *)args[0]; + json = (struct json_object *)args[1]; + + zl3vni = (zebra_l3vni_t *)backet->data; + if (!zl3vni) { + if (json) + vty_out(vty, "{}\n"); + return; + } + + num_rmacs = hashcount(zl3vni->rmac_table); + if (!num_rmacs) + return; + + if (json) { + json_vni = json_object_new_object(); + snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni); + } + + if (json == NULL) { + vty_out(vty, "\nVNI %u #MACs %u\n\n", + zl3vni->vni, num_rmacs); + vty_out(vty, "%-17s %-21s %-6s\n", "MAC", + "Remote VTEP", "Refcnt"); + } else + json_object_int_add(json_vni, "numRmacs", num_rmacs); + + /* assign per-vni to wctx->json object to fill macs + * under the vni. Re-assign primary json object to fill + * next vni information. + */ + memset(&wctx, 0, sizeof(struct rmac_walk_ctx)); + wctx.vty = vty; + wctx.json = json_vni; + hash_iterate(zl3vni->rmac_table, zl3vni_print_rmac_hash, &wctx); + if (json) + json_object_object_add(json, vni_str, json_vni); +} + +static void zl3vni_print_rmac_hash(struct hash_backet *backet, + void *ctx) +{ + zebra_mac_t *zrmac = NULL; + struct rmac_walk_ctx *wctx = NULL; + struct vty *vty = NULL; + struct json_object *json = NULL; + struct json_object *json_rmac = NULL; + char buf[ETHER_ADDR_STRLEN]; + + wctx = (struct rmac_walk_ctx *)ctx; + vty = wctx->vty; + json = wctx->json; + if (json) + json_rmac = json_object_new_object(); + zrmac = (zebra_mac_t *)backet->data; + if (!zrmac) + return; + + if (!json) { + vty_out(vty, "%-17s %-21s %-6d\n", + prefix_mac2str(&zrmac->macaddr, buf, sizeof(buf)), + inet_ntoa(zrmac->fwd_info.r_vtep_ip), + listcount(zrmac->host_list)); + } else { + json_object_string_add(json_rmac, "rmac", + prefix_mac2str(&zrmac->macaddr, buf, + sizeof(buf))); + json_object_string_add(json_rmac, "vtep-ip", + inet_ntoa(zrmac->fwd_info.r_vtep_ip)); + json_object_int_add(json_rmac, "refcnt", + listcount(zrmac->host_list)); + json_object_object_add(json, + prefix_mac2str(&zrmac->macaddr, buf, + sizeof(buf)), + json_rmac); + } +} + +/* print a specific L3 VNI entry */ +static void zl3vni_print(zebra_l3vni_t *zl3vni, void **ctx) +{ + char buf[ETHER_ADDR_STRLEN]; + struct vty *vty = NULL; + json_object *json = NULL; + zebra_vni_t *zvni = NULL; + json_object *json_vni_list = NULL; + struct listnode *node = NULL, *nnode = NULL; + + vty = ctx[0]; + json = ctx[1]; + + if (!json) { + vty_out(vty, "VNI: %u\n", zl3vni->vni); + vty_out(vty, " Local Vtep Ip: %s", + inet_ntoa(zl3vni->local_vtep_ip)); + vty_out(vty, " Vxlan-Intf: %s\n", + zl3vni_vxlan_if_name(zl3vni)); + vty_out(vty, " SVI-If: %s\n", + zl3vni_svi_if_name(zl3vni)); + vty_out(vty, " State: %s\n", + zl3vni_state2str(zl3vni)); + vty_out(vty, " Vrf: %s\n", + zl3vni_vrf_name(zl3vni)); + vty_out(vty, " Rmac: %s\n", + zl3vni_rmac2str(zl3vni, buf, sizeof(buf))); + vty_out(vty, " L2-VNIs: "); + for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zvni)) + vty_out(vty, "%u ", zvni->vni); + vty_out(vty, "\n"); + } else { + json_vni_list = json_object_new_array(); + json_object_int_add(json, "vni", zl3vni->vni); + json_object_string_add(json, "local-vtep-ip", + inet_ntoa(zl3vni->local_vtep_ip)); + json_object_string_add(json, "vxlan-intf", + zl3vni_vxlan_if_name(zl3vni)); + json_object_string_add(json, "svi-if", + zl3vni_svi_if_name(zl3vni)); + json_object_string_add(json, "state", + zl3vni_state2str(zl3vni)); + json_object_string_add(json, "vrf", + zl3vni_vrf_name(zl3vni)); + json_object_string_add(json, "rmac", + zl3vni_rmac2str(zl3vni, buf, + sizeof(buf))); + for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zvni)) { + json_object_array_add(json_vni_list, + json_object_new_int(zvni->vni)); + } + json_object_object_add(json, "l2-vnis", json_vni_list); + } +} + /* * Print a specific VNI entry. */ @@ -619,10 +962,14 @@ static void zvni_print(zebra_vni_t *zvni, void **ctxt) vty = ctxt[0]; json = ctxt[1]; - if (json == NULL) + if (json == NULL) { vty_out(vty, "VNI: %u\n", zvni->vni); - else + vty_out(vty, " VRF: %s\n", vrf_id_to_name(zvni->vrf_id)); + } else { json_object_int_add(json, "vni", zvni->vni); + json_object_string_add(json, "vrf", + vrf_id_to_name(zvni->vrf_id)); + } if (!zvni->vxlan_if) { // unexpected if (json == NULL) @@ -682,6 +1029,56 @@ static void zvni_print(zebra_vni_t *zvni, void **ctxt) } } +/* print a L3 VNI hash entry */ +static void zl3vni_print_hash(struct hash_backet *backet, + void *ctx[]) +{ + char buf[ETHER_ADDR_STRLEN]; + struct vty *vty = NULL; + json_object *json = NULL; + json_object *json_vni = NULL; + zebra_l3vni_t *zl3vni = NULL; + + vty = (struct vty *)ctx[0]; + json = (json_object *)ctx[1]; + + zl3vni = (zebra_l3vni_t *)backet->data; + if (!zl3vni) + return; + + if (!json) { + vty_out(vty, "%-10u %-15s %-20s %-20s %-5s %-37s %-18s\n", + zl3vni->vni, + inet_ntoa(zl3vni->local_vtep_ip), + zl3vni_vxlan_if_name(zl3vni), + zl3vni_svi_if_name(zl3vni), + zl3vni_state2str(zl3vni), + zl3vni_vrf_name(zl3vni), + zl3vni_rmac2str(zl3vni, buf, sizeof(buf))); + } else { + char vni_str[VNI_STR_LEN]; + + snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni); + json_vni = json_object_new_object(); + json_object_int_add(json_vni, "vni", zl3vni->vni); + json_object_string_add(json_vni, "local-ip", + inet_ntoa(zl3vni->local_vtep_ip)); + json_object_string_add(json_vni, "vxlan-if", + zl3vni_vxlan_if_name(zl3vni)); + json_object_string_add(json_vni, "svi-if", + zl3vni_svi_if_name(zl3vni)); + json_object_string_add(json_vni, "state", + zl3vni_state2str(zl3vni)); + json_object_string_add(json_vni, "vrf", + zl3vni_vrf_name(zl3vni)); + json_object_string_add(json_vni, "rmac", + zl3vni_rmac2str(zl3vni, buf, + sizeof(buf))); + json_object_object_add(json, vni_str, json_vni); + } + +} + /* * Print a VNI hash entry - called for display of all VNIs. */ @@ -714,10 +1111,12 @@ static void zvni_print_hash(struct hash_backet *backet, void *ctxt[]) num_macs = num_valid_macs(zvni); num_neigh = hashcount(zvni->neigh_table); if (json == NULL) - vty_out(vty, "%-10u %-21s %-15s %-8u %-8u %-15u\n", zvni->vni, + vty_out(vty, "%-10u %-21s %-15s %-8u %-8u %-15u %-37s\n", + zvni->vni, zvni->vxlan_if ? zvni->vxlan_if->name : "unknown", inet_ntoa(zvni->local_vtep_ip), num_macs, num_neigh, - num_vteps); + num_vteps, + vrf_id_to_name(zvni->vrf_id)); else { char vni_str[VNI_STR_LEN]; snprintf(vni_str, VNI_STR_LEN, "%u", zvni->vni); @@ -753,11 +1152,11 @@ static int zvni_macip_send_msg_to_client(vni_t vni, struct ipaddr *ip, u_char flags, u_int16_t cmd) { - struct zserv *client; - struct stream *s; - int ipa_len; char buf[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; + int ipa_len; + struct zserv *client = NULL; + struct stream *s = NULL; client = zebra_find_client(ZEBRA_ROUTE_BGP, 0); /* BGP may not be running. */ @@ -785,12 +1184,13 @@ static int zvni_macip_send_msg_to_client(vni_t vni, stream_putc(s, flags); /* sticky mac/gateway mac */ + /* Write packet size. */ stream_putw_at(s, 0, stream_get_endp(s)); if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - "Send MACIP %s flags 0x%x MAC %s IP %s VNI %u to %s", + "Send MACIP %s flags 0x%x MAC %s IP %s L2-VNI %u to %s", (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del", flags, prefix_mac2str(macaddr, buf, sizeof(buf)), ipaddr2str(ip, buf2, sizeof(buf2)), vni, @@ -1000,7 +1400,7 @@ static void zvni_process_neigh_on_local_mac_add(zebra_vni_t *zvni, if (IS_ZEBRA_NEIGH_INACTIVE(n)) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - "neigh %s (MAC %s) on VNI %u is now ACTIVE", + "neigh %s (MAC %s) on L2-VNI %u is now ACTIVE", ipaddr2str(&n->ip, buf2, sizeof(buf2)), prefix_mac2str(&n->emac, buf, @@ -1040,7 +1440,7 @@ static void zvni_process_neigh_on_local_mac_del(zebra_vni_t *zvni, if (IS_ZEBRA_NEIGH_ACTIVE(n)) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - "neigh %s (MAC %s) on VNI %u is now INACTIVE", + "neigh %s (MAC %s) on L2-VNI %u is now INACTIVE", ipaddr2str(&n->ip, buf2, sizeof(buf2)), prefix_mac2str(&n->emac, buf, @@ -1077,7 +1477,7 @@ static void zvni_process_neigh_on_remote_mac_add(zebra_vni_t *zvni, if (IS_ZEBRA_NEIGH_ACTIVE(n)) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - "neigh %s (MAC %s) on VNI %u INACTIVE", + "neigh %s (MAC %s) on L2-VNI %u is now INACTIVE", ipaddr2str(&n->ip, buf2, sizeof(buf2)), prefix_mac2str(&n->emac, buf, @@ -1302,12 +1702,12 @@ static int zvni_add_macip_for_intf(struct interface *ifp, zebra_vni_t *zvni) static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni, struct ethaddr *macaddr, struct ipaddr *ip) { - struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan *vxl = NULL; - zebra_neigh_t *n = NULL; - zebra_mac_t *mac = NULL; char buf[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; + zebra_neigh_t *n = NULL; + zebra_mac_t *mac = NULL; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; zif = zvni->vxlan_if->info; if (!zif) @@ -1353,7 +1753,7 @@ static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni, if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - "SVI %s(%u) VNI %u, sending GW MAC %s IP %s add to BGP", + "SVI %s(%u) L2-VNI %u, sending GW MAC %s IP %s add to BGP", ifp->name, ifp->ifindex, zvni->vni, prefix_mac2str(macaddr, buf, sizeof(buf)), ipaddr2str(ip, buf2, sizeof(buf2))); @@ -1370,10 +1770,10 @@ static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni, static int zvni_gw_macip_del(struct interface *ifp, zebra_vni_t *zvni, struct ipaddr *ip) { - zebra_neigh_t *n = NULL; - zebra_mac_t *mac = NULL; char buf1[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; + zebra_neigh_t *n = NULL; + zebra_mac_t *mac = NULL; /* If the neigh entry is not present nothing to do*/ n = zvni_neigh_lookup(zvni, ip); @@ -1395,7 +1795,7 @@ static int zvni_gw_macip_del(struct interface *ifp, zebra_vni_t *zvni, if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - "SVI %s(%u) VNI %u, sending GW MAC %s IP %s del to BGP", + "SVI %s(%u) L2-VNI %u, sending GW MAC %s IP %s del to BGP", ifp->name, ifp->ifindex, zvni->vni, prefix_mac2str(&(n->emac), buf1, sizeof(buf1)), ipaddr2str(ip, buf2, sizeof(buf2))); @@ -1747,7 +2147,8 @@ static zebra_vni_t *zvni_map_vlan(struct interface *ifp, * Map SVI and associated bridge to a VNI. This is invoked upon getting * neighbor notifications, to see if they are of interest. */ -static zebra_vni_t *zvni_map_svi(struct interface *ifp, struct interface *br_if) +static zebra_vni_t *zvni_from_svi(struct interface *ifp, + struct interface *br_if) { struct zebra_ns *zns; struct route_node *rn; @@ -2133,13 +2534,15 @@ static int zvni_send_add_to_client(zebra_vni_t *zvni) zserv_create_header(s, ZEBRA_VNI_ADD, VRF_DEFAULT); stream_putl(s, zvni->vni); stream_put_in_addr(s, &zvni->local_vtep_ip); + stream_put(s, &zvni->vrf_id, sizeof(vrf_id_t)); /* tenant vrf */ /* Write packet size. */ stream_putw_at(s, 0, stream_get_endp(s)); if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Send VNI_ADD %u %s to %s", + zlog_debug("Send VNI_ADD %u %s tenant vrf %s to %s", zvni->vni, inet_ntoa(zvni->local_vtep_ip), + vrf_id_to_name(zvni->vrf_id), zebra_route_string(client->proto)); client->vniadd_cnt++; @@ -2189,10 +2592,9 @@ static void zvni_build_hash_table() /* Walk VxLAN interfaces and create VNI hash. */ zns = zebra_ns_lookup(NS_DEFAULT); for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { + vni_t vni; struct zebra_if *zif; struct zebra_l2info_vxlan *vxl; - zebra_vni_t *zvni; - vni_t vni; ifp = (struct interface *)rn->info; if (!ifp) @@ -2200,39 +2602,83 @@ static void zvni_build_hash_table() zif = ifp->info; if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) continue; - vxl = &zif->l2info.vxl; + vxl = &zif->l2info.vxl; vni = vxl->vni; - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Create VNI hash for intf %s(%u) VNI %u local IP %s", - ifp->name, ifp->ifindex, vni, - inet_ntoa(vxl->vtep_ip)); + if (is_vni_l3(vni)) { + zebra_l3vni_t *zl3vni = NULL; - /* VNI hash entry is not expected to exist. */ - zvni = zvni_lookup(vni); - if (zvni) { - zlog_err( - "VNI hash already present for IF %s(%u) VNI %u", - ifp->name, ifp->ifindex, vni); - continue; - } + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("create L3-VNI hash for Intf %s(%u) L3-VNI %u", + ifp->name, ifp->ifindex, vni); - zvni = zvni_add(vni); - if (!zvni) { - zlog_err( - "Failed to add VNI hash, IF %s(%u) VNI %u", - ifp->name, ifp->ifindex, vni); - return; - } + zl3vni = zl3vni_lookup(vni); + if (!zl3vni) { + zlog_err( + "Failed to locate L3-VNI hash at UP, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return; + } - zvni->local_vtep_ip = vxl->vtep_ip; - zvni->vxlan_if = ifp; + /* associate with vxlan_if */ + zl3vni->local_vtep_ip = vxl->vtep_ip; + zl3vni->vxlan_if = ifp; - /* Inform BGP if interface is up and mapped to bridge. */ - if (if_is_operative(ifp) && zif->brslave_info.br_if) - zvni_send_add_to_client(zvni); + /* + * 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); + + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up(zl3vni); + + } else { + zebra_vni_t *zvni = NULL; + zebra_l3vni_t *zl3vni = NULL; + 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 %s", + ifp->name, ifp->ifindex, vni, + inet_ntoa(vxl->vtep_ip)); + + /* VNI hash entry is not expected to exist. */ + zvni = zvni_lookup(vni); + if (zvni) { + zlog_err( + "VNI hash already present for IF %s(%u) L2-VNI %u", + ifp->name, ifp->ifindex, vni); + continue; + } + + zvni = zvni_add(vni); + if (!zvni) { + zlog_err( + "Failed to add VNI hash, IF %s(%u) L2-VNI %u", + ifp->name, ifp->ifindex, vni); + return; + } + + zvni->local_vtep_ip = vxl->vtep_ip; + zvni->vxlan_if = ifp; + vlan_if = zvni_map_to_svi(vxl->access_vlan, + zif->brslave_info.br_if); + if (vlan_if) { + zvni->vrf_id = vlan_if->vrf_id; + zl3vni = zl3vni_from_vrf(vlan_if->vrf_id); + if (zl3vni) + listnode_add_sort(zl3vni->l2vnis, zvni); + } + + + /* Inform BGP if intf is up and mapped to bridge. */ + if (if_is_operative(ifp) && zif->brslave_info.br_if) + zvni_send_add_to_client(zvni); + } } } @@ -2351,12 +2797,18 @@ static int zvni_vtep_uninstall(zebra_vni_t *zvni, struct in_addr *vtep_ip) */ static void zvni_cleanup_all(struct hash_backet *backet, void *zvrf) { - zebra_vni_t *zvni; + zebra_vni_t *zvni = NULL; + zebra_l3vni_t *zl3vni = NULL; zvni = (zebra_vni_t *)backet->data; if (!zvni) return; + /* remove from l3-vni list */ + zl3vni = zl3vni_from_vrf(zvni->vrf_id); + if (zl3vni) + listnode_delete(zl3vni->l2vnis, zvni); + /* Free up all neighbors and MACs, if any. */ zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH); zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC); @@ -2368,9 +2820,1288 @@ static void zvni_cleanup_all(struct hash_backet *backet, void *zvrf) zvni_del(zvni); } +/* cleanup L3VNI */ +static void zl3vni_cleanup_all(struct hash_backet *backet, + void *args) +{ + zebra_l3vni_t *zl3vni = NULL; + + zl3vni = (zebra_l3vni_t *)backet->data; + if (!zl3vni) + return; + + zebra_vxlan_process_l3vni_oper_down(zl3vni); +} + +static int is_host_present_in_host_list(struct list *list, + struct prefix *host) +{ + struct listnode *node = NULL; + struct prefix *p = NULL; + + for (ALL_LIST_ELEMENTS_RO(list, node, p)) { + if (prefix_same(p, host)) + return 1; + } + return 0; +} + +static void host_list_add_host(struct list *list, + struct prefix *host) +{ + struct prefix *p = NULL; + + p = XCALLOC(MTYPE_HOST_PREFIX, sizeof(struct prefix)); + memcpy(p, host, sizeof(struct prefix)); + + listnode_add_sort(list, p); +} + +static void host_list_delete_host(struct list *list, + struct prefix *host) +{ + struct listnode *node = NULL, *nnode = NULL, *node_to_del = NULL; + struct prefix *p = NULL; + + for (ALL_LIST_ELEMENTS(list, node, nnode, p)) { + if (prefix_same(p, host)) { + XFREE(MTYPE_HOST_PREFIX, p); + node_to_del = node; + } + } + + if (node_to_del) + list_delete_node(list, node_to_del); +} + +/* + * Look up MAC hash entry. + */ +static zebra_mac_t *zl3vni_rmac_lookup(zebra_l3vni_t *zl3vni, + struct ethaddr *rmac) +{ + zebra_mac_t tmp; + zebra_mac_t *pmac; + + memset(&tmp, 0, sizeof(tmp)); + memcpy(&tmp.macaddr, rmac, ETH_ALEN); + pmac = hash_lookup(zl3vni->rmac_table, &tmp); + + return pmac; +} + +/* + * Callback to allocate RMAC hash entry. + */ +static void *zl3vni_rmac_alloc(void *p) +{ + const zebra_mac_t *tmp_rmac = p; + zebra_mac_t *zrmac; + + zrmac = XCALLOC(MTYPE_MAC, sizeof(zebra_mac_t)); + *zrmac = *tmp_rmac; + + return ((void *)zrmac); +} + +/* + * Add RMAC entry to l3-vni + */ +static zebra_mac_t *zl3vni_rmac_add(zebra_l3vni_t *zl3vni, + struct ethaddr *rmac) +{ + zebra_mac_t tmp_rmac; + zebra_mac_t *zrmac = NULL; + + memset(&tmp_rmac, 0, sizeof(zebra_mac_t)); + memcpy(&tmp_rmac.macaddr, rmac, ETH_ALEN); + zrmac = hash_get(zl3vni->rmac_table, &tmp_rmac, zl3vni_rmac_alloc); + assert(zrmac); + + zrmac->host_list = list_new(); + zrmac->host_list->cmp = (int (*)(void *, void *))prefix_cmp; + + SET_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE); + SET_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE_RMAC); + + return zrmac; +} + +/* + * Delete MAC entry. + */ +static int zl3vni_rmac_del(zebra_l3vni_t *zl3vni, + zebra_mac_t *zrmac) +{ + zebra_mac_t *tmp_rmac; + + if (zrmac->host_list) + list_delete_and_null(&zrmac->host_list); + zrmac->host_list = NULL; + + tmp_rmac = hash_release(zl3vni->rmac_table, zrmac); + if (tmp_rmac) + XFREE(MTYPE_MAC, tmp_rmac); + + return 0; +} + +/* + * Install remote RMAC into the kernel. + */ +static int zl3vni_rmac_install(zebra_l3vni_t *zl3vni, + zebra_mac_t *zrmac) +{ + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; + + if (!(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE)) || + !(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE_RMAC))) + return 0; + + zif = zl3vni->vxlan_if->info; + if (!zif) + return -1; + + vxl = &zif->l2info.vxl; + + return kernel_add_mac(zl3vni->vxlan_if, vxl->access_vlan, + &zrmac->macaddr, + zrmac->fwd_info.r_vtep_ip, 0); +} + +/* + * Uninstall remote RMAC from the kernel. + */ +static int zl3vni_rmac_uninstall(zebra_l3vni_t *zl3vni, + zebra_mac_t *zrmac) +{ + char buf[ETHER_ADDR_STRLEN]; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; + + if (!(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE)) || + !(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE_RMAC))) + return 0; + + if (!zl3vni->vxlan_if) { + zlog_err( + "RMAC %s on L3-VNI %u hash %p couldn't be uninstalled - no vxlan_if", + prefix_mac2str(&zrmac->macaddr, buf, sizeof(buf)), + zl3vni->vni, zl3vni); + return -1; + } + + zif = zl3vni->vxlan_if->info; + if (!zif) + return -1; + + vxl = &zif->l2info.vxl; + + return kernel_del_mac(zl3vni->vxlan_if, vxl->access_vlan, + &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip, 0); +} + +/* handle rmac add */ +static int zl3vni_remote_rmac_add(zebra_l3vni_t *zl3vni, + struct ethaddr *rmac, + struct ipaddr *vtep_ip, + struct prefix *host_prefix) +{ + char buf[ETHER_ADDR_STRLEN]; + char buf1[INET6_ADDRSTRLEN]; + zebra_mac_t *zrmac = NULL; + + zrmac = zl3vni_rmac_lookup(zl3vni, rmac); + if (!zrmac) { + + zrmac = zl3vni_rmac_add(zl3vni, rmac); + if (!zrmac) { + zlog_warn( + "Failed to add RMAC %s L3VNI %u Remote VTEP %s", + prefix_mac2str(rmac, buf, + sizeof(buf)), + zl3vni->vni, ipaddr2str(vtep_ip, buf1, + sizeof(buf1))); + return -1; + } + memset(&zrmac->fwd_info, 0, sizeof(zrmac->fwd_info)); + zrmac->fwd_info.r_vtep_ip = vtep_ip->ipaddr_v4; + + /* install rmac in kernel */ + zl3vni_rmac_install(zl3vni, zrmac); + } + + if (!is_host_present_in_host_list(zrmac->host_list, host_prefix)) + host_list_add_host(zrmac->host_list, host_prefix); + return 0; +} + + +/* handle rmac delete */ +static int zl3vni_remote_rmac_del(zebra_l3vni_t *zl3vni, + struct ethaddr *rmac, + struct prefix *host_prefix) +{ + zebra_mac_t *zrmac = NULL; + + zrmac = zl3vni_rmac_lookup(zl3vni, rmac); + if (!zrmac) + return -1; + + host_list_delete_host(zrmac->host_list, host_prefix); + if (list_isempty(zrmac->host_list)) { + + /* uninstall from kernel */ + zl3vni_rmac_uninstall(zl3vni, zrmac); + + /* del the rmac entry */ + zl3vni_rmac_del(zl3vni, zrmac); + } + return 0; +} + +/* + * Look up nh hash entry on a l3-vni. + */ +static zebra_neigh_t *zl3vni_nh_lookup(zebra_l3vni_t *zl3vni, + struct ipaddr *ip) +{ + zebra_neigh_t tmp; + zebra_neigh_t *n; + + memset(&tmp, 0, sizeof(tmp)); + memcpy(&tmp.ip, ip, sizeof(struct ipaddr)); + n = hash_lookup(zl3vni->nh_table, &tmp); + + return n; +} + + +/* + * Callback to allocate NH hash entry on L3-VNI. + */ +static void *zl3vni_nh_alloc(void *p) +{ + const zebra_neigh_t *tmp_n = p; + zebra_neigh_t *n; + + n = XCALLOC(MTYPE_NEIGH, sizeof(zebra_neigh_t)); + *n = *tmp_n; + + return ((void *)n); +} + +/* + * Add neighbor entry. + */ +static zebra_neigh_t *zl3vni_nh_add(zebra_l3vni_t *zl3vni, + struct ipaddr *ip, + struct ethaddr *mac) +{ + zebra_neigh_t tmp_n; + zebra_neigh_t *n = NULL; + + memset(&tmp_n, 0, sizeof(zebra_neigh_t)); + memcpy(&tmp_n.ip, ip, sizeof(struct ipaddr)); + n = hash_get(zl3vni->nh_table, &tmp_n, zl3vni_nh_alloc); + assert(n); + + n->host_list = list_new(); + n->host_list->cmp = (int (*)(void *, void *))prefix_cmp; + + memcpy(&n->emac, mac, ETH_ALEN); + SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); + SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE_NH); + + return n; +} + +/* + * Delete neighbor entry. + */ +static int zl3vni_nh_del(zebra_l3vni_t *zl3vni, + zebra_neigh_t *n) +{ + zebra_neigh_t *tmp_n; + + if (n->host_list) + list_delete_and_null(&n->host_list); + n->host_list = NULL; + + tmp_n = hash_release(zl3vni->nh_table, n); + if (tmp_n) + XFREE(MTYPE_NEIGH, tmp_n); + + return 0; +} + +/* + * Install remote nh as neigh into the kernel. + */ +static int zl3vni_nh_install(zebra_l3vni_t *zl3vni, + zebra_neigh_t *n) +{ + if (!is_l3vni_oper_up(zl3vni)) + return -1; + + if (!(n->flags & ZEBRA_NEIGH_REMOTE) || + !(n->flags & ZEBRA_NEIGH_REMOTE_NH)) + return 0; + + return kernel_add_neigh(zl3vni->svi_if, &n->ip, &n->emac); +} + +/* + * Uninstall remote nh from the kernel. + */ +static int zl3vni_nh_uninstall(zebra_l3vni_t *zl3vni, + zebra_neigh_t *n) +{ + if (!is_l3vni_oper_up(zl3vni)) + return -1; + + if (!(n->flags & ZEBRA_NEIGH_REMOTE) || + !(n->flags & ZEBRA_NEIGH_REMOTE_NH)) + return 0; + + return kernel_del_neigh(zl3vni->svi_if, &n->ip); +} + +/* add remote vtep as a neigh entry */ +static int zl3vni_remote_nh_add(zebra_l3vni_t *zl3vni, + struct ipaddr *vtep_ip, + struct ethaddr *rmac, + struct prefix *host_prefix) +{ + char buf[ETHER_ADDR_STRLEN]; + char buf1[INET6_ADDRSTRLEN]; + zebra_neigh_t *nh = NULL; + + nh = zl3vni_nh_lookup(zl3vni, vtep_ip); + if (!nh) { + nh = zl3vni_nh_add(zl3vni, vtep_ip, rmac); + if (!nh) { + + zlog_warn( + "Failed to add NH as Neigh (IP %s MAC %s L3-VNI %u)", + ipaddr2str(vtep_ip, buf1, + sizeof(buf1)), + prefix_mac2str(rmac, buf, + sizeof(buf)), + zl3vni->vni); + return -1; + } + + /* install the nh neigh in kernel */ + zl3vni_nh_install(zl3vni, nh); + } + + if (!is_host_present_in_host_list(nh->host_list, host_prefix)) + host_list_add_host(nh->host_list, host_prefix); + + return 0; +} + +/* handle nh neigh delete */ +static int zl3vni_remote_nh_del(zebra_l3vni_t *zl3vni, + struct ipaddr *vtep_ip, + struct prefix *host_prefix) +{ + zebra_neigh_t *nh = NULL; + + nh = zl3vni_nh_lookup(zl3vni, vtep_ip); + if (!nh) + return -1; + + host_list_delete_host(nh->host_list, host_prefix); + if (list_isempty(nh->host_list)) { + + /* uninstall from kernel */ + zl3vni_nh_uninstall(zl3vni, nh); + + /* delete the nh entry */ + zl3vni_nh_del(zl3vni, nh); + } + + return 0; +} + +/* handle neigh update from kernel - the only thing of interest is to + * readd stale entries. + */ +static int zl3vni_local_nh_add_update(zebra_l3vni_t *zl3vni, + struct ipaddr *ip, u_int16_t state) +{ +#ifdef GNU_LINUX + zebra_neigh_t *n = NULL; + + n = zl3vni_nh_lookup(zl3vni, ip); + if (!n) + return 0; + + /* all next hop neigh are remote and installed by frr. + * If the kernel has aged this entry, re-install. + */ + if (state & NUD_STALE) + zl3vni_nh_install(zl3vni, n); +#endif + return 0; +} + +/* handle neigh delete from kernel */ +static int zl3vni_local_nh_del(zebra_l3vni_t *zl3vni, + struct ipaddr *ip) +{ + zebra_neigh_t *n = NULL; + + n = zl3vni_nh_lookup(zl3vni, ip); + if (!n) + return 0; + + /* all next hop neigh are remote and installed by frr. + * If we get an age out notification for these neigh entries, we have to + * install it back + */ + zl3vni_nh_install(zl3vni, n); + + return 0; +} + +/* + * Hash function for L3 VNI. + */ +static unsigned int l3vni_hash_keymake(void *p) +{ + const zebra_l3vni_t *zl3vni = p; + + return jhash_1word(zl3vni->vni, 0); +} + +/* + * Compare 2 L3 VNI hash entries. + */ +static int l3vni_hash_cmp(const void *p1, const void *p2) +{ + const zebra_l3vni_t *zl3vni1 = p1; + const zebra_l3vni_t *zl3vni2 = p2; + + return (zl3vni1->vni == zl3vni2->vni); +} + +/* + * Callback to allocate L3 VNI hash entry. + */ +static void *zl3vni_alloc(void *p) +{ + zebra_l3vni_t *zl3vni = NULL; + const zebra_l3vni_t *tmp_l3vni = p; + + zl3vni = XCALLOC(MTYPE_ZL3VNI, sizeof(zebra_l3vni_t)); + zl3vni->vni = tmp_l3vni->vni; + return ((void *)zl3vni); +} + +/* + * Look up L3 VNI hash entry. + */ +static zebra_l3vni_t *zl3vni_lookup(vni_t vni) +{ + struct zebra_ns *zns; + zebra_l3vni_t tmp_l3vni; + zebra_l3vni_t *zl3vni = NULL; + + zns = zebra_ns_lookup(NS_DEFAULT); + assert(zns); + memset(&tmp_l3vni, 0, sizeof(zebra_l3vni_t)); + tmp_l3vni.vni = vni; + zl3vni = hash_lookup(zns->l3vni_table, &tmp_l3vni); + + return zl3vni; +} + +/* + * Add L3 VNI hash entry. + */ +static zebra_l3vni_t *zl3vni_add(vni_t vni, vrf_id_t vrf_id) +{ + zebra_l3vni_t tmp_zl3vni; + struct zebra_ns *zns = NULL; + zebra_l3vni_t *zl3vni = NULL; + + zns = zebra_ns_lookup(NS_DEFAULT); + assert(zns); + + memset(&tmp_zl3vni, 0, sizeof(zebra_l3vni_t)); + tmp_zl3vni.vni = vni; + + zl3vni = hash_get(zns->l3vni_table, &tmp_zl3vni, zl3vni_alloc); + assert(zl3vni); + + zl3vni->vrf_id = vrf_id; + zl3vni->svi_if = NULL; + zl3vni->vxlan_if = NULL; + zl3vni->l2vnis = list_new(); + zl3vni->l2vnis->cmp = (int (*)(void *, void *))vni_hash_cmp; + + /* Create hash table for remote RMAC */ + zl3vni->rmac_table = + hash_create(mac_hash_keymake, mac_cmp, + "Zebra L3-VNI RMAC-Table"); + + /* Create hash table for neighbors */ + zl3vni->nh_table = hash_create(neigh_hash_keymake, neigh_cmp, + "Zebra L3-VNI next-hop table"); + + return zl3vni; +} + +/* + * Delete L3 VNI hash entry. + */ +static int zl3vni_del(zebra_l3vni_t *zl3vni) +{ + struct zebra_ns *zns; + zebra_l3vni_t *tmp_zl3vni; + + zns = zebra_ns_lookup(NS_DEFAULT); + assert(zns); + + /* free the list of l2vnis */ + list_delete_and_null(&zl3vni->l2vnis); + zl3vni->l2vnis = NULL; + + /* Free the rmac table */ + hash_free(zl3vni->rmac_table); + zl3vni->rmac_table = NULL; + + /* Free the nh table */ + hash_free(zl3vni->nh_table); + zl3vni->nh_table = NULL; + + /* Free the VNI hash entry and allocated memory. */ + tmp_zl3vni = hash_release(zns->l3vni_table, zl3vni); + if (tmp_zl3vni) + XFREE(MTYPE_ZL3VNI, tmp_zl3vni); + + return 0; +} + +static int is_vni_l3(vni_t vni) +{ + zebra_l3vni_t *zl3vni = NULL; + + zl3vni = zl3vni_lookup(vni); + if (zl3vni) + return 1; + return 0; +} + +static struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni) +{ + struct zebra_ns *zns = NULL; + struct route_node *rn = NULL; + struct interface *ifp = NULL; + + /* loop through all vxlan-interface */ + zns = zebra_ns_lookup(NS_DEFAULT); + for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { + + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; + + ifp = (struct interface *)rn->info; + if (!ifp) + continue; + + zif = ifp->info; + if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) + continue; + + vxl = &zif->l2info.vxl; + if (vxl->vni == zl3vni->vni) { + zl3vni->local_vtep_ip = vxl->vtep_ip; + return ifp; + } + } + + return NULL; +} + +static struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni) +{ + struct zebra_if *zif = NULL; /* zebra_if for vxlan_if */ + struct zebra_l2info_vxlan *vxl = NULL; /* l2 info for vxlan_if */ + + if (!zl3vni->vxlan_if) + return NULL; + + zif = zl3vni->vxlan_if->info; + if (!zif) + return NULL; + + vxl = &zif->l2info.vxl; + + return zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); +} + +static zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t vrf_id) +{ + struct zebra_vrf *zvrf = NULL; + + zvrf = zebra_vrf_lookup_by_id(vrf_id); + if (!zvrf) + return NULL; + + return zl3vni_lookup(zvrf->l3vni); +} + +/* + * Map SVI and associated bridge to a VNI. This is invoked upon getting + * neighbor notifications, to see if they are of interest. + */ +static zebra_l3vni_t *zl3vni_from_svi(struct interface *ifp, + struct interface *br_if) +{ + int found = 0; + vlanid_t vid = 0; + u_char bridge_vlan_aware = 0; + zebra_l3vni_t *zl3vni = NULL; + struct zebra_ns *zns = NULL; + struct route_node *rn = NULL; + struct zebra_if *zif = NULL; + struct interface *tmp_if = NULL; + struct zebra_l2info_bridge *br = NULL; + struct zebra_l2info_vxlan *vxl = NULL; + + 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; + bridge_vlan_aware = br->vlan_aware; + if (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; + vid = vl->vid; + } + + /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ + /* TODO: Optimize with a hash. */ + zns = zebra_ns_lookup(NS_DEFAULT); + for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { + tmp_if = (struct interface *)rn->info; + if (!tmp_if) + 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 (!bridge_vlan_aware || vxl->access_vlan == vid) { + found = 1; + break; + } + } + + if (!found) + return NULL; + + zl3vni = zl3vni_lookup(vxl->vni); + return zl3vni; +} + +/* + * Inform BGP about l3-vni. + */ +static int zl3vni_send_add_to_client(zebra_l3vni_t *zl3vni) +{ + struct stream *s = NULL; + struct zserv *client = NULL; + struct ethaddr rmac; + char buf[ETHER_ADDR_STRLEN]; + + client = zebra_find_client(ZEBRA_ROUTE_BGP, 0); + /* BGP may not be running. */ + if (!client) + return 0; + + /* get the rmac */ + memset(&rmac, 0, sizeof(struct ethaddr)); + zl3vni_get_rmac(zl3vni, &rmac); + + s = client->obuf; + stream_reset(s); + + zserv_create_header(s, ZEBRA_L3VNI_ADD, + zl3vni_vrf_id(zl3vni)); + stream_putl(s, zl3vni->vni); + stream_put(s, &rmac, sizeof(struct ethaddr)); + stream_put_in_addr(s, &zl3vni->local_vtep_ip); + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Send L3_VNI_ADD %u VRF %s RMAC %s local-ip %s to %s", + zl3vni->vni, vrf_id_to_name(zl3vni_vrf_id(zl3vni)), + prefix_mac2str(&rmac, buf, sizeof(buf)), + inet_ntoa(zl3vni->local_vtep_ip), + zebra_route_string(client->proto)); + + client->l3vniadd_cnt++; + return zebra_server_send_message(client); +} + +/* + * Inform BGP about local l3-VNI deletion. + */ +static int zl3vni_send_del_to_client(zebra_l3vni_t *zl3vni) +{ + struct stream *s = NULL; + struct zserv *client = NULL; + + client = zebra_find_client(ZEBRA_ROUTE_BGP, 0); + /* BGP may not be running. */ + if (!client) + return 0; + + s = client->obuf; + stream_reset(s); + + zserv_create_header(s, ZEBRA_L3VNI_DEL, + zl3vni_vrf_id(zl3vni)); + stream_putl(s, zl3vni->vni); + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Send L3_VNI_DEL %u VRF %s to %s", + zl3vni->vni, + vrf_id_to_name(zl3vni_vrf_id(zl3vni)), + zebra_route_string(client->proto)); + + client->l3vnidel_cnt++; + return zebra_server_send_message(client); +} + +static void zebra_vxlan_process_l3vni_oper_up(zebra_l3vni_t *zl3vni) +{ + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("L3-VNI %u is UP - send add to BGP", + zl3vni->vni); + + /* send l3vni add to BGP */ + zl3vni_send_add_to_client(zl3vni); +} + +static void zebra_vxlan_process_l3vni_oper_down(zebra_l3vni_t *zl3vni) +{ + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("L3-VNI %u is Down - Send del to BGP", + zl3vni->vni); + + /* send l3-vni del to BGP*/ + zl3vni_send_del_to_client(zl3vni); +} + +static void zvni_add_to_l3vni_list(struct hash_backet *backet, + void *ctxt) +{ + zebra_vni_t *zvni = (zebra_vni_t *) backet->data; + zebra_l3vni_t *zl3vni = (zebra_l3vni_t *) ctxt; + + if (zvni->vrf_id == zl3vni_vrf_id(zl3vni)) + listnode_add_sort(zl3vni->l2vnis, zvni); +} + +/* + * handle transition of vni from l2 to l3 and vice versa + */ +static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf, + vni_t vni, int add) +{ + zebra_vni_t *zvni = NULL; + + /* There is a possibility that VNI notification was already received + * from kernel and we programmed it as L2-VNI + * In such a case we need to delete this L2-VNI first, so + * that it can be reprogrammed as L3-VNI in the system. It is also + * possible that the vrf-vni mapping is removed from FRR while the vxlan + * interface is still present in kernel. In this case to keep it + * symmetric, we will delete the l3-vni and reprogram it as l2-vni + */ + if (add) { + /* Locate hash entry */ + zvni = zvni_lookup(vni); + if (!zvni) + return 0; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Del L2-VNI %u - transition to L3-VNI", + vni); + + /* Delete VNI from BGP. */ + zvni_send_del_to_client(zvni->vni); + + /* Free up all neighbors and MAC, if any. */ + zvni_neigh_del_all(zvni, 0, 0, DEL_ALL_NEIGH); + zvni_mac_del_all(zvni, 0, 0, DEL_ALL_MAC); + + /* Free up all remote VTEPs, if any. */ + zvni_vtep_del_all(zvni, 0); + + /* Delete the hash entry. */ + if (zvni_del(zvni)) { + zlog_err("Failed to del VNI hash %p, VNI %u", + zvni, zvni->vni); + return -1; + } + } else { + /* TODO_MITESH: This needs to be thought through. We don't have + * enough information at this point to reprogram the vni as + * l2-vni. One way is to store the required info in l3-vni and + * used it solely for this purpose + */ + } + + return 0; +} + +/* delete and uninstall rmac hash entry */ +static void zl3vni_del_rmac_hash_entry(struct hash_backet *backet, + void *ctx) +{ + zebra_mac_t *zrmac = NULL; + zebra_l3vni_t *zl3vni = NULL; + + zrmac = (zebra_mac_t *)backet->data; + zl3vni = (zebra_l3vni_t *)ctx; + zl3vni_rmac_uninstall(zl3vni, zrmac); + zl3vni_rmac_del(zl3vni, zrmac); +} + +/* delete and uninstall nh hash entry */ +static void zl3vni_del_nh_hash_entry(struct hash_backet *backet, + void *ctx) +{ + zebra_neigh_t *n = NULL; + zebra_l3vni_t *zl3vni = NULL; + + n = (zebra_neigh_t *)backet->data; + zl3vni = (zebra_l3vni_t *)ctx; + zl3vni_nh_uninstall(zl3vni, n); + zl3vni_nh_del(zl3vni, n); +} /* Public functions */ +/* handle evpn route in vrf table */ +void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id, + struct ethaddr *rmac, + struct ipaddr *vtep_ip, + struct prefix *host_prefix) +{ + zebra_l3vni_t *zl3vni = NULL; + + zl3vni = zl3vni_from_vrf(vrf_id); + if (!zl3vni || !is_l3vni_oper_up(zl3vni)) + return; + + /* add the next hop neighbor */ + zl3vni_remote_nh_add(zl3vni, vtep_ip, rmac, host_prefix); + + /* add the rmac */ + zl3vni_remote_rmac_add(zl3vni, rmac, vtep_ip, host_prefix); +} + +/* handle evpn vrf route delete */ +void zebra_vxlan_evpn_vrf_route_del(vrf_id_t vrf_id, + struct ethaddr *rmac, + struct ipaddr *vtep_ip, + struct prefix *host_prefix) +{ + zebra_l3vni_t *zl3vni = NULL; + + zl3vni = zl3vni_from_vrf(vrf_id); + if (!zl3vni) + return; + + /* delete the next hop entry */ + zl3vni_remote_nh_del(zl3vni, vtep_ip, host_prefix); + + /* delete the rmac entry */ + zl3vni_remote_rmac_del(zl3vni, rmac, host_prefix); +} + +void zebra_vxlan_print_specific_rmac_l3vni(struct vty *vty, + vni_t l3vni, + struct ethaddr *rmac, + u_char use_json) +{ + zebra_l3vni_t *zl3vni = NULL; + zebra_mac_t *zrmac = NULL; + json_object *json = NULL; + + if (!is_evpn_enabled()) { + if (use_json) + vty_out(vty, "{}\n"); + return; + } + + 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 doesnt exist\n", + l3vni); + return; + } + + zrmac = zl3vni_rmac_lookup(zl3vni, rmac); + if (!zrmac) { + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, + "%% Requested RMAC doesnt exist in L3-VNI %u", + l3vni); + return; + } + + zl3vni_print_rmac(zrmac, vty, json); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +void zebra_vxlan_print_rmacs_l3vni(struct vty *vty, + vni_t l3vni, + u_char use_json) +{ + zebra_l3vni_t *zl3vni; + u_int32_t num_rmacs; + struct rmac_walk_ctx wctx; + json_object *json = 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_rmacs = hashcount(zl3vni->rmac_table); + if (!num_rmacs) + return; + + if (use_json) + json = json_object_new_object(); + + memset(&wctx, 0, sizeof(struct rmac_walk_ctx)); + wctx.vty = vty; + wctx.json = json; + if (!use_json) { + vty_out(vty, + "Number of Remote RMACs known for this VNI: %u\n", + num_rmacs); + vty_out(vty, "%-17s %-21s %-6s\n", "MAC", + "Remote VTEP", "Refcnt"); + } else + json_object_int_add(json, "numRmacs", num_rmacs); + + hash_iterate(zl3vni->rmac_table, zl3vni_print_rmac_hash, &wctx); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +void zebra_vxlan_print_rmacs_all_l3vni(struct vty *vty, + u_char use_json) +{ + struct zebra_ns *zns = NULL; + json_object *json = NULL; + void *args[2]; + + if (!is_evpn_enabled()) { + if (use_json) + vty_out(vty, "{}\n"); + return; + } + + zns = zebra_ns_lookup(NS_DEFAULT); + if (!zns) { + if (use_json) + vty_out(vty, "{}\n"); + return; + } + + if (use_json) + json = json_object_new_object(); + + args[0] = vty; + args[1] = json; + hash_iterate(zns->l3vni_table, + (void (*)(struct hash_backet *, + void *))zl3vni_print_rmac_hash_all_vni, + args); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +void zebra_vxlan_print_specific_nh_l3vni(struct vty *vty, + vni_t l3vni, + struct ipaddr *ip, + u_char use_json) +{ + zebra_l3vni_t *zl3vni = NULL; + zebra_neigh_t *n = NULL; + json_object *json = NULL; + + if (!is_evpn_enabled()) { + if (use_json) + vty_out(vty, "{}\n"); + return; + } + + 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; + } + + 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", + l3vni); + return; + } + + zl3vni_print_nh(n, vty, json); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +void zebra_vxlan_print_nh_l3vni(struct vty *vty, + vni_t l3vni, + u_char use_json) +{ + u_int32_t num_nh; + struct nh_walk_ctx wctx; + json_object *json = NULL; + zebra_l3vni_t *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); + if (!num_nh) + return; + + if (use_json) + json = json_object_new_object(); + + wctx.vty = vty; + wctx.json = json; + if (!use_json) { + vty_out(vty, + "Number of NH Neighbors known for this VNI: %u\n", + num_nh); + vty_out(vty, "%-15s %-17s %6s\n", "IP", + "RMAC", "Refcnt"); + } else + json_object_int_add(json, "numNh", num_nh); + + hash_iterate(zl3vni->nh_table, zl3vni_print_nh_hash, &wctx); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +void zebra_vxlan_print_nh_all_l3vni(struct vty *vty, + u_char use_json) +{ + struct zebra_ns *zns = NULL; + json_object *json = NULL; + void *args[2]; + + if (!is_evpn_enabled()) { + if (use_json) + vty_out(vty, "{}\n"); + return; + } + + zns = zebra_ns_lookup(NS_DEFAULT); + if (!zns) + return; + + if (use_json) + json = json_object_new_object(); + + args[0] = vty; + args[1] = json; + hash_iterate(zns->l3vni_table, + (void (*)(struct hash_backet *, + void *))zl3vni_print_nh_hash_all_vni, + args); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + return; +} + +/* + * Display L3 VNI information (VTY command handler). + */ +void zebra_vxlan_print_l3vni(struct vty *vty, vni_t vni, u_char use_json) +{ + void *args[2]; + json_object *json = NULL; + zebra_l3vni_t *zl3vni = NULL; + + if (!is_evpn_enabled()) { + if (use_json) + vty_out(vty, "{}\n"); + return; + } + + zl3vni = zl3vni_lookup(vni); + if (!zl3vni) { + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, "%% VNI %u does not exist\n", vni); + return; + } + + if (use_json) + json = json_object_new_object(); + + args[0] = vty; + args[1] = json; + zl3vni_print(zl3vni, (void *)args); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +/* + * Display L3 VNI hash table (VTY command handler). + */ +void zebra_vxlan_print_l3vnis(struct vty *vty, u_char use_json) +{ + u_int32_t num_vnis; + void *args[2]; + json_object *json = NULL; + struct zebra_ns *zns = NULL; + + if (!is_evpn_enabled()) { + if (use_json) + vty_out(vty, "{}\n"); + return; + } + + zns = zebra_ns_lookup(NS_DEFAULT); + assert(zns); + + num_vnis = hashcount(zns->l3vni_table); + if (!num_vnis) { + if (use_json) + vty_out(vty, "{}\n"); + return; + } + + if (use_json) { + json = json_object_new_object(); + json_object_int_add(json, "numVnis", num_vnis); + } else { + vty_out(vty, "Number of L3 VNIs: %u\n", num_vnis); + vty_out(vty, "%-10s %-15s %-20s %-20s %-5s %-37s %-18s\n", + "VNI", "Local-ip", "Vx-intf", "L3-SVI", "State", + "VRF", "Rmac"); + } + + args[0] = vty; + args[1] = json; + hash_iterate(zns->l3vni_table, + (void (*)(struct hash_backet *, void *))zl3vni_print_hash, + args); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + /* * Display Neighbors for a VNI (VTY command handler). */ @@ -2789,9 +4520,9 @@ void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf, vty_out(vty, "Advertise gateway mac-ip: %s\n", zvrf->advertise_gw_macip ? "Yes" : "No"); vty_out(vty, "Number of VNIs: %u\n", num_vnis); - vty_out(vty, "%-10s %-21s %-15s %-8s %-8s %-15s\n", "VNI", + vty_out(vty, "%-10s %-21s %-15s %-8s %-8s %-15s %-37s\n", "VNI", "VxLAN IF", "VTEP IP", "# MACs", "# ARPs", - "# Remote VTEPs"); + "# Remote VTEPs", "VRF"); } args[0] = vty; args[1] = json; @@ -2816,18 +4547,27 @@ void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf, int zebra_vxlan_local_neigh_del(struct interface *ifp, struct interface *link_if, struct ipaddr *ip) { - zebra_vni_t *zvni; - zebra_neigh_t *n; char buf[INET6_ADDRSTRLEN]; char buf2[ETHER_ADDR_STRLEN]; - zebra_mac_t *zmac; + zebra_neigh_t *n = NULL; + zebra_vni_t *zvni = NULL; + zebra_mac_t *zmac = NULL; + zebra_l3vni_t *zl3vni = NULL; + + /* check if this is a remote neigh entry corresponding to remote + * next-hop + */ + zl3vni = zl3vni_from_svi(ifp, link_if); + if (zl3vni) + return zl3vni_local_nh_del(zl3vni, ip); /* We are only interested in neighbors on an SVI that resides on top * of a VxLAN bridge. */ - zvni = zvni_map_svi(ifp, link_if); + zvni = zvni_from_svi(ifp, link_if); if (!zvni) return 0; + if (!zvni->vxlan_if) { zlog_err( "VNI %u hash %p doesn't have intf upon local neighbor DEL", @@ -2836,7 +4576,7 @@ int zebra_vxlan_local_neigh_del(struct interface *ifp, } if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Del neighbor %s intf %s(%u) -> VNI %u", + zlog_debug("Del neighbor %s intf %s(%u) -> L2-VNI %u", ipaddr2str(ip, buf, sizeof(buf)), ifp->name, ifp->ifindex, zvni->vni); @@ -2891,23 +4631,30 @@ int zebra_vxlan_local_neigh_add_update(struct interface *ifp, struct ethaddr *macaddr, u_int16_t state, u_char ext_learned) { - zebra_vni_t *zvni; - zebra_neigh_t *n; - zebra_mac_t *zmac, *old_zmac; char buf[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; + zebra_vni_t *zvni = NULL; + zebra_neigh_t *n = NULL; + zebra_mac_t *zmac = NULL, *old_zmac = NULL; + zebra_l3vni_t *zl3vni = NULL; + + /* check if this is a remote neigh entry corresponding to remote + * next-hop + */ + zl3vni = zl3vni_from_svi(ifp, link_if); + if (zl3vni) + return zl3vni_local_nh_add_update(zl3vni, ip, state); /* We are only interested in neighbors on an SVI that resides on top * of a VxLAN bridge. */ - zvni = zvni_map_svi(ifp, link_if); + zvni = zvni_from_svi(ifp, link_if); if (!zvni) return 0; if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - "Add/Update neighbor %s MAC %s intf %s(%u) state 0x%x " - "%s-> VNI %u", + "Add/Update neighbor %s MAC %s intf %s(%u) state 0x%x %s-> L2-VNI %u", ipaddr2str(ip, buf2, sizeof(buf2)), prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name, ifp->ifindex, state, ext_learned ? "ext-learned " : "", @@ -3015,12 +4762,12 @@ int zebra_vxlan_local_neigh_add_update(struct interface *ifp, /* Inform BGP. */ if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("neigh %s (MAC %s) is now ACTIVE on VNI %u", + zlog_debug("neigh %s (MAC %s) is now ACTIVE on L2-VNI %u", ipaddr2str(ip, buf2, sizeof(buf2)), prefix_mac2str(macaddr, buf, sizeof(buf)), zvni->vni); - ZEBRA_NEIGH_SET_ACTIVE(n); + return zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr, 0); } @@ -3045,6 +4792,10 @@ int zebra_vxlan_remote_macip_del(struct zserv *client, u_short length, struct interface *ifp = NULL; struct zebra_if *zif = NULL; + memset(&macaddr, 0, sizeof(struct ethaddr)); + memset(&ip, 0, sizeof(struct ipaddr)); + memset(&vtep_ip, 0, sizeof(struct in_addr)); + s = client->ibuf; while (l < length) { @@ -3189,6 +4940,10 @@ int zebra_vxlan_remote_macip_add(struct zserv *client, u_short length, struct interface *ifp = NULL; struct zebra_if *zif = NULL; + memset(&macaddr, 0, sizeof(struct ethaddr)); + memset(&ip, 0, sizeof(struct ipaddr)); + memset(&vtep_ip, 0, sizeof(struct in_addr)); + if (!EVPN_ENABLED(zvrf)) { zlog_warn("%s: EVPN Not turned on yet we have received a remote_macip add zapi callback", __PRETTY_FUNCTION__); @@ -3898,14 +5653,14 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, svi_if_link = if_lookup_by_index_per_ns( zebra_ns_lookup(NS_DEFAULT), svi_if_zif->link_ifindex); - zvni = zvni_map_svi(svi_if, svi_if_link); + zvni = zvni_from_svi(svi_if, svi_if_link); } } else if (IS_ZEBRA_IF_BRIDGE(svi_if)) { /* * If it is a vlan unaware bridge then svi is the bridge * itself */ - zvni = zvni_map_svi(svi_if, svi_if); + zvni = zvni_from_svi(svi_if, svi_if); } } else if (IS_ZEBRA_IF_VLAN(ifp)) { struct zebra_if *svi_if_zif = @@ -3917,9 +5672,9 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, svi_if_link = if_lookup_by_index_per_ns( zebra_ns_lookup(NS_DEFAULT), svi_if_zif->link_ifindex); if (svi_if_zif && svi_if_link) - zvni = zvni_map_svi(ifp, svi_if_link); + zvni = zvni_from_svi(ifp, svi_if_link); } else if (IS_ZEBRA_IF_BRIDGE(ifp)) { - zvni = zvni_map_svi(ifp, ifp); + zvni = zvni_from_svi(ifp, ifp); } if (!zvni) @@ -3958,56 +5713,108 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, } /* - * Handle SVI interface going down. At this point, this is a NOP since - * the kernel deletes the neighbor entries on this SVI (if any). + * Handle SVI interface going down. + * SVI can be associated to either L3-VNI or L2-VNI. + * For L2-VNI: At this point, this is a NOP since + * the kernel deletes the neighbor entries on this SVI (if any). + * We only need to update the vrf corresponding to zvni. + * For L3-VNI: L3-VNI is operationally down, update mac-ip routes and delete + * from bgp */ int zebra_vxlan_svi_down(struct interface *ifp, struct interface *link_if) { + zebra_l3vni_t *zl3vni = NULL; + + zl3vni = zl3vni_from_svi(ifp, link_if); + if (zl3vni) { + + /* process l3-vni down */ + zebra_vxlan_process_l3vni_oper_down(zl3vni); + + /* remove association with svi-if */ + zl3vni->svi_if = NULL; + } else { + zebra_vni_t *zvni = NULL; + + /* since we dont have svi corresponding to zvni, we associate it + * to default vrf. Note: the corresponding neigh entries on the + * SVI would have already been deleted */ + zvni = zvni_from_svi(ifp, link_if); + if (zvni) { + zvni->vrf_id = VRF_DEFAULT; + + /* update the tenant vrf in BGP */ + zvni_send_add_to_client(zvni); + } + } return 0; } /* - * Handle SVI interface coming up. This may or may not be of interest, - * but if this is a SVI on a VxLAN bridge, we need to install any remote - * neighbor entries (which will be used for EVPN ARP suppression). + * Handle SVI interface coming up. + * SVI can be associated to L3-VNI (l3vni vxlan interface) or L2-VNI (l2-vni + * vxlan intf). + * For L2-VNI: we need to install any remote neighbors entried (used for + * apr-suppression) + * For L3-VNI: SVI will be used to get the rmac to be used with L3-VNI */ int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if) { - zebra_vni_t *zvni; - struct neigh_walk_ctx n_wctx; + zebra_vni_t *zvni = NULL; + zebra_l3vni_t *zl3vni = NULL; - zvni = zvni_map_svi(ifp, link_if); - if (!zvni) - return 0; + zl3vni = zl3vni_from_svi(ifp, link_if); + if (zl3vni) { - if (!zvni->vxlan_if) { - zlog_err("VNI %u hash %p doesn't have intf upon SVI up", - zvni->vni, zvni); - return -1; - } + /* associate with svi */ + zl3vni->svi_if = ifp; - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("SVI %s(%u) VNI %u is UP, installing neighbors", - ifp->name, ifp->ifindex, zvni->vni); + /* process oper-up */ + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up(zl3vni); + } else { + + /* process SVI up for l2-vni */ + struct neigh_walk_ctx n_wctx; - /* Install any remote neighbors for this VNI. */ - memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx)); - n_wctx.zvni = zvni; - hash_iterate(zvni->neigh_table, zvni_install_neigh_hash, &n_wctx); + zvni = zvni_from_svi(ifp, link_if); + if (!zvni) + return 0; + + if (!zvni->vxlan_if) { + zlog_err("VNI %u hash %p doesn't have intf upon SVI up", + zvni->vni, zvni); + return -1; + } + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("SVI %s(%u) VNI %u VRF %s is UP, installing neighbors", + ifp->name, ifp->ifindex, zvni->vni, + vrf_id_to_name(ifp->vrf_id)); + + /* update the vrf information for l2-vni and inform bgp */ + zvni->vrf_id = ifp->vrf_id; + zvni_send_add_to_client(zvni); + + /* Install any remote neighbors for this VNI. */ + memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx)); + n_wctx.zvni = zvni; + hash_iterate(zvni->neigh_table, + zvni_install_neigh_hash, + &n_wctx); + } return 0; } /* - * Handle VxLAN interface down - update BGP if required, and do - * internal cleanup. + * Handle VxLAN interface down */ int zebra_vxlan_if_down(struct interface *ifp) { - struct zebra_if *zif; - zebra_vni_t *zvni; - struct zebra_l2info_vxlan *vxl; vni_t vni; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; /* Check if EVPN is enabled. */ if (!is_evpn_enabled()) @@ -4018,31 +5825,55 @@ int zebra_vxlan_if_down(struct interface *ifp) vxl = &zif->l2info.vxl; vni = vxl->vni; - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Intf %s(%u) VNI %u is DOWN", - ifp->name, ifp->ifindex, vni); - /* Locate hash entry; it is expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { - zlog_err( - "Failed to locate VNI hash at DOWN, IF %s(%u) VNI %u", - ifp->name, ifp->ifindex, vni); - return -1; - } + if (is_vni_l3(vni)) { - assert(zvni->vxlan_if == ifp); + /* process-if-down for l3-vni */ + zebra_l3vni_t *zl3vni = NULL; - /* Delete this VNI from BGP. */ - zvni_send_del_to_client(zvni->vni); + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Intf %s(%u) L3-VNI %u is DOWN", + ifp->name, ifp->ifindex, vni); - /* Free up all neighbors and MACs, if any. */ - zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH); - zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC); + zl3vni = zl3vni_lookup(vni); + if (!zl3vni) { + zlog_err( + "Failed to locate L3-VNI hash at DOWN, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return -1; + } - /* Free up all remote VTEPs, if any. */ - zvni_vtep_del_all(zvni, 1); + zebra_vxlan_process_l3vni_oper_down(zl3vni); + + } else { + /* process if-down for l2-vni */ + zebra_vni_t *zvni; + + 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. */ + zvni = zvni_lookup(vni); + if (!zvni) { + zlog_err( + "Failed to locate VNI hash at DOWN, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return -1; + } + + assert(zvni->vxlan_if == ifp); + + /* Delete this VNI from BGP. */ + zvni_send_del_to_client(zvni->vni); + + /* Free up all neighbors and MACs, if any. */ + zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH); + zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC); + + /* Free up all remote VTEPs, if any. */ + zvni_vtep_del_all(zvni, 1); + } return 0; } @@ -4051,10 +5882,9 @@ int zebra_vxlan_if_down(struct interface *ifp) */ int zebra_vxlan_if_up(struct interface *ifp) { - struct zebra_if *zif; - zebra_vni_t *zvni; - struct zebra_l2info_vxlan *vxl; vni_t vni; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; /* Check if EVPN is enabled. */ if (!is_evpn_enabled()) @@ -4065,26 +5895,66 @@ int zebra_vxlan_if_up(struct interface *ifp) vxl = &zif->l2info.vxl; vni = vxl->vni; - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Intf %s(%u) VNI %u is UP", - ifp->name, ifp->ifindex, vni); + if (is_vni_l3(vni)) { - /* Locate hash entry; it is expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { - zlog_err( - "Failed to locate VNI hash at UP, IF %s(%u) VNI %u", - ifp->name, ifp->ifindex, vni); - return -1; - } + /* Handle L3-VNI add */ + zebra_l3vni_t *zl3vni = NULL; - assert(zvni->vxlan_if == ifp); + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Intf %s(%u) L3-VNI %u is UP", + ifp->name, ifp->ifindex, vni); - /* If part of a bridge, inform BGP about this VNI. */ - /* Also, read and populate local MACs and neighbors. */ - if (zif->brslave_info.br_if) { - zvni_send_add_to_client(zvni); - zvni_read_mac_neigh(zvni, ifp); + zl3vni = zl3vni_lookup(vni); + if (!zl3vni) { + zlog_err( + "Failed to locate L3-VNI hash at UP, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return -1; + } + + /* 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); + + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up(zl3vni); + } else { + /* Handle L2-VNI add */ + + zebra_vni_t *zvni = NULL; + zebra_l3vni_t *zl3vni = NULL; + 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. */ + zvni = zvni_lookup(vni); + if (!zvni) { + zlog_err( + "Failed to locate VNI hash at UP, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return -1; + } + + assert(zvni->vxlan_if == ifp); + vlan_if = zvni_map_to_svi(vxl->access_vlan, + zif->brslave_info.br_if); + if (vlan_if) { + zvni->vrf_id = vlan_if->vrf_id; + zl3vni = zl3vni_from_vrf(vlan_if->vrf_id); + if (zl3vni) + listnode_add_sort(zl3vni->l2vnis, zvni); + } + + /* If part of a bridge, inform BGP about this VNI. */ + /* Also, read and populate local MACs and neighbors. */ + if (zif->brslave_info.br_if) { + zvni_send_add_to_client(zvni); + zvni_read_mac_neigh(zvni, ifp); + } } return 0; @@ -4096,10 +5966,9 @@ int zebra_vxlan_if_up(struct interface *ifp) */ int zebra_vxlan_if_del(struct interface *ifp) { - struct zebra_if *zif; - zebra_vni_t *zvni; - struct zebra_l2info_vxlan *vxl; vni_t vni; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; /* Check if EVPN is enabled. */ if (!is_evpn_enabled()) @@ -4110,34 +5979,69 @@ int zebra_vxlan_if_del(struct interface *ifp) vxl = &zif->l2info.vxl; vni = vxl->vni; - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Del VNI %u intf %s(%u)", - vni, ifp->name, ifp->ifindex); + if (is_vni_l3(vni)) { - /* Locate hash entry; it is expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { - zlog_err( - "Failed to locate VNI hash at del, IF %s(%u) VNI %u", - ifp->name, ifp->ifindex, vni); - return 0; - } + /* process if-del for l3-vni */ + zebra_l3vni_t *zl3vni = NULL; - /* Delete VNI from BGP. */ - zvni_send_del_to_client(zvni->vni); + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Del L3-VNI %u intf %s(%u)", + vni, ifp->name, ifp->ifindex); - /* Free up all neighbors and MAC, if any. */ - zvni_neigh_del_all(zvni, 0, 0, DEL_ALL_NEIGH); - zvni_mac_del_all(zvni, 0, 0, DEL_ALL_MAC); + zl3vni = zl3vni_lookup(vni); + if (!zl3vni) { + zlog_err( + "Failed to locate L3-VNI hash at del, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return 0; + } - /* Free up all remote VTEPs, if any. */ - zvni_vtep_del_all(zvni, 0); + /* process oper-down for l3-vni */ + zebra_vxlan_process_l3vni_oper_down(zl3vni); - /* Delete the hash entry. */ - if (zvni_del(zvni)) { - zlog_err("Failed to del VNI hash %p, IF %s(%u) VNI %u", - zvni, ifp->name, ifp->ifindex, zvni->vni); - return -1; + /* 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*/ + zebra_vni_t *zvni = NULL; + zebra_l3vni_t *zl3vni = NULL; + + 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. */ + zvni = zvni_lookup(vni); + if (!zvni) { + zlog_err( + "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(zvni->vrf_id); + if (zl3vni) + listnode_delete(zl3vni->l2vnis, zvni); + + /* Delete VNI from BGP. */ + zvni_send_del_to_client(zvni->vni); + + /* Free up all neighbors and MAC, if any. */ + zvni_neigh_del_all(zvni, 0, 0, DEL_ALL_NEIGH); + zvni_mac_del_all(zvni, 0, 0, DEL_ALL_MAC); + + /* Free up all remote VTEPs, if any. */ + zvni_vtep_del_all(zvni, 0); + + /* Delete the hash entry. */ + if (zvni_del(zvni)) { + zlog_err("Failed to del VNI hash %p, IF %s(%u) VNI %u", + zvni, ifp->name, ifp->ifindex, zvni->vni); + return -1; + } } return 0; @@ -4148,10 +6052,9 @@ int zebra_vxlan_if_del(struct interface *ifp) */ int zebra_vxlan_if_update(struct interface *ifp, u_int16_t chgflags) { - struct zebra_if *zif; - zebra_vni_t *zvni; - struct zebra_l2info_vxlan *vxl; vni_t vni; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; /* Check if EVPN is enabled. */ if (!is_evpn_enabled()) @@ -4162,79 +6065,129 @@ int zebra_vxlan_if_update(struct interface *ifp, u_int16_t chgflags) vxl = &zif->l2info.vxl; vni = vxl->vni; - /* Update VNI hash. */ - zvni = zvni_lookup(vni); - if (!zvni) { - zlog_err( - "Failed to find VNI hash on update, IF %s(%u) VNI %u", - ifp->name, ifp->ifindex, vni); - return -1; - } + if (is_vni_l3(vni)) { + zebra_l3vni_t *zl3vni = NULL; - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Update VNI %u intf %s(%u) VLAN %u local IP %s " - "master %u chg 0x%x", - vni, ifp->name, ifp->ifindex, - vxl->access_vlan, inet_ntoa(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. */ - zvni_send_del_to_client(zvni->vni); - zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH); - zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC); - zvni_vtep_del_all(zvni, 1); - return 0; - } + zl3vni = zl3vni_lookup(vni); + if (!zl3vni) { + zlog_err( + "Failed to find L3-VNI hash on update, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return -1; + } + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Update L3-VNI %u intf %s(%u) VLAN %u local IP %s master %u chg 0x%x", + vni, ifp->name, ifp->ifindex, + vxl->access_vlan, inet_ntoa(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; + } - /* Handle other changes. */ - if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { - /* Remove all existing local neighbors and MACs for this VNI - * (including from BGP) + /* access-vlan change - process oper down, associate with new + * svi_if and then process oper up again */ - zvni_neigh_del_all(zvni, 0, 1, DEL_LOCAL_MAC); - zvni_mac_del_all(zvni, 0, 1, DEL_LOCAL_MAC); - } + 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); + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up( + zl3vni); + } + } - zvni->local_vtep_ip = vxl->vtep_ip; - zvni->vxlan_if = ifp; + /* if we have a valid new master, process l3-vni oper up */ + if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE) { + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up(zl3vni); + } + } else { + zebra_vni_t *zvni = NULL; - /* 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; + /* Update VNI hash. */ + zvni = zvni_lookup(vni); + if (!zvni) { + zlog_err( + "Failed to find L2-VNI hash on update, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return -1; + } - /* Inform BGP, if there is a change of interest. */ - if (chgflags - & (ZEBRA_VXLIF_MASTER_CHANGE | ZEBRA_VXLIF_LOCAL_IP_CHANGE)) - zvni_send_add_to_client(zvni); + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Update L2-VNI %u intf %s(%u) VLAN %u local IP %s master %u chg 0x%x", + vni, ifp->name, ifp->ifindex, + vxl->access_vlan, inet_ntoa(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. */ + zvni_send_del_to_client(zvni->vni); + zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH); + zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC); + zvni_vtep_del_all(zvni, 1); + return 0; + } - /* 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) - zvni_read_mac_neigh(zvni, ifp); - else if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { - struct mac_walk_ctx m_wctx; - struct neigh_walk_ctx n_wctx; + /* Handle other changes. */ + if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { + /* Remove all existing local neigh and MACs for this VNI + * (including from BGP) + */ + zvni_neigh_del_all(zvni, 0, 1, DEL_LOCAL_MAC); + zvni_mac_del_all(zvni, 0, 1, DEL_LOCAL_MAC); + } - zvni_read_mac_neigh(zvni, ifp); + zvni->local_vtep_ip = vxl->vtep_ip; + zvni->vxlan_if = ifp; - memset(&m_wctx, 0, sizeof(struct mac_walk_ctx)); - m_wctx.zvni = zvni; - hash_iterate(zvni->mac_table, zvni_install_mac_hash, &m_wctx); + /* 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; - memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx)); - n_wctx.zvni = zvni; - hash_iterate(zvni->neigh_table, zvni_install_neigh_hash, - &n_wctx); + /* Inform BGP, if there is a change of interest. */ + if (chgflags + & (ZEBRA_VXLIF_MASTER_CHANGE | ZEBRA_VXLIF_LOCAL_IP_CHANGE)) + zvni_send_add_to_client(zvni); + + /* 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) + zvni_read_mac_neigh(zvni, ifp); + else if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { + struct mac_walk_ctx m_wctx; + struct neigh_walk_ctx n_wctx; + + zvni_read_mac_neigh(zvni, ifp); + + memset(&m_wctx, 0, sizeof(struct mac_walk_ctx)); + m_wctx.zvni = zvni; + hash_iterate(zvni->mac_table, + zvni_install_mac_hash, + &m_wctx); + + memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx)); + n_wctx.zvni = zvni; + hash_iterate(zvni->neigh_table, zvni_install_neigh_hash, + &n_wctx); + } } return 0; @@ -4245,10 +6198,9 @@ int zebra_vxlan_if_update(struct interface *ifp, u_int16_t chgflags) */ int zebra_vxlan_if_add(struct interface *ifp) { - struct zebra_if *zif; - zebra_vni_t *zvni; - struct zebra_l2info_vxlan *vxl; vni_t vni; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; /* Check if EVPN is enabled. */ if (!is_evpn_enabled()) @@ -4259,37 +6211,200 @@ int zebra_vxlan_if_add(struct interface *ifp) vxl = &zif->l2info.vxl; vni = vxl->vni; - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Add VNI %u intf %s(%u) VLAN %u local IP %s master %u", - vni, ifp->name, ifp->ifindex, - vxl->access_vlan, inet_ntoa(vxl->vtep_ip), - zif->brslave_info.bridge_ifindex); + if (is_vni_l3(vni)) { - /* Create or update VNI hash. */ - zvni = zvni_lookup(vni); - if (!zvni) { - zvni = zvni_add(vni); - if (!zvni) { + /* process if-add for l3-vni*/ + zebra_l3vni_t *zl3vni = NULL; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Add L3-VNI %u intf %s(%u) VLAN %u local IP %s master %u", + vni, ifp->name, ifp->ifindex, + vxl->access_vlan, inet_ntoa(vxl->vtep_ip), + zif->brslave_info.bridge_ifindex); + + /* + * we expect the l3-vni has entry to be present here. + * The only place l3-vni is created in zebra is vrf-vni mapping + * command. This might change when we have the switchd support + * for l3-vxlan interface. + */ + zl3vni = zl3vni_lookup(vni); + if (!zl3vni) { zlog_err( - "Failed to add VNI hash, IF %s(%u) VNI %u", + "Failed to locate L3-VNI hash at del, IF %s(%u) VNI %u", ifp->name, ifp->ifindex, vni); + return 0; + } + + /* 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); + + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up(zl3vni); + } else { + + /* process if-add for l2-vni */ + zebra_vni_t *zvni = NULL; + zebra_l3vni_t *zl3vni = NULL; + struct interface *vlan_if = NULL; + + /* Create or update VNI hash. */ + zvni = zvni_lookup(vni); + if (!zvni) { + zvni = zvni_add(vni); + if (!zvni) { + zlog_err( + "Failed to add VNI hash, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return -1; + } + } + + zvni->local_vtep_ip = vxl->vtep_ip; + zvni->vxlan_if = ifp; + vlan_if = zvni_map_to_svi(vxl->access_vlan, + zif->brslave_info.br_if); + if (vlan_if) { + zvni->vrf_id = vlan_if->vrf_id; + zl3vni = zl3vni_from_vrf(vlan_if->vrf_id); + if (zl3vni) + listnode_add_sort(zl3vni->l2vnis, zvni); + } + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Add L2-VNI %u VRF %s intf %s(%u) VLAN %u local IP %s master %u", + vni, + vlan_if ? vrf_id_to_name(vlan_if->vrf_id) : + "Default", + ifp->name, ifp->ifindex, + vxl->access_vlan, inet_ntoa(vxl->vtep_ip), + 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 */ + zvni_send_add_to_client(zvni); + + /* Read and populate local MACs and neighbors */ + zvni_read_mac_neigh(zvni, ifp); + } + + return 0; +} + +int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, + vni_t vni, + char *err, int err_str_sz, + int add) +{ + zebra_l3vni_t *zl3vni = NULL; + struct zebra_vrf *zvrf_default = NULL; + + zvrf_default = zebra_vrf_lookup_by_id(VRF_DEFAULT); + if (!zvrf_default) + return -1; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("vrf %s vni %u %s", + zvrf_name(zvrf), + vni, + add ? "ADD" : "DEL"); + + if (add) { + + zebra_vxlan_handle_vni_transition(zvrf, vni, add); + + /* check if the vni is already present under zvrf */ + if (zvrf->l3vni) { + snprintf(err, err_str_sz, + "VNI is already configured under the vrf"); return -1; } + + /* check if this VNI is already present in the system */ + zl3vni = zl3vni_lookup(vni); + if (zl3vni) { + snprintf(err, err_str_sz, + "VNI is already configured as L3-VNI"); + return -1; + } + + /* add the L3-VNI to the global table */ + zl3vni = zl3vni_add(vni, zvrf_id(zvrf)); + if (!zl3vni) { + snprintf(err, err_str_sz, + "Could not add L3-VNI"); + return -1; + } + + /* associate the vrf with vni */ + zvrf->l3vni = vni; + + /* associate with vxlan-intf; + * we need to associate with the vxlan-intf first + */ + zl3vni->vxlan_if = zl3vni_map_to_vxlan_if(zl3vni); + + /* associate with corresponding SVI interface, we can associate + * with svi-if only after vxlan interface association is + * complete + */ + zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni); + + /* formulate l2vni list */ + hash_iterate(zvrf_default->vni_table, + zvni_add_to_l3vni_list, zl3vni); + + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up(zl3vni); + + } else { + zl3vni = zl3vni_lookup(vni); + if (!zl3vni) { + snprintf(err, err_str_sz, "VNI doesn't exist"); + return -1; + } + + zebra_vxlan_process_l3vni_oper_down(zl3vni); + + /* delete and uninstall all rmacs */ + hash_iterate(zl3vni->rmac_table, + zl3vni_del_rmac_hash_entry, + zl3vni); + + /* delete and uninstall all next-hops */ + hash_iterate(zl3vni->nh_table, + zl3vni_del_nh_hash_entry, + zl3vni); + + zvrf->l3vni = 0; + zl3vni_del(zl3vni); + + zebra_vxlan_handle_vni_transition(zvrf, vni, add); } + return 0; +} - zvni->local_vtep_ip = vxl->vtep_ip; - zvni->vxlan_if = ifp; +int zebra_vxlan_vrf_delete(struct zebra_vrf *zvrf) +{ + zebra_l3vni_t *zl3vni = NULL; - /* If down or not mapped to a bridge, we're done. */ - if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + zl3vni = zl3vni_from_vrf(zvrf_id(zvrf)); + if (!zl3vni) return 0; - /* Inform BGP */ - zvni_send_add_to_client(zvni); - - /* Read and populate local MACs and neighbors */ - zvni_read_mac_neigh(zvni, ifp); + zebra_vxlan_process_l3vni_oper_down(zl3vni); + zl3vni_del(zl3vni); + zebra_vxlan_handle_vni_transition(zvrf, zl3vni->vni, 0); return 0; } @@ -4410,8 +6525,9 @@ stream_failure: int zebra_vxlan_advertise_all_vni(struct zserv *client, u_short length, struct zebra_vrf *zvrf) { - struct stream *s; - int advertise; + struct stream *s = NULL; + int advertise = 0; + struct zebra_ns *zns = NULL; if (zvrf_id(zvrf) != VRF_DEFAULT) { zlog_err("EVPN VNI Adv for non-default VRF %u", @@ -4449,6 +6565,13 @@ int zebra_vxlan_advertise_all_vni(struct zserv *client, * kernel and free entries. */ hash_iterate(zvrf->vni_table, zvni_cleanup_all, zvrf); + + /* cleanup all l3vnis */ + zns = zebra_ns_lookup(NS_DEFAULT); + if (!zns) + return -1; + + hash_iterate(zns->l3vni_table, zl3vni_cleanup_all, NULL); } stream_failure: @@ -4475,3 +6598,28 @@ void zebra_vxlan_close_tables(struct zebra_vrf *zvrf) hash_iterate(zvrf->vni_table, zvni_cleanup_all, zvrf); hash_free(zvrf->vni_table); } + +/* init the l3vni table */ +void zebra_vxlan_ns_init(struct zebra_ns *zns) +{ + zns->l3vni_table = hash_create(l3vni_hash_keymake, l3vni_hash_cmp, + "Zebra VRF L3 VNI table"); +} + +/* free l3vni table */ +void zebra_vxlan_ns_disable(struct zebra_ns *zns) +{ + hash_free(zns->l3vni_table); +} + +/* get the l3vni svi ifindex */ +ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id) +{ + zebra_l3vni_t *zl3vni = NULL; + + zl3vni = zl3vni_from_vrf(vrf_id); + if (!zl3vni || !is_l3vni_oper_up(zl3vni)) + return 0; + + return zl3vni->svi_if->ifindex; +} diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h index 290d19bcf3..b7def6acf8 100644 --- a/zebra/zebra_vxlan.h +++ b/zebra/zebra_vxlan.h @@ -51,6 +51,13 @@ is_evpn_enabled() #define VNI_STR_LEN 32 +extern ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id); +extern int zebra_vxlan_vrf_delete(struct zebra_vrf *zvrf); +extern void zebra_vxlan_print_specific_nh_l3vni(struct vty *vty, vni_t l3vni, + struct ipaddr *ip, u_char uj); +extern void zebra_vxlan_print_specific_rmac_l3vni(struct vty *vty, vni_t l3vni, + struct ethaddr *rmac, + u_char use_json); extern void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, u_char use_json); extern void zebra_vxlan_print_macs_all_vni(struct vty *vty, @@ -84,6 +91,16 @@ extern void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, u_char use_json); extern void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf, u_char use_json); +extern void zebra_vxlan_print_rmacs_l3vni(struct vty *vty, vni_t vni, u_char + use_json); +extern void zebra_vxlan_print_rmacs_all_l3vni(struct vty *vty, u_char use_json); +extern void zebra_vxlan_print_nh_l3vni(struct vty *vty, vni_t vni, u_char + use_json); +extern void zebra_vxlan_print_nh_all_l3vni(struct vty *vty, u_char use_json); +extern void zebra_vxlan_print_l3vni(struct vty *vty, vni_t vni, + u_char use_json); +extern void zebra_vxlan_print_l3vnis(struct vty *vty, + u_char use_json); extern int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, int add); @@ -129,7 +146,20 @@ extern int zebra_vxlan_advertise_gw_macip(struct zserv *client, extern int zebra_vxlan_advertise_all_vni(struct zserv *client, u_short length, struct zebra_vrf *zvrf); +extern int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni, + char *err, + int err_str_sz, int add); extern void zebra_vxlan_init_tables(struct zebra_vrf *zvrf); extern void zebra_vxlan_close_tables(struct zebra_vrf *); +extern void zebra_vxlan_ns_init(struct zebra_ns *zns); +extern void zebra_vxlan_ns_disable(struct zebra_ns *zns); +extern void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id, + struct ethaddr *rmac, + struct ipaddr *ip, + struct prefix *host_prefix); +extern void zebra_vxlan_evpn_vrf_route_del(vrf_id_t vrf_id, + struct ethaddr *rmac, + struct ipaddr *vtep_ip, + struct prefix *host_prefix); #endif /* _ZEBRA_VXLAN_H */ diff --git a/zebra/zebra_vxlan_null.c b/zebra/zebra_vxlan_null.c index bbed5ddb05..db828c337e 100644 --- a/zebra/zebra_vxlan_null.c +++ b/zebra/zebra_vxlan_null.c @@ -83,6 +83,30 @@ void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf) { } +void zebra_vxlan_print_rmacs_l3vni(struct vty*, vni_t, u_char) +{ +} + +void zebra_vxlan_print_rmacs_all_l3vni(struct vty*, u_char) +{ +} + +void zebra_vxlan_print_nh_l3vni(struct vty*, vni_t, u_char) +{ +} + +void zebra_vxlan_print_nh_all_l3vni(struct vty*, u_char) +{ +} + +void zebra_vxlan_print_l3vni(struct vty *vty, vni_t vni) +{ +} + +void zebra_vxlan_print_l3vnis(struct vty *vty) +{ +} + int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if) { return 0; diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h index fa7d0e9457..ef6f9b99cb 100644 --- a/zebra/zebra_vxlan_private.h +++ b/zebra/zebra_vxlan_private.h @@ -30,12 +30,16 @@ #include "if.h" #include "linklist.h" +#include "zebra_vxlan.h" + +#define ERR_STR_SZ 256 /* definitions */ typedef struct zebra_vni_t_ zebra_vni_t; typedef struct zebra_vtep_t_ zebra_vtep_t; typedef struct zebra_mac_t_ zebra_mac_t; typedef struct zebra_neigh_t_ zebra_neigh_t; +typedef struct zebra_l3vni_t_ zebra_l3vni_t; /* * VTEP info @@ -75,6 +79,9 @@ struct zebra_vni_t_ { /* Local IP */ struct in_addr local_vtep_ip; + /* tenant VRF, if any */ + vrf_id_t vrf_id; + /* List of local or remote MAC */ struct hash *mac_table; @@ -82,6 +89,128 @@ struct zebra_vni_t_ { struct hash *neigh_table; }; +/* L3 VNI hash table */ +struct zebra_l3vni_t_ { + + /* VNI key */ + vni_t vni; + + /* vrf_id */ + vrf_id_t vrf_id; + + /* Local IP */ + struct in_addr local_vtep_ip; + + /* kernel interface for l3vni */ + struct interface *vxlan_if; + + /* SVI interface corresponding to the l3vni */ + struct interface *svi_if; + + /* list of L2 VNIs associated with the L3 VNI */ + struct list *l2vnis; + + /* list of remote router-macs */ + struct hash *rmac_table; + + /* list of remote vtep-ip neigh */ + struct hash *nh_table; +}; + +/* get the vx-intf name for l3vni */ +static inline const char *zl3vni_vxlan_if_name(zebra_l3vni_t *zl3vni) +{ + return zl3vni->vxlan_if ? zl3vni->vxlan_if->name : "None"; +} + +/* get the svi intf name for l3vni */ +static inline const char *zl3vni_svi_if_name(zebra_l3vni_t *zl3vni) +{ + return zl3vni->svi_if ? zl3vni->svi_if->name : "None"; +} + +/* get the vrf name for l3vni */ +static inline const char *zl3vni_vrf_name(zebra_l3vni_t *zl3vni) +{ + return vrf_id_to_name(zl3vni->vrf_id); +} + +/* get the rmac string */ +static inline const char *zl3vni_rmac2str(zebra_l3vni_t *zl3vni, char *buf, + int size) +{ + char *ptr; + + if (!buf) + ptr = (char *)XMALLOC(MTYPE_TMP, + ETHER_ADDR_STRLEN * sizeof(char)); + else { + assert(size >= ETHER_ADDR_STRLEN); + ptr = buf; + } + + if (zl3vni->svi_if) + snprintf(ptr, (ETHER_ADDR_STRLEN), + "%02x:%02x:%02x:%02x:%02x:%02x", + (uint8_t)zl3vni->svi_if->hw_addr[0], + (uint8_t)zl3vni->svi_if->hw_addr[1], + (uint8_t)zl3vni->svi_if->hw_addr[2], + (uint8_t)zl3vni->svi_if->hw_addr[3], + (uint8_t)zl3vni->svi_if->hw_addr[4], + (uint8_t)zl3vni->svi_if->hw_addr[5]); + else + snprintf(ptr, ETHER_ADDR_STRLEN, "None"); + + return ptr; +} + +/* + * l3-vni is oper up when: + * 0. if EVPN is enabled (advertise-all-vni cfged) + * 1. it is associated to a vxlan-intf + * 2. Associated vxlan-intf is oper up + * 3. it is associated to an SVI + * 4. associated SVI is oper up + */ +static inline int is_l3vni_oper_up(zebra_l3vni_t *zl3vni) +{ + return (is_evpn_enabled() && zl3vni && + (zl3vni->vrf_id != VRF_UNKNOWN) && + zl3vni->vxlan_if && if_is_operative(zl3vni->vxlan_if) && + zl3vni->svi_if && if_is_operative(zl3vni->svi_if)); +} + +static inline const char *zl3vni_state2str(zebra_l3vni_t *zl3vni) +{ + if (!zl3vni) + return NULL; + + if (is_l3vni_oper_up(zl3vni)) + return "Up"; + else + return "Down"; + + return NULL; +} + +static inline vrf_id_t zl3vni_vrf_id(zebra_l3vni_t *zl3vni) +{ + return zl3vni->vrf_id; +} + +static inline void zl3vni_get_rmac(zebra_l3vni_t *zl3vni, + struct ethaddr *rmac) +{ + if (!zl3vni) + return; + + if (!is_l3vni_oper_up(zl3vni)) + return; + + if (zl3vni->svi_if && if_is_operative(zl3vni->svi_if)) + memcpy(rmac->octet, zl3vni->svi_if->hw_addr, ETH_ALEN); +} + /* * MAC hash table. * @@ -103,6 +232,7 @@ struct zebra_mac_t_ { #define ZEBRA_MAC_REMOTE 0x02 #define ZEBRA_MAC_AUTO 0x04 /* Auto created for neighbor. */ #define ZEBRA_MAC_STICKY 0x08 /* Static MAC */ +#define ZEBRA_MAC_REMOTE_RMAC 0x10 /* remote router mac */ /* Local or remote info. */ union { @@ -116,6 +246,9 @@ struct zebra_mac_t_ { /* List of neigh associated with this mac */ struct list *neigh_list; + + /* list of hosts pointing to this remote RMAC */ + struct list *host_list; }; /* @@ -141,6 +274,11 @@ struct mac_walk_ctx { struct json_object *json; /* Used for JSON Output */ }; +struct rmac_walk_ctx { + struct vty *vty; + struct json_object *json; +}; + enum zebra_neigh_state { ZEBRA_NEIGH_INACTIVE = 0, ZEBRA_NEIGH_ACTIVE = 1 }; #define IS_ZEBRA_NEIGH_ACTIVE(n) n->state == ZEBRA_NEIGH_ACTIVE @@ -175,11 +313,15 @@ struct zebra_neigh_t_ { u_int32_t flags; #define ZEBRA_NEIGH_LOCAL 0x01 #define ZEBRA_NEIGH_REMOTE 0x02 +#define ZEBRA_NEIGH_REMOTE_NH 0x04 /* neigh entry for remote vtep */ enum zebra_neigh_state state; /* Remote VTEP IP - applicable only for remote neighbors. */ struct in_addr r_vtep_ip; + + /* list of hosts pointing to this remote NH entry */ + struct list *host_list; }; /* @@ -206,4 +348,18 @@ struct neigh_walk_ctx { struct json_object *json; /* Used for JSON Output */ }; +/* context for neigh hash walk - update l3vni and rmac */ +struct neigh_l3info_walk_ctx { + + zebra_vni_t *zvni; + zebra_l3vni_t *zl3vni; + int add; +}; + +struct nh_walk_ctx { + + struct vty *vty; + struct json_object *json; +}; + #endif /* _ZEBRA_VXLAN_PRIVATE_H */ diff --git a/zebra/zserv.c b/zebra/zserv.c index 684ba49e7d..7eded89f6d 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -1132,23 +1132,26 @@ static int zread_route_add(struct zserv *client, u_short length, struct route_entry *re; struct nexthop *nexthop = NULL; int i, ret; + vrf_id_t vrf_id = 0; s = client->ibuf; if (zapi_route_decode(s, &api) < 0) return -1; /* Allocate new route. */ + vrf_id = zvrf_id(zvrf); re = XCALLOC(MTYPE_RE, sizeof(struct route_entry)); re->type = api.type; re->instance = api.instance; re->flags = api.flags; re->uptime = time(NULL); - re->vrf_id = zvrf_id(zvrf); + re->vrf_id = vrf_id; re->table = zvrf->table_id; if (CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP)) { for (i = 0; i < api.nexthop_num; i++) { api_nh = &api.nexthops[i]; + ifindex_t ifindex = 0; switch (api_nh->type) { case NEXTHOP_TYPE_IFINDEX: @@ -1159,11 +1162,42 @@ static int zread_route_add(struct zserv *client, u_short length, nexthop = route_entry_nexthop_ipv4_add( re, &api_nh->gate.ipv4, NULL); break; - case NEXTHOP_TYPE_IPV4_IFINDEX: + case NEXTHOP_TYPE_IPV4_IFINDEX: { + + struct ipaddr vtep_ip; + + memset(&vtep_ip, 0, sizeof(struct ipaddr)); + if (CHECK_FLAG(api.flags, + ZEBRA_FLAG_EVPN_ROUTE)) { + ifindex = + get_l3vni_svi_ifindex(vrf_id); + } else { + ifindex = api_nh->ifindex; + } + nexthop = route_entry_nexthop_ipv4_ifindex_add( re, &api_nh->gate.ipv4, NULL, - api_nh->ifindex); + ifindex); + + /* if this an EVPN route entry, + program the nh as neigh + */ + if (CHECK_FLAG(api.flags, + ZEBRA_FLAG_EVPN_ROUTE)) { + SET_FLAG(nexthop->flags, + NEXTHOP_FLAG_EVPN_RVTEP); + vtep_ip.ipa_type = IPADDR_V4; + memcpy(&(vtep_ip.ipaddr_v4), + &(api_nh->gate.ipv4), + sizeof(struct in_addr)); + zebra_vxlan_evpn_vrf_route_add( + vrf_id, + &api.rmac, + &vtep_ip, + &api.prefix); + } break; + } case NEXTHOP_TYPE_IPV6: nexthop = route_entry_nexthop_ipv6_add( re, &api_nh->gate.ipv6); @@ -1265,7 +1299,7 @@ static int zread_route_del(struct zserv *client, u_short length, rib_delete(afi, api.safi, zvrf_id(zvrf), api.type, api.instance, api.flags, &api.prefix, src_p, NULL, zvrf->table_id, - api.metric, false); + api.metric, false, &api.rmac); /* Stats */ switch (api.prefix.family) { @@ -1466,7 +1500,7 @@ static int zread_ipv4_delete(struct zserv *client, u_short length, table_id = zvrf->table_id; rib_delete(AFI_IP, api.safi, zvrf_id(zvrf), api.type, api.instance, - api.flags, &p, NULL, NULL, table_id, 0, false); + api.flags, &p, NULL, NULL, table_id, 0, false, NULL); client->v4_route_del_cnt++; stream_failure: @@ -1884,7 +1918,8 @@ static int zread_ipv6_delete(struct zserv *client, u_short length, src_pp = NULL; rib_delete(AFI_IP6, api.safi, zvrf_id(zvrf), api.type, api.instance, - api.flags, &p, src_pp, NULL, client->rtm_table, 0, false); + api.flags, &p, src_pp, NULL, client->rtm_table, 0, false, + NULL); client->v6_route_del_cnt++; @@ -2979,6 +3014,8 @@ static void zebra_show_client_detail(struct vty *vty, struct zserv *client) vty_out(vty, "Interface Down Notifications: %d\n", client->ifdown_cnt); vty_out(vty, "VNI add notifications: %d\n", client->vniadd_cnt); vty_out(vty, "VNI delete notifications: %d\n", client->vnidel_cnt); + vty_out(vty, "L3-VNI add notifications: %d\n", client->l3vniadd_cnt); + vty_out(vty, "L3-VNI delete notifications: %d\n", client->l3vnidel_cnt); vty_out(vty, "MAC-IP add notifications: %d\n", client->macipadd_cnt); vty_out(vty, "MAC-IP delete notifications: %d\n", client->macipdel_cnt); diff --git a/zebra/zserv.h b/zebra/zserv.h index c4b4e20df2..63db9d9eb9 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -110,6 +110,8 @@ struct zserv { u_int32_t bfd_client_reg_cnt; u_int32_t vniadd_cnt; u_int32_t vnidel_cnt; + u_int32_t l3vniadd_cnt; + u_int32_t l3vnidel_cnt; u_int32_t macipadd_cnt; u_int32_t macipdel_cnt; |
