From 2232a77c2bb35586fbdc8156e9c0781fc7020066 Mon Sep 17 00:00:00 2001 From: vivek Date: Sun, 14 May 2017 22:44:13 -0700 Subject: [PATCH] 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(-) 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); -- 2.39.5