From 6675513d00b8cb59942ecb05f990852d14ef3e41 Mon Sep 17 00:00:00 2001 From: vivek Date: Sun, 14 May 2017 22:31:08 -0700 Subject: zebra: Layer-2 interface handling Define interface types of interest and recognize the types. Store layer-2 information (VLAN Id, VNI etc.) for interfaces, process bridge interfaces and map bridge members to bridge. Display all the additional information to user (through "show interface"). Note: Only implemented for the netlink interface. Signed-off-by: Vivek Venkatraman Reviewed-by: Donald Sharp --- zebra/interface.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) (limited to 'zebra/interface.c') diff --git a/zebra/interface.c b/zebra/interface.c index b8426c6890..78ba8cdde8 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -680,6 +680,8 @@ if_delete_connected (struct interface *ifp) void if_delete_update (struct interface *ifp) { + struct zebra_if *zif; + if (if_is_up(ifp)) { zlog_err ("interface %s vrf %u index %d is still up while being deleted.", @@ -713,6 +715,16 @@ if_delete_update (struct interface *ifp) /* if the ifp is in a vrf, move it to default so vrf can be deleted if desired */ if (ifp->vrf_id) if_handle_vrf_change (ifp, VRF_DEFAULT); + + /* Reset some zebra interface params to default values. */ + zif = ifp->info; + if (zif) + { + zif->zif_type = ZEBRA_IF_OTHER; + zif->zif_slave_type = ZEBRA_IF_SLAVE_NONE; + memset (&zif->l2info, 0, sizeof (union zebra_l2if_info)); + memset (&zif->brslave_info, 0, sizeof (struct zebra_l2info_brslave)); + } } /* VRF change for an interface */ @@ -904,6 +916,17 @@ if_refresh (struct interface *ifp) if_get_flags (ifp); } +void +zebra_if_update_link (struct interface *ifp, ifindex_t link_ifindex) +{ + struct zebra_if *zif; + + zif = (struct zebra_if *)ifp->info; + zif->link_ifindex = link_ifindex; + zif->link = if_lookup_by_index_per_ns (zebra_ns_lookup (NS_DEFAULT), + link_ifindex); +} + /* Output prefix string to vty. */ static int @@ -1020,6 +1043,37 @@ nd_dump_vty (struct vty *vty, struct interface *ifp) } #endif /* HAVE_RTADV */ +static const char * +zebra_ziftype_2str (zebra_iftype_t zif_type) +{ + switch (zif_type) + { + case ZEBRA_IF_OTHER: + return "Other"; + break; + + case ZEBRA_IF_BRIDGE: + return "Bridge"; + break; + + case ZEBRA_IF_VLAN: + return "Vlan"; + break; + + case ZEBRA_IF_VXLAN: + return "Vxlan"; + break; + + case ZEBRA_IF_VRF: + return "VRF"; + break; + + default: + return "Unknown"; + break; + } +} + /* Interface's information print out to vty interface. */ static void if_dump_vty (struct vty *vty, struct interface *ifp) @@ -1115,6 +1169,51 @@ if_dump_vty (struct vty *vty, struct interface *ifp) connected_dump_vty (vty, connected); } + vty_out(vty, " Interface Type %s%s", + zebra_ziftype_2str (zebra_if->zif_type), VTY_NEWLINE); + if (IS_ZEBRA_IF_BRIDGE (ifp)) + { + struct zebra_l2info_bridge *bridge_info; + + bridge_info = &zebra_if->l2info.br; + vty_out(vty, " Bridge VLAN-aware: %s%s", + bridge_info->vlan_aware ? "yes" : "no", VTY_NEWLINE); + } + else if (IS_ZEBRA_IF_VLAN(ifp)) + { + struct zebra_l2info_vlan *vlan_info; + + vlan_info = &zebra_if->l2info.vl; + vty_out(vty, " VLAN Id %u%s", + vlan_info->vid, VTY_NEWLINE); + } + else if (IS_ZEBRA_IF_VXLAN (ifp)) + { + struct zebra_l2info_vxlan *vxlan_info; + + vxlan_info = &zebra_if->l2info.vxl; + vty_out(vty, " VxLAN Id %u", vxlan_info->vni); + if (vxlan_info->vtep_ip.s_addr != INADDR_ANY) + vty_out(vty, " VTEP IP: %s", inet_ntoa (vxlan_info->vtep_ip)); + if (vxlan_info->access_vlan) + vty_out(vty, " Access VLAN Id %u", vxlan_info->access_vlan); + vty_out(vty, "%s", VTY_NEWLINE); + } + + if (IS_ZEBRA_IF_BRIDGE_SLAVE (ifp)) + { + struct zebra_l2info_brslave *br_slave; + + br_slave = &zebra_if->brslave_info; + if (br_slave->bridge_ifindex != IFINDEX_INTERNAL) + vty_out(vty, " Master (bridge) ifindex %u%s", + br_slave->bridge_ifindex, VTY_NEWLINE); + } + + if (zebra_if->link_ifindex != IFINDEX_INTERNAL) + vty_out(vty, " Link ifindex %u%s", + zebra_if->link_ifindex, VTY_NEWLINE); + if (HAS_LINK_PARAMS(ifp)) { int i; -- cgit v1.2.3 From 13d60d351c4c70e8a2949ef45d88ec4efe382367 Mon Sep 17 00:00:00 2001 From: vivek Date: Sun, 14 May 2017 22:38:26 -0700 Subject: zebra: VNI and VTEP handling Implement fundamental handling for VNIs and VTEPs: - Handle EVPN enable/disable by client (advertise-all-vni) - Create/update/delete VNIs based on VxLAN interface events and inform client - Handle VTEP add/delete from client and install into kernel - New debug command for VxLAN/EVPN - kernel interface (Linux/netlink only) Signed-off-by: Vivek Venkatraman Reviewed-by: Donald Sharp --- lib/log.c | 5 + lib/zclient.h | 5 + zebra/Makefile.am | 3 +- zebra/debug.c | 34 ++ zebra/debug.h | 4 + zebra/if_netlink.c | 9 + zebra/interface.c | 7 + zebra/rt.h | 5 + zebra/rt_netlink.c | 67 ++++ zebra/rt_socket.c | 13 + zebra/zebra_l2.c | 15 +- zebra/zebra_vrf.c | 5 + zebra/zebra_vrf.h | 9 + zebra/zebra_vxlan.c | 905 +++++++++++++++++++++++++++++++++++++++++++++++ zebra/zebra_vxlan.h | 59 +++ zebra/zebra_vxlan_null.c | 89 +++++ zebra/zserv.c | 13 +- zebra/zserv.h | 2 + 18 files changed, 1246 insertions(+), 3 deletions(-) create mode 100644 zebra/zebra_vxlan.c create mode 100644 zebra/zebra_vxlan.h create mode 100644 zebra/zebra_vxlan_null.c (limited to 'zebra/interface.c') diff --git a/lib/log.c b/lib/log.c index 1c61d72168..7a7545201d 100644 --- a/lib/log.c +++ b/lib/log.c @@ -946,6 +946,11 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY (ZEBRA_LABEL_MANAGER_CONNECT), DESC_ENTRY (ZEBRA_GET_LABEL_CHUNK), DESC_ENTRY (ZEBRA_RELEASE_LABEL_CHUNK), + DESC_ENTRY (ZEBRA_ADVERTISE_ALL_VNI), + DESC_ENTRY (ZEBRA_VNI_ADD), + DESC_ENTRY (ZEBRA_VNI_DEL), + DESC_ENTRY (ZEBRA_REMOTE_VTEP_ADD), + DESC_ENTRY (ZEBRA_REMOTE_VTEP_DEL), }; #undef DESC_ENTRY diff --git a/lib/zclient.h b/lib/zclient.h index 59412fdd4c..0c9f70751c 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -96,6 +96,11 @@ typedef enum { ZEBRA_FEC_REGISTER, ZEBRA_FEC_UNREGISTER, ZEBRA_FEC_UPDATE, + ZEBRA_ADVERTISE_ALL_VNI, + ZEBRA_VNI_ADD, + ZEBRA_VNI_DEL, + ZEBRA_REMOTE_VTEP_ADD, + ZEBRA_REMOTE_VTEP_DEL, } zebra_message_types_t; struct redist_proto diff --git a/zebra/Makefile.am b/zebra/Makefile.am index 25e6b1bc1b..2bb9da0a9e 100644 --- a/zebra/Makefile.am +++ b/zebra/Makefile.am @@ -34,6 +34,7 @@ zebra_SOURCES = \ zebra_mroute.c \ label_manager.c \ zebra_l2.c \ + zebra_vxlan.c \ # end noinst_HEADERS = \ @@ -44,7 +45,7 @@ noinst_HEADERS = \ zebra_ptm_redistribute.h zebra_ptm.h zebra_routemap.h \ zebra_ns.h zebra_vrf.h ioctl_solaris.h zebra_static.h zebra_mpls.h \ kernel_netlink.h if_netlink.h zebra_mroute.h label_manager.h \ - zebra_l2.h zebra_vxlan_private.h + zebra_l2.h zebra_vxlan_private.h zebra_vxlan.h zebra_LDADD = $(otherobj) ../lib/libfrr.la $(LIBCAP) diff --git a/zebra/debug.c b/zebra/debug.c index ba2a9ad2a3..0b0a0e9886 100644 --- a/zebra/debug.c +++ b/zebra/debug.c @@ -31,6 +31,7 @@ unsigned long zebra_debug_rib; unsigned long zebra_debug_fpm; unsigned long zebra_debug_nht; unsigned long zebra_debug_mpls; +unsigned long zebra_debug_vxlan; DEFUN (show_debugging_zebra, show_debugging_zebra_cmd, @@ -121,6 +122,17 @@ DEFUN (debug_zebra_mpls, return CMD_WARNING; } +DEFUN (debug_zebra_vxlan, + debug_zebra_vxlan_cmd, + "debug zebra vxlan", + DEBUG_STR + "Zebra configuration\n" + "Debug option set for zebra VxLAN (EVPN)\n") +{ + zebra_debug_vxlan = ZEBRA_DEBUG_VXLAN; + return CMD_WARNING; +} + DEFUN (debug_zebra_packet, debug_zebra_packet_cmd, "debug zebra packet [] [detail]", @@ -251,6 +263,18 @@ DEFUN (no_debug_zebra_mpls, return CMD_SUCCESS; } +DEFUN (no_debug_zebra_vxlan, + no_debug_zebra_vxlan_cmd, + "no debug zebra vxlan", + NO_STR + DEBUG_STR + "Zebra configuration\n" + "Debug option set for zebra VxLAN (EVPN)\n") +{ + zebra_debug_vxlan = 0; + return CMD_SUCCESS; +} + DEFUN (no_debug_zebra_packet, no_debug_zebra_packet_cmd, "no debug zebra packet []", @@ -419,6 +443,11 @@ config_write_debug (struct vty *vty) vty_out (vty, "debug zebra mpls%s", VTYNL); write++; } + if (IS_ZEBRA_DEBUG_VXLAN) + { + vty_out (vty, "debug zebra vxlan%s", VTY_NEWLINE); + write++; + } return write; } @@ -431,6 +460,7 @@ zebra_debug_init (void) zebra_debug_rib = 0; zebra_debug_fpm = 0; zebra_debug_mpls = 0; + zebra_debug_vxlan = 0; install_node (&debug_node, config_write_debug); @@ -439,6 +469,7 @@ zebra_debug_init (void) install_element (ENABLE_NODE, &debug_zebra_events_cmd); install_element (ENABLE_NODE, &debug_zebra_nht_cmd); install_element (ENABLE_NODE, &debug_zebra_mpls_cmd); + install_element (ENABLE_NODE, &debug_zebra_vxlan_cmd); install_element (ENABLE_NODE, &debug_zebra_packet_cmd); install_element (ENABLE_NODE, &debug_zebra_kernel_cmd); install_element (ENABLE_NODE, &debug_zebra_kernel_msgdump_cmd); @@ -448,6 +479,7 @@ zebra_debug_init (void) install_element (ENABLE_NODE, &no_debug_zebra_events_cmd); install_element (ENABLE_NODE, &no_debug_zebra_nht_cmd); install_element (ENABLE_NODE, &no_debug_zebra_mpls_cmd); + install_element (ENABLE_NODE, &no_debug_zebra_vxlan_cmd); install_element (ENABLE_NODE, &no_debug_zebra_packet_cmd); install_element (ENABLE_NODE, &no_debug_zebra_kernel_cmd); install_element (ENABLE_NODE, &no_debug_zebra_kernel_msgdump_cmd); @@ -458,6 +490,7 @@ zebra_debug_init (void) install_element (CONFIG_NODE, &debug_zebra_events_cmd); install_element (CONFIG_NODE, &debug_zebra_nht_cmd); install_element (CONFIG_NODE, &debug_zebra_mpls_cmd); + install_element (CONFIG_NODE, &debug_zebra_vxlan_cmd); install_element (CONFIG_NODE, &debug_zebra_packet_cmd); install_element (CONFIG_NODE, &debug_zebra_kernel_cmd); install_element (CONFIG_NODE, &debug_zebra_kernel_msgdump_cmd); @@ -467,6 +500,7 @@ zebra_debug_init (void) install_element (CONFIG_NODE, &no_debug_zebra_events_cmd); install_element (CONFIG_NODE, &no_debug_zebra_nht_cmd); install_element (CONFIG_NODE, &no_debug_zebra_mpls_cmd); + install_element (CONFIG_NODE, &no_debug_zebra_vxlan_cmd); install_element (CONFIG_NODE, &no_debug_zebra_packet_cmd); install_element (CONFIG_NODE, &no_debug_zebra_kernel_cmd); install_element (CONFIG_NODE, &no_debug_zebra_kernel_msgdump_cmd); diff --git a/zebra/debug.h b/zebra/debug.h index 0a50da8176..5687a3516b 100644 --- a/zebra/debug.h +++ b/zebra/debug.h @@ -42,6 +42,8 @@ #define ZEBRA_DEBUG_MPLS 0x01 +#define ZEBRA_DEBUG_VXLAN 0x01 + /* Debug related macro. */ #define IS_ZEBRA_DEBUG_EVENT (zebra_debug_event & ZEBRA_DEBUG_EVENT) @@ -63,6 +65,7 @@ #define IS_ZEBRA_DEBUG_FPM (zebra_debug_fpm & ZEBRA_DEBUG_FPM) #define IS_ZEBRA_DEBUG_NHT (zebra_debug_nht & ZEBRA_DEBUG_NHT) #define IS_ZEBRA_DEBUG_MPLS (zebra_debug_mpls & ZEBRA_DEBUG_MPLS) +#define IS_ZEBRA_DEBUG_VXLAN (zebra_debug_vxlan & ZEBRA_DEBUG_VXLAN) extern unsigned long zebra_debug_event; extern unsigned long zebra_debug_packet; @@ -71,6 +74,7 @@ extern unsigned long zebra_debug_rib; extern unsigned long zebra_debug_fpm; extern unsigned long zebra_debug_nht; extern unsigned long zebra_debug_mpls; +extern unsigned long zebra_debug_vxlan; extern void zebra_debug_init (void); diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 972e6447f6..d0907a2670 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -710,6 +710,15 @@ interface_lookup_netlink (struct zebra_ns *zns) if (ret < 0) return ret; + /* Get interface information - for bridge interfaces. */ + ret = netlink_request_intf_addr (zns, AF_BRIDGE, RTM_GETLINK, + RTEXT_FILTER_BRVLAN); + if (ret < 0) + return ret; + ret = netlink_parse_info (netlink_interface, &zns->netlink_cmd, zns, 0, 0); + if (ret < 0) + return ret; + /* Get IPv4 address of the interfaces. */ ret = netlink_request_intf_addr (zns, AF_INET, RTM_GETADDR, 0); if (ret < 0) diff --git a/zebra/interface.c b/zebra/interface.c index 78ba8cdde8..87b0896f9f 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -47,6 +47,7 @@ #include "zebra/zebra_ptm.h" #include "zebra/rt_netlink.h" #include "zebra/interface.h" +#include "zebra/zebra_vxlan.h" #define ZEBRA_PTM_SUPPORT @@ -880,6 +881,9 @@ if_up (struct interface *ifp) rib_update (ifp->vrf_id, RIB_UPDATE_IF_CHANGE); zebra_vrf_static_route_interface_fixup (ifp); + + if (IS_ZEBRA_IF_VXLAN (ifp)) + zebra_vxlan_if_up (ifp); } /* Interface goes down. We have to manage different behavior of based @@ -893,6 +897,9 @@ if_down (struct interface *ifp) zif->down_count++; quagga_timestamp (2, zif->down_last, sizeof (zif->down_last)); + if (IS_ZEBRA_IF_VXLAN (ifp)) + zebra_vxlan_if_down (ifp); + /* Notify to the protocol daemons. */ zebra_interface_down_update (ifp); diff --git a/zebra/rt.h b/zebra/rt.h index a39af87b5a..10ca6c95cf 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -24,6 +24,7 @@ #include "prefix.h" #include "if.h" +#include "vxlan.h" #include "zebra/rib.h" #include "zebra/zebra_ns.h" #include "zebra/zebra_mpls.h" @@ -41,4 +42,8 @@ extern int kernel_del_lsp (zebra_lsp_t *); extern int mpls_kernel_init (void); extern int kernel_get_ipmr_sg_stats (void *mroute); +extern int kernel_add_vtep (vni_t vni, struct interface *ifp, + struct in_addr *vtep_ip); +extern int kernel_del_vtep (vni_t vni, struct interface *ifp, + struct in_addr *vtep_ip); #endif /* _ZEBRA_RT_H */ diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 471f650588..7ff03e7c04 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -41,6 +41,7 @@ #include "vrf.h" #include "vty.h" #include "mpls.h" +#include "vxlan.h" #include "zebra/zserv.h" #include "zebra/zebra_ns.h" @@ -1555,6 +1556,72 @@ kernel_neigh_update (int add, int ifindex, uint32_t addr, char *lla, int llalen) lla, llalen); } +/* + * Add remote VTEP to the flood list for this VxLAN interface (VNI). This + * is done by adding an FDB entry with a MAC of 00:00:00:00:00:00. + */ +static int +netlink_vxlan_flood_list_update (struct interface *ifp, + struct in_addr *vtep_ip, + int cmd) +{ + struct zebra_ns *zns = zebra_ns_lookup (NS_DEFAULT); + struct + { + struct nlmsghdr n; + struct ndmsg ndm; + char buf[256]; + } req; + u_char dst_mac[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; + + memset(&req.n, 0, sizeof(req.n)); + memset(&req.ndm, 0, sizeof(req.ndm)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + if (cmd == RTM_NEWNEIGH) + req.n.nlmsg_flags |= (NLM_F_CREATE | NLM_F_APPEND); + req.n.nlmsg_type = cmd; + req.ndm.ndm_family = PF_BRIDGE; + req.ndm.ndm_state = NUD_NOARP | NUD_PERMANENT; + req.ndm.ndm_flags |= NTF_SELF; // Handle by "self", not "master" + + + addattr_l (&req.n, sizeof (req), NDA_LLADDR, &dst_mac, 6); + req.ndm.ndm_ifindex = ifp->ifindex; + addattr_l (&req.n, sizeof (req), NDA_DST, &vtep_ip->s_addr, 4); + + return netlink_talk (netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, 0); +} + +/* + * Add remote VTEP for this VxLAN interface (VNI). In Linux, this involves adding + * a "flood" MAC FDB entry. + */ +int +kernel_add_vtep (vni_t vni, struct interface *ifp, struct in_addr *vtep_ip) +{ + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("Install %s into flood list for VNI %u intf %s(%u)", + inet_ntoa (*vtep_ip), vni, ifp->name, ifp->ifindex); + + return netlink_vxlan_flood_list_update (ifp, vtep_ip, RTM_NEWNEIGH); +} + +/* + * Remove remote VTEP for this VxLAN interface (VNI). In Linux, this involves + * deleting the "flood" MAC FDB entry. + */ +int +kernel_del_vtep (vni_t vni, struct interface *ifp, struct in_addr *vtep_ip) +{ + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("Uninstall %s from flood list for VNI %u intf %s(%u)", + inet_ntoa (*vtep_ip), vni, ifp->name, ifp->ifindex); + + return netlink_vxlan_flood_list_update (ifp, vtep_ip, RTM_DELNEIGH); +} + /* * MPLS label forwarding table change via netlink interface. */ diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index 9859a31627..bf7e3403e4 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -29,6 +29,7 @@ #include "sockunion.h" #include "log.h" #include "privs.h" +#include "vxlan.h" #include "zebra/debug.h" #include "zebra/rib.h" @@ -428,3 +429,15 @@ kernel_get_ipmr_sg_stats (void *mroute) { return 0; } + +int +kernel_add_vtep (vni_t vni, struct interface *ifp, struct in_addr *vtep_ip) +{ + return 0; +} + +int +kernel_del_vtep (vni_t vni, struct interface *ifp, struct in_addr *vtep_ip) +{ + return 0; +} diff --git a/zebra/zebra_l2.c b/zebra/zebra_l2.c index 5dd83baedd..b71b96a18b 100644 --- a/zebra/zebra_l2.c +++ b/zebra/zebra_l2.c @@ -42,6 +42,7 @@ #include "zebra/zebra_vrf.h" #include "zebra/rt_netlink.h" #include "zebra/zebra_l2.h" +#include "zebra/zebra_vxlan.h" /* definitions */ @@ -175,6 +176,7 @@ zebra_l2_vxlanif_add_update (struct interface *ifp, if (add) { memcpy (&zif->l2info.vxl, vxlan_info, sizeof (*vxlan_info)); + zebra_vxlan_if_add (ifp); return; } @@ -183,6 +185,7 @@ zebra_l2_vxlanif_add_update (struct interface *ifp, return; zif->l2info.vxl.vtep_ip = vxlan_info->vtep_ip; + zebra_vxlan_if_update (ifp, ZEBRA_VXLIF_LOCAL_IP_CHANGE); } /* @@ -193,11 +196,17 @@ zebra_l2_vxlanif_update_access_vlan (struct interface *ifp, vlanid_t access_vlan) { struct zebra_if *zif; + vlanid_t old_access_vlan; zif = ifp->info; assert(zif); + old_access_vlan = zif->l2info.vxl.access_vlan; + if (old_access_vlan == access_vlan) + return; + zif->l2info.vxl.access_vlan = access_vlan; + zebra_vxlan_if_update (ifp, ZEBRA_VXLIF_VLAN_CHANGE); } /* @@ -206,7 +215,7 @@ zebra_l2_vxlanif_update_access_vlan (struct interface *ifp, void zebra_l2_vxlanif_del (struct interface *ifp) { - /* No action currently. */ + zebra_vxlan_if_del (ifp); } /* @@ -235,4 +244,8 @@ zebra_l2if_update_bridge_slave (struct interface *ifp, zebra_l2_map_slave_to_bridge (&zif->brslave_info); else if (old_bridge_ifindex != IFINDEX_INTERNAL) zebra_l2_unmap_slave_from_bridge (&zif->brslave_info); + + /* In the case of VxLAN, invoke the handler for EVPN. */ + if (zif->zif_type == ZEBRA_IF_VXLAN) + zebra_vxlan_if_update (ifp, ZEBRA_VXLIF_MASTER_CHANGE); } diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 1656892675..32c636e5e7 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -37,6 +37,7 @@ #include "zebra/zebra_static.h" #include "zebra/interface.h" #include "zebra/zebra_mpls.h" +#include "zebra/zebra_vxlan.h" extern struct zebra_t zebrad; @@ -244,6 +245,9 @@ zebra_vrf_delete (struct vrf *vrf) rib_close_table (zvrf->other_table[afi][table_id]); } + /* Cleanup Vxlan table and update kernel */ + zebra_vxlan_close_tables (zvrf); + zebra_mpls_close_tables (zvrf); for (ALL_LIST_ELEMENTS_RO (vrf->iflist, node, ifp)) @@ -421,6 +425,7 @@ zebra_vrf_alloc (void) zvrf->import_check_table[afi] = table; } + zebra_vxlan_init_tables (zvrf); zebra_mpls_init_tables (zvrf); return zvrf; diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index 790e2e53d2..29f7df00fd 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -95,6 +95,15 @@ struct zebra_vrf u_int16_t mpls_flags; #define MPLS_FLAG_SCHEDULE_LSPS (1 << 0) + /* + * VNI hash table (for EVPN). Only in default instance. + */ + struct hash *vni_table; + /* + * Whether EVPN is enabled or not. + */ + int advertise_all_vni; + /* Route Installs */ uint64_t installs; uint64_t removals; diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c new file mode 100644 index 0000000000..fcf4328478 --- /dev/null +++ b/zebra/zebra_vxlan.c @@ -0,0 +1,905 @@ +/* + * Zebra EVPN for VxLAN code + * Copyright (C) 2016, 2017 Cumulus Networks, Inc. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include + +#include "if.h" +#include "prefix.h" +#include "table.h" +#include "memory.h" +#include "log.h" +#include "linklist.h" +#include "stream.h" +#include "hash.h" +#include "jhash.h" +#include "vlan.h" +#include "vxlan.h" + +#include "zebra/rib.h" +#include "zebra/rt.h" +#include "zebra/zebra_ns.h" +#include "zebra/zserv.h" +#include "zebra/debug.h" +#include "zebra/interface.h" +#include "zebra/zebra_vrf.h" +#include "zebra/rt_netlink.h" +#include "zebra/zebra_vxlan_private.h" +#include "zebra/zebra_vxlan.h" +#include "zebra/zebra_memory.h" +#include "zebra/zebra_l2.h" + +DEFINE_MTYPE_STATIC(ZEBRA, ZVNI, "VNI hash"); +DEFINE_MTYPE_STATIC(ZEBRA, ZVNI_VTEP, "VNI remote VTEP"); + +/* definitions */ + + +/* static function declarations */ +static unsigned int +vni_hash_keymake (void *p); +static int +vni_hash_cmp (const void *p1, const void *p2); +static void * +zvni_alloc (void *p); +static zebra_vni_t * +zvni_lookup (struct zebra_vrf *zvrf, vni_t vni); +static zebra_vni_t * +zvni_add (struct zebra_vrf *zvrf, vni_t vni); +static int +zvni_del (struct zebra_vrf *zvrf, zebra_vni_t *zvni); +static int +zvni_send_add_to_client (struct zebra_vrf *zvrf, zebra_vni_t *zvni); +static int +zvni_send_del_to_client (struct zebra_vrf *zvrf, vni_t vni); +static void +zvni_build_hash_table (struct zebra_vrf *zvrf); +static int +zvni_vtep_match (struct in_addr *vtep_ip, zebra_vtep_t *zvtep); +static zebra_vtep_t * +zvni_vtep_find (zebra_vni_t *zvni, struct in_addr *vtep_ip); +static zebra_vtep_t * +zvni_vtep_add (zebra_vni_t *zvni, struct in_addr *vtep_ip); +static int +zvni_vtep_del (zebra_vni_t *zvni, zebra_vtep_t *zvtep); +static int +zvni_vtep_del_all (zebra_vni_t *zvni, int uninstall); +static int +zvni_vtep_install (zebra_vni_t *zvni, struct in_addr *vtep_ip); +static int +zvni_vtep_uninstall (zebra_vni_t *zvni, struct in_addr *vtep_ip); + + +/* Private functions */ + +/* + * Hash function for VNI. + */ +static unsigned int +vni_hash_keymake (void *p) +{ + const zebra_vni_t *zvni = p; + + return (jhash_1word(zvni->vni, 0)); +} + +/* + * Compare 2 VNI hash entries. + */ +static int +vni_hash_cmp (const void *p1, const void *p2) +{ + const zebra_vni_t *zvni1 = p1; + const zebra_vni_t *zvni2 = p2; + + return (zvni1->vni == zvni2->vni); +} + +/* + * Callback to allocate VNI hash entry. + */ +static void * +zvni_alloc (void *p) +{ + const zebra_vni_t *tmp_vni = p; + zebra_vni_t *zvni; + + zvni = XCALLOC (MTYPE_ZVNI, sizeof(zebra_vni_t)); + zvni->vni = tmp_vni->vni; + return ((void *)zvni); +} + +/* + * Look up VNI hash entry. + */ +static zebra_vni_t * +zvni_lookup (struct zebra_vrf *zvrf, vni_t vni) +{ + zebra_vni_t tmp_vni; + zebra_vni_t *zvni = NULL; + + memset (&tmp_vni, 0, sizeof (zebra_vni_t)); + tmp_vni.vni = vni; + zvni = hash_lookup (zvrf->vni_table, &tmp_vni); + + return zvni; +} + +/* + * Add VNI hash entry. + */ +static zebra_vni_t * +zvni_add (struct zebra_vrf *zvrf, vni_t vni) +{ + zebra_vni_t tmp_zvni; + zebra_vni_t *zvni = NULL; + + memset (&tmp_zvni, 0, sizeof (zebra_vni_t)); + tmp_zvni.vni = vni; + zvni = hash_get (zvrf->vni_table, &tmp_zvni, zvni_alloc); + assert (zvni); + + return zvni; +} + +/* + * Delete VNI hash entry. + */ +static int +zvni_del (struct zebra_vrf *zvrf, zebra_vni_t *zvni) +{ + zebra_vni_t *tmp_zvni; + + zvni->vxlan_if = NULL; + + /* Free the VNI hash entry and allocated memory. */ + tmp_zvni = hash_release (zvrf->vni_table, zvni); + if (tmp_zvni) + XFREE(MTYPE_ZVNI, tmp_zvni); + + return 0; +} + +/* + * Inform BGP about local VNI addition. + */ +static int +zvni_send_add_to_client (struct zebra_vrf *zvrf, + zebra_vni_t *zvni) +{ + struct zserv *client; + struct stream *s; + + client = zebra_find_client (ZEBRA_ROUTE_BGP); + /* BGP may not be running. */ + if (!client) + return 0; + + s = client->obuf; + stream_reset (s); + + zserv_create_header (s, ZEBRA_VNI_ADD, zvrf_id (zvrf)); + stream_putl (s, zvni->vni); + stream_put_in_addr (s, &zvni->local_vtep_ip); + + /* Write packet size. */ + stream_putw_at (s, 0, stream_get_endp (s)); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("%u:Send VNI_ADD %u %s to %s", + zvrf_id (zvrf), zvni->vni, + inet_ntoa(zvni->local_vtep_ip), + zebra_route_string (client->proto)); + + client->vniadd_cnt++; + return zebra_server_send_message(client); +} + +/* + * Inform BGP about local VNI deletion. + */ +static int +zvni_send_del_to_client (struct zebra_vrf *zvrf, vni_t vni) +{ + struct zserv *client; + struct stream *s; + + client = zebra_find_client (ZEBRA_ROUTE_BGP); + /* BGP may not be running. */ + if (!client) + return 0; + + s = client->obuf; + stream_reset (s); + + zserv_create_header (s, ZEBRA_VNI_DEL, zvrf_id (zvrf)); + stream_putl (s, vni); + + /* Write packet size. */ + stream_putw_at (s, 0, stream_get_endp (s)); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("%u:Send VNI_DEL %u to %s", zvrf_id (zvrf), vni, + zebra_route_string (client->proto)); + + client->vnidel_cnt++; + return zebra_server_send_message(client); +} + +/* + * Build the VNI hash table by going over the VxLAN interfaces. This + * is called when EVPN (advertise-all-vni) is enabled. + */ +static void +zvni_build_hash_table (struct zebra_vrf *zvrf) +{ + struct listnode *node; + struct interface *ifp; + + /* Walk VxLAN interfaces and create VNI hash. */ + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (zvrf_id (zvrf)), node, ifp)) + { + struct zebra_if *zif; + struct zebra_l2info_vxlan *vxl; + zebra_vni_t *zvni; + vni_t vni; + + zif = ifp->info; + if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) + continue; + vxl = &zif->l2info.vxl; + + vni = vxl->vni; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("%u:Create VNI hash for intf %s(%u) VNI %u local IP %s", + zvrf_id (zvrf), ifp->name, ifp->ifindex, vni, + inet_ntoa (vxl->vtep_ip)); + + /* VNI hash entry is not expected to exist. */ + zvni = zvni_lookup (zvrf, vni); + if (zvni) + { + zlog_err ("VNI hash already present for VRF %d IF %s(%u) VNI %u", + zvrf_id (zvrf), ifp->name, ifp->ifindex, vni); + continue; + } + + zvni = zvni_add (zvrf, vni); + if (!zvni) + { + zlog_err ("Failed to add VNI hash, VRF %d IF %s(%u) VNI %u", + zvrf_id (zvrf), ifp->name, ifp->ifindex, vni); + return; + } + + zvni->local_vtep_ip = vxl->vtep_ip; + zvni->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 (zvrf, zvni); + } +} + +/* + * See if remote VTEP matches with prefix. + */ +static int +zvni_vtep_match (struct in_addr *vtep_ip, zebra_vtep_t *zvtep) +{ + return (IPV4_ADDR_SAME (vtep_ip, &zvtep->vtep_ip)); +} + +/* + * Locate remote VTEP in VNI hash table. + */ +static zebra_vtep_t * +zvni_vtep_find (zebra_vni_t *zvni, struct in_addr *vtep_ip) +{ + zebra_vtep_t *zvtep; + + if (!zvni) + return NULL; + + for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) + { + if (zvni_vtep_match (vtep_ip, zvtep)) + break; + } + + return zvtep; +} + +/* + * Add remote VTEP to VNI hash table. + */ +static zebra_vtep_t * +zvni_vtep_add (zebra_vni_t *zvni, struct in_addr *vtep_ip) +{ + zebra_vtep_t *zvtep; + + zvtep = XCALLOC (MTYPE_ZVNI_VTEP, sizeof(zebra_vtep_t)); + if (!zvtep) + { + zlog_err ("Failed to alloc VTEP entry, VNI %u", zvni->vni); + return NULL; + } + + zvtep->vtep_ip = *vtep_ip; + + if (zvni->vteps) + zvni->vteps->prev = zvtep; + zvtep->next = zvni->vteps; + zvni->vteps = zvtep; + + return zvtep; +} + +/* + * Remove remote VTEP from VNI hash table. + */ +static int +zvni_vtep_del (zebra_vni_t *zvni, zebra_vtep_t *zvtep) +{ + if (zvtep->next) + zvtep->next->prev = zvtep->prev; + if (zvtep->prev) + zvtep->prev->next = zvtep->next; + else + zvni->vteps = zvtep->next; + + zvtep->prev = zvtep->next = NULL; + XFREE (MTYPE_ZVNI_VTEP, zvtep); + + return 0; +} + +/* + * Delete all remote VTEPs for this VNI (upon VNI delete). Also + * uninstall from kernel if asked to. + */ +static int +zvni_vtep_del_all (zebra_vni_t *zvni, int uninstall) +{ + zebra_vtep_t *zvtep, *zvtep_next; + + if (!zvni) + return -1; + + for (zvtep = zvni->vteps; zvtep; zvtep = zvtep_next) + { + zvtep_next = zvtep->next; + if (uninstall) + zvni_vtep_uninstall (zvni, &zvtep->vtep_ip); + zvni_vtep_del (zvni, zvtep); + } + + return 0; +} + +/* + * Install remote VTEP into the kernel. + */ +static int +zvni_vtep_install (zebra_vni_t *zvni, struct in_addr *vtep_ip) +{ + return kernel_add_vtep (zvni->vni, zvni->vxlan_if, vtep_ip); +} + +/* + * Uninstall remote VTEP from the kernel. + */ +static int +zvni_vtep_uninstall (zebra_vni_t *zvni, struct in_addr *vtep_ip) +{ + if (!zvni->vxlan_if) + { + zlog_err ("VNI %u hash %p couldn't be uninstalled - no intf", + zvni->vni, zvni); + return -1; + } + + return kernel_del_vtep (zvni->vni, zvni->vxlan_if, vtep_ip); +} + +/* + * Cleanup VNI/VTEP and update kernel + */ +static void +zvni_cleanup_all (struct hash_backet *backet, void *zvrf) +{ + zebra_vni_t *zvni; + + zvni = (zebra_vni_t *) backet->data; + if (!zvni) + return; + + /* Free up all remote VTEPs, if any. */ + zvni_vtep_del_all (zvni, 1); + + /* Delete the hash entry. */ + zvni_del (zvrf, zvni); +} + + +/* Public functions */ + +/* + * Handle message from client to delete a remote VTEP for a VNI. + */ +int zebra_vxlan_remote_vtep_del (struct zserv *client, int sock, + u_short length, struct zebra_vrf *zvrf) +{ + struct stream *s; + u_short l = 0; + vni_t vni; + struct in_addr vtep_ip; + zebra_vni_t *zvni; + zebra_vtep_t *zvtep; + + s = client->ibuf; + + while (l < length) + { + /* Obtain each remote VTEP and process. */ + vni = (vni_t) stream_getl (s); + l += 4; + vtep_ip.s_addr = stream_get_ipv4 (s); + l += IPV4_MAX_BYTELEN; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("%u:Recv VTEP_DEL %s VNI %u from %s", + zvrf_id (zvrf), inet_ntoa (vtep_ip), + vni, zebra_route_string (client->proto)); + + /* Locate VNI hash entry - expected to exist. */ + zvni = zvni_lookup (zvrf, vni); + if (!zvni) + { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("Failed to locate VNI hash upon remote VTEP DEL, " + "VRF %d VNI %u", zvrf_id (zvrf), vni); + continue; + } + + /* If the remote VTEP does not exist, there's nothing more to do. + * Otherwise, uninstall any remote MACs pointing to this VTEP and + * then, the VTEP entry itself and remove it. + */ + zvtep = zvni_vtep_find (zvni, &vtep_ip); + if (!zvtep) + continue; + + zvni_vtep_uninstall (zvni, &vtep_ip); + zvni_vtep_del (zvni, zvtep); + } + + return 0; +} + +/* + * Handle message from client to add a remote VTEP for a VNI. + */ +int zebra_vxlan_remote_vtep_add (struct zserv *client, int sock, + u_short length, struct zebra_vrf *zvrf) +{ + struct stream *s; + u_short l = 0; + vni_t vni; + struct in_addr vtep_ip; + zebra_vni_t *zvni; + + assert (EVPN_ENABLED (zvrf)); + + s = client->ibuf; + + while (l < length) + { + /* Obtain each remote VTEP and process. */ + vni = (vni_t) stream_getl (s); + l += 4; + vtep_ip.s_addr = stream_get_ipv4 (s); + l += IPV4_MAX_BYTELEN; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("%u:Recv VTEP_ADD %s VNI %u from %s", + zvrf_id (zvrf), inet_ntoa (vtep_ip), + vni, zebra_route_string (client->proto)); + + /* Locate VNI hash entry - expected to exist. */ + zvni = zvni_lookup (zvrf, vni); + if (!zvni) + { + zlog_err ("Failed to locate VNI hash upon remote VTEP ADD, VRF %d VNI %u", + zvrf_id (zvrf), vni); + continue; + } + if (!zvni->vxlan_if) + { + zlog_err ("VNI %u hash %p doesn't have intf upon remote VTEP ADD", + zvni->vni, zvni); + continue; + } + + + /* If the remote VTEP already exists, or the local VxLAN interface is + * not up (should be a transient event), there's nothing more to do. + * Otherwise, add and install the entry. + */ + if (zvni_vtep_find (zvni, &vtep_ip)) + continue; + + if (!if_is_operative (zvni->vxlan_if)) + continue; + + if (zvni_vtep_add (zvni, &vtep_ip) == NULL) + { + zlog_err ("Failed to add remote VTEP, VRF %d VNI %u zvni %p", + zvrf_id (zvrf), vni, zvni); + continue; + } + + zvni_vtep_install (zvni, &vtep_ip); + } + + return 0; +} + +/* + * Handle VxLAN interface down - update BGP if required, and do + * internal cleanup. + */ +int +zebra_vxlan_if_down (struct interface *ifp) +{ + struct zebra_if *zif; + struct zebra_vrf *zvrf; + zebra_vni_t *zvni; + struct zebra_l2info_vxlan *vxl; + vni_t vni; + + /* Locate VRF corresponding to interface. */ + zvrf = vrf_info_lookup(ifp->vrf_id); + assert(zvrf); + + /* If EVPN is not enabled, nothing further to be done. */ + if (!EVPN_ENABLED(zvrf)) + return 0; + + zif = ifp->info; + assert(zif); + vxl = &zif->l2info.vxl; + vni = vxl->vni; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("%u:Intf %s(%u) VNI %u is DOWN", + ifp->vrf_id, ifp->name, ifp->ifindex, vni); + + /* Locate hash entry; it is expected to exist. */ + zvni = zvni_lookup (zvrf, vni); + if (!zvni) + { + zlog_err ("Failed to locate VNI hash at DOWN, VRF %d IF %s(%u) VNI %u", + ifp->vrf_id, ifp->name, ifp->ifindex, vni); + return -1; + } + + assert (zvni->vxlan_if == ifp); + + /* Delete this VNI from BGP. */ + zvni_send_del_to_client (zvrf, zvni->vni); + + /* Free up all remote VTEPs, if any. */ + zvni_vtep_del_all (zvni, 1); + + return 0; +} + +/* + * Handle VxLAN interface up - update BGP if required. + */ +int +zebra_vxlan_if_up (struct interface *ifp) +{ + struct zebra_if *zif; + struct zebra_vrf *zvrf; + zebra_vni_t *zvni; + struct zebra_l2info_vxlan *vxl; + vni_t vni; + + /* Locate VRF corresponding to interface. */ + zvrf = vrf_info_lookup(ifp->vrf_id); + assert(zvrf); + + /* If EVPN is not enabled, nothing further to be done. */ + if (!EVPN_ENABLED(zvrf)) + return 0; + + zif = ifp->info; + assert(zif); + vxl = &zif->l2info.vxl; + vni = vxl->vni; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("%u:Intf %s(%u) VNI %u is UP", + ifp->vrf_id, ifp->name, ifp->ifindex, vni); + + /* Locate hash entry; it is expected to exist. */ + zvni = zvni_lookup (zvrf, vni); + if (!zvni) + { + zlog_err ("Failed to locate VNI hash at UP, VRF %d IF %s(%u) VNI %u", + ifp->vrf_id, ifp->name, ifp->ifindex, vni); + return -1; + } + + assert (zvni->vxlan_if == ifp); + + /* If part of a bridge, inform BGP about this VNI. */ + if (zif->brslave_info.br_if) + zvni_send_add_to_client (zvrf, zvni); + + return 0; +} + +/* + * Handle VxLAN interface delete. Locate and remove entry in hash table + * and update BGP, if required. + */ +int +zebra_vxlan_if_del (struct interface *ifp) +{ + struct zebra_if *zif; + struct zebra_vrf *zvrf; + zebra_vni_t *zvni; + struct zebra_l2info_vxlan *vxl; + vni_t vni; + + /* Locate VRF corresponding to interface. */ + zvrf = vrf_info_lookup(ifp->vrf_id); + assert(zvrf); + + /* If EVPN is not enabled, nothing further to be done. */ + if (!EVPN_ENABLED(zvrf)) + return 0; + + zif = ifp->info; + assert(zif); + vxl = &zif->l2info.vxl; + vni = vxl->vni; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("%u:Del intf %s(%u) VNI %u", + ifp->vrf_id, ifp->name, ifp->ifindex, vni); + + /* Locate hash entry; it is expected to exist. */ + zvni = zvni_lookup (zvrf, vni); + if (!zvni) + { + zlog_err ("Failed to locate VNI hash at del, VRF %d IF %s(%u) VNI %u", + ifp->vrf_id, ifp->name, ifp->ifindex, vni); + return 0; + } + + /* Delete VNI from BGP. */ + zvni_send_del_to_client (zvrf, zvni->vni); + + /* Free up all remote VTEPs, if any. */ + zvni_vtep_del_all (zvni, 0); + + /* Delete the hash entry. */ + if (zvni_del (zvrf, zvni)) + { + zlog_err ("Failed to del VNI hash %p, VRF %d IF %s(%u) VNI %u", + zvni, ifp->vrf_id, ifp->name, ifp->ifindex, zvni->vni); + return -1; + } + + return 0; +} + +/* + * Handle VxLAN interface update - change to tunnel IP, master or VLAN. + */ +int +zebra_vxlan_if_update (struct interface *ifp, u_int16_t chgflags) +{ + struct zebra_if *zif; + struct zebra_vrf *zvrf; + zebra_vni_t *zvni; + struct zebra_l2info_vxlan *vxl; + vni_t vni; + + /* Locate VRF corresponding to interface. */ + zvrf = vrf_info_lookup(ifp->vrf_id); + assert(zvrf); + + /* If EVPN is not enabled, nothing further to be done. */ + if (!EVPN_ENABLED(zvrf)) + return 0; + + zif = ifp->info; + assert(zif); + vxl = &zif->l2info.vxl; + vni = vxl->vni; + + /* Update VNI hash. */ + zvni = zvni_lookup (zvrf, vni); + if (!zvni) + { + zlog_err ("Failed to find VNI hash on update, VRF %d IF %s(%u) VNI %u", + ifp->vrf_id, ifp->name, ifp->ifindex, vni); + return -1; + } + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("%u:Update intf %s(%u) VNI %u VLAN %u local IP %s " + "master %u chg 0x%x", + ifp->vrf_id, ifp->name, ifp->ifindex, + vni, vxl->access_vlan, + inet_ntoa (vxl->vtep_ip), + zif->brslave_info.bridge_ifindex, chgflags); + + /* Removed from bridge? */ + if ((chgflags & ZEBRA_VXLIF_MASTER_CHANGE) && + (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) + { + /* Delete from client, remove all remote VTEPs */ + zvni_send_del_to_client (zvrf, zvni->vni); + zvni_vtep_del_all (zvni, 1); + } + + zvni->local_vtep_ip = vxl->vtep_ip; + zvni->vxlan_if = ifp; + + /* Take further actions needed. Note that if we are here, there is a + * change of interest. + */ + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative (ifp) || !zif->brslave_info.br_if) + return 0; + + /* Inform BGP. */ + zvni_send_add_to_client (zvrf, zvni); + + return 0; +} + +/* + * Handle VxLAN interface add. + */ +int +zebra_vxlan_if_add (struct interface *ifp) +{ + struct zebra_if *zif; + struct zebra_vrf *zvrf; + zebra_vni_t *zvni; + struct zebra_l2info_vxlan *vxl; + vni_t vni; + + /* Locate VRF corresponding to interface. */ + zvrf = vrf_info_lookup(ifp->vrf_id); + assert(zvrf); + + /* If EVPN is not enabled, nothing further to be done. */ + if (!EVPN_ENABLED(zvrf)) + return 0; + + zif = ifp->info; + assert(zif); + vxl = &zif->l2info.vxl; + vni = vxl->vni; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("%u:Add intf %s(%u) VNI %u VLAN %u local IP %s master %u", + ifp->vrf_id, ifp->name, ifp->ifindex, + vni, vxl->access_vlan, + inet_ntoa (vxl->vtep_ip), + zif->brslave_info.bridge_ifindex); + + /* Create or update VNI hash. */ + zvni = zvni_lookup (zvrf, vni); + if (!zvni) + { + zvni = zvni_add (zvrf, vni); + if (!zvni) + { + zlog_err ("Failed to add VNI hash, VRF %d IF %s(%u) VNI %u", + ifp->vrf_id, ifp->name, ifp->ifindex, vni); + return -1; + } + } + + zvni->local_vtep_ip = vxl->vtep_ip; + zvni->vxlan_if = ifp; + + /* 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 (zvrf, zvni); + + return 0; +} + +/* + * Handle message from client to learn (or stop learning) about VNIs and MACs. + * When enabled, the VNI hash table will be built and MAC FDB table read; + * when disabled, the entries should be deleted and remote VTEPs and MACs + * uninstalled from the kernel. + */ +int zebra_vxlan_advertise_all_vni (struct zserv *client, int sock, + u_short length, struct zebra_vrf *zvrf) +{ + struct stream *s; + int advertise; + + s = client->ibuf; + advertise = stream_getc (s); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("%u:EVPN VNI Adv %s, currently %s", + zvrf_id (zvrf), advertise ? "enabled" : "disabled", + EVPN_ENABLED(zvrf) ? "enabled" : "disabled"); + + if (zvrf->advertise_all_vni == advertise) + return 0; + + zvrf->advertise_all_vni = advertise; + if (EVPN_ENABLED(zvrf)) + { + /* Build VNI hash table and inform BGP. */ + zvni_build_hash_table (zvrf); + } + else + { + /* Cleanup VTEPs for all VNIs - uninstall from + * kernel and free entries. + */ + hash_iterate (zvrf->vni_table, zvni_cleanup_all, zvrf); + } + + return 0; +} + +/* + * Allocate VNI hash table for this VRF and do other initialization. + * NOTE: Currently supported only for default VRF. + */ +void +zebra_vxlan_init_tables (struct zebra_vrf *zvrf) +{ + if (!zvrf) + return; + zvrf->vni_table = hash_create(vni_hash_keymake, + vni_hash_cmp, + "Zebra VRF VNI Table"); +} + +/* Close all VNI handling */ +void +zebra_vxlan_close_tables (struct zebra_vrf *zvrf) +{ + hash_iterate (zvrf->vni_table, zvni_cleanup_all, zvrf); +} diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h new file mode 100644 index 0000000000..0e8d783a3c --- /dev/null +++ b/zebra/zebra_vxlan.h @@ -0,0 +1,59 @@ +/* + * Zebra VxLAN (EVPN) Data structures and definitions + * These are public definitions referenced by other files. + * Copyright (C) 2016, 2017 Cumulus Networks, Inc. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _ZEBRA_VXLAN_H +#define _ZEBRA_VXLAN_H + +#include + +#include "linklist.h" +#include "if.h" +#include "vlan.h" +#include "vxlan.h" + +#include "zebra/interface.h" +#include "zebra/zebra_vrf.h" + +/* Is EVPN enabled? */ +#define EVPN_ENABLED(zvrf) (zvrf)->advertise_all_vni + +/* VxLAN interface change flags of interest. */ +#define ZEBRA_VXLIF_LOCAL_IP_CHANGE 0x1 +#define ZEBRA_VXLIF_MASTER_CHANGE 0x2 +#define ZEBRA_VXLIF_VLAN_CHANGE 0x4 + +extern int zebra_vxlan_if_up (struct interface *ifp); +extern int zebra_vxlan_if_down (struct interface *ifp); +extern int zebra_vxlan_if_add (struct interface *ifp); +extern int zebra_vxlan_if_update (struct interface *ifp, u_int16_t chgflags); +extern int zebra_vxlan_if_del (struct interface *ifp); +extern int zebra_vxlan_remote_vtep_add (struct zserv *client, int sock, + u_short length, struct zebra_vrf *zvrf); +extern int zebra_vxlan_remote_vtep_del (struct zserv *client, int sock, + u_short length, struct zebra_vrf *zvrf); +extern int zebra_vxlan_advertise_all_vni (struct zserv *client, int sock, + u_short length, struct zebra_vrf *zvrf); +extern void zebra_vxlan_init_tables (struct zebra_vrf *zvrf); +extern void zebra_vxlan_close_tables (struct zebra_vrf *); + +#endif /* _ZEBRA_VXLAN_H */ diff --git a/zebra/zebra_vxlan_null.c b/zebra/zebra_vxlan_null.c new file mode 100644 index 0000000000..532ab64290 --- /dev/null +++ b/zebra/zebra_vxlan_null.c @@ -0,0 +1,89 @@ +/* + * Zebra VxLAN (EVPN) + * Copyright (C) 2016, 2017 Cumulus Networks, Inc. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include + +#include "if.h" +#include "zebra/debug.h" +#include "zebra/zserv.h" +#include "zebra/rib.h" +#include "zebra/zebra_vrf.h" +#include "zebra/zebra_l2.h" +#include "zebra/zebra_vxlan.h" + +int +zebra_vxlan_if_up (struct interface *ifp) +{ + return 0; +} + +int +zebra_vxlan_if_down (struct interface *ifp) +{ + return 0; +} + +int +zebra_vxlan_if_add (struct interface *ifp) +{ + return 0; +} + +int +zebra_vxlan_if_update (struct interface *ifp, u_int16_t chgflags) +{ + return 0; +} + +int +zebra_vxlan_if_del (struct interface *ifp) +{ + return 0; +} + +int zebra_vxlan_remote_vtep_add (struct zserv *client, int sock, + u_short length, struct zebra_vrf *zvrf) +{ + return 0; +} + +int zebra_vxlan_remote_vtep_del (struct zserv *client, int sock, + u_short length, struct zebra_vrf *zvrf) +{ + return 0; +} + +int zebra_vxlan_advertise_all_vni (struct zserv *client, int sock, + u_short length, struct zebra_vrf *zvrf) +{ + return 0; +} + +void +zebra_vxlan_init_tables (struct zebra_vrf *zvrf) +{ +} + +void +zebra_vxlan_close_tables (struct zebra_vrf *zvrf) +{ +} diff --git a/zebra/zserv.c b/zebra/zserv.c index 3da94459f7..c6a9c38c35 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -54,6 +54,7 @@ #include "zebra/zebra_mpls.h" #include "zebra/zebra_mroute.h" #include "zebra/label_manager.h" +#include "zebra/zebra_vxlan.h" /* Event list of zebra. */ enum event { ZEBRA_SERV, ZEBRA_READ, ZEBRA_WRITE }; @@ -2434,6 +2435,15 @@ zebra_client_read (struct thread *thread) case ZEBRA_FEC_UNREGISTER: zserv_fec_unregister (client, sock, length); break; + case ZEBRA_ADVERTISE_ALL_VNI: + zebra_vxlan_advertise_all_vni (client, sock, length, zvrf); + break; + case ZEBRA_REMOTE_VTEP_ADD: + zebra_vxlan_remote_vtep_add (client, sock, length, zvrf); + break; + case ZEBRA_REMOTE_VTEP_DEL: + zebra_vxlan_remote_vtep_del (client, sock, length, zvrf); + break; default: zlog_info ("Zebra received unknown command %d", command); break; @@ -2725,7 +2735,8 @@ zebra_show_client_detail (struct vty *vty, struct zserv *client) client->bfd_peer_upd8_cnt, client->bfd_peer_del_cnt); vty_outln (vty, "Interface Up Notifications: %d",client->ifup_cnt); vty_outln (vty, "Interface Down Notifications: %d",client->ifdown_cnt); - + vty_outln (vty, "VNI add notifications: %d", client->vniadd_cnt); + vty_outln (vty, "VNI delete notifications: %d", client->vnidel_cnt); vty_out (vty, VTYNL); return; } diff --git a/zebra/zserv.h b/zebra/zserv.h index dcc98d83f7..c8f006d44b 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -105,6 +105,8 @@ struct zserv u_int32_t vrfdel_cnt; u_int32_t if_vrfchg_cnt; u_int32_t bfd_client_reg_cnt; + u_int32_t vniadd_cnt; + u_int32_t vnidel_cnt; time_t connect_time; time_t last_read_time; -- cgit v1.2.3 From 2232a77c2bb35586fbdc8156e9c0781fc7020066 Mon Sep 17 00:00:00 2001 From: vivek Date: Sun, 14 May 2017 22:44:13 -0700 Subject: zebra: MAC and Neighbor (ARP/ND) handling Implement handling of MACs and Neighbors (ARP/ND entries) in zebra: - MAC and Neighbor database handlers - Read MACs and Neighbors from the kernel, when needed and create entries in zebra's MAC and Neighbor databases. - Handle add/update/delete notifications from the kernel for MACs and Neighbors and update zebra's database appropriately - Inform locally learnt MACs and Neighbors to client - Handle MACIP add/delete from client and install appriporiate entries into the kernel - Since Neighbor entries will be installed on an SVI, implement the needed mappings NOTE: kernel interface is only implemented for Linux/netlink Signed-off-by: Vivek Venkatraman Reviewed-by: Donald Sharp --- lib/log.c | 4 + lib/zclient.h | 4 + zebra/interface.c | 33 + zebra/kernel_netlink.c | 28 +- zebra/rt.h | 10 + zebra/rt_netlink.c | 645 +++++++++++++ zebra/rt_netlink.h | 9 + zebra/rt_socket.c | 25 + zebra/rtread_getmsg.c | 20 + zebra/rtread_netlink.c | 21 + zebra/rtread_sysctl.c | 20 + zebra/zebra_vxlan.c | 2247 +++++++++++++++++++++++++++++++++++++++++----- zebra/zebra_vxlan.h | 27 + zebra/zebra_vxlan_null.c | 50 ++ zebra/zserv.c | 9 + zebra/zserv.h | 7 + 16 files changed, 2922 insertions(+), 237 deletions(-) (limited to 'zebra/interface.c') diff --git a/lib/log.c b/lib/log.c index 7a7545201d..0628b163bf 100644 --- a/lib/log.c +++ b/lib/log.c @@ -951,6 +951,10 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY (ZEBRA_VNI_DEL), DESC_ENTRY (ZEBRA_REMOTE_VTEP_ADD), DESC_ENTRY (ZEBRA_REMOTE_VTEP_DEL), + DESC_ENTRY (ZEBRA_MACIP_ADD), + DESC_ENTRY (ZEBRA_MACIP_DEL), + DESC_ENTRY (ZEBRA_REMOTE_MACIP_ADD), + DESC_ENTRY (ZEBRA_REMOTE_MACIP_DEL), }; #undef DESC_ENTRY diff --git a/lib/zclient.h b/lib/zclient.h index 0c9f70751c..2191be2c2f 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -101,6 +101,10 @@ typedef enum { ZEBRA_VNI_DEL, ZEBRA_REMOTE_VTEP_ADD, ZEBRA_REMOTE_VTEP_DEL, + ZEBRA_MACIP_ADD, + ZEBRA_MACIP_DEL, + ZEBRA_REMOTE_MACIP_ADD, + ZEBRA_REMOTE_MACIP_DEL, } zebra_message_types_t; struct redist_proto diff --git a/zebra/interface.c b/zebra/interface.c index 87b0896f9f..f4ed93f7b8 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -847,6 +847,7 @@ void if_up (struct interface *ifp) { struct zebra_if *zif; + struct interface *link_if; zif = ifp->info; zif->up_count++; @@ -882,8 +883,23 @@ if_up (struct interface *ifp) zebra_vrf_static_route_interface_fixup (ifp); + /* Handle interface up for specific types for EVPN. Non-VxLAN interfaces + * are checked to see if (remote) neighbor entries need to be installed + * on them for ARP suppression. + */ if (IS_ZEBRA_IF_VXLAN (ifp)) zebra_vxlan_if_up (ifp); + else if (IS_ZEBRA_IF_BRIDGE (ifp)) + { + link_if = ifp; + zebra_vxlan_svi_up (ifp, link_if); + } + else if (IS_ZEBRA_IF_VLAN (ifp)) + { + link_if = zif->link; + if (link_if) + zebra_vxlan_svi_up (ifp, link_if); + } } /* Interface goes down. We have to manage different behavior of based @@ -892,13 +908,30 @@ void if_down (struct interface *ifp) { struct zebra_if *zif; + struct interface *link_if; zif = ifp->info; zif->down_count++; quagga_timestamp (2, zif->down_last, sizeof (zif->down_last)); + /* Handle interface down for specific types for EVPN. Non-VxLAN interfaces + * are checked to see if (remote) neighbor entries need to be purged + * for ARP suppression. + */ if (IS_ZEBRA_IF_VXLAN (ifp)) zebra_vxlan_if_down (ifp); + else if (IS_ZEBRA_IF_BRIDGE (ifp)) + { + link_if = ifp; + zebra_vxlan_svi_down (ifp, link_if); + } + else if (IS_ZEBRA_IF_VLAN (ifp)) + { + link_if = zif->link; + if (link_if) + zebra_vxlan_svi_down (ifp, link_if); + } + /* Notify to the protocol daemons. */ zebra_interface_down_update (ifp); diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index 3570676a4a..91d4946b5f 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -269,6 +269,12 @@ netlink_information_fetch (struct sockaddr_nl *snl, struct nlmsghdr *h, case RTM_DELADDR: return netlink_interface_addr (snl, h, ns_id, startup); break; + case RTM_NEWNEIGH: + return netlink_neigh_change (snl, h, ns_id); + break; + case RTM_DELNEIGH: + return netlink_neigh_change (snl, h, ns_id); + break; default: zlog_warn ("Unknown netlink nlmsg_type %d vrf %u\n", h->nlmsg_type, ns_id); @@ -297,17 +303,21 @@ static void netlink_install_filter (int sock, __u32 pid) struct sock_filter filter[] = { /* 0: ldh [4] */ BPF_STMT(BPF_LD|BPF_ABS|BPF_H, offsetof(struct nlmsghdr, nlmsg_type)), - /* 1: jeq 0x18 jt 3 jf 6 */ - BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_NEWROUTE), 1, 0), - /* 2: jeq 0x19 jt 3 jf 6 */ - BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_DELROUTE), 0, 3), - /* 3: ldw [12] */ + /* 1: jeq 0x18 jt 5 jf next */ + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_NEWROUTE), 3, 0), + /* 2: jeq 0x19 jt 5 jf next */ + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_DELROUTE), 2, 0), + /* 3: jeq 0x19 jt 5 jf next */ + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_NEWNEIGH), 1, 0), + /* 4: jeq 0x19 jt 5 jf 8 */ + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_DELNEIGH), 0, 3), + /* 5: ldw [12] */ BPF_STMT(BPF_LD|BPF_ABS|BPF_W, offsetof(struct nlmsghdr, nlmsg_pid)), - /* 4: jeq XX jt 5 jf 6 */ + /* 6: jeq XX jt 7 jf 8 */ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htonl(pid), 0, 1), - /* 5: ret 0 (skip) */ + /* 7: ret 0 (skip) */ BPF_STMT(BPF_RET|BPF_K, 0), - /* 6: ret 0xffff (keep) */ + /* 8: ret 0xffff (keep) */ BPF_STMT(BPF_RET|BPF_K, 0xffff), }; @@ -786,7 +796,7 @@ kernel_init (struct zebra_ns *zns) /* Initialize netlink sockets */ groups = RTMGRP_LINK | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFADDR | - RTMGRP_IPV4_MROUTE; + RTMGRP_IPV4_MROUTE | RTMGRP_NEIGH; snprintf (zns->netlink.name, sizeof (zns->netlink.name), "netlink-listen (NS %u)", zns->ns_id); diff --git a/zebra/rt.h b/zebra/rt.h index 10ca6c95cf..ee85eceeca 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -24,6 +24,7 @@ #include "prefix.h" #include "if.h" +#include "vlan.h" #include "vxlan.h" #include "zebra/rib.h" #include "zebra/zebra_ns.h" @@ -46,4 +47,13 @@ extern int kernel_add_vtep (vni_t vni, struct interface *ifp, struct in_addr *vtep_ip); extern int kernel_del_vtep (vni_t vni, struct interface *ifp, struct in_addr *vtep_ip); +extern int kernel_add_mac (struct interface *ifp, vlanid_t vid, + struct ethaddr *mac, struct in_addr vtep_ip); +extern int kernel_del_mac (struct interface *ifp, vlanid_t vid, + struct ethaddr *mac, struct in_addr vtep_ip, int local); + +extern int kernel_add_neigh (struct interface *ifp, struct ipaddr *ip, + struct ethaddr *mac); +extern int kernel_del_neigh (struct interface *ifp, struct ipaddr *ip); + #endif /* _ZEBRA_RT_H */ diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 7ff03e7c04..4be1e96ce5 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -56,6 +56,7 @@ #include "zebra/kernel_netlink.h" #include "zebra/rt_netlink.h" #include "zebra/zebra_mroute.h" +#include "zebra/zebra_vxlan.h" /* TODO - Temporary definitions, need to refine. */ @@ -94,8 +95,22 @@ #ifndef NDA_MASTER #define NDA_MASTER 9 #endif + +#ifndef NTF_SELF +#define NTF_SELF 0x02 +#endif + +#ifndef NTF_EXT_LEARNED +#define NTF_EXT_LEARNED 0x10 +#endif + +#ifndef NDA_VLAN +#define NDA_VLAN 5 +#endif /* End of temporary definitions */ +static vlanid_t filter_vlan = 0; + struct gw_family_t { u_int16_t filler; @@ -1622,6 +1637,636 @@ kernel_del_vtep (vni_t vni, struct interface *ifp, struct in_addr *vtep_ip) return netlink_vxlan_flood_list_update (ifp, vtep_ip, RTM_DELNEIGH); } +#ifndef NDA_RTA +#define NDA_RTA(r) \ + ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg)))) +#endif + +static int +netlink_macfdb_change (struct sockaddr_nl *snl, struct nlmsghdr *h, int len) +{ + struct ndmsg *ndm; + struct interface *ifp; + struct zebra_if *zif; + struct zebra_vrf *zvrf; + struct rtattr *tb[NDA_MAX + 1]; + struct interface *br_if; + struct ethaddr mac; + vlanid_t vid = 0; + struct prefix vtep_ip; + int vid_present = 0, dst_present = 0; + char buf[ETHER_ADDR_STRLEN]; + char vid_buf[20]; + char dst_buf[30]; + + ndm = NLMSG_DATA (h); + + /* The interface should exist. */ + ifp = if_lookup_by_index_per_ns (zebra_ns_lookup (NS_DEFAULT), ndm->ndm_ifindex); + if (!ifp) + return 0; + + /* Locate VRF corresponding to interface. We only process MAC notifications + * if EVPN is enabled on this VRF. + */ + zvrf = vrf_info_lookup(ifp->vrf_id); + if (!zvrf || !EVPN_ENABLED(zvrf)) + return 0; + if (!ifp->info) + return 0; + + /* The interface should be something we're interested in. */ + if (!IS_ZEBRA_IF_BRIDGE_SLAVE(ifp)) + return 0; + + /* Drop "permanent" entries. */ + if (ndm->ndm_state & NUD_PERMANENT) + return 0; + + zif = (struct zebra_if *)ifp->info; + if ((br_if = zif->brslave_info.br_if) == NULL) + { + zlog_warn ("%s family %s IF %s(%u) brIF %u - no bridge master", + nl_msg_type_to_str (h->nlmsg_type), + nl_family_to_str (ndm->ndm_family), + ifp->name, ndm->ndm_ifindex, + zif->brslave_info.bridge_ifindex); + return 0; + } + + /* Parse attributes and extract fields of interest. */ + memset (tb, 0, sizeof tb); + netlink_parse_rtattr (tb, NDA_MAX, NDA_RTA (ndm), len); + + if (!tb[NDA_LLADDR]) + { + zlog_warn ("%s family %s IF %s(%u) brIF %u - no LLADDR", + nl_msg_type_to_str (h->nlmsg_type), + nl_family_to_str (ndm->ndm_family), + ifp->name, ndm->ndm_ifindex, + zif->brslave_info.bridge_ifindex); + return 0; + } + + if (RTA_PAYLOAD (tb[NDA_LLADDR]) != ETHER_ADDR_LEN) + { + zlog_warn ("%s family %s IF %s(%u) brIF %u - LLADDR is not MAC, len %ld", + nl_msg_type_to_str (h->nlmsg_type), + nl_family_to_str (ndm->ndm_family), + ifp->name, ndm->ndm_ifindex, + zif->brslave_info.bridge_ifindex, + RTA_PAYLOAD (tb[NDA_LLADDR])); + return 0; + } + + memcpy (&mac, RTA_DATA (tb[NDA_LLADDR]), ETHER_ADDR_LEN); + + if ((NDA_VLAN <= NDA_MAX) && tb[NDA_VLAN]) + { + vid_present = 1; + vid = *(u_int16_t *) RTA_DATA(tb[NDA_VLAN]); + sprintf (vid_buf, " VLAN %u", vid); + } + + if (tb[NDA_DST]) + { + /* TODO: Only IPv4 supported now. */ + dst_present = 1; + vtep_ip.family = AF_INET; + vtep_ip.prefixlen = IPV4_MAX_BITLEN; + memcpy (&(vtep_ip.u.prefix4.s_addr), RTA_DATA (tb[NDA_DST]), IPV4_MAX_BYTELEN); + sprintf (dst_buf, " dst %s", inet_ntoa (vtep_ip.u.prefix4)); + } + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug ("Rx %s family %s IF %s(%u)%s MAC %s%s", + nl_msg_type_to_str (h->nlmsg_type), + nl_family_to_str (ndm->ndm_family), + ifp->name, ndm->ndm_ifindex, + vid_present ? vid_buf : "", + prefix_mac2str (&mac, buf, sizeof (buf)), + dst_present ? dst_buf: ""); + + if (filter_vlan && vid != filter_vlan) + return 0; + + /* If add or update, do accordingly if learnt on a "local" interface; if + * the notification is over VxLAN, this has to be related to multi-homing, + * so perform an implicit delete of any local entry (if it exists). + */ + if (h->nlmsg_type == RTM_NEWNEIGH) + { + /* Drop "permanent" entries. */ + if (ndm->ndm_state & NUD_PERMANENT) + return 0; + + if (IS_ZEBRA_IF_VXLAN(ifp)) + return zebra_vxlan_check_del_local_mac (ifp, br_if, &mac, vid); + + return zebra_vxlan_local_mac_add_update (ifp, br_if, &mac, vid); + } + + /* This is a delete notification. + * 1. For a MAC over VxLan, check if it needs to be refreshed(readded) + * 2. For a MAC over "local" interface, delete the mac + * Note: We will get notifications from both bridge driver and VxLAN driver. + * Ignore the notification from VxLan driver as it is also generated + * when mac moves from remote to local. + */ + if (dst_present) + return 0; + + if (IS_ZEBRA_IF_VXLAN(ifp)) + return zebra_vxlan_check_readd_remote_mac (ifp, br_if, &mac, vid); + + return zebra_vxlan_local_mac_del (ifp, br_if, &mac, vid); +} + +static int +netlink_macfdb_table (struct sockaddr_nl *snl, struct nlmsghdr *h, + ns_id_t ns_id, int startup) +{ + int len; + struct ndmsg *ndm; + + if (h->nlmsg_type != RTM_NEWNEIGH) + return 0; + + /* Length validity. */ + len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct ndmsg)); + if (len < 0) + return -1; + + /* We are interested only in AF_BRIDGE notifications. */ + ndm = NLMSG_DATA (h); + if (ndm->ndm_family != AF_BRIDGE) + return 0; + + return netlink_macfdb_change (snl, h, len); +} + +/* Request for MAC FDB information from the kernel */ +static int +netlink_request_macs (struct zebra_ns *zns, int family, int type, + ifindex_t master_ifindex) +{ + struct + { + struct nlmsghdr n; + struct ifinfomsg ifm; + char buf[256]; + } req; + + /* Form the request, specifying filter (rtattr) if needed. */ + memset (&req, 0, sizeof (req)); + req.n.nlmsg_type = type; + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.ifm.ifi_family = family; + if (master_ifindex) + addattr32 (&req.n, sizeof(req), IFLA_MASTER, master_ifindex); + + return netlink_request (&zns->netlink_cmd, &req.n); +} + +/* + * MAC forwarding database read using netlink interface. This is invoked + * at startup. + */ +int +netlink_macfdb_read (struct zebra_ns *zns) +{ + int ret; + + /* Get bridge FDB table. */ + ret = netlink_request_macs (zns, AF_BRIDGE, RTM_GETNEIGH, 0); + if (ret < 0) + return ret; + /* We are reading entire table. */ + filter_vlan = 0; + ret = netlink_parse_info (netlink_macfdb_table, &zns->netlink_cmd, zns, 0, 1); + + return ret; +} + +/* + * MAC forwarding database read using netlink interface. This is for a + * specific bridge and matching specific access VLAN (if VLAN-aware bridge). + */ +int +netlink_macfdb_read_for_bridge (struct zebra_ns *zns, struct interface *ifp, + struct interface *br_if) +{ + struct zebra_if *br_zif; + struct zebra_if *zif; + struct zebra_l2info_vxlan *vxl; + int ret = 0; + + + /* Save VLAN we're filtering on, if needed. */ + br_zif = (struct zebra_if *) br_if->info; + zif = (struct zebra_if *) ifp->info; + vxl = &zif->l2info.vxl; + if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif)) + filter_vlan = vxl->access_vlan; + + /* Get bridge FDB table for specific bridge - we do the VLAN filtering. */ + ret = netlink_request_macs (zns, AF_BRIDGE, RTM_GETNEIGH, br_if->ifindex); + if (ret < 0) + return ret; + ret = netlink_parse_info (netlink_macfdb_table, &zns->netlink_cmd, zns, 0, 0); + + /* Reset VLAN filter. */ + filter_vlan = 0; + return ret; +} + +static int +netlink_macfdb_update (struct interface *ifp, vlanid_t vid, + struct ethaddr *mac, + struct in_addr vtep_ip, + int local, int cmd) +{ + struct zebra_ns *zns = zebra_ns_lookup (NS_DEFAULT); + struct + { + struct nlmsghdr n; + struct ndmsg ndm; + char buf[256]; + } req; + int dst_alen; + struct zebra_if *zif; + struct interface *br_if; + struct zebra_if *br_zif; + char buf[ETHER_ADDR_STRLEN]; + int vid_present = 0, dst_present = 0; + char vid_buf[20]; + char dst_buf[30]; + + zif = ifp->info; + if ((br_if = zif->brslave_info.br_if) == NULL) + { + zlog_warn ("MAC %s on IF %s(%u) - no mapping to bridge", + (cmd == RTM_NEWNEIGH) ? "add" : "del", + ifp->name, ifp->ifindex); + return -1; + } + + memset(&req.n, 0, sizeof(req.n)); + memset(&req.ndm, 0, sizeof(req.ndm)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + if (cmd == RTM_NEWNEIGH) + req.n.nlmsg_flags |= (NLM_F_CREATE | NLM_F_REPLACE); + req.n.nlmsg_type = cmd; + req.ndm.ndm_family = AF_BRIDGE; + req.ndm.ndm_flags |= NTF_SELF | NTF_MASTER; + req.ndm.ndm_state = NUD_REACHABLE; + + req.ndm.ndm_flags |= NTF_EXT_LEARNED; + + addattr_l (&req.n, sizeof (req), NDA_LLADDR, mac, 6); + req.ndm.ndm_ifindex = ifp->ifindex; + if (!local) + { + dst_alen = 4; // TODO: hardcoded + addattr_l (&req.n, sizeof (req), NDA_DST, &vtep_ip, dst_alen); + dst_present = 1; + sprintf (dst_buf, " dst %s", inet_ntoa (vtep_ip)); + } + br_zif = (struct zebra_if *) br_if->info; + if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif) && vid > 0) + { + addattr16 (&req.n, sizeof (req), NDA_VLAN, vid); + vid_present = 1; + sprintf (vid_buf, " VLAN %u", vid); + } + addattr32 (&req.n, sizeof (req), NDA_MASTER, br_if->ifindex); + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug ("Tx %s family %s IF %s(%u)%s MAC %s%s", + nl_msg_type_to_str (cmd), + nl_family_to_str (req.ndm.ndm_family), + ifp->name, ifp->ifindex, + vid_present ? vid_buf : "", + prefix_mac2str (mac, buf, sizeof (buf)), + dst_present ? dst_buf : ""); + + return netlink_talk (netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, 0); +} + +#define NUD_VALID (NUD_PERMANENT | NUD_NOARP | NUD_REACHABLE | \ + NUD_PROBE | NUD_STALE | NUD_DELAY) + +static int +netlink_ipneigh_change (struct sockaddr_nl *snl, struct nlmsghdr *h, int len) +{ + struct ndmsg *ndm; + struct interface *ifp; + struct zebra_if *zif; + struct zebra_vrf *zvrf; + struct rtattr *tb[NDA_MAX + 1]; + struct interface *link_if; + struct ethaddr mac; + struct ipaddr ip; + char buf[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + int mac_present = 0; + u_char ext_learned; + + ndm = NLMSG_DATA (h); + + /* The interface should exist. */ + ifp = if_lookup_by_index_per_ns (zebra_ns_lookup (NS_DEFAULT), ndm->ndm_ifindex); + if (!ifp) + return 0; + + /* Locate VRF corresponding to interface. We only process neigh notifications + * if EVPN is enabled on this VRF. + */ + zvrf = vrf_info_lookup(ifp->vrf_id); + if (!zvrf || !EVPN_ENABLED(zvrf)) + return 0; + if (!ifp->info) + return 0; + + /* Drop "permanent" entries. */ + if (ndm->ndm_state & NUD_PERMANENT) + return 0; + + zif = (struct zebra_if *)ifp->info; + /* The neighbor is present on an SVI. From this, we locate the underlying + * bridge because we're only interested in neighbors on a VxLAN bridge. + * The bridge is located based on the nature of the SVI: + * (a) In the case of a VLAN-aware bridge, the SVI is a L3 VLAN interface + * and is linked to the bridge + * (b) In the case of a VLAN-unaware bridge, the SVI is the bridge inteface + * itself + */ + if (IS_ZEBRA_IF_VLAN(ifp)) + { + link_if = zif->link; + if (!link_if) + return 0; + } + else if (IS_ZEBRA_IF_BRIDGE(ifp)) + link_if = ifp; + else + return 0; + + /* Parse attributes and extract fields of interest. */ + memset (tb, 0, sizeof tb); + netlink_parse_rtattr (tb, NDA_MAX, NDA_RTA (ndm), len); + + if (!tb[NDA_DST]) + { + zlog_warn ("%s family %s IF %s(%u) - no DST", + nl_msg_type_to_str (h->nlmsg_type), + nl_family_to_str (ndm->ndm_family), + ifp->name, ndm->ndm_ifindex); + return 0; + } + memset (&mac, 0, sizeof (struct ethaddr)); + memset (&ip, 0, sizeof (struct ipaddr)); + ip.ipa_type = (ndm->ndm_family == AF_INET) ? IPADDR_V4 : IPADDR_V6; + memcpy (&ip.ip.addr, RTA_DATA(tb[NDA_DST]), RTA_PAYLOAD(tb[NDA_DST])); + + if (h->nlmsg_type == RTM_NEWNEIGH) + { + if (tb[NDA_LLADDR]) + { + if (RTA_PAYLOAD (tb[NDA_LLADDR]) != ETHER_ADDR_LEN) + { + zlog_warn ("%s family %s IF %s(%u) - LLADDR is not MAC, len %ld", + nl_msg_type_to_str (h->nlmsg_type), + nl_family_to_str (ndm->ndm_family), + ifp->name, ndm->ndm_ifindex, + RTA_PAYLOAD (tb[NDA_LLADDR])); + return 0; + } + + mac_present = 1; + memcpy (&mac, RTA_DATA (tb[NDA_LLADDR]), ETHER_ADDR_LEN); + } + + ext_learned = (ndm->ndm_flags & NTF_EXT_LEARNED) ? 1 : 0; + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug ("Rx %s family %s IF %s(%u) IP %s MAC %s state 0x%x flags 0x%x", + nl_msg_type_to_str (h->nlmsg_type), + nl_family_to_str (ndm->ndm_family), + ifp->name, ndm->ndm_ifindex, + ipaddr2str (&ip, buf2, sizeof(buf2)), + mac_present ? prefix_mac2str (&mac, buf, sizeof (buf)) : "", + ndm->ndm_state, ndm->ndm_flags); + + /* If the neighbor state is valid for use, process as an add or update + * else process as a delete. Note that the delete handling may result + * in re-adding the neighbor if it is a valid "remote" neighbor. + */ + if (ndm->ndm_state & NUD_VALID) + return zebra_vxlan_local_neigh_add_update (ifp, link_if, + &ip, &mac, + ndm->ndm_state, ext_learned); + + return zebra_vxlan_local_neigh_del (ifp, link_if, &ip); + } + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug ("Rx %s family %s IF %s(%u) IP %s", + nl_msg_type_to_str (h->nlmsg_type), + nl_family_to_str (ndm->ndm_family), + ifp->name, ndm->ndm_ifindex, + ipaddr2str (&ip, buf2, sizeof(buf2))); + + /* Process the delete - it may result in re-adding the neighbor if it is + * a valid "remote" neighbor. + */ + return zebra_vxlan_local_neigh_del (ifp, link_if, &ip); +} + +static int +netlink_neigh_table (struct sockaddr_nl *snl, struct nlmsghdr *h, + ns_id_t ns_id, int startup) +{ + int len; + struct ndmsg *ndm; + + if (h->nlmsg_type != RTM_NEWNEIGH) + return 0; + + /* Length validity. */ + len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct ndmsg)); + if (len < 0) + return -1; + + /* We are interested only in AF_INET or AF_INET6 notifications. */ + ndm = NLMSG_DATA (h); + if (ndm->ndm_family != AF_INET && ndm->ndm_family != AF_INET6) + return 0; + + return netlink_neigh_change (snl, h, len); +} + +/* Request for IP neighbor information from the kernel */ +static int +netlink_request_neigh (struct zebra_ns *zns, int family, int type, + ifindex_t ifindex) +{ + struct + { + struct nlmsghdr n; + struct ndmsg ndm; + char buf[256]; + } req; + + /* Form the request, specifying filter (rtattr) if needed. */ + memset (&req, 0, sizeof (req)); + req.n.nlmsg_type = type; + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); + req.ndm.ndm_family = family; + if (ifindex) + addattr32 (&req.n, sizeof(req), NDA_IFINDEX, ifindex); + + return netlink_request (&zns->netlink_cmd, &req.n); +} + +/* + * IP Neighbor table read using netlink interface. This is invoked + * at startup. + */ +int +netlink_neigh_read (struct zebra_ns *zns) +{ + int ret; + + /* Get IP neighbor table. */ + ret = netlink_request_neigh (zns, AF_UNSPEC, RTM_GETNEIGH, 0); + if (ret < 0) + return ret; + ret = netlink_parse_info (netlink_neigh_table, &zns->netlink_cmd, zns, 0, 1); + + return ret; +} + +/* + * IP Neighbor table read using netlink interface. This is for a specific + * VLAN device. + */ +int +netlink_neigh_read_for_vlan (struct zebra_ns *zns, struct interface *vlan_if) +{ + int ret = 0; + + ret = netlink_request_neigh (zns, AF_UNSPEC, RTM_GETNEIGH, vlan_if->ifindex); + if (ret < 0) + return ret; + ret = netlink_parse_info (netlink_neigh_table, &zns->netlink_cmd, zns, 0, 0); + + return ret; +} + +int +netlink_neigh_change (struct sockaddr_nl *snl, struct nlmsghdr *h, + ns_id_t ns_id) +{ + int len; + struct ndmsg *ndm; + + if (!(h->nlmsg_type == RTM_NEWNEIGH || h->nlmsg_type == RTM_DELNEIGH)) + return 0; + + /* Length validity. */ + len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct ndmsg)); + if (len < 0) + return -1; + + /* Is this a notification for the MAC FDB or IP neighbor table? */ + ndm = NLMSG_DATA (h); + if (ndm->ndm_family == AF_BRIDGE) + return netlink_macfdb_change (snl, h, len); + + if (ndm->ndm_type != RTN_UNICAST) + return 0; + + if (ndm->ndm_family == AF_INET || ndm->ndm_family == AF_INET6) + return netlink_ipneigh_change (snl, h, len); + + return 0; +} + +static int +netlink_neigh_update2 (struct interface *ifp, struct ipaddr *ip, + struct ethaddr *mac, u_int32_t flags, int cmd) +{ + struct { + struct nlmsghdr n; + struct ndmsg ndm; + char buf[256]; + } req; + int ipa_len; + + struct zebra_ns *zns = zebra_ns_lookup (NS_DEFAULT); + char buf[INET6_ADDRSTRLEN]; + char buf2[ETHER_ADDR_STRLEN]; + + memset(&req.n, 0, sizeof(req.n)); + memset(&req.ndm, 0, sizeof(req.ndm)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + if (cmd == RTM_NEWNEIGH) + req.n.nlmsg_flags |= (NLM_F_CREATE | NLM_F_REPLACE); + req.n.nlmsg_type = cmd; //RTM_NEWNEIGH or RTM_DELNEIGH + req.ndm.ndm_family = IS_IPADDR_V4(ip) ? AF_INET : AF_INET6; + req.ndm.ndm_state = flags; + req.ndm.ndm_ifindex = ifp->ifindex; + req.ndm.ndm_type = RTN_UNICAST; + req.ndm.ndm_flags = NTF_EXT_LEARNED; + + + ipa_len = IS_IPADDR_V4(ip) ? IPV4_MAX_BYTELEN : IPV6_MAX_BYTELEN; + addattr_l(&req.n, sizeof(req), NDA_DST, &ip->ip.addr, ipa_len); + if (mac) + addattr_l (&req.n, sizeof (req), NDA_LLADDR, mac, 6); + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug ("Tx %s family %s IF %s(%u) Neigh %s MAC %s", + nl_msg_type_to_str (cmd), + nl_family_to_str (req.ndm.ndm_family), + ifp->name, ifp->ifindex, + ipaddr2str (ip, buf, sizeof(buf)), + mac ? prefix_mac2str (mac, buf2, sizeof (buf2)) : "null"); + + return netlink_talk (netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, 0); +} + +int +kernel_add_mac (struct interface *ifp, vlanid_t vid, + struct ethaddr *mac, struct in_addr vtep_ip) +{ + return netlink_macfdb_update (ifp, vid, mac, vtep_ip, 0, RTM_NEWNEIGH); +} + +int +kernel_del_mac (struct interface *ifp, vlanid_t vid, + struct ethaddr *mac, struct in_addr vtep_ip, int local) +{ + return netlink_macfdb_update (ifp, vid, mac, vtep_ip, local, RTM_DELNEIGH); +} + +int kernel_add_neigh (struct interface *ifp, struct ipaddr *ip, + struct ethaddr *mac) +{ + return netlink_neigh_update2 (ifp, ip, mac, NUD_REACHABLE, + RTM_NEWNEIGH); +} + +int kernel_del_neigh (struct interface *ifp, struct ipaddr *ip) +{ + return netlink_neigh_update2 (ifp, ip, NULL, 0, RTM_DELNEIGH); +} + /* * MPLS label forwarding table change via netlink interface. */ diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h index 9ba86003b8..8b061fc2ed 100644 --- a/zebra/rt_netlink.h +++ b/zebra/rt_netlink.h @@ -51,6 +51,15 @@ extern int netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h, ns_id_t ns_id, int startup); extern int netlink_route_read (struct zebra_ns *zns); +extern int netlink_neigh_change (struct sockaddr_nl *snl, struct nlmsghdr *h, + ns_id_t ns_id); +extern int netlink_macfdb_read (struct zebra_ns *zns); +extern int netlink_macfdb_read_for_bridge (struct zebra_ns *zns, + struct interface *ifp, struct interface *br_if); +extern int netlink_neigh_read (struct zebra_ns *zns); +extern int netlink_neigh_read_for_vlan (struct zebra_ns *zns, + struct interface *vlan_if); + #endif /* HAVE_NETLINK */ #endif /* _ZEBRA_RT_NETLINK_H */ diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index bf7e3403e4..c03ed27c63 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -441,3 +441,28 @@ kernel_del_vtep (vni_t vni, struct interface *ifp, struct in_addr *vtep_ip) { return 0; } + +int +kernel_add_mac (struct interface *ifp, vlanid_t vid, + struct ethaddr *mac, struct in_addr vtep_ip) +{ + return 0; +} + +int +kernel_del_mac (struct interface *ifp, vlanid_t vid, + struct ethaddr *mac, struct in_addr vtep_ip, int local) +{ + return 0; +} + +int kernel_add_neigh (struct interface *ifp, struct ipaddr *ip, + struct ethaddr *mac) +{ + return 0; +} + +int kernel_del_neigh (struct interface *ifp, struct ipaddr *ip) +{ + return 0; +} diff --git a/zebra/rtread_getmsg.c b/zebra/rtread_getmsg.c index 1fb2984ddf..b1bef571be 100644 --- a/zebra/rtread_getmsg.c +++ b/zebra/rtread_getmsg.c @@ -237,3 +237,23 @@ route_read (struct zebra_ns *zns) exit: close (dev); } + +/* Only implemented for netlink method */ +void +macfdb_read (struct zebra_ns *zns) +{ +} + +void macfdb_read_for_bridge (struct zebra_ns *zns, struct interface *ifp, + struct interface *br_if) +{ +} + +void +neigh_read (struct zebra_ns *zns) +{ +} + +void neigh_read_for_vlan (struct zebra_ns *zns, struct interface *vlan_if) +{ +} diff --git a/zebra/rtread_netlink.c b/zebra/rtread_netlink.c index d59883445d..d26aa59f87 100644 --- a/zebra/rtread_netlink.c +++ b/zebra/rtread_netlink.c @@ -29,3 +29,24 @@ void route_read (struct zebra_ns *zns) { netlink_route_read (zns); } + +void macfdb_read (struct zebra_ns *zns) +{ + netlink_macfdb_read (zns); +} + +void macfdb_read_for_bridge (struct zebra_ns *zns, struct interface *ifp, + struct interface *br_if) +{ + netlink_macfdb_read_for_bridge (zns, ifp, br_if); +} + +void neigh_read (struct zebra_ns *zns) +{ + netlink_neigh_read (zns); +} + +void neigh_read_for_vlan (struct zebra_ns *zns, struct interface *vlan_if) +{ + netlink_neigh_read_for_vlan (zns, vlan_if); +} diff --git a/zebra/rtread_sysctl.c b/zebra/rtread_sysctl.c index cabb1f7714..30e593d87e 100644 --- a/zebra/rtread_sysctl.c +++ b/zebra/rtread_sysctl.c @@ -83,3 +83,23 @@ route_read (struct zebra_ns *zns) return; } + +/* Only implemented for the netlink method. */ +void +macfdb_read (struct zebra_ns *zns) +{ +} + +void macfdb_read_for_bridge (struct zebra_ns *zns, struct interface *ifp, + struct interface *br_if) +{ +} + +void +neigh_read (struct zebra_ns *zns) +{ +} + +void neigh_read_for_vlan (struct zebra_ns *zns, struct interface *vlan_if) +{ +} diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index fcf4328478..808d7eb07e 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -49,11 +49,89 @@ DEFINE_MTYPE_STATIC(ZEBRA, ZVNI, "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"); /* definitions */ /* static function declarations */ +static int +zvni_macip_send_msg_to_client (struct zebra_vrf *zvrf, vni_t vni, + struct ethaddr *macaddr, + struct ipaddr *ip, + u_int16_t cmd); +static unsigned int +neigh_hash_keymake (void *p); +static int +neigh_cmp (const void *p1, const void *p2); +static void * +zvni_neigh_alloc (void *p); +static zebra_neigh_t * +zvni_neigh_add (zebra_vni_t *zvni, struct ipaddr *ip); +static int +zvni_neigh_del (zebra_vni_t *zvni, zebra_neigh_t *n); +static int +zvni_neigh_del_hash_entry (struct hash_backet *backet, void *arg); +static void +zvni_neigh_del_from_vtep (zebra_vni_t *zvni, int uninstall, + struct in_addr *r_vtep_ip); +static void +zvni_neigh_del_all (struct zebra_vrf *zvrf, zebra_vni_t *zvni, + int uninstall, int upd_client, u_int32_t flags); +static zebra_neigh_t * +zvni_neigh_lookup (zebra_vni_t *zvni, struct ipaddr *ip); +static int +zvni_neigh_send_add_to_client (struct zebra_vrf *zvrf, vni_t vni, + struct ipaddr *ip, struct ethaddr *macaddr); +static int +zvni_neigh_send_del_to_client (struct zebra_vrf *zvrf, vni_t vni, + struct ipaddr *ip, struct ethaddr *macaddr); +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, struct interface *br_if); +static struct interface * +zvni_map_to_svi (struct zebra_vrf *zvrf, vlanid_t vid, + struct interface *br_if); + +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); +static zebra_mac_t * +zvni_mac_add (zebra_vni_t *zvni, struct ethaddr *macaddr); +static int +zvni_mac_del (zebra_vni_t *zvni, zebra_mac_t *mac); +static int +zvni_mac_del_hash_entry (struct hash_backet *backet, void *arg); +static void +zvni_mac_del_from_vtep (zebra_vni_t *zvni, int uninstall, + struct in_addr *r_vtep_ip); +static void +zvni_mac_del_all (struct zebra_vrf *zvrf, zebra_vni_t *zvni, + int uninstall, int upd_client, u_int32_t flags); +static zebra_mac_t * +zvni_mac_lookup (zebra_vni_t *zvni, struct ethaddr *macaddr); +static int +zvni_mac_send_add_to_client (struct zebra_vrf *zvrf, vni_t vni, + struct ethaddr *macaddr); +static int +zvni_mac_send_del_to_client (struct zebra_vrf *zvrf, vni_t vni, + struct ethaddr *macaddr); +static zebra_vni_t * +zvni_map_vlan (struct interface *ifp, struct interface *br_if, vlanid_t vid); +static int +zvni_mac_install (zebra_vni_t *zvni, zebra_mac_t *mac); +static int +zvni_mac_uninstall (zebra_vni_t *zvni, zebra_mac_t *mac, int local); +static void +zvni_install_mac_hash (struct hash_backet *backet, void *ctxt); + static unsigned int vni_hash_keymake (void *p); static int @@ -91,358 +169,1972 @@ zvni_vtep_uninstall (zebra_vni_t *zvni, struct in_addr *vtep_ip); /* Private functions */ /* - * Hash function for VNI. + * Inform BGP about local MACIP. + */ +static int +zvni_macip_send_msg_to_client (struct zebra_vrf *zvrf, vni_t vni, + struct ethaddr *macaddr, + struct ipaddr *ip, + u_int16_t cmd) +{ + struct zserv *client; + struct stream *s; + int ipa_len; + char buf[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + + client = zebra_find_client (ZEBRA_ROUTE_BGP); + /* BGP may not be running. */ + if (!client) + return 0; + + s = client->obuf; + stream_reset (s); + + zserv_create_header (s, cmd, zvrf_id (zvrf)); + stream_putl (s, vni); + stream_put (s, macaddr->octet, ETHER_ADDR_LEN); + if (ip) + { + ipa_len = 0; + if (IS_IPADDR_V4(ip)) + ipa_len = IPV4_MAX_BYTELEN; + else if (IS_IPADDR_V6(ip)) + ipa_len = IPV6_MAX_BYTELEN; + + stream_putl (s, ipa_len); /* IP address length */ + if (ipa_len) + stream_put (s, &ip->ip.addr, ipa_len); /* IP address */ + } + else + stream_putl (s, 0); /* Just MAC. */ + + /* Write packet size. */ + stream_putw_at (s, 0, stream_get_endp (s)); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("%u:Send MACIP %s MAC %s IP %s VNI %u to %s", + zvrf_id (zvrf), (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del", + prefix_mac2str (macaddr, buf, sizeof (buf)), + ipaddr2str (ip, buf2, sizeof(buf2)), vni, + zebra_route_string (client->proto)); + + if (cmd == ZEBRA_MACIP_ADD) + client->macipadd_cnt++; + else + client->macipdel_cnt++; + + return zebra_server_send_message(client); +} + +/* + * Make hash key for neighbors. */ static unsigned int -vni_hash_keymake (void *p) +neigh_hash_keymake (void *p) { - const zebra_vni_t *zvni = p; + zebra_neigh_t *n = p; + struct ipaddr *ip = &n->ip; - return (jhash_1word(zvni->vni, 0)); + if (IS_IPADDR_V4(ip)) + return jhash_1word (ip->ipaddr_v4.s_addr, 0); + + return jhash2 (ip->ipaddr_v6.s6_addr32, + ZEBRA_NUM_OF(ip->ipaddr_v6.s6_addr32), 0); } /* - * Compare 2 VNI hash entries. + * Compare two neighbor hash structures. */ static int -vni_hash_cmp (const void *p1, const void *p2) +neigh_cmp (const void *p1, const void *p2) { - const zebra_vni_t *zvni1 = p1; - const zebra_vni_t *zvni2 = p2; + const zebra_neigh_t *n1 = p1; + const zebra_neigh_t *n2 = p2; - return (zvni1->vni == zvni2->vni); + if (n1 == NULL && n2 == NULL) + return 1; + + if (n1 == NULL || n2 == NULL) + return 0; + + return (memcmp(&n1->ip, &n2->ip, sizeof (struct ipaddr)) == 0); } /* - * Callback to allocate VNI hash entry. + * Callback to allocate neighbor hash entry. */ static void * -zvni_alloc (void *p) +zvni_neigh_alloc (void *p) { - const zebra_vni_t *tmp_vni = p; - zebra_vni_t *zvni; + const zebra_neigh_t *tmp_n = p; + zebra_neigh_t *n; - zvni = XCALLOC (MTYPE_ZVNI, sizeof(zebra_vni_t)); - zvni->vni = tmp_vni->vni; - return ((void *)zvni); + n = XCALLOC (MTYPE_NEIGH, sizeof(zebra_neigh_t)); + *n = *tmp_n; + + return ((void *)n); } /* - * Look up VNI hash entry. + * Add neighbor entry. */ -static zebra_vni_t * -zvni_lookup (struct zebra_vrf *zvrf, vni_t vni) +static zebra_neigh_t * +zvni_neigh_add (zebra_vni_t *zvni, struct ipaddr *ip) { - zebra_vni_t tmp_vni; - zebra_vni_t *zvni = NULL; + zebra_neigh_t tmp_n; + zebra_neigh_t *n = NULL; - memset (&tmp_vni, 0, sizeof (zebra_vni_t)); - tmp_vni.vni = vni; - zvni = hash_lookup (zvrf->vni_table, &tmp_vni); + memset (&tmp_n, 0, sizeof (zebra_neigh_t)); + memcpy (&tmp_n.ip, ip, sizeof (struct ipaddr)); + n = hash_get (zvni->neigh_table, &tmp_n, zvni_neigh_alloc); + assert (n); - return zvni; + return n; } /* - * Add VNI hash entry. + * Delete neighbor entry. */ -static zebra_vni_t * -zvni_add (struct zebra_vrf *zvrf, vni_t vni) +static int +zvni_neigh_del (zebra_vni_t *zvni, zebra_neigh_t *n) { - zebra_vni_t tmp_zvni; - zebra_vni_t *zvni = NULL; + zebra_neigh_t *tmp_n; - memset (&tmp_zvni, 0, sizeof (zebra_vni_t)); - tmp_zvni.vni = vni; - zvni = hash_get (zvrf->vni_table, &tmp_zvni, zvni_alloc); - assert (zvni); + /* Free the VNI hash entry and allocated memory. */ + tmp_n = hash_release (zvni->neigh_table, n); + if (tmp_n) + XFREE(MTYPE_NEIGH, tmp_n); - return zvni; + return 0; } /* - * Delete VNI hash entry. + * Free neighbor hash entry (callback) */ static int -zvni_del (struct zebra_vrf *zvrf, zebra_vni_t *zvni) +zvni_neigh_del_hash_entry (struct hash_backet *backet, void *arg) { - zebra_vni_t *tmp_zvni; + struct neigh_walk_ctx *wctx = arg; + zebra_neigh_t *n = backet->data; + + if (((wctx->flags & DEL_LOCAL_NEIGH) && (n->flags & ZEBRA_NEIGH_LOCAL)) || + ((wctx->flags & DEL_REMOTE_NEIGH) && (n->flags & ZEBRA_NEIGH_REMOTE)) || + ((wctx->flags & DEL_REMOTE_NEIGH_FROM_VTEP) && + (n->flags & ZEBRA_NEIGH_REMOTE) && + IPV4_ADDR_SAME(&n->r_vtep_ip, &wctx->r_vtep_ip) + )) + { + if (wctx->upd_client && (n->flags & ZEBRA_NEIGH_LOCAL)) + zvni_neigh_send_del_to_client (wctx->zvrf, wctx->zvni->vni, &n->ip, + &n->emac); - zvni->vxlan_if = NULL; + if (wctx->uninstall) + zvni_neigh_uninstall (wctx->zvni, n); - /* Free the VNI hash entry and allocated memory. */ - tmp_zvni = hash_release (zvrf->vni_table, zvni); - if (tmp_zvni) - XFREE(MTYPE_ZVNI, tmp_zvni); + return zvni_neigh_del (wctx->zvni, n); + } return 0; } /* - * Inform BGP about local VNI addition. + * Delete all neighbor entries from specific VTEP for a particular VNI. */ -static int -zvni_send_add_to_client (struct zebra_vrf *zvrf, - zebra_vni_t *zvni) +static void +zvni_neigh_del_from_vtep (zebra_vni_t *zvni, int uninstall, + struct in_addr *r_vtep_ip) { - struct zserv *client; - struct stream *s; + struct neigh_walk_ctx wctx; - client = zebra_find_client (ZEBRA_ROUTE_BGP); - /* BGP may not be running. */ - if (!client) - return 0; + if (!zvni->neigh_table) + return; - s = client->obuf; - stream_reset (s); + memset (&wctx, 0, sizeof (struct neigh_walk_ctx)); + wctx.zvni = zvni; + wctx.uninstall = uninstall; + wctx.flags = DEL_REMOTE_NEIGH_FROM_VTEP; + wctx.r_vtep_ip = *r_vtep_ip; - zserv_create_header (s, ZEBRA_VNI_ADD, zvrf_id (zvrf)); - stream_putl (s, zvni->vni); - stream_put_in_addr (s, &zvni->local_vtep_ip); + hash_iterate (zvni->neigh_table, + (void (*) (struct hash_backet *, void *)) + zvni_neigh_del_hash_entry, &wctx); +} - /* Write packet size. */ - stream_putw_at (s, 0, stream_get_endp (s)); +/* + * Delete all neighbor entries for this VNI. + */ +static void +zvni_neigh_del_all (struct zebra_vrf *zvrf, zebra_vni_t *zvni, + int uninstall, int upd_client, u_int32_t flags) +{ + struct neigh_walk_ctx wctx; - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug ("%u:Send VNI_ADD %u %s to %s", - zvrf_id (zvrf), zvni->vni, - inet_ntoa(zvni->local_vtep_ip), - zebra_route_string (client->proto)); + if (!zvni->neigh_table) + return; - client->vniadd_cnt++; - return zebra_server_send_message(client); + memset (&wctx, 0, sizeof (struct neigh_walk_ctx)); + wctx.zvni = zvni; + wctx.zvrf = zvrf; + wctx.uninstall = uninstall; + wctx.upd_client = upd_client; + wctx.flags = flags; + + hash_iterate (zvni->neigh_table, + (void (*) (struct hash_backet *, void *)) + zvni_neigh_del_hash_entry, &wctx); } /* - * Inform BGP about local VNI deletion. + * Look up neighbor hash entry. + */ +static zebra_neigh_t * +zvni_neigh_lookup (zebra_vni_t *zvni, 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 (zvni->neigh_table, &tmp); + + return n; +} + +/* + * Inform BGP about local neighbor addition. */ static int -zvni_send_del_to_client (struct zebra_vrf *zvrf, vni_t vni) +zvni_neigh_send_add_to_client (struct zebra_vrf *zvrf, vni_t vni, + struct ipaddr *ip, struct ethaddr *macaddr) { - struct zserv *client; - struct stream *s; + return zvni_macip_send_msg_to_client (zvrf, vni, macaddr, ip, + ZEBRA_MACIP_ADD); +} - client = zebra_find_client (ZEBRA_ROUTE_BGP); - /* BGP may not be running. */ - if (!client) +/* + * Inform BGP about local neighbor deletion. + */ +static int +zvni_neigh_send_del_to_client (struct zebra_vrf *zvrf, vni_t vni, + struct ipaddr *ip, struct ethaddr *macaddr) +{ + return zvni_macip_send_msg_to_client (zvrf, vni, macaddr, ip, + ZEBRA_MACIP_DEL); +} + +/* + * Install remote neighbor into the kernel. + */ +static int +zvni_neigh_install (zebra_vni_t *zvni, zebra_neigh_t *n) +{ + struct zebra_vrf *zvrf; + struct zebra_if *zif; + struct zebra_l2info_vxlan *vxl; + struct interface *vlan_if; + + if (!(n->flags & ZEBRA_NEIGH_REMOTE)) return 0; - s = client->obuf; - stream_reset (s); + zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); + assert(zvrf); + zif = zvni->vxlan_if->info; + if (!zif) + return -1; + vxl = &zif->l2info.vxl; - zserv_create_header (s, ZEBRA_VNI_DEL, zvrf_id (zvrf)); - stream_putl (s, vni); + vlan_if = zvni_map_to_svi (zvrf, vxl->access_vlan, + zif->brslave_info.br_if); + if (!vlan_if) + return -1; - /* Write packet size. */ - stream_putw_at (s, 0, stream_get_endp (s)); + return kernel_add_neigh (vlan_if, &n->ip, &n->emac); +} - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug ("%u:Send VNI_DEL %u to %s", zvrf_id (zvrf), vni, - zebra_route_string (client->proto)); +/* + * Uninstall remote neighbor from the kernel. + */ +static int +zvni_neigh_uninstall (zebra_vni_t *zvni, zebra_neigh_t *n) +{ + struct zebra_vrf *zvrf; + struct zebra_if *zif; + struct zebra_l2info_vxlan *vxl; + struct interface *vlan_if; - client->vnidel_cnt++; - return zebra_server_send_message(client); + if (!(n->flags & ZEBRA_NEIGH_REMOTE)) + return 0; + + zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); + assert(zvrf); + if (!zvni->vxlan_if) + { + zlog_err ("VNI %u hash %p couldn't be uninstalled - no intf", + zvni->vni, zvni); + return -1; + } + + zif = zvni->vxlan_if->info; + if (!zif) + return -1; + vxl = &zif->l2info.vxl; + vlan_if = zvni_map_to_svi (zvrf, vxl->access_vlan, + zif->brslave_info.br_if); + if (!vlan_if) + return -1; + + return kernel_del_neigh (vlan_if, &n->ip); } /* - * Build the VNI hash table by going over the VxLAN interfaces. This - * is called when EVPN (advertise-all-vni) is enabled. + * Install neighbor hash entry - called upon access VLAN change. */ static void -zvni_build_hash_table (struct zebra_vrf *zvrf) +zvni_install_neigh_hash (struct hash_backet *backet, void *ctxt) { - struct listnode *node; - struct interface *ifp; + zebra_neigh_t *n; + struct neigh_walk_ctx *wctx = ctxt; - /* Walk VxLAN interfaces and create VNI hash. */ - for (ALL_LIST_ELEMENTS_RO (vrf_iflist (zvrf_id (zvrf)), node, ifp)) - { - struct zebra_if *zif; - struct zebra_l2info_vxlan *vxl; - zebra_vni_t *zvni; - vni_t vni; + n = (zebra_neigh_t *) backet->data; + if (!n) + return; - zif = ifp->info; - if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) - continue; - vxl = &zif->l2info.vxl; + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) + zvni_neigh_install (wctx->zvni, n); +} - vni = vxl->vni; +/* + * Make hash key for MAC. + */ +static unsigned int +mac_hash_keymake (void *p) +{ + zebra_mac_t *pmac = p; + char *pnt = (char *) pmac->macaddr.octet; + unsigned int key = 0; + int c = 0; + + key += pnt[c]; + key += pnt[c + 1]; + key += pnt[c + 2]; + key += pnt[c + 3]; + key += pnt[c + 4]; + key += pnt[c + 5]; + + return (key); +} - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug ("%u:Create VNI hash for intf %s(%u) VNI %u local IP %s", - zvrf_id (zvrf), ifp->name, ifp->ifindex, vni, - inet_ntoa (vxl->vtep_ip)); +/* + * Compare two MAC addresses. + */ +static int +mac_cmp (const void *p1, const void *p2) +{ + const zebra_mac_t *pmac1 = p1; + const zebra_mac_t *pmac2 = p2; + + if (pmac1 == NULL && pmac2 == NULL) + return 1; + + if (pmac1 == NULL || pmac2 == NULL) + return 0; + + return(memcmp(pmac1->macaddr.octet, pmac2->macaddr.octet, ETHER_ADDR_LEN) == 0); +} + +/* + * Callback to allocate MAC hash entry. + */ +static void * +zvni_mac_alloc (void *p) +{ + const zebra_mac_t *tmp_mac = p; + zebra_mac_t *mac; + + mac = XCALLOC (MTYPE_MAC, sizeof(zebra_mac_t)); + *mac = *tmp_mac; + + return ((void *)mac); +} + +/* + * Add MAC entry. + */ +static zebra_mac_t * +zvni_mac_add (zebra_vni_t *zvni, struct ethaddr *macaddr) +{ + zebra_mac_t tmp_mac; + zebra_mac_t *mac = NULL; + + memset (&tmp_mac, 0, sizeof (zebra_mac_t)); + memcpy(&tmp_mac.macaddr, macaddr, ETHER_ADDR_LEN); + mac = hash_get (zvni->mac_table, &tmp_mac, zvni_mac_alloc); + assert (mac); + + return mac; +} + +/* + * Delete MAC entry. + */ +static int +zvni_mac_del (zebra_vni_t *zvni, zebra_mac_t *mac) +{ + zebra_mac_t *tmp_mac; + + /* Free the VNI hash entry and allocated memory. */ + tmp_mac = hash_release (zvni->mac_table, mac); + if (tmp_mac) + XFREE(MTYPE_MAC, tmp_mac); + + return 0; +} + +/* + * Free MAC hash entry (callback) + */ +static int +zvni_mac_del_hash_entry (struct hash_backet *backet, void *arg) +{ + struct mac_walk_ctx *wctx = arg; + zebra_mac_t *mac = backet->data; + + if (((wctx->flags & DEL_LOCAL_MAC) && (mac->flags & ZEBRA_MAC_LOCAL)) || + ((wctx->flags & DEL_REMOTE_MAC) && (mac->flags & ZEBRA_MAC_REMOTE)) || + ((wctx->flags & DEL_REMOTE_MAC_FROM_VTEP) && + (mac->flags & ZEBRA_MAC_REMOTE) && + IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, &wctx->r_vtep_ip) + )) + { + if (wctx->upd_client && (mac->flags & ZEBRA_MAC_LOCAL)) + { + zvni_mac_send_del_to_client (wctx->zvrf, wctx->zvni->vni, + &mac->macaddr); + } + + if (wctx->uninstall) + zvni_mac_uninstall (wctx->zvni, mac, 0); + + return zvni_mac_del (wctx->zvni, mac); + } + + return 0; +} + +/* + * Delete all MAC entries from specific VTEP for a particular VNI. + */ +static void +zvni_mac_del_from_vtep (zebra_vni_t *zvni, int uninstall, + struct in_addr *r_vtep_ip) +{ + struct mac_walk_ctx wctx; + + if (!zvni->mac_table) + return; + + memset (&wctx, 0, sizeof (struct mac_walk_ctx)); + wctx.zvni = zvni; + wctx.uninstall = uninstall; + wctx.flags = DEL_REMOTE_MAC_FROM_VTEP; + wctx.r_vtep_ip = *r_vtep_ip; + + hash_iterate (zvni->mac_table, + (void (*) (struct hash_backet *, void *)) + zvni_mac_del_hash_entry, &wctx); +} + +/* + * Delete all MAC entries for this VNI. + */ +static void +zvni_mac_del_all (struct zebra_vrf *zvrf, zebra_vni_t *zvni, + int uninstall, int upd_client, u_int32_t flags) +{ + struct mac_walk_ctx wctx; + + if (!zvni->mac_table) + return; + + memset (&wctx, 0, sizeof (struct mac_walk_ctx)); + wctx.zvni = zvni; + wctx.zvrf = zvrf; + wctx.uninstall = uninstall; + wctx.upd_client = upd_client; + wctx.flags = flags; + + hash_iterate (zvni->mac_table, + (void (*) (struct hash_backet *, void *)) + zvni_mac_del_hash_entry, &wctx); +} + +/* + * Look up MAC hash entry. + */ +static zebra_mac_t * +zvni_mac_lookup (zebra_vni_t *zvni, struct ethaddr *mac) +{ + zebra_mac_t tmp; + zebra_mac_t *pmac; + + memset(&tmp, 0, sizeof(tmp)); + memcpy(&tmp.macaddr, mac, ETHER_ADDR_LEN); + pmac = hash_lookup (zvni->mac_table, &tmp); + + return pmac; +} + +/* + * Inform BGP about local MAC addition. + */ +static int +zvni_mac_send_add_to_client (struct zebra_vrf *zvrf, vni_t vni, + struct ethaddr *macaddr) +{ + return zvni_macip_send_msg_to_client (zvrf, vni, macaddr, NULL, + ZEBRA_MACIP_ADD); +} + +/* + * Inform BGP about local MAC deletion. + */ +static int +zvni_mac_send_del_to_client (struct zebra_vrf *zvrf, vni_t vni, + struct ethaddr *macaddr) +{ + return zvni_macip_send_msg_to_client (zvrf, vni, macaddr, NULL, + ZEBRA_MACIP_DEL); +} + +/* + * Map port or (port, VLAN) to a VNI. This is invoked upon getting MAC + * notifications, to see if there are of interest. + * TODO: Need to make this as a hash table. + */ +static zebra_vni_t * +zvni_map_vlan (struct interface *ifp, struct interface *br_if, vlanid_t vid) +{ + struct zebra_vrf *zvrf; + struct listnode *node; + struct interface *tmp_if; + struct zebra_if *zif; + struct zebra_l2info_bridge *br; + struct zebra_l2info_vxlan *vxl; + u_char bridge_vlan_aware; + zebra_vni_t *zvni; + + /* Locate VRF corresponding to interface. */ + zvrf = vrf_info_lookup(ifp->vrf_id); + assert(zvrf); + + /* Determine if bridge is VLAN-aware or not */ + zif = br_if->info; + assert (zif); + br = &zif->l2info.br; + bridge_vlan_aware = br->vlan_aware; + + /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ + /* TODO: Optimize with a hash. */ + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (zvrf_id (zvrf)), node, tmp_if)) + { + 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) + break; + + if (vxl->access_vlan == vid) + break; + } + + if (!tmp_if) + return NULL; + + zvni = zvni_lookup (zvrf, vxl->vni); + return zvni; +} + +/* + * Map SVI and associated bridge to a VNI. This is invoked upon getting + * neighbor notifications, to see if they are of interest. + * TODO: Need to make this as a hash table. + */ +static zebra_vni_t * +zvni_map_svi (struct interface *ifp, struct interface *br_if) +{ + struct zebra_vrf *zvrf; + struct listnode *node; + struct interface *tmp_if; + struct zebra_if *zif; + struct zebra_l2info_bridge *br; + struct zebra_l2info_vxlan *vxl; + u_char bridge_vlan_aware; + vlanid_t vid = 0; + zebra_vni_t *zvni; + + /* Make sure the linked interface is a bridge. */ + if (!IS_ZEBRA_IF_BRIDGE (br_if)) + return NULL; + + /* Locate VRF corresponding to interface. */ + zvrf = vrf_info_lookup(ifp->vrf_id); + assert(zvrf); + + /* 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. */ + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (zvrf_id (zvrf)), node, tmp_if)) + { + 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) + break; + + if (vxl->access_vlan == vid) + break; + } + + if (!tmp_if) + return NULL; + + zvni = zvni_lookup (zvrf, vxl->vni); + return zvni; +} + +/* Map to SVI on bridge corresponding to specified VLAN. This can be one + * of two cases: + * (a) In the case of a VLAN-aware bridge, the SVI is a L3 VLAN interface + * linked to the bridge + * (b) In the case of a VLAN-unaware bridge, the SVI is the bridge inteface + * itself + */ +static struct interface * +zvni_map_to_svi (struct zebra_vrf *zvrf, vlanid_t vid, + struct interface *br_if) +{ + struct listnode *node; + struct interface *tmp_if; + struct zebra_if *zif; + struct zebra_l2info_bridge *br; + struct zebra_l2info_vlan *vl; + u_char bridge_vlan_aware; + + /* Determine if bridge is VLAN-aware or not */ + zif = br_if->info; + assert (zif); + br = &zif->l2info.br; + bridge_vlan_aware = br->vlan_aware; + + /* Check oper status of the SVI. */ + if (!bridge_vlan_aware) + return if_is_operative (br_if) ? br_if : NULL; + + /* Identify corresponding VLAN interface. */ + /* TODO: Optimize with a hash. */ + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (zvrf_id (zvrf)), node, tmp_if)) + { + /* Check oper status of the SVI. */ + if (!if_is_operative (tmp_if)) + continue; + zif = tmp_if->info; + if (!zif || + zif->zif_type != ZEBRA_IF_VLAN || + zif->link != br_if) + continue; + vl = (struct zebra_l2info_vlan *)&zif->l2info.vl; + + if (vl->vid == vid) + break; + } + + return tmp_if; +} + +/* + * Install remote MAC into the kernel. + */ +static int +zvni_mac_install (zebra_vni_t *zvni, zebra_mac_t *mac) +{ + struct zebra_if *zif; + struct zebra_l2info_vxlan *vxl; + + if (!(mac->flags & ZEBRA_MAC_REMOTE)) + return 0; + + zif = zvni->vxlan_if->info; + if (!zif) + return -1; + vxl = &zif->l2info.vxl; + + return kernel_add_mac (zvni->vxlan_if, vxl->access_vlan, + &mac->macaddr, mac->fwd_info.r_vtep_ip); +} + +/* + * Uninstall remote MAC from the kernel. In the scenario where the MAC + * moves to remote, we have to uninstall any existing local entry first. + */ +static int +zvni_mac_uninstall (zebra_vni_t *zvni, zebra_mac_t *mac, int local) +{ + struct zebra_if *zif; + struct zebra_l2info_vxlan *vxl; + struct in_addr vtep_ip = { .s_addr = 0 }; + struct zebra_ns *zns; + struct interface *ifp; + + if (!local && !(mac->flags & ZEBRA_MAC_REMOTE)) + return 0; + + if (!zvni->vxlan_if) + { + zlog_err ("VNI %u hash %p couldn't be uninstalled - no intf", + zvni->vni, zvni); + return -1; + } + + zif = zvni->vxlan_if->info; + if (!zif) + return -1; + vxl = &zif->l2info.vxl; + + if (local) + { + zns = zebra_ns_lookup (NS_DEFAULT); + ifp = if_lookup_by_index_per_ns (zns, mac->fwd_info.local.ifindex); + if (!ifp) // unexpected + return -1; + } + else + { + ifp = zvni->vxlan_if; + vtep_ip = mac->fwd_info.r_vtep_ip; + } + + return kernel_del_mac (ifp, vxl->access_vlan, + &mac->macaddr, vtep_ip, local); +} + +/* + * Install MAC hash entry - called upon access VLAN change. + */ +static void +zvni_install_mac_hash (struct hash_backet *backet, void *ctxt) +{ + zebra_mac_t *mac; + struct mac_walk_ctx *wctx = ctxt; + + mac = (zebra_mac_t *) backet->data; + if (!mac) + return; + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) + zvni_mac_install (wctx->zvni, mac); +} + +/* + * Decrement neighbor refcount of MAC; uninstall and free it if + * appropriate. + */ +static void +zvni_deref_ip2mac (zebra_vni_t *zvni, zebra_mac_t *mac, int uninstall) +{ + if (mac->neigh_refcnt) + mac->neigh_refcnt--; + + if (!CHECK_FLAG (mac->flags, ZEBRA_MAC_AUTO) || + mac->neigh_refcnt > 0) + return; + + if (uninstall) + zvni_mac_uninstall (zvni, mac, 0); + + zvni_mac_del (zvni, mac); +} + +/* + * Read and populate local MACs and neighbors corresponding to this VNI. + */ +static void +zvni_read_mac_neigh (struct zebra_vrf *zvrf, zebra_vni_t *zvni, + struct interface *ifp) +{ + struct zebra_if *zif; + struct interface *vlan_if; + struct zebra_l2info_vxlan *vxl; + + zif = ifp->info; + vxl = &zif->l2info.vxl; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("%u:Reading MAC FDB and Neighbors for intf %s(%u) VNI %u master %u", + ifp->vrf_id, ifp->name, ifp->ifindex, zvni->vni, + zif->brslave_info.bridge_ifindex); + + macfdb_read_for_bridge (zvrf->zns, ifp, zif->brslave_info.br_if); + vlan_if = zvni_map_to_svi (zvrf, vxl->access_vlan, + zif->brslave_info.br_if); + if (vlan_if) + neigh_read_for_vlan (zvrf->zns, vlan_if); +} + +/* + * Hash function for VNI. + */ +static unsigned int +vni_hash_keymake (void *p) +{ + const zebra_vni_t *zvni = p; + + return (jhash_1word(zvni->vni, 0)); +} + +/* + * Compare 2 VNI hash entries. + */ +static int +vni_hash_cmp (const void *p1, const void *p2) +{ + const zebra_vni_t *zvni1 = p1; + const zebra_vni_t *zvni2 = p2; + + return (zvni1->vni == zvni2->vni); +} + +/* + * Callback to allocate VNI hash entry. + */ +static void * +zvni_alloc (void *p) +{ + const zebra_vni_t *tmp_vni = p; + zebra_vni_t *zvni; + + zvni = XCALLOC (MTYPE_ZVNI, sizeof(zebra_vni_t)); + zvni->vni = tmp_vni->vni; + return ((void *)zvni); +} + +/* + * Look up VNI hash entry. + */ +static zebra_vni_t * +zvni_lookup (struct zebra_vrf *zvrf, vni_t vni) +{ + zebra_vni_t tmp_vni; + zebra_vni_t *zvni = NULL; + + memset (&tmp_vni, 0, sizeof (zebra_vni_t)); + tmp_vni.vni = vni; + zvni = hash_lookup (zvrf->vni_table, &tmp_vni); + + return zvni; +} + +/* + * Add VNI hash entry. + */ +static zebra_vni_t * +zvni_add (struct zebra_vrf *zvrf, vni_t vni) +{ + zebra_vni_t tmp_zvni; + zebra_vni_t *zvni = NULL; + + memset (&tmp_zvni, 0, sizeof (zebra_vni_t)); + tmp_zvni.vni = vni; + zvni = hash_get (zvrf->vni_table, &tmp_zvni, zvni_alloc); + assert (zvni); + + /* Create hash table for MAC */ + zvni->mac_table = hash_create(mac_hash_keymake, + mac_cmp, + "Zebra VNI MAC Table"); + + /* Create hash table for neighbors */ + zvni->neigh_table = hash_create(neigh_hash_keymake, + neigh_cmp, + "Zebra VNI Neighbor Table"); + + return zvni; +} + +/* + * Delete VNI hash entry. + */ +static int +zvni_del (struct zebra_vrf *zvrf, zebra_vni_t *zvni) +{ + zebra_vni_t *tmp_zvni; + + zvni->vxlan_if = NULL; + + /* Free the neighbor hash table. */ + hash_free(zvni->neigh_table); + zvni->neigh_table = NULL; + + /* Free the MAC hash table. */ + hash_free(zvni->mac_table); + zvni->mac_table = NULL; + + /* Free the VNI hash entry and allocated memory. */ + tmp_zvni = hash_release (zvrf->vni_table, zvni); + if (tmp_zvni) + XFREE(MTYPE_ZVNI, tmp_zvni); + + return 0; +} + +/* + * Inform BGP about local VNI addition. + */ +static int +zvni_send_add_to_client (struct zebra_vrf *zvrf, + zebra_vni_t *zvni) +{ + struct zserv *client; + struct stream *s; + + client = zebra_find_client (ZEBRA_ROUTE_BGP); + /* BGP may not be running. */ + if (!client) + return 0; + + s = client->obuf; + stream_reset (s); + + zserv_create_header (s, ZEBRA_VNI_ADD, zvrf_id (zvrf)); + stream_putl (s, zvni->vni); + stream_put_in_addr (s, &zvni->local_vtep_ip); + + /* Write packet size. */ + stream_putw_at (s, 0, stream_get_endp (s)); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("%u:Send VNI_ADD %u %s to %s", + zvrf_id (zvrf), zvni->vni, + inet_ntoa(zvni->local_vtep_ip), + zebra_route_string (client->proto)); + + client->vniadd_cnt++; + return zebra_server_send_message(client); +} + +/* + * Inform BGP about local VNI deletion. + */ +static int +zvni_send_del_to_client (struct zebra_vrf *zvrf, vni_t vni) +{ + struct zserv *client; + struct stream *s; + + client = zebra_find_client (ZEBRA_ROUTE_BGP); + /* BGP may not be running. */ + if (!client) + return 0; + + s = client->obuf; + stream_reset (s); + + zserv_create_header (s, ZEBRA_VNI_DEL, zvrf_id (zvrf)); + stream_putl (s, vni); + + /* Write packet size. */ + stream_putw_at (s, 0, stream_get_endp (s)); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("%u:Send VNI_DEL %u to %s", zvrf_id (zvrf), vni, + zebra_route_string (client->proto)); + + client->vnidel_cnt++; + return zebra_server_send_message(client); +} + +/* + * Build the VNI hash table by going over the VxLAN interfaces. This + * is called when EVPN (advertise-all-vni) is enabled. + */ +static void +zvni_build_hash_table (struct zebra_vrf *zvrf) +{ + struct listnode *node; + struct interface *ifp; + + /* Walk VxLAN interfaces and create VNI hash. */ + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (zvrf_id (zvrf)), node, ifp)) + { + struct zebra_if *zif; + struct zebra_l2info_vxlan *vxl; + zebra_vni_t *zvni; + vni_t vni; + + zif = ifp->info; + if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) + continue; + vxl = &zif->l2info.vxl; + + vni = vxl->vni; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("%u:Create VNI hash for intf %s(%u) VNI %u local IP %s", + zvrf_id (zvrf), ifp->name, ifp->ifindex, vni, + inet_ntoa (vxl->vtep_ip)); + + /* VNI hash entry is not expected to exist. */ + zvni = zvni_lookup (zvrf, vni); + if (zvni) + { + zlog_err ("VNI hash already present for VRF %d IF %s(%u) VNI %u", + zvrf_id (zvrf), ifp->name, ifp->ifindex, vni); + continue; + } + + zvni = zvni_add (zvrf, vni); + if (!zvni) + { + zlog_err ("Failed to add VNI hash, VRF %d IF %s(%u) VNI %u", + zvrf_id (zvrf), ifp->name, ifp->ifindex, vni); + return; + } + + zvni->local_vtep_ip = vxl->vtep_ip; + zvni->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 (zvrf, zvni); + } +} + +/* + * See if remote VTEP matches with prefix. + */ +static int +zvni_vtep_match (struct in_addr *vtep_ip, zebra_vtep_t *zvtep) +{ + return (IPV4_ADDR_SAME (vtep_ip, &zvtep->vtep_ip)); +} + +/* + * Locate remote VTEP in VNI hash table. + */ +static zebra_vtep_t * +zvni_vtep_find (zebra_vni_t *zvni, struct in_addr *vtep_ip) +{ + zebra_vtep_t *zvtep; + + if (!zvni) + return NULL; + + for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) + { + if (zvni_vtep_match (vtep_ip, zvtep)) + break; + } + + return zvtep; +} + +/* + * Add remote VTEP to VNI hash table. + */ +static zebra_vtep_t * +zvni_vtep_add (zebra_vni_t *zvni, struct in_addr *vtep_ip) +{ + zebra_vtep_t *zvtep; + + zvtep = XCALLOC (MTYPE_ZVNI_VTEP, sizeof(zebra_vtep_t)); + if (!zvtep) + { + zlog_err ("Failed to alloc VTEP entry, VNI %u", zvni->vni); + return NULL; + } + + zvtep->vtep_ip = *vtep_ip; + + if (zvni->vteps) + zvni->vteps->prev = zvtep; + zvtep->next = zvni->vteps; + zvni->vteps = zvtep; + + return zvtep; +} + +/* + * Remove remote VTEP from VNI hash table. + */ +static int +zvni_vtep_del (zebra_vni_t *zvni, zebra_vtep_t *zvtep) +{ + if (zvtep->next) + zvtep->next->prev = zvtep->prev; + if (zvtep->prev) + zvtep->prev->next = zvtep->next; + else + zvni->vteps = zvtep->next; + + zvtep->prev = zvtep->next = NULL; + XFREE (MTYPE_ZVNI_VTEP, zvtep); + + return 0; +} + +/* + * Delete all remote VTEPs for this VNI (upon VNI delete). Also + * uninstall from kernel if asked to. + */ +static int +zvni_vtep_del_all (zebra_vni_t *zvni, int uninstall) +{ + zebra_vtep_t *zvtep, *zvtep_next; + + if (!zvni) + return -1; + + for (zvtep = zvni->vteps; zvtep; zvtep = zvtep_next) + { + zvtep_next = zvtep->next; + if (uninstall) + zvni_vtep_uninstall (zvni, &zvtep->vtep_ip); + zvni_vtep_del (zvni, zvtep); + } + + return 0; +} + +/* + * Install remote VTEP into the kernel. + */ +static int +zvni_vtep_install (zebra_vni_t *zvni, struct in_addr *vtep_ip) +{ + return kernel_add_vtep (zvni->vni, zvni->vxlan_if, vtep_ip); +} + +/* + * Uninstall remote VTEP from the kernel. + */ +static int +zvni_vtep_uninstall (zebra_vni_t *zvni, struct in_addr *vtep_ip) +{ + if (!zvni->vxlan_if) + { + zlog_err ("VNI %u hash %p couldn't be uninstalled - no intf", + zvni->vni, zvni); + return -1; + } + + return kernel_del_vtep (zvni->vni, zvni->vxlan_if, vtep_ip); +} + +/* + * Cleanup VNI/VTEP and update kernel + */ +static void +zvni_cleanup_all (struct hash_backet *backet, void *zvrf) +{ + zebra_vni_t *zvni; + + zvni = (zebra_vni_t *) backet->data; + if (!zvni) + return; + + /* Free up all neighbors and MACs, if any. */ + zvni_neigh_del_all (zvrf, zvni, 1, 0, DEL_ALL_NEIGH); + zvni_mac_del_all (zvrf, zvni, 1, 0, DEL_ALL_MAC); + + /* Free up all remote VTEPs, if any. */ + zvni_vtep_del_all (zvni, 1); + + /* Delete the hash entry. */ + zvni_del (zvrf, zvni); +} + + +/* Public functions */ + +/* + * Handle neighbor delete (on a VLAN device / L3 interface) from the + * kernel. This may result in either the neighbor getting deleted from + * our database or being re-added to the kernel (if it is a valid + * remote neighbor). + */ +int +zebra_vxlan_local_neigh_del (struct interface *ifp, + struct interface *link_if, + struct ipaddr *ip) +{ + zebra_vni_t *zvni; + zebra_neigh_t *n; + struct zebra_vrf *zvrf; + char buf[INET6_ADDRSTRLEN]; + + /* We are only interested in neighbors on an SVI that resides on top + * of a VxLAN bridge. + */ + zvni = zvni_map_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", + zvni->vni, zvni); + return -1; + } + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("%u:Del neighbor %s intf %s(%u) -> VNI %u", + ifp->vrf_id, ipaddr2str (ip, buf, sizeof(buf)), + ifp->name, ifp->ifindex, zvni->vni); + + /* If entry doesn't exist, nothing to do. */ + n = zvni_neigh_lookup (zvni, ip); + if (!n) + return 0; + + /* If it is a remote entry, the kernel has aged this out or someone has + * deleted it, it needs to be re-installed as Quagga is the owner. + */ + if (CHECK_FLAG (n->flags, ZEBRA_NEIGH_REMOTE)) + { + zvni_neigh_install (zvni, n); + return 0; + } + + /* Locate VRF corresponding to interface. */ + zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); + assert(zvrf); + + /* Remove neighbor from BGP. */ + zvni_neigh_send_del_to_client (zvrf, zvni->vni, &n->ip, &n->emac); + + /* Delete this neighbor entry. */ + zvni_neigh_del (zvni, n); + + return 0; +} + +/* + * Handle neighbor add or update (on a VLAN device / L3 interface) + * from the kernel. + */ +int +zebra_vxlan_local_neigh_add_update (struct interface *ifp, + struct interface *link_if, + struct ipaddr *ip, + struct ethaddr *macaddr, + u_int16_t state, + u_char ext_learned) +{ + zebra_vni_t *zvni; + zebra_neigh_t *n; + struct zebra_vrf *zvrf; + char buf[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + int send_upd = 1, send_del = 0; + + /* We are only interested in neighbors on an SVI that resides on top + * of a VxLAN bridge. + */ + zvni = zvni_map_svi (ifp, link_if); + if (!zvni) + return 0; + + /* Locate VRF corresponding to interface. */ + zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); + assert(zvrf); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("%u:Add/Update neighbor %s MAC %s intf %s(%u) state 0x%x " + "%s-> VNI %u", + ifp->vrf_id, ipaddr2str (ip, buf2, sizeof(buf2)), + prefix_mac2str (macaddr, buf, sizeof (buf)), + ifp->name, ifp->ifindex, state, + ext_learned ? "ext-learned " : "", zvni->vni); + + /* If same entry already exists, it might be a change or it might be a + * move from remote to local. + */ + n = zvni_neigh_lookup (zvni, ip); + if (n) + { + if (CHECK_FLAG (n->flags, ZEBRA_NEIGH_LOCAL)) + { + if (memcmp (n->emac.octet, macaddr->octet, ETHER_ADDR_LEN) == 0) + { + if (n->ifindex == ifp->ifindex) + /* we're not interested in whatever has changed. */ + return 0; + /* client doesn't care about a purely local change. */ + send_upd = 0; + } + else + /* If the MAC has changed, issue a delete first as this means a + * different MACIP route. + */ + send_del = 1; + } + else if (ext_learned) + /* The neighbor is remote and that is the notification we got. */ + { + /* TODO: Evaluate if we need to do anything here. */ + return 0; + } + else + /* Neighbor has moved from remote to local. */ + { + UNSET_FLAG (n->flags, ZEBRA_NEIGH_REMOTE); + n->r_vtep_ip.s_addr = 0; + } + } + else + { + n = zvni_neigh_add (zvni, ip); + if (!n) + { + zlog_err ("%u:Failed to add neighbor %s MAC %s intf %s(%u) -> VNI %u", + ifp->vrf_id, ipaddr2str (ip, buf2, sizeof(buf2)), + prefix_mac2str (macaddr, buf, sizeof (buf)), + ifp->name, ifp->ifindex, zvni->vni); + return -1; + } + } + + /* Issue delete for older info, if needed. */ + if (send_del) + zvni_neigh_send_del_to_client (zvrf, zvni->vni, &n->ip, &n->emac); + + /* Set "local" forwarding info. */ + SET_FLAG (n->flags, ZEBRA_NEIGH_LOCAL); + memcpy (&n->emac, macaddr, ETHER_ADDR_LEN); + n->ifindex = ifp->ifindex; + + /* Inform BGP if required. */ + if (send_upd) + return zvni_neigh_send_add_to_client (zvrf, zvni->vni, ip, macaddr); + + return 0; +} + +/* + * Handle message from client to delete a remote MACIP for a VNI. + */ +int zebra_vxlan_remote_macip_del (struct zserv *client, int sock, + u_short length, struct zebra_vrf *zvrf) +{ + struct stream *s; + vni_t vni; + struct ethaddr macaddr; + struct ipaddr ip; + struct in_addr vtep_ip; + zebra_vni_t *zvni; + zebra_mac_t *mac; + zebra_neigh_t *n; + u_short l = 0, ipa_len; + char buf[ETHER_ADDR_STRLEN]; + char buf1[INET6_ADDRSTRLEN]; + + s = client->ibuf; + + while (l < length) + { + /* Obtain each remote MACIP and process. */ + /* Message contains VNI, followed by MAC followed by IP (if any) + * followed by remote VTEP IP. + */ + mac = NULL; + n = NULL; + memset (&ip, 0, sizeof (ip)); + vni = (vni_t) stream_getl (s); + stream_get (&macaddr.octet, s, ETHER_ADDR_LEN); + ipa_len = stream_getl (s); + if (ipa_len) + { + ip.ipa_type = (ipa_len == IPV4_MAX_BYTELEN) ? IPADDR_V4: IPADDR_V6; + stream_get (&ip.ip.addr, s, ipa_len); + } + l += 4 + ETHER_ADDR_LEN + 4 + ipa_len; + vtep_ip.s_addr = stream_get_ipv4(s); + l += IPV4_MAX_BYTELEN; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("%u:Recv MACIP Del MAC %s IP %s VNI %u Remote VTEP %s from %s", + zvrf_id (zvrf), + prefix_mac2str (&macaddr, buf, sizeof (buf)), + ipaddr2str (&ip, buf1, sizeof (buf1)), + vni, inet_ntoa (vtep_ip), + zebra_route_string (client->proto)); + + /* Locate VNI hash entry - expected to exist. */ + zvni = zvni_lookup (zvrf, vni); + if (!zvni) + { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("Failed to locate VNI hash upon remote MACIP DEL, " + "VRF %d VNI %u", zvrf_id (zvrf), vni); + continue; + } + if (!zvni->vxlan_if) + { + zlog_err ("VNI %u hash %p doesn't have intf upon remote MACIP DEL", + vni, zvni); + continue; + } + + /* The remote VTEP specified is normally expected to exist, but it is + * possible that the peer may delete the VTEP before deleting any MACs + * referring to the VTEP, in which case the handler (see remote_vtep_del) + * would have already deleted the MACs. + */ + if (!zvni_vtep_find (zvni, &vtep_ip)) + continue; + + /* If the local VxLAN interface is not up (should be a transient + * event), there's nothing more to do. + */ + if (!if_is_operative (zvni->vxlan_if)) + continue; + + mac = zvni_mac_lookup (zvni, &macaddr); + if (ipa_len) + n = zvni_neigh_lookup (zvni, &ip); + + if (n && !mac) + { + zlog_err ("failed to locate MAC %s for neigh %s in VRF %u VNI %u", + prefix_mac2str (&macaddr, buf, sizeof (buf)), + ipaddr2str (&ip, buf1, sizeof (buf1)), + zvrf_id (zvrf), vni); + continue; + } + + /* If the remote mac or neighbor doesn't exist there is nothing more + * to do. Otherwise, uninstall the entry and then remove it. + */ + if (!mac && !n) + continue; + + /* Uninstall remote neighbor or MAC. */ + if (n) + { + /* When the MAC changes for an IP, it is possible the client may + * update the new MAC before trying to delete the "old" neighbor + * (as these are two different MACIP routes). Do the delete only + * if the MAC matches. + */ + if (CHECK_FLAG (n->flags, ZEBRA_NEIGH_REMOTE) && + (memcmp (n->emac.octet, macaddr.octet, ETHER_ADDR_LEN) == 0)) + { + zvni_neigh_uninstall (zvni, n); + zvni_neigh_del (zvni, n); + zvni_deref_ip2mac (zvni, mac, 1); + } + } + else + { + if (CHECK_FLAG (mac->flags, ZEBRA_MAC_REMOTE)) + { + if (!mac->neigh_refcnt) + { + zvni_mac_uninstall (zvni, mac, 0); + zvni_mac_del (zvni, mac); + } + else + SET_FLAG (mac->flags, ZEBRA_MAC_AUTO); + } + } + } + + return 0; +} + +/* + * Handle message from client to add a remote MACIP for a VNI. This + * could be just the add of a MAC address or the add of a neighbor + * (IP+MAC). + */ +int +zebra_vxlan_remote_macip_add (struct zserv *client, int sock, + u_short length, struct zebra_vrf *zvrf) +{ + struct stream *s; + vni_t vni; + struct ethaddr macaddr; + struct ipaddr ip; + struct in_addr vtep_ip; + zebra_vni_t *zvni; + zebra_vtep_t *zvtep; + zebra_mac_t *mac, *old_mac; + zebra_neigh_t *n; + u_short l = 0, ipa_len; + int update_mac = 0, update_neigh = 0; + char buf[ETHER_ADDR_STRLEN]; + char buf1[INET6_ADDRSTRLEN]; + + assert (EVPN_ENABLED (zvrf)); + + s = client->ibuf; + + while (l < length) + { + /* Obtain each remote MACIP and process. */ + /* Message contains VNI, followed by MAC followed by IP (if any) + * followed by remote VTEP IP. + */ + update_mac = update_neigh = 0; + mac = NULL; + n = NULL; + memset (&ip, 0, sizeof (ip)); + vni = (vni_t) stream_getl (s); + stream_get (&macaddr.octet, s, ETHER_ADDR_LEN); + ipa_len = stream_getl (s); + if (ipa_len) + { + ip.ipa_type = (ipa_len == IPV4_MAX_BYTELEN) ? IPADDR_V4: IPADDR_V6; + stream_get (&ip.ip.addr, s, ipa_len); + } + l += 4 + ETHER_ADDR_LEN + 4 + ipa_len; + vtep_ip.s_addr = stream_get_ipv4 (s); + l += IPV4_MAX_BYTELEN; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("%u:Recv MACIP Add MAC %s IP %s VNI %u Remote VTEP %s from %s", + zvrf_id (zvrf), + prefix_mac2str (&macaddr, buf, sizeof (buf)), + ipaddr2str (&ip, buf1, sizeof (buf1)), + vni, inet_ntoa (vtep_ip), + zebra_route_string (client->proto)); + + /* Locate VNI hash entry - expected to exist. */ + zvni = zvni_lookup (zvrf, vni); + if (!zvni) + { + zlog_err ("Failed to locate VNI hash upon remote MACIP ADD, VRF %d VNI %u", + zvrf_id (zvrf), vni); + continue; + } + if (!zvni->vxlan_if) + { + zlog_err ("VNI %u hash %p doesn't have intf upon remote MACIP add", + vni, zvni); + continue; + } + /* If the local VxLAN interface is not up (should be a transient + * event), there's nothing more to do. + */ + if (!if_is_operative (zvni->vxlan_if)) + continue; + + /* The remote VTEP specified should normally exist, but it is possible + * that when peering comes up, peer may advertise MACIP routes before + * advertising type-3 routes. + */ + zvtep = zvni_vtep_find (zvni, &vtep_ip); + if (!zvtep) + { + if (zvni_vtep_add (zvni, &vtep_ip) == NULL) + { + zlog_err ("Failed to add remote VTEP, VRF %d VNI %u zvni %p", + zvrf_id (zvrf), vni, zvni); + continue; + } + + zvni_vtep_install (zvni, &vtep_ip); + } + + /* First, check if the remote MAC is unknown or has a change. If so, + * that needs to be updated first. Note that client could install + * MAC and MACIP separately or just install the latter. + */ + mac = zvni_mac_lookup (zvni, &macaddr); + if (!mac || !CHECK_FLAG (mac->flags, ZEBRA_MAC_REMOTE) || + !IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, &vtep_ip)) + update_mac = 1; - /* VNI hash entry is not expected to exist. */ - zvni = zvni_lookup (zvrf, vni); - if (zvni) + if (update_mac) { - zlog_err ("VNI hash already present for VRF %d IF %s(%u) VNI %u", - zvrf_id (zvrf), ifp->name, ifp->ifindex, vni); - continue; + if (!mac) + { + mac = zvni_mac_add (zvni, &macaddr); + if (!mac) + { + zlog_warn ("%u:Failed to add MAC %s VNI %u Remote VTEP %s", + zvrf_id (zvrf), + prefix_mac2str (&macaddr, buf, sizeof (buf)), + vni, inet_ntoa (vtep_ip)); + return -1; + } + + /* Is this MAC created for a MACIP? */ + if (ipa_len) + SET_FLAG (mac->flags, ZEBRA_MAC_AUTO); + } + else if (CHECK_FLAG (mac->flags, ZEBRA_MAC_LOCAL)) + { + /* Moving from local to remote, issue delete. */ + zvni_mac_uninstall (zvni, mac, 1); + } + + /* Set "auto" and "remote" forwarding info. */ + UNSET_FLAG (mac->flags, ZEBRA_MAC_LOCAL); + memset (&mac->fwd_info, 0, sizeof (mac->fwd_info)); + SET_FLAG (mac->flags, ZEBRA_MAC_REMOTE); + mac->fwd_info.r_vtep_ip = vtep_ip; + + /* Install the entry. */ + zvni_mac_install (zvni, mac); } - zvni = zvni_add (zvrf, vni); - if (!zvni) + /* If there is no IP, continue - after clearing AUTO flag of MAC. */ + if (!ipa_len) { - zlog_err ("Failed to add VNI hash, VRF %d IF %s(%u) VNI %u", - zvrf_id (zvrf), ifp->name, ifp->ifindex, vni); - return; + UNSET_FLAG (mac->flags, ZEBRA_MAC_AUTO); + continue; } - zvni->local_vtep_ip = vxl->vtep_ip; - zvni->vxlan_if = ifp; + /* Check if the remote neighbor itself is unknown or has a change. + * If so, create or update and then install the entry. + */ + n = zvni_neigh_lookup (zvni, &ip); + if (!n || !CHECK_FLAG (n->flags, ZEBRA_NEIGH_REMOTE) || + (memcmp(&n->emac, &macaddr, sizeof (macaddr)) != 0) || + !IPV4_ADDR_SAME(&n->r_vtep_ip, &vtep_ip)) + update_neigh = 1; - /* 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 (zvrf, zvni); + if (update_neigh) + { + if (!n) + { + n = zvni_neigh_add (zvni, &ip); + if (!n) + { + zlog_warn ("%u:Failed to add Neigh %s MAC %s VNI %u Remote VTEP %s", + zvrf_id (zvrf), ipaddr2str (&ip, buf1, sizeof (buf1)), + prefix_mac2str (&macaddr, buf, sizeof (buf)), + vni, inet_ntoa (vtep_ip)); + return -1; + } + + /* New neighbor referring to this MAC. */ + mac->neigh_refcnt++; + } + else if (memcmp(&n->emac, &macaddr, sizeof (macaddr)) != 0) + { + /* MAC change, update ref counts for old and new MAC. */ + old_mac = zvni_mac_lookup (zvni, &n->emac); + if (old_mac) + zvni_deref_ip2mac (zvni, old_mac, 1); + mac->neigh_refcnt++; + } + + /* Set "remote" forwarding info. */ + UNSET_FLAG (n->flags, ZEBRA_NEIGH_LOCAL); + /* TODO: Handle MAC change. */ + memcpy (&n->emac, &macaddr, ETHER_ADDR_LEN); + n->r_vtep_ip = vtep_ip; + SET_FLAG (n->flags, ZEBRA_NEIGH_REMOTE); + + /* Install the entry. */ + zvni_neigh_install (zvni, n); + } } -} -/* - * See if remote VTEP matches with prefix. - */ -static int -zvni_vtep_match (struct in_addr *vtep_ip, zebra_vtep_t *zvtep) -{ - return (IPV4_ADDR_SAME (vtep_ip, &zvtep->vtep_ip)); + return 0; } /* - * Locate remote VTEP in VNI hash table. + * Handle notification of MAC add/update over VxLAN. If the kernel is notifying + * us, this must involve a multihoming scenario. Treat this as implicit delete + * of any prior local MAC. */ -static zebra_vtep_t * -zvni_vtep_find (zebra_vni_t *zvni, struct in_addr *vtep_ip) +int +zebra_vxlan_check_del_local_mac (struct interface *ifp, + struct interface *br_if, + struct ethaddr *macaddr, + vlanid_t vid) { - zebra_vtep_t *zvtep; - - if (!zvni) - return NULL; + struct zebra_if *zif; + struct zebra_vrf *zvrf; + struct zebra_l2info_vxlan *vxl; + vni_t vni; + zebra_vni_t *zvni; + zebra_mac_t *mac; + char buf[ETHER_ADDR_STRLEN]; - for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) - { - if (zvni_vtep_match (vtep_ip, zvtep)) - break; - } + zif = ifp->info; + assert(zif); + vxl = &zif->l2info.vxl; + vni = vxl->vni; - return zvtep; -} + /* Locate VRF corresponding to interface. */ + zvrf = vrf_info_lookup(ifp->vrf_id); + assert(zvrf); -/* - * Add remote VTEP to VNI hash table. - */ -static zebra_vtep_t * -zvni_vtep_add (zebra_vni_t *zvni, struct in_addr *vtep_ip) -{ - zebra_vtep_t *zvtep; + /* If EVPN is not enabled, nothing to do. */ + if (!EVPN_ENABLED(zvrf)) + return 0; - zvtep = XCALLOC (MTYPE_ZVNI_VTEP, sizeof(zebra_vtep_t)); - if (!zvtep) - { - zlog_err ("Failed to alloc VTEP entry, VNI %u", zvni->vni); - return NULL; - } + /* Locate hash entry; it is expected to exist. */ + zvni = zvni_lookup (zvrf, vni); + if (!zvni) + return 0; - zvtep->vtep_ip = *vtep_ip; + /* If entry doesn't exist, nothing to do. */ + mac = zvni_mac_lookup (zvni, macaddr); + if (!mac) + return 0; - if (zvni->vteps) - zvni->vteps->prev = zvtep; - zvtep->next = zvni->vteps; - zvni->vteps = zvtep; + /* Is it a local entry? */ + if (!CHECK_FLAG (mac->flags, ZEBRA_MAC_LOCAL)) + return 0; - return zvtep; -} + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("%u:Add/update remote MAC %s intf %s(%u) VNI %u - del local", + ifp->vrf_id, prefix_mac2str (macaddr, buf, sizeof (buf)), + ifp->name, ifp->ifindex, vni); -/* - * Remove remote VTEP from VNI hash table. - */ -static int -zvni_vtep_del (zebra_vni_t *zvni, zebra_vtep_t *zvtep) -{ - if (zvtep->next) - zvtep->next->prev = zvtep->prev; - if (zvtep->prev) - zvtep->prev->next = zvtep->next; - else - zvni->vteps = zvtep->next; + /* Remove MAC from BGP. */ + zvni_mac_send_del_to_client (zvrf, zvni->vni, macaddr); - zvtep->prev = zvtep->next = NULL; - XFREE (MTYPE_ZVNI_VTEP, zvtep); + /* Delete this MAC entry. */ + zvni_mac_del (zvni, mac); return 0; } /* - * Delete all remote VTEPs for this VNI (upon VNI delete). Also - * uninstall from kernel if asked to. + * Handle remote MAC delete by kernel; readd the remote MAC if we have it. + * This can happen because the remote MAC entries are also added as "dynamic", + * so the kernel can ageout the entry. */ -static int -zvni_vtep_del_all (zebra_vni_t *zvni, int uninstall) +int +zebra_vxlan_check_readd_remote_mac (struct interface *ifp, + struct interface *br_if, + struct ethaddr *macaddr, + vlanid_t vid) { - zebra_vtep_t *zvtep, *zvtep_next; + struct zebra_if *zif; + struct zebra_vrf *zvrf; + struct zebra_l2info_vxlan *vxl; + vni_t vni; + zebra_vni_t *zvni; + zebra_mac_t *mac; + char buf[ETHER_ADDR_STRLEN]; + + zif = ifp->info; + assert(zif); + vxl = &zif->l2info.vxl; + vni = vxl->vni; + + /* Locate VRF corresponding to interface. */ + zvrf = vrf_info_lookup(ifp->vrf_id); + assert(zvrf); + /* If EVPN is not enabled, nothing to do. */ + if (!EVPN_ENABLED(zvrf)) + return 0; + + /* Locate hash entry; it is expected to exist. */ + zvni = zvni_lookup (zvrf, vni); if (!zvni) - return -1; + return 0; - for (zvtep = zvni->vteps; zvtep; zvtep = zvtep_next) - { - zvtep_next = zvtep->next; - if (uninstall) - zvni_vtep_uninstall (zvni, &zvtep->vtep_ip); - zvni_vtep_del (zvni, zvtep); - } + /* If entry doesn't exist, nothing to do. */ + mac = zvni_mac_lookup (zvni, macaddr); + if (!mac) + return 0; + + /* Is it a remote entry? */ + if (!CHECK_FLAG (mac->flags, ZEBRA_MAC_REMOTE)) + return 0; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("%u:Del remote MAC %s intf %s(%u) VNI %u - readd", + ifp->vrf_id, prefix_mac2str (macaddr, buf, sizeof (buf)), + ifp->name, ifp->ifindex, vni); + zvni_mac_install (zvni, mac); return 0; } /* - * Install remote VTEP into the kernel. + * Handle local MAC delete (on a port or VLAN corresponding to this VNI). */ -static int -zvni_vtep_install (zebra_vni_t *zvni, struct in_addr *vtep_ip) +int +zebra_vxlan_local_mac_del (struct interface *ifp, struct interface *br_if, + struct ethaddr *macaddr, vlanid_t vid) { - return kernel_add_vtep (zvni->vni, zvni->vxlan_if, vtep_ip); -} + zebra_vni_t *zvni; + zebra_mac_t *mac; + struct zebra_vrf *zvrf; + char buf[ETHER_ADDR_STRLEN]; -/* - * Uninstall remote VTEP from the kernel. - */ -static int -zvni_vtep_uninstall (zebra_vni_t *zvni, struct in_addr *vtep_ip) -{ + /* We are interested in MACs only on ports or (port, VLAN) that + * map to a VNI. + */ + zvni = zvni_map_vlan (ifp, br_if, vid); + if (!zvni) + return 0; if (!zvni->vxlan_if) { - zlog_err ("VNI %u hash %p couldn't be uninstalled - no intf", + zlog_err ("VNI %u hash %p doesn't have intf upon local MAC DEL", zvni->vni, zvni); return -1; } - return kernel_del_vtep (zvni->vni, zvni->vxlan_if, vtep_ip); + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("%u:Del MAC %s intf %s(%u) VID %u -> VNI %u", + ifp->vrf_id, prefix_mac2str (macaddr, buf, sizeof (buf)), + ifp->name, ifp->ifindex, vid, zvni->vni); + + /* If entry doesn't exist, nothing to do. */ + mac = zvni_mac_lookup (zvni, macaddr); + if (!mac) + return 0; + + /* Is it a local entry? */ + if (!CHECK_FLAG (mac->flags, ZEBRA_MAC_LOCAL)) + return 0; + + /* Locate VRF corresponding to interface. */ + zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); + assert(zvrf); + + /* Remove MAC from BGP. */ + zvni_mac_send_del_to_client (zvrf, zvni->vni, macaddr); + + /* Delete this MAC entry. */ + zvni_mac_del (zvni, mac); + + return 0; } /* - * Cleanup VNI/VTEP and update kernel + * Handle local MAC add (on a port or VLAN corresponding to this VNI). */ -static void -zvni_cleanup_all (struct hash_backet *backet, void *zvrf) +int +zebra_vxlan_local_mac_add_update (struct interface *ifp, struct interface *br_if, + struct ethaddr *macaddr, vlanid_t vid) { zebra_vni_t *zvni; + zebra_mac_t *mac; + struct zebra_vrf *zvrf; + char buf[ETHER_ADDR_STRLEN]; + int add = 1; - zvni = (zebra_vni_t *) backet->data; + /* We are interested in MACs only on ports or (port, VLAN) that + * map to a VNI. + */ + zvni = zvni_map_vlan (ifp, br_if, vid); if (!zvni) - return; + { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("%u:Add/Update MAC %s intf %s(%u) VID %u, could not find VNI", + ifp->vrf_id, + prefix_mac2str (macaddr, buf, sizeof (buf)), + ifp->name, ifp->ifindex, vid); + return 0; + } - /* Free up all remote VTEPs, if any. */ - zvni_vtep_del_all (zvni, 1); + if (!zvni->vxlan_if) + { + zlog_err ("VNI %u hash %p doesn't have intf upon local MAC ADD", + zvni->vni, zvni); + return -1; + } - /* Delete the hash entry. */ - zvni_del (zvrf, zvni); -} + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("%u:Add/Update MAC %s intf %s(%u) VID %u -> VNI %u", + ifp->vrf_id, + prefix_mac2str (macaddr, buf, sizeof (buf)), + ifp->name, ifp->ifindex, vid, zvni->vni); + + /* If same entry already exists, nothing to do. */ + mac = zvni_mac_lookup (zvni, macaddr); + if (mac) + { + if (CHECK_FLAG (mac->flags, ZEBRA_MAC_LOCAL)) + { + if (mac->fwd_info.local.ifindex == ifp->ifindex && + mac->fwd_info.local.vid == vid) + { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug ("%u:Add/Update MAC %s intf %s(%u) VID %u -> VNI %u, " + "entry exists and has not changed ", + ifp->vrf_id, + prefix_mac2str (macaddr, buf, sizeof (buf)), + ifp->name, ifp->ifindex, vid, zvni->vni); + return 0; + } + + add = 0; /* This is an update of local interface. */ + } + } + /* Locate VRF corresponding to interface. */ + zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); + assert(zvrf); -/* Public functions */ + if (!mac) + { + mac = zvni_mac_add (zvni, macaddr); + if (!mac) + { + zlog_err ("%u:Failed to add MAC %s intf %s(%u) VID %u", + ifp->vrf_id, prefix_mac2str (macaddr, buf, sizeof (buf)), + ifp->name, ifp->ifindex, vid); + return -1; + } + } + + /* Set "local" forwarding info. */ + UNSET_FLAG (mac->flags, ZEBRA_MAC_REMOTE); + memset (&mac->fwd_info, 0, sizeof (mac->fwd_info)); + SET_FLAG (mac->flags, ZEBRA_MAC_LOCAL); + mac->fwd_info.local.ifindex = ifp->ifindex; + mac->fwd_info.local.vid = vid; + + /* Inform BGP if required. */ + if (add) + return zvni_mac_send_add_to_client (zvrf, zvni->vni, macaddr); + + return 0; +} /* * Handle message from client to delete a remote VTEP for a VNI. @@ -490,6 +2182,8 @@ int zebra_vxlan_remote_vtep_del (struct zserv *client, int sock, if (!zvtep) continue; + zvni_neigh_del_from_vtep (zvni, 1, &vtep_ip); + zvni_mac_del_from_vtep (zvni, 1, &vtep_ip); zvni_vtep_uninstall (zvni, &vtep_ip); zvni_vtep_del (zvni, zvtep); } @@ -565,6 +2259,50 @@ int zebra_vxlan_remote_vtep_add (struct zserv *client, int sock, return 0; } +/* + * Handle SVI interface going down. At this point, this is a NOP since + * the kernel deletes the neighbor entries on this SVI (if any). + */ +int +zebra_vxlan_svi_down (struct interface *ifp, struct interface *link_if) +{ + 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). + */ +int +zebra_vxlan_svi_up (struct interface *ifp, struct interface *link_if) +{ + zebra_vni_t *zvni; + struct neigh_walk_ctx n_wctx; + + zvni = zvni_map_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 ("%u:SVI %s(%u) VNI %u is UP, installing neighbors", + ifp->vrf_id, ifp->name, ifp->ifindex, zvni->vni); + + /* 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. @@ -609,6 +2347,10 @@ zebra_vxlan_if_down (struct interface *ifp) /* Delete this VNI from BGP. */ zvni_send_del_to_client (zvrf, zvni->vni); + /* Free up all neighbors and MACs, if any. */ + zvni_neigh_del_all (zvrf, zvni, 1, 0, DEL_ALL_NEIGH); + zvni_mac_del_all (zvrf, zvni, 1, 0, DEL_ALL_MAC); + /* Free up all remote VTEPs, if any. */ zvni_vtep_del_all (zvni, 1); @@ -656,8 +2398,12 @@ zebra_vxlan_if_up (struct interface *ifp) assert (zvni->vxlan_if == ifp); /* 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 (zvrf, zvni); + { + zvni_send_add_to_client (zvrf, zvni); + zvni_read_mac_neigh (zvrf, zvni, ifp); + } return 0; } @@ -689,8 +2435,8 @@ zebra_vxlan_if_del (struct interface *ifp) vni = vxl->vni; if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug ("%u:Del intf %s(%u) VNI %u", - ifp->vrf_id, ifp->name, ifp->ifindex, vni); + zlog_debug ("%u:Del VNI %u intf %s(%u)", + ifp->vrf_id, vni, ifp->name, ifp->ifindex); /* Locate hash entry; it is expected to exist. */ zvni = zvni_lookup (zvrf, vni); @@ -704,6 +2450,10 @@ zebra_vxlan_if_del (struct interface *ifp) /* Delete VNI from BGP. */ zvni_send_del_to_client (zvrf, zvni->vni); + /* Free up all neighbors and MAC, if any. */ + zvni_neigh_del_all (zvrf, zvni, 0, 0, DEL_ALL_NEIGH); + zvni_mac_del_all (zvrf, zvni, 0, 0, DEL_ALL_MAC); + /* Free up all remote VTEPs, if any. */ zvni_vtep_del_all (zvni, 0); @@ -753,11 +2503,10 @@ zebra_vxlan_if_update (struct interface *ifp, u_int16_t chgflags) } if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug ("%u:Update intf %s(%u) VNI %u VLAN %u local IP %s " + zlog_debug ("%u:Update VNI %u intf %s(%u) VLAN %u local IP %s " "master %u chg 0x%x", - ifp->vrf_id, ifp->name, ifp->ifindex, - vni, vxl->access_vlan, - inet_ntoa (vxl->vtep_ip), + ifp->vrf_id, vni, ifp->name, ifp->ifindex, + vxl->access_vlan, inet_ntoa (vxl->vtep_ip), zif->brslave_info.bridge_ifindex, chgflags); /* Removed from bridge? */ @@ -765,9 +2514,20 @@ zebra_vxlan_if_update (struct interface *ifp, u_int16_t chgflags) (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 (zvrf, zvni->vni); + zvni_neigh_del_all (zvrf, zvni, 1, 0, DEL_ALL_NEIGH); + zvni_mac_del_all (zvrf, zvni, 1, 0, DEL_ALL_MAC); zvni_vtep_del_all (zvni, 1); } + else if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) + { + /* Remove all existing local neighbors and MACs for this VNI + * (including from BGP) + */ + zvni_neigh_del_all (zvrf, zvni, 0, 1, DEL_LOCAL_MAC); + zvni_mac_del_all (zvrf, zvni, 0, 1, DEL_LOCAL_MAC); + } zvni->local_vtep_ip = vxl->vtep_ip; zvni->vxlan_if = ifp; @@ -779,9 +2539,32 @@ zebra_vxlan_if_update (struct interface *ifp, u_int16_t chgflags) if (!if_is_operative (ifp) || !zif->brslave_info.br_if) return 0; - /* Inform BGP. */ + /* 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 (zvrf, 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 (zvrf, 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 (zvrf, 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; } @@ -811,10 +2594,9 @@ zebra_vxlan_if_add (struct interface *ifp) vni = vxl->vni; if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug ("%u:Add intf %s(%u) VNI %u VLAN %u local IP %s master %u", - ifp->vrf_id, ifp->name, ifp->ifindex, - vni, vxl->access_vlan, - inet_ntoa (vxl->vtep_ip), + zlog_debug ("%u:Add VNI %u intf %s(%u) VLAN %u local IP %s master %u", + ifp->vrf_id, vni, ifp->name, ifp->ifindex, + vxl->access_vlan, inet_ntoa (vxl->vtep_ip), zif->brslave_info.bridge_ifindex); /* Create or update VNI hash. */ @@ -840,6 +2622,9 @@ zebra_vxlan_if_add (struct interface *ifp) /* Inform BGP */ zvni_send_add_to_client (zvrf, zvni); + /* Read and populate local MACs and neighbors */ + zvni_read_mac_neigh (zvrf, zvni, ifp); + return 0; } @@ -871,6 +2656,12 @@ int zebra_vxlan_advertise_all_vni (struct zserv *client, int sock, { /* Build VNI hash table and inform BGP. */ zvni_build_hash_table (zvrf); + + /* Read the MAC FDB */ + macfdb_read (zvrf->zns); + + /* Read neighbors */ + neigh_read (zvrf->zns); } else { diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h index 0e8d783a3c..be991f426c 100644 --- a/zebra/zebra_vxlan.h +++ b/zebra/zebra_vxlan.h @@ -42,6 +42,33 @@ #define ZEBRA_VXLIF_MASTER_CHANGE 0x2 #define ZEBRA_VXLIF_VLAN_CHANGE 0x4 +extern int zebra_vxlan_svi_up (struct interface *ifp, struct interface *link_if); +extern int zebra_vxlan_svi_down (struct interface *ifp, struct interface *link_if); +extern int zebra_vxlan_local_neigh_add_update (struct interface *ifp, + struct interface *link_if, + struct ipaddr *ip, + struct ethaddr *macaddr, + u_int16_t state, + u_char ext_learned); +extern int zebra_vxlan_local_neigh_del (struct interface *ifp, + struct interface *link_if, + struct ipaddr *ip); +extern int zebra_vxlan_remote_macip_add (struct zserv *client, int sock, + u_short length, struct zebra_vrf *zvrf); +extern int zebra_vxlan_remote_macip_del (struct zserv *client, int sock, + u_short length, struct zebra_vrf *zvrf); +extern int zebra_vxlan_local_mac_add_update (struct interface *ifp, + struct interface *br_if, + struct ethaddr *mac, vlanid_t vid); +extern int zebra_vxlan_local_mac_del (struct interface *ifp, + struct interface *br_if, + struct ethaddr *mac, vlanid_t vid); +extern int zebra_vxlan_check_readd_remote_mac (struct interface *ifp, + struct interface *br_if, + struct ethaddr *mac, vlanid_t vid); +extern int zebra_vxlan_check_del_local_mac (struct interface *ifp, + struct interface *br_if, + struct ethaddr *mac, vlanid_t vid); extern int zebra_vxlan_if_up (struct interface *ifp); extern int zebra_vxlan_if_down (struct interface *ifp); extern int zebra_vxlan_if_add (struct interface *ifp); diff --git a/zebra/zebra_vxlan_null.c b/zebra/zebra_vxlan_null.c index 532ab64290..23bbddfcea 100644 --- a/zebra/zebra_vxlan_null.c +++ b/zebra/zebra_vxlan_null.c @@ -30,6 +30,56 @@ #include "zebra/zebra_l2.h" #include "zebra/zebra_vxlan.h" +int zebra_vxlan_svi_up (struct interface *ifp, struct interface *link_if) +{ + return 0; +} + +int zebra_vxlan_svi_down (struct interface *ifp, struct interface *link_if) +{ + return 0; +} + +int zebra_vxlan_remote_macip_add (struct zserv *client, int sock, + u_short length, struct zebra_vrf *zvrf) +{ + return 0; +} + +int zebra_vxlan_remote_macip_del (struct zserv *client, int sock, + u_short length, struct zebra_vrf *zvrf) +{ + return 0; +} + +int zebra_vxlan_local_mac_add_update (struct interface *ifp, + struct interface *br_if, + struct ethaddr *mac, vlanid_t vid) +{ + return 0; +} + +int zebra_vxlan_local_mac_del (struct interface *ifp, + struct interface *br_if, + struct ethaddr *mac, vlanid_t vid) +{ + return 0; +} + +int zebra_vxlan_check_readd_remote_mac (struct interface *ifp, + struct interface *br_if, + struct ethaddr *mac, vlanid_t vid) +{ + return 0; +} + +int zebra_vxlan_check_del_local_mac (struct interface *ifp, + struct interface *br_if, + struct ethaddr *mac, vlanid_t vid) +{ + return 0; +} + int zebra_vxlan_if_up (struct interface *ifp) { diff --git a/zebra/zserv.c b/zebra/zserv.c index c6a9c38c35..a2dbefee0a 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -2444,6 +2444,12 @@ zebra_client_read (struct thread *thread) case ZEBRA_REMOTE_VTEP_DEL: zebra_vxlan_remote_vtep_del (client, sock, length, zvrf); break; + case ZEBRA_REMOTE_MACIP_ADD: + zebra_vxlan_remote_macip_add (client, sock, length, zvrf); + break; + case ZEBRA_REMOTE_MACIP_DEL: + zebra_vxlan_remote_macip_del (client, sock, length, zvrf); + break; default: zlog_info ("Zebra received unknown command %d", command); break; @@ -2737,6 +2743,9 @@ zebra_show_client_detail (struct vty *vty, struct zserv *client) vty_outln (vty, "Interface Down Notifications: %d",client->ifdown_cnt); vty_outln (vty, "VNI add notifications: %d", client->vniadd_cnt); vty_outln (vty, "VNI delete notifications: %d", client->vnidel_cnt); + vty_outln (vty, "MAC-IP add notifications: %d", client->macipadd_cnt); + vty_outln (vty, "MAC-IP delete notifications: %d", client->macipdel_cnt); + vty_out (vty, VTYNL); return; } diff --git a/zebra/zserv.h b/zebra/zserv.h index c8f006d44b..2d6f6fae77 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -107,6 +107,8 @@ struct zserv u_int32_t bfd_client_reg_cnt; u_int32_t vniadd_cnt; u_int32_t vnidel_cnt; + u_int32_t macipadd_cnt; + u_int32_t macipdel_cnt; time_t connect_time; time_t last_read_time; @@ -147,6 +149,11 @@ extern void hostinfo_get (void); extern void rib_init (void); extern void interface_list (struct zebra_ns *); extern void route_read (struct zebra_ns *); +extern void macfdb_read (struct zebra_ns *); +extern void macfdb_read_for_bridge (struct zebra_ns *, struct interface *, + struct interface *); +extern void neigh_read (struct zebra_ns *); +extern void neigh_read_for_vlan (struct zebra_ns *, struct interface *); extern void kernel_init (struct zebra_ns *); extern void kernel_terminate (struct zebra_ns *); extern void zebra_route_map_init (void); -- cgit v1.2.3 From 2850f0ea580764393ae1253ef87a57d772dafafd Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Fri, 14 Jul 2017 08:52:45 -0400 Subject: bgpd, zebra: Cleanup warnings from new code 1) Clean up VTY_NEWLINE -> \n 2) Remove usages of VTY_GET_INTEGER Signed-off-by: Donald Sharp --- bgpd/bgp_evpn_vty.c | 213 +++++++++++++++++++++++++--------------------------- bgpd/bgp_route.c | 17 ++--- zebra/debug.c | 2 +- zebra/interface.c | 22 +++--- zebra/zebra_vty.c | 24 +++--- zebra/zebra_vxlan.c | 133 ++++++++++++++++---------------- 6 files changed, 201 insertions(+), 210 deletions(-) (limited to 'zebra/interface.c') diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index fc4944a6d1..b5a58f0147 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -123,12 +123,11 @@ display_import_rt (struct vty *vty, struct irt_node *irt) return; } - vty_out (vty, "%s", VTY_NEWLINE); - vty_out (vty, "List of VNIs importing routes with this route-target:%s", - VTY_NEWLINE); + vty_out (vty, "\n"); + vty_out (vty, "List of VNIs importing routes with this route-target:\n"); for (ALL_LIST_ELEMENTS (irt->vnis, node, nnode, tmp_vpn)) - vty_out (vty, " %u%s", tmp_vpn->vni, VTY_NEWLINE); + vty_out (vty, " %u\n", tmp_vpn->vni); } static void @@ -170,25 +169,22 @@ bgp_evpn_show_route_rd_header (struct vty *vty, struct bgp_node *rd_rn) break; } - vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, "\n"); } static void bgp_evpn_show_route_header (struct vty *vty, struct bgp *bgp) { - char ri_header[] = " Network Next Hop Metric LocPrf Weight Path%s"; + char ri_header[] = " Network Next Hop Metric LocPrf Weight Path\n"; - vty_out (vty, "BGP table version is 0, local router ID is %s%s", - inet_ntoa (bgp->router_id), VTY_NEWLINE); + vty_out (vty, "BGP table version is 0, local router ID is %s\n", + inet_ntoa (bgp->router_id)); vty_out (vty, "Status codes: s suppressed, d damped, h history, " - "* valid, > best, i - internal%s", VTY_NEWLINE); - vty_out (vty, "Origin codes: i - IGP, e - EGP, ? - incomplete%s", - VTY_NEWLINE); - vty_out (vty, "EVPN type-2 prefix: [2]:[ESI]:[EthTag]:[MAClen]:[MAC]:[IPlen]:[IP]%s", - VTY_NEWLINE); - vty_out (vty, "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]%s%s", - VTY_NEWLINE, VTY_NEWLINE); - vty_out (vty, ri_header, VTY_NEWLINE); + "* valid, > best, i - internal\n"); + vty_out (vty, "Origin codes: i - IGP, e - EGP, ? - incomplete\n"); + vty_out (vty, "EVPN type-2 prefix: [2]:[ESI]:[EthTag]:[MAClen]:[MAC]:[IPlen]:[IP]\n"); + vty_out (vty, "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]\n\n"); + vty_out (vty, "%s", ri_header); } static void @@ -202,27 +198,26 @@ display_vni (struct vty *vty, struct bgpevpn *vpn) vty_out (vty, "VNI: %d", vpn->vni); if (is_vni_live (vpn)) vty_out (vty, " (known to the kernel)"); - vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, "\n"); - vty_out (vty, " RD: %s%s", - prefix_rd2str (&vpn->prd, buf1, RD_ADDRSTRLEN), - VTY_NEWLINE); - vty_out (vty, " Originator IP: %s%s", - inet_ntoa(vpn->originator_ip), VTY_NEWLINE); + vty_out (vty, " RD: %s\n", + prefix_rd2str (&vpn->prd, buf1, RD_ADDRSTRLEN)); + vty_out (vty, " Originator IP: %s\n", + inet_ntoa(vpn->originator_ip)); - vty_out (vty, " Import Route Target:%s", VTY_NEWLINE); + vty_out (vty, " Import Route Target:\n"); for (ALL_LIST_ELEMENTS (vpn->import_rtl, node, nnode, ecom)) { ecom_str = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - vty_out (vty, " %s%s", ecom_str, VTY_NEWLINE); + vty_out (vty, " %s\n", ecom_str); XFREE (MTYPE_ECOMMUNITY_STR, ecom_str); } - vty_out (vty, " Export Route Target:%s", VTY_NEWLINE); + vty_out (vty, " Export Route Target:\n"); for (ALL_LIST_ELEMENTS (vpn->export_rtl, node, nnode, ecom)) { ecom_str = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - vty_out (vty, " %s%s", ecom_str, VTY_NEWLINE); + vty_out (vty, " %s\n", ecom_str); XFREE (MTYPE_ECOMMUNITY_STR, ecom_str); } } @@ -273,12 +268,12 @@ show_vni_routes (struct bgp *bgp, struct bgpevpn *vpn, int type, } if (prefix_cnt == 0) - vty_out (vty, "No EVPN prefixes %sexist for this VNI%s", - type ? "(of requested type) " : "", VTY_NEWLINE); + vty_out (vty, "No EVPN prefixes %sexist for this VNI\n", + type ? "(of requested type) " : ""); else - vty_out (vty, "%sDisplayed %u prefixes (%u paths)%s%s", - VTY_NEWLINE, prefix_cnt, path_cnt, - type ? " (of requested type)" : "", VTY_NEWLINE); + vty_out (vty, "\nDisplayed %u prefixes (%u paths)%s\n", + prefix_cnt, path_cnt, + type ? " (of requested type)" : ""); } static void @@ -288,7 +283,7 @@ show_vni_routes_hash (struct hash_backet *backet, void *arg) struct vni_walk_ctx *wctx = arg; struct vty *vty = wctx->vty; - vty_out (vty, "%sVNI: %d%s%s", VTY_NEWLINE, vpn->vni, VTY_NEWLINE, VTY_NEWLINE); + vty_out (vty, "\nVNI: %d\n\n", vpn->vni); show_vni_routes (wctx->bgp, vpn, 0, wctx->vty, wctx->vtep_ip); } @@ -338,7 +333,7 @@ show_vni_entry (struct hash_backet *backet, struct vty *vty) XFREE (MTYPE_ECOMMUNITY_STR, ecom_str); break; } - vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, "\n"); } #endif /* HAVE_CUMULUS */ @@ -1385,7 +1380,7 @@ evpn_show_route_vni_multicast (struct vty *vty, struct bgp *bgp, vpn = bgp_evpn_lookup_vni (bgp, vni); if (!vpn) { - vty_out (vty, "VNI not found%s", VTY_NEWLINE); + vty_out (vty, "VNI not found\n"); return; } @@ -1394,7 +1389,7 @@ evpn_show_route_vni_multicast (struct vty *vty, struct bgp *bgp, rn = bgp_node_lookup (vpn->route_table, (struct prefix *)&p); if (!rn || !rn->info) { - vty_out (vty, "%% Network not in table%s", VTY_NEWLINE); + vty_out (vty, "%% Network not in table\n"); return; } @@ -1408,8 +1403,8 @@ evpn_show_route_vni_multicast (struct vty *vty, struct bgp *bgp, path_cnt++; } - vty_out (vty, "%sDisplayed %u paths for requested prefix%s", - VTY_NEWLINE, path_cnt, VTY_NEWLINE); + vty_out (vty, "\nDisplayed %u paths for requested prefix\n", + path_cnt); } /* @@ -1436,7 +1431,7 @@ evpn_show_route_vni_macip (struct vty *vty, struct bgp *bgp, vpn = bgp_evpn_lookup_vni (bgp, vni); if (!vpn) { - vty_out (vty, "VNI not found%s", VTY_NEWLINE); + vty_out (vty, "VNI not found\n"); return; } @@ -1445,7 +1440,7 @@ evpn_show_route_vni_macip (struct vty *vty, struct bgp *bgp, rn = bgp_node_lookup (vpn->route_table, (struct prefix *)&p); if (!rn || !rn->info) { - vty_out (vty, "%% Network not in table%s", VTY_NEWLINE); + vty_out (vty, "%% Network not in table\n"); return; } @@ -1459,8 +1454,8 @@ evpn_show_route_vni_macip (struct vty *vty, struct bgp *bgp, path_cnt++; } - vty_out (vty, "%sDisplayed %u paths for requested prefix%s", - VTY_NEWLINE, path_cnt, VTY_NEWLINE); + vty_out (vty, "\nDisplayed %u paths for requested prefix\n", + path_cnt); } /* @@ -1478,7 +1473,7 @@ evpn_show_routes_vni (struct vty *vty, struct bgp *bgp, vpn = bgp_evpn_lookup_vni (bgp, vni); if (!vpn) { - vty_out (vty, "VNI not found%s", VTY_NEWLINE); + vty_out (vty, "VNI not found\n"); return; } @@ -1512,7 +1507,7 @@ evpn_show_route_rd_macip (struct vty *vty, struct bgp *bgp, (struct prefix *)&p, prd); if (!rn || !rn->info) { - vty_out (vty, "%% Network not in table%s", VTY_NEWLINE); + vty_out (vty, "%% Network not in table\n"); return; } @@ -1526,8 +1521,8 @@ evpn_show_route_rd_macip (struct vty *vty, struct bgp *bgp, path_cnt++; } - vty_out (vty, "%sDisplayed %u paths for requested prefix%s", - VTY_NEWLINE, path_cnt, VTY_NEWLINE); + vty_out (vty, "\nDisplayed %u paths for requested prefix\n", + path_cnt); } /* @@ -1573,9 +1568,9 @@ evpn_show_route_rd (struct vty *vty, struct bgp *bgp, if (rd_header) { vty_out (vty, "EVPN type-2 prefix: [2]:[ESI]:[EthTag]:[MAClen]:" - "[MAC]%s", VTY_NEWLINE); + "[MAC]\n"); vty_out (vty, "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:" - "[OrigIP]%s%s", VTY_NEWLINE, VTY_NEWLINE); + "[OrigIP]\n\n"); rd_header = 0; } @@ -1594,12 +1589,12 @@ evpn_show_route_rd (struct vty *vty, struct bgp *bgp, } if (prefix_cnt == 0) - vty_out (vty, "No prefixes exist with this RD%s%s", - type ? " (of requested type)" : "", VTY_NEWLINE); + vty_out (vty, "No prefixes exist with this RD%s\n", + type ? " (of requested type)" : ""); else - vty_out (vty, "%sDisplayed %u prefixes (%u paths) with this RD%s%s", - VTY_NEWLINE, prefix_cnt, path_cnt, - type ? " (of requested type)" : "", VTY_NEWLINE); + vty_out (vty, "\nDisplayed %u prefixes (%u paths) with this RD%s\n", + prefix_cnt, path_cnt, + type ? " (of requested type)" : ""); } /* @@ -1675,12 +1670,12 @@ evpn_show_all_routes (struct vty *vty, struct bgp *bgp, int type) } if (prefix_cnt == 0) - vty_out (vty, "No EVPN prefixes %sexist%s", - type ? "(of requested type) " : "", VTY_NEWLINE); + vty_out (vty, "No EVPN prefixes %sexist\n", + type ? "(of requested type) " : ""); else - vty_out (vty, "%sDisplayed %u prefixes (%u paths)%s%s", - VTY_NEWLINE, prefix_cnt, path_cnt, - type ? " (of requested type)" : "", VTY_NEWLINE); + vty_out (vty, "\nDisplayed %u prefixes (%u paths)%s\n", + prefix_cnt, path_cnt, + type ? " (of requested type)" : ""); } /* @@ -1694,7 +1689,7 @@ evpn_show_vni (struct vty *vty, struct bgp *bgp, vni_t vni) vpn = bgp_evpn_lookup_vni (bgp, vni); if (!vpn) { - vty_out (vty, "VNI not found%s", VTY_NEWLINE); + vty_out (vty, "VNI not found\n"); return; } @@ -1712,11 +1707,10 @@ evpn_show_all_vnis (struct vty *vty, struct bgp *bgp) num_vnis = hashcount(bgp->vnihash); if (!num_vnis) return; - vty_out(vty, "Number of VNIs: %u%s", - num_vnis, VTY_NEWLINE); - vty_out(vty, "Flags: * - Kernel %s", VTY_NEWLINE); - vty_out(vty, " %-10s %-15s %-21s %-25s %-25s%s", - "VNI", "Orig IP", "RD", "Import RT", "Export RT", VTY_NEWLINE); + vty_out(vty, "Number of VNIs: %u\n", num_vnis); + vty_out(vty, "Flags: * - Kernel \n"); + vty_out(vty, " %-10s %-15s %-21s %-25s %-25s\n", + "VNI", "Orig IP", "RD", "Import RT", "Export RT"); hash_iterate (bgp->vnihash, (void (*) (struct hash_backet *, void *)) show_vni_entry, vty); @@ -1758,18 +1752,17 @@ write_vni_config (struct vty *vty, struct bgpevpn *vpn, int *write) if (is_vni_configured (vpn)) { bgp_config_write_family_header (vty, afi, safi, write); - vty_out (vty, " vni %d%s", vpn->vni, VTY_NEWLINE); + vty_out (vty, " vni %d\n", vpn->vni); if (is_rd_configured (vpn)) - vty_out (vty, " rd %s%s", - prefix_rd2str (&vpn->prd, buf1, RD_ADDRSTRLEN), - VTY_NEWLINE); + vty_out (vty, " rd %s\n", + prefix_rd2str (&vpn->prd, buf1, RD_ADDRSTRLEN)); if (is_import_rt_configured (vpn)) { for (ALL_LIST_ELEMENTS (vpn->import_rtl, node, nnode, ecom)) { ecom_str = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - vty_out (vty, " route-target import %s%s", ecom_str, VTY_NEWLINE); + vty_out (vty, " route-target import %s\n", ecom_str); XFREE (MTYPE_ECOMMUNITY_STR, ecom_str); } } @@ -1779,12 +1772,12 @@ write_vni_config (struct vty *vty, struct bgpevpn *vpn, int *write) for (ALL_LIST_ELEMENTS (vpn->export_rtl, node, nnode, ecom)) { ecom_str = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - vty_out (vty, " route-target export %s%s", ecom_str, VTY_NEWLINE); + vty_out (vty, " route-target export %s\n", ecom_str); XFREE (MTYPE_ECOMMUNITY_STR, ecom_str); } } - vty_out (vty, " exit-vni%s", VTY_NEWLINE); + vty_out (vty, " exit-vni\n"); } } @@ -1838,8 +1831,8 @@ DEFUN (show_bgp_evpn_vni, if (!bgp) return CMD_WARNING; - vty_out (vty, "Advertise All VNI flag: %s%s", - bgp->advertise_all_vni? "Enabled" : "Disabled", VTY_NEWLINE); + vty_out (vty, "Advertise All VNI flag: %s\n", + bgp->advertise_all_vni? "Enabled" : "Disabled"); evpn_show_all_vnis (vty, bgp); return CMD_SUCCESS; @@ -1861,7 +1854,7 @@ DEFUN (show_bgp_evpn_vni_num, if (!bgp) return CMD_WARNING; - VTY_GET_INTEGER_RANGE ("VNI", vni, argv[4]->arg, 1, VNI_MAX); + vni = strtoul(argv[4]->arg, NULL, 10); evpn_show_vni (vty, bgp, vni); return CMD_SUCCESS; @@ -1939,7 +1932,7 @@ DEFUN (show_bgp_evpn_route_rd, ret = str2prefix_rd (argv[5]->arg, &prd); if (! ret) { - vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); + vty_out (vty, "%% Malformed Route Distinguisher\n"); return CMD_WARNING; } @@ -1984,12 +1977,12 @@ DEFUN (show_bgp_evpn_route_rd_macip, ret = str2prefix_rd (argv[5]->arg, &prd); if (! ret) { - vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); + vty_out (vty, "%% Malformed Route Distinguisher\n"); return CMD_WARNING; } if (!prefix_str2mac (argv[7]->arg, &mac)) { - vty_out (vty, "%% Malformed MAC address%s", VTY_NEWLINE); + vty_out (vty, "%% Malformed MAC address\n"); return CMD_WARNING; } memset (&ip, 0, sizeof (ip)); @@ -1997,7 +1990,7 @@ DEFUN (show_bgp_evpn_route_rd_macip, { if (str2ipaddr (argv[9]->arg, &ip) != 0) { - vty_out (vty, "%% Malformed IP address%s", VTY_NEWLINE); + vty_out (vty, "%% Malformed IP address\n"); return CMD_WARNING; } } @@ -2032,7 +2025,7 @@ DEFUN (show_bgp_evpn_route_vni, vtep_ip.s_addr = 0; - VTY_GET_INTEGER_RANGE ("VNI", vni, argv[5]->arg, 1, VNI_MAX); + vni = strtoul(argv[5]->arg, NULL, 10); if (argc == 8 && argv[6]->arg) { @@ -2049,7 +2042,7 @@ DEFUN (show_bgp_evpn_route_vni, { if (!inet_aton (argv[7]->arg, &vtep_ip)) { - vty_out (vty, "%% Malformed VTEP IP address%s", VTY_NEWLINE); + vty_out (vty, "%% Malformed VTEP IP address\n"); return CMD_WARNING; } } @@ -2084,10 +2077,10 @@ DEFUN (show_bgp_evpn_route_vni_macip, if (!bgp) return CMD_WARNING; - VTY_GET_INTEGER_RANGE ("VNI", vni, argv[5]->arg, 1, VNI_MAX); + vni = strtoul(argv[5]->arg, NULL, 10); if (!prefix_str2mac (argv[7]->arg, &mac)) { - vty_out (vty, "%% Malformed MAC address%s", VTY_NEWLINE); + vty_out (vty, "%% Malformed MAC address\n"); return CMD_WARNING; } memset (&ip, 0, sizeof (ip)); @@ -2095,7 +2088,7 @@ DEFUN (show_bgp_evpn_route_vni_macip, { if (str2ipaddr (argv[9]->arg, &ip) != 0) { - vty_out (vty, "%% Malformed IP address%s", VTY_NEWLINE); + vty_out (vty, "%% Malformed IP address\n"); return CMD_WARNING; } } @@ -2125,11 +2118,11 @@ DEFUN (show_bgp_evpn_route_vni_multicast, if (!bgp) return CMD_WARNING; - VTY_GET_INTEGER_RANGE ("VNI", vni, argv[5]->arg, 1, VNI_MAX); + vni = strtoul(argv[5]->arg, NULL, 10); ret = inet_aton (argv[7]->arg, &orig_ip); if (!ret) { - vty_out (vty, "%% Malformed Originating Router IP address%s", VTY_NEWLINE); + vty_out (vty, "%% Malformed Originating Router IP address\n"); return CMD_WARNING; } @@ -2161,7 +2154,7 @@ DEFUN (show_bgp_evpn_route_vni_all, { if (!inet_aton (argv[7]->arg, &vtep_ip)) { - vty_out (vty, "%% Malformed VTEP IP address%s", VTY_NEWLINE); + vty_out (vty, "%% Malformed VTEP IP address\n"); return CMD_WARNING; } } @@ -2201,13 +2194,13 @@ DEFUN_NOSH (bgp_evpn_vni, if (!bgp) return CMD_WARNING; - VTY_GET_INTEGER_RANGE ("VNI", vni, argv[1]->arg, 1, VNI_MAX); + vni = strtoul(argv[1]->arg, NULL, 10); /* Create VNI, or mark as configured. */ vpn = evpn_create_update_vni (bgp, vni); if (!vpn) { - vty_out (vty, "%% Failed to create VNI %s", VTY_NEWLINE); + vty_out (vty, "%% Failed to create VNI \n"); return CMD_WARNING; } @@ -2229,18 +2222,18 @@ DEFUN (no_bgp_evpn_vni, if (!bgp) return CMD_WARNING; - VTY_GET_INTEGER_RANGE ("VNI", vni, argv[2]->arg, 1, VNI_MAX); + vni = strtoul(argv[2]->arg, NULL, 10); /* Check if we should disallow. */ vpn = bgp_evpn_lookup_vni (bgp, vni); if (!vpn) { - vty_out (vty, "%% Specified VNI does not exist%s", VTY_NEWLINE); + vty_out (vty, "%% Specified VNI does not exist\n"); return CMD_WARNING; } if (!is_vni_configured (vpn)) { - vty_out (vty, "%% Specified VNI is not configured%s", VTY_NEWLINE); + vty_out (vty, "%% Specified VNI is not configured\n"); return CMD_WARNING; } @@ -2275,7 +2268,7 @@ DEFUN (bgp_evpn_vni_rd, ret = str2prefix_rd (argv[1]->arg, &prd); if (! ret) { - vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); + vty_out (vty, "%% Malformed Route Distinguisher\n"); return CMD_WARNING; } @@ -2306,20 +2299,20 @@ DEFUN (no_bgp_evpn_vni_rd, ret = str2prefix_rd (argv[2]->arg, &prd); if (! ret) { - vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); + vty_out (vty, "%% Malformed Route Distinguisher\n"); return CMD_WARNING; } /* Check if we should disallow. */ if (!is_rd_configured (vpn)) { - vty_out (vty, "%% RD is not configured for this VNI%s", VTY_NEWLINE); + vty_out (vty, "%% RD is not configured for this VNI\n"); return CMD_WARNING; } if (!bgp_evpn_rd_matches_existing(vpn, &prd)) { - vty_out (vty, "%% RD specified does not match configuration for this VNI%s", VTY_NEWLINE); + vty_out (vty, "%% RD specified does not match configuration for this VNI\n"); return CMD_WARNING; } @@ -2342,7 +2335,7 @@ DEFUN (no_bgp_evpn_vni_rd_without_val, /* Check if we should disallow. */ if (!is_rd_configured (vpn)) { - vty_out (vty, "%% RD is not configured for this VNI%s", VTY_NEWLINE); + vty_out (vty, "%% RD is not configured for this VNI\n"); return CMD_WARNING; } @@ -2396,7 +2389,7 @@ DEFUN (bgp_evpn_vni_rt, rt_type = RT_TYPE_BOTH; else { - vty_out (vty, "%% Invalid Route Target type%s", VTY_NEWLINE); + vty_out (vty, "%% Invalid Route Target type\n"); return CMD_WARNING; } @@ -2408,7 +2401,7 @@ DEFUN (bgp_evpn_vni_rt, ecommunity_str(ecomadd); if (!ecomadd) { - vty_out (vty, "%% Malformed Route Target list%s", VTY_NEWLINE); + vty_out (vty, "%% Malformed Route Target list\n"); return CMD_WARNING; } @@ -2425,7 +2418,7 @@ DEFUN (bgp_evpn_vni_rt, ecommunity_str(ecomadd); if (!ecomadd) { - vty_out (vty, "%% Malformed Route Target list%s", VTY_NEWLINE); + vty_out (vty, "%% Malformed Route Target list\n"); return CMD_WARNING; } @@ -2463,7 +2456,7 @@ DEFUN (no_bgp_evpn_vni_rt, rt_type = RT_TYPE_BOTH; else { - vty_out (vty, "%% Invalid Route Target type%s", VTY_NEWLINE); + vty_out (vty, "%% Invalid Route Target type\n"); return CMD_WARNING; } @@ -2473,7 +2466,7 @@ DEFUN (no_bgp_evpn_vni_rt, { if (!is_import_rt_configured (vpn)) { - vty_out (vty, "%% Import RT is not configured for this VNI%s", VTY_NEWLINE); + vty_out (vty, "%% Import RT is not configured for this VNI\n"); return CMD_WARNING; } } @@ -2481,7 +2474,7 @@ DEFUN (no_bgp_evpn_vni_rt, { if (!is_export_rt_configured (vpn)) { - vty_out (vty, "%% Export RT is not configured for this VNI%s", VTY_NEWLINE); + vty_out (vty, "%% Export RT is not configured for this VNI\n"); return CMD_WARNING; } } @@ -2489,7 +2482,7 @@ DEFUN (no_bgp_evpn_vni_rt, { if (!is_import_rt_configured (vpn) && !is_export_rt_configured (vpn)) { - vty_out (vty, "%% Import/Export RT is not configured for this VNI%s", VTY_NEWLINE); + vty_out (vty, "%% Import/Export RT is not configured for this VNI\n"); return CMD_WARNING; } } @@ -2498,7 +2491,7 @@ DEFUN (no_bgp_evpn_vni_rt, ecommunity_str(ecomdel); if (!ecomdel) { - vty_out (vty, "%% Malformed Route Target list%s", VTY_NEWLINE); + vty_out (vty, "%% Malformed Route Target list\n"); return CMD_WARNING; } @@ -2506,7 +2499,7 @@ DEFUN (no_bgp_evpn_vni_rt, { if (!bgp_evpn_rt_matches_existing (vpn->import_rtl, ecomdel)) { - vty_out (vty, "%% RT specified does not match configuration for this VNI%s", VTY_NEWLINE); + vty_out (vty, "%% RT specified does not match configuration for this VNI\n"); return CMD_WARNING; } evpn_unconfigure_import_rt (bgp, vpn, ecomdel); @@ -2515,7 +2508,7 @@ DEFUN (no_bgp_evpn_vni_rt, { if (!bgp_evpn_rt_matches_existing (vpn->export_rtl, ecomdel)) { - vty_out (vty, "%% RT specified does not match configuration for this VNI%s", VTY_NEWLINE); + vty_out (vty, "%% RT specified does not match configuration for this VNI\n"); return CMD_WARNING; } evpn_unconfigure_export_rt (bgp, vpn, ecomdel); @@ -2538,7 +2531,7 @@ DEFUN (no_bgp_evpn_vni_rt, if (! found_ecomdel) { - vty_out (vty, "%% RT specified does not match configuration for this VNI%s", VTY_NEWLINE); + vty_out (vty, "%% RT specified does not match configuration for this VNI\n"); return CMD_WARNING; } } @@ -2571,7 +2564,7 @@ DEFUN (no_bgp_evpn_vni_rt_without_val, } else { - vty_out (vty, "%% Invalid Route Target type%s", VTY_NEWLINE); + vty_out (vty, "%% Invalid Route Target type\n"); return CMD_WARNING; } @@ -2580,7 +2573,7 @@ DEFUN (no_bgp_evpn_vni_rt_without_val, { if (!is_import_rt_configured (vpn)) { - vty_out (vty, "%% Import RT is not configured for this VNI%s", VTY_NEWLINE); + vty_out (vty, "%% Import RT is not configured for this VNI\n"); return CMD_WARNING; } } @@ -2588,7 +2581,7 @@ DEFUN (no_bgp_evpn_vni_rt_without_val, { if (!is_export_rt_configured (vpn)) { - vty_out (vty, "%% Export RT is not configured for this VNI%s", VTY_NEWLINE); + vty_out (vty, "%% Export RT is not configured for this VNI\n"); return CMD_WARNING; } } @@ -2623,7 +2616,7 @@ bgp_config_write_evpn_info (struct vty *vty, struct bgp *bgp, afi_t afi, if (bgp->advertise_all_vni) { bgp_config_write_family_header (vty, afi, safi, write); - vty_out (vty, " advertise-all-vni%s", VTY_NEWLINE); + vty_out (vty, " advertise-all-vni\n"); } } diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 00708c4871..88b63107e9 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -7254,7 +7254,7 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, bgp_evpn_label2str (&binfo->extra->label, tag_buf, sizeof (tag_buf)); vty_out (vty, " VNI %s", tag_buf); } - vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, "\n"); if (binfo->extra && binfo->extra->parent) { struct bgp_info *parent_ri; @@ -7265,10 +7265,10 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, if (rn && rn->prn) { prn = rn->prn; - vty_out (vty, " Imported from %s:%s%s", + vty_out (vty, " Imported from %s:%s\n", prefix_rd2str ((struct prefix_rd *)&prn->p, buf1, RD_ADDRSTRLEN), - buf2, VTY_NEWLINE); + buf2); } } } @@ -7761,7 +7761,7 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, /* Line 6 display Large community */ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES)) - vty_outln (vty, " Large Community: %s\n", + vty_out (vty, " Large Community: %s\n", attr->lcommunity->str); /* Line 7 display Originator, Cluster-id */ @@ -8329,19 +8329,18 @@ route_vty_out_detail_header (struct vty *vty, struct bgp *bgp, { #if defined (HAVE_CUMULUS) if (safi == SAFI_EVPN) - vty_out (vty, "BGP routing table entry for %s%s%s%s", + vty_out (vty, "BGP routing table entry for %s%s%s\n", prd ? prefix_rd2str (prd, buf1, RD_ADDRSTRLEN) : "", prd ? ":" : "", bgp_evpn_route2str ((struct prefix_evpn *)p, - buf3, sizeof (buf3)), - VTY_NEWLINE); + buf3, sizeof (buf3))); else - vty_out (vty, "BGP routing table entry for %s%s%s/%d%s", + vty_out (vty, "BGP routing table entry for %s%s%s/%d\n", ((safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP) ? prefix_rd2str (prd, buf1, RD_ADDRSTRLEN) : ""), safi == SAFI_MPLS_VPN ? ":" : "", inet_ntop (p->family, &p->u.prefix, buf2, INET6_ADDRSTRLEN), - p->prefixlen, VTY_NEWLINE); + p->prefixlen); #else if (p->family == AF_ETHERNET) prefix2str (p, buf2, INET6_ADDRSTRLEN); diff --git a/zebra/debug.c b/zebra/debug.c index 763bf359ba..1c3cf9a3da 100644 --- a/zebra/debug.c +++ b/zebra/debug.c @@ -439,7 +439,7 @@ config_write_debug (struct vty *vty) } if (IS_ZEBRA_DEBUG_VXLAN) { - vty_out (vty, "debug zebra vxlan%s", VTY_NEWLINE); + vty_out (vty, "debug zebra vxlan\n"); write++; } return write; diff --git a/zebra/interface.c b/zebra/interface.c index fb856165b3..9618e9bb17 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -1209,23 +1209,23 @@ if_dump_vty (struct vty *vty, struct interface *ifp) connected_dump_vty (vty, connected); } - vty_out(vty, " Interface Type %s%s", - zebra_ziftype_2str (zebra_if->zif_type), VTY_NEWLINE); + vty_out(vty, " Interface Type %s\n", + zebra_ziftype_2str (zebra_if->zif_type)); if (IS_ZEBRA_IF_BRIDGE (ifp)) { struct zebra_l2info_bridge *bridge_info; bridge_info = &zebra_if->l2info.br; - vty_out(vty, " Bridge VLAN-aware: %s%s", - bridge_info->vlan_aware ? "yes" : "no", VTY_NEWLINE); + vty_out(vty, " Bridge VLAN-aware: %s\n", + bridge_info->vlan_aware ? "yes" : "no"); } else if (IS_ZEBRA_IF_VLAN(ifp)) { struct zebra_l2info_vlan *vlan_info; vlan_info = &zebra_if->l2info.vl; - vty_out(vty, " VLAN Id %u%s", - vlan_info->vid, VTY_NEWLINE); + vty_out(vty, " VLAN Id %u\n", + vlan_info->vid); } else if (IS_ZEBRA_IF_VXLAN (ifp)) { @@ -1237,7 +1237,7 @@ if_dump_vty (struct vty *vty, struct interface *ifp) vty_out(vty, " VTEP IP: %s", inet_ntoa (vxlan_info->vtep_ip)); if (vxlan_info->access_vlan) vty_out(vty, " Access VLAN Id %u", vxlan_info->access_vlan); - vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, "\n"); } if (IS_ZEBRA_IF_BRIDGE_SLAVE (ifp)) @@ -1246,13 +1246,13 @@ if_dump_vty (struct vty *vty, struct interface *ifp) br_slave = &zebra_if->brslave_info; if (br_slave->bridge_ifindex != IFINDEX_INTERNAL) - vty_out(vty, " Master (bridge) ifindex %u%s", - br_slave->bridge_ifindex, VTY_NEWLINE); + vty_out(vty, " Master (bridge) ifindex %u\n", + br_slave->bridge_ifindex); } if (zebra_if->link_ifindex != IFINDEX_INTERNAL) - vty_out(vty, " Link ifindex %u%s", - zebra_if->link_ifindex, VTY_NEWLINE); + vty_out(vty, " Link ifindex %u\n", + zebra_if->link_ifindex); if (HAS_LINK_PARAMS(ifp)) { diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 52d404aeed..b1da38a7b6 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -3074,7 +3074,7 @@ DEFUN (show_evpn_vni_vni, struct zebra_vrf *zvrf; vni_t vni; - VTY_GET_INTEGER_RANGE ("VNI", vni, argv[3]->arg, 1, VNI_MAX); + vni = strtoul(argv[3]->arg, NULL, 10); zvrf = vrf_info_lookup(VRF_DEFAULT); zebra_vxlan_print_vni(vty, zvrf, vni); return CMD_SUCCESS; @@ -3092,7 +3092,7 @@ DEFUN (show_evpn_mac_vni, struct zebra_vrf *zvrf; vni_t vni; - VTY_GET_INTEGER_RANGE ("VNI", vni, argv[4]->arg, 1, VNI_MAX); + vni = strtoul(argv[4]->arg, NULL, 10); zvrf = vrf_info_lookup(VRF_DEFAULT); zebra_vxlan_print_macs_vni(vty, zvrf, vni); return CMD_SUCCESS; @@ -3130,7 +3130,7 @@ DEFUN (show_evpn_mac_vni_all_vtep, if (!inet_aton (argv[6]->arg, &vtep_ip)) { - vty_out (vty, "%% Malformed VTEP IP address%s", VTY_NEWLINE); + vty_out (vty, "%% Malformed VTEP IP address\n"); return CMD_WARNING; } zvrf = vrf_info_lookup(VRF_DEFAULT); @@ -3155,10 +3155,10 @@ DEFUN (show_evpn_mac_vni_mac, vni_t vni; struct ethaddr mac; - VTY_GET_INTEGER_RANGE ("VNI", vni, argv[4]->arg, 1, VNI_MAX); + vni = strtoul(argv[4]->arg, NULL, 10); if (!prefix_str2mac (argv[6]->arg, &mac)) { - vty_out (vty, "%% Malformed MAC address%s", VTY_NEWLINE); + vty_out (vty, "%% Malformed MAC address"); return CMD_WARNING; } zvrf = vrf_info_lookup(VRF_DEFAULT); @@ -3181,10 +3181,10 @@ DEFUN (show_evpn_mac_vni_vtep, vni_t vni; struct in_addr vtep_ip; - VTY_GET_INTEGER_RANGE ("VNI", vni, argv[4]->arg, 1, VNI_MAX); + vni = strtoul(argv[4]->arg, NULL, 10); if (!inet_aton (argv[6]->arg, &vtep_ip)) { - vty_out (vty, "%% Malformed VTEP IP address%s", VTY_NEWLINE); + vty_out (vty, "%% Malformed VTEP IP address\n"); return CMD_WARNING; } @@ -3205,7 +3205,7 @@ DEFUN (show_evpn_neigh_vni, struct zebra_vrf *zvrf; vni_t vni; - VTY_GET_INTEGER_RANGE ("VNI", vni, argv[4]->arg, 1, VNI_MAX); + vni = strtoul(argv[4]->arg, NULL, 10); zvrf = vrf_info_lookup(VRF_DEFAULT); zebra_vxlan_print_neigh_vni(vty, zvrf, vni); return CMD_SUCCESS; @@ -3242,10 +3242,10 @@ DEFUN (show_evpn_neigh_vni_neigh, vni_t vni; struct ipaddr ip; - VTY_GET_INTEGER_RANGE ("VNI", vni, argv[4]->arg, 1, VNI_MAX); + vni = strtoul(argv[4]->arg, NULL, 10); if (str2ipaddr (argv[6]->arg, &ip) != 0) { - vty_out (vty, "%% Malformed Neighbor address%s", VTY_NEWLINE); + vty_out (vty, "%% Malformed Neighbor address\n"); return CMD_WARNING; } zvrf = vrf_info_lookup(VRF_DEFAULT); @@ -3268,10 +3268,10 @@ DEFUN (show_evpn_neigh_vni_vtep, vni_t vni; struct in_addr vtep_ip; - VTY_GET_INTEGER_RANGE ("VNI", vni, argv[4]->arg, 1, VNI_MAX); + vni = strtoul(argv[4]->arg, NULL, 10); if (!inet_aton (argv[6]->arg, &vtep_ip)) { - vty_out (vty, "%% Malformed VTEP IP address%s", VTY_NEWLINE); + vty_out (vty, "%% Malformed VTEP IP address\n"); return CMD_WARNING; } diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 5eca8dc5d9..7df31cb93d 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -220,12 +220,12 @@ zvni_print_neigh (zebra_neigh_t *n, void *ctxt) ipaddr2str (&n->ip, buf2, sizeof(buf2)), vty = (struct vty *) ctxt; - vty_out(vty, "IP: %s%s", - ipaddr2str (&n->ip, buf2, sizeof(buf2)), VTY_NEWLINE); + vty_out(vty, "IP: %s\n", + ipaddr2str (&n->ip, buf2, sizeof(buf2))); vty_out(vty, " MAC: %s", prefix_mac2str (&n->emac, buf1, sizeof (buf1))); if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) vty_out(vty, " Remote VTEP: %s", inet_ntoa (n->r_vtep_ip)); - vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, "\n"); } /* @@ -250,8 +250,8 @@ zvni_print_neigh_hash (struct hash_backet *backet, void *ctxt) if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL) && !(wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP)) { - vty_out(vty, "%*s %-6s %-17s %s", - -wctx->addr_width, buf2, "local", buf1, VTY_NEWLINE); + vty_out(vty, "%*s %-6s %-17s\n", + -wctx->addr_width, buf2, "local", buf1); wctx->count++; } else @@ -261,20 +261,20 @@ zvni_print_neigh_hash (struct hash_backet *backet, void *ctxt) if (IPV4_ADDR_SAME(&n->r_vtep_ip, &wctx->r_vtep_ip)) { if (wctx->count == 0) - vty_out(vty, "%*s %-6s %-17s %-21s%s", + vty_out(vty, "%*s %-6s %-17s %-21s\n", -wctx->addr_width, "Neighbor", "Type", "MAC", - "Remote VTEP", VTY_NEWLINE); - vty_out(vty, "%*s %-6s %-17s %-21s%s", + "Remote VTEP"); + vty_out(vty, "%*s %-6s %-17s %-21s\n", -wctx->addr_width, buf2, "remote", buf1, - inet_ntoa (n->r_vtep_ip), VTY_NEWLINE); + inet_ntoa (n->r_vtep_ip)); wctx->count++; } } else { - vty_out(vty, "%*s %-6s %-17s %-21s%s", + vty_out(vty, "%*s %-6s %-17s %-21s\n", -wctx->addr_width, buf2, "remote", buf1, - inet_ntoa (n->r_vtep_ip), VTY_NEWLINE); + inet_ntoa (n->r_vtep_ip)); wctx->count++; } } @@ -297,8 +297,8 @@ zvni_print_neigh_hash_all_vni (struct hash_backet *backet, void *ctxt) return; num_neigh = hashcount(zvni->neigh_table); - vty_out(vty, "%sVNI %u #ARP (IPv4 and IPv6, local and remote) %u%s%s", - VTY_NEWLINE, zvni->vni, num_neigh, VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "\nVNI %u #ARP (IPv4 and IPv6, local and remote) %u\n\n", + zvni->vni, num_neigh); if (!num_neigh) return; @@ -312,9 +312,9 @@ zvni_print_neigh_hash_all_vni (struct hash_backet *backet, void *ctxt) wctx.addr_width = 15; hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx); - vty_out(vty, "%*s %-6s %-17s %-21s%s", + vty_out(vty, "%*s %-6s %-17s %-21s\n", -wctx.addr_width, "IP", "Type", "MAC", - "Remote VTEP", VTY_NEWLINE); + "Remote VTEP"); hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx); } @@ -328,8 +328,8 @@ zvni_print_mac (zebra_mac_t *mac, void *ctxt) char buf1[20]; vty = (struct vty *) ctxt; - vty_out(vty, "MAC: %s%s", - prefix_mac2str (&mac->macaddr, buf1, sizeof (buf1)), VTY_NEWLINE); + vty_out(vty, "MAC: %s", + prefix_mac2str (&mac->macaddr, buf1, sizeof (buf1))); if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { struct zebra_ns *zns; @@ -351,7 +351,7 @@ zvni_print_mac (zebra_mac_t *mac, void *ctxt) inet_ntoa (mac->fwd_info.r_vtep_ip)); } vty_out(vty, " ARP ref: %u", mac->neigh_refcnt); - vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, "\n"); } /* @@ -389,7 +389,7 @@ zvni_print_mac_hash (struct hash_backet *backet, void *ctxt) buf1, "local", ifp->name); if (vid) vty_out(vty, " %-5u", vid); - vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, "\n"); wctx->count++; } else @@ -401,24 +401,23 @@ zvni_print_mac_hash (struct hash_backet *backet, void *ctxt) { if (wctx->count == 0) { - vty_out(vty, "%sVNI %u%s%s", - VTY_NEWLINE, wctx->zvni->vni,VTY_NEWLINE, VTY_NEWLINE); - vty_out(vty, "%-17s %-6s %-21s %-5s%s", + vty_out(vty, "\nVNI %u", + wctx->zvni->vni); + vty_out(vty, "%-17s %-6s %-21s %-5s", "MAC", "Type", "Intf/Remote VTEP", - "VLAN", VTY_NEWLINE); + "VLAN"); } - vty_out(vty, "%-17s %-6s %-21s%s", + vty_out(vty, "%-17s %-6s %-21s", buf1, "remote", - inet_ntoa (mac->fwd_info.r_vtep_ip), - VTY_NEWLINE); + inet_ntoa (mac->fwd_info.r_vtep_ip)); wctx->count++; } } else { - vty_out(vty, "%-17s %-6s %-21s%s", + vty_out(vty, "%-17s %-6s %-21s", buf1, "remote", - inet_ntoa (mac->fwd_info.r_vtep_ip), VTY_NEWLINE); + inet_ntoa (mac->fwd_info.r_vtep_ip)); wctx->count++; } } @@ -451,10 +450,10 @@ zvni_print_mac_hash_all_vni (struct hash_backet *backet, void *ctxt) return; if (!CHECK_FLAG(wctx->flags, SHOW_REMOTE_MAC_FROM_VTEP)) { - vty_out(vty, "%sVNI %u #MACs (local and remote) %u%s%s", - VTY_NEWLINE, zvni->vni, num_macs, VTY_NEWLINE, VTY_NEWLINE); - vty_out(vty, "%-17s %-6s %-21s %-5s%s", - "MAC", "Type", "Intf/Remote VTEP", "VLAN", VTY_NEWLINE); + vty_out(vty, "\nVNI %u #MACs (local and remote) %u\n\n", + zvni->vni, num_macs); + vty_out(vty, "%-17s %-6s %-21s %-5s\n", + "MAC", "Type", "Intf/Remote VTEP", "VLAN"); } hash_iterate(zvni->mac_table, zvni_print_mac_hash, wctx); @@ -473,33 +472,33 @@ zvni_print (zebra_vni_t *zvni, void *ctxt) vty = (struct vty *) ctxt; - vty_out(vty, "VNI: %u%s", zvni->vni, VTY_NEWLINE); + vty_out(vty, "VNI: %u\n", zvni->vni); if (!zvni->vxlan_if) { // unexpected - vty_out(vty, " VxLAN interface: unknown%s", VTY_NEWLINE); + vty_out(vty, " VxLAN interface: unknown\n"); return; } - vty_out(vty, " VxLAN interface: %s ifIndex: %u VTEP IP: %s%s", + vty_out(vty, " VxLAN interface: %s ifIndex: %u VTEP IP: %s\n", zvni->vxlan_if->name, zvni->vxlan_if->ifindex, - inet_ntoa(zvni->local_vtep_ip), VTY_NEWLINE); + inet_ntoa(zvni->local_vtep_ip)); if (!zvni->vteps) { - vty_out(vty, " No remote VTEPs known for this VNI%s", VTY_NEWLINE); + vty_out(vty, " No remote VTEPs known for this VNI\n"); } else { - vty_out(vty, " Remote VTEPs for this VNI:%s", VTY_NEWLINE); + vty_out(vty, " Remote VTEPs for this VNI:\n"); for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) - vty_out(vty, " %s%s", - inet_ntoa (zvtep->vtep_ip), VTY_NEWLINE); + vty_out(vty, " %s\n", + inet_ntoa (zvtep->vtep_ip)); } num_macs = hashcount(zvni->mac_table); - vty_out(vty, " Number of MACs (local and remote) known for this VNI: %u%s", - num_macs, VTY_NEWLINE); + vty_out(vty, " Number of MACs (local and remote) known for this VNI: %u\n", + num_macs); num_neigh = hashcount(zvni->neigh_table); vty_out(vty, " Number of ARPs (IPv4 and IPv6, local and remote) " - "known for this VNI: %u%s", num_neigh, VTY_NEWLINE); + "known for this VNI: %u", num_neigh); } /* @@ -529,11 +528,11 @@ zvni_print_hash (struct hash_backet *backet, void *ctxt) num_macs = hashcount(zvni->mac_table); num_neigh = hashcount(zvni->neigh_table); - vty_out(vty, "%-10u %-21s %-15s %-8u %-8u %-15u%s", + vty_out(vty, "%-10u %-21s %-15s %-8u %-8u %-15u\n", zvni->vni, zvni->vxlan_if ? zvni->vxlan_if->name : "unknown", inet_ntoa(zvni->local_vtep_ip), - num_macs, num_neigh, num_vteps, VTY_NEWLINE); + num_macs, num_neigh, num_vteps); } /* @@ -1779,7 +1778,7 @@ zebra_vxlan_print_neigh_vni (struct vty *vty, struct zebra_vrf *zvrf, vni_t vni) zvni = zvni_lookup (zvrf, vni); if (!zvni) { - vty_out (vty, "%% VNI %u does not exist%s", vni, VTY_NEWLINE); + vty_out (vty, "%% VNI %u does not exist\n", vni); return; } num_neigh = hashcount(zvni->neigh_table); @@ -1796,11 +1795,11 @@ zebra_vxlan_print_neigh_vni (struct vty *vty, struct zebra_vrf *zvrf, vni_t vni) wctx.addr_width = 15; hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx); - vty_out(vty, "Number of ARPs (local and remote) known for this VNI: %u%s", - num_neigh, VTY_NEWLINE); - vty_out(vty, "%*s %-6s %-17s %-21s%s", + vty_out(vty, "Number of ARPs (local and remote) known for this VNI: %u\n", + num_neigh); + vty_out(vty, "%*s %-6s %-17s %-21s\n", -wctx.addr_width, "IP", "Type", "MAC", - "Remote VTEP", VTY_NEWLINE); + "Remote VTEP"); hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx); } @@ -1831,14 +1830,14 @@ zebra_vxlan_print_specific_neigh_vni (struct vty *vty, struct zebra_vrf *zvrf, zvni = zvni_lookup (zvrf, vni); if (!zvni) { - vty_out (vty, "%% VNI %u does not exist%s", vni, VTY_NEWLINE); + vty_out (vty, "%% VNI %u does not exist", vni); return; } n = zvni_neigh_lookup (zvni, ip); if (!n) { - vty_out (vty, "%% Requested neighbor does not exist in VNI %u%s", - vni, VTY_NEWLINE); + vty_out (vty, "%% Requested neighbor does not exist in VNI %u\n", + vni); return; } @@ -1862,7 +1861,7 @@ zebra_vxlan_print_neigh_vni_vtep (struct vty *vty, struct zebra_vrf *zvrf, zvni = zvni_lookup (zvrf, vni); if (!zvni) { - vty_out (vty, "%% VNI %u does not exist%s", vni, VTY_NEWLINE); + vty_out (vty, "%% VNI %u does not exist\n", vni); return; } num_neigh = hashcount(zvni->neigh_table); @@ -1893,7 +1892,7 @@ zebra_vxlan_print_macs_vni (struct vty *vty, struct zebra_vrf *zvrf, vni_t vni) zvni = zvni_lookup (zvrf, vni); if (!zvni) { - vty_out (vty, "%% VNI %u does not exist%s", vni, VTY_NEWLINE); + vty_out (vty, "%% VNI %u does not exist\n", vni); return; } num_macs = hashcount(zvni->mac_table); @@ -1904,10 +1903,10 @@ zebra_vxlan_print_macs_vni (struct vty *vty, struct zebra_vrf *zvrf, vni_t vni) wctx.zvni = zvni; wctx.vty = vty; - vty_out(vty, "Number of MACs (local and remote) known for this VNI: %u%s", - num_macs, VTY_NEWLINE); - vty_out(vty, "%-17s %-6s %-21s %-5s%s", - "MAC", "Type", "Intf/Remote VTEP", "VLAN", VTY_NEWLINE); + vty_out(vty, "Number of MACs (local and remote) known for this VNI: %u\n", + num_macs); + vty_out(vty, "%-17s %-6s %-21s %-5s\n", + "MAC", "Type", "Intf/Remote VTEP", "VLAN"); hash_iterate(zvni->mac_table, zvni_print_mac_hash, &wctx); } @@ -1960,14 +1959,14 @@ zebra_vxlan_print_specific_mac_vni (struct vty *vty, struct zebra_vrf *zvrf, zvni = zvni_lookup (zvrf, vni); if (!zvni) { - vty_out (vty, "%% VNI %u does not exist%s", vni, VTY_NEWLINE); + vty_out (vty, "%% VNI %u does not exist\n", vni); return; } mac = zvni_mac_lookup (zvni, macaddr); if (!mac) { - vty_out (vty, "%% Requested MAC does not exist in VNI %u%s", - vni, VTY_NEWLINE); + vty_out (vty, "%% Requested MAC does not exist in VNI %u\n", + vni); return; } @@ -1990,7 +1989,7 @@ zebra_vxlan_print_macs_vni_vtep (struct vty *vty, struct zebra_vrf *zvrf, zvni = zvni_lookup (zvrf, vni); if (!zvni) { - vty_out (vty, "%% VNI %u does not exist%s", vni, VTY_NEWLINE); + vty_out (vty, "%% VNI %u does not exist\n", vni); return; } num_macs = hashcount(zvni->mac_table); @@ -2018,7 +2017,7 @@ zebra_vxlan_print_vni (struct vty *vty, struct zebra_vrf *zvrf, vni_t vni) zvni = zvni_lookup (zvrf, vni); if (!zvni) { - vty_out (vty, "%% VNI %u does not exist%s", vni, VTY_NEWLINE); + vty_out (vty, "%% VNI %u does not exist\n", vni); return; } zvni_print (zvni, (void *)vty); @@ -2037,10 +2036,10 @@ zebra_vxlan_print_vnis (struct vty *vty, struct zebra_vrf *zvrf) num_vnis = hashcount(zvrf->vni_table); if (!num_vnis) return; - vty_out(vty, "Number of VNIs: %u%s", num_vnis, VTY_NEWLINE); - vty_out(vty, "%-10s %-21s %-15s %-8s %-8s %-15s%s", + vty_out(vty, "Number of VNIs: %u\n", num_vnis); + vty_out(vty, "%-10s %-21s %-15s %-8s %-8s %-15s\n", "VNI", "VxLAN IF", "VTEP IP", "# MACs", "# ARPs", - "# Remote VTEPs", VTY_NEWLINE); + "# Remote VTEPs"); hash_iterate(zvrf->vni_table, zvni_print_hash, vty); } -- cgit v1.2.3