#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. */
#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;
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.
*/
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
/* 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.
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);
}
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.
/* 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);
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;
}
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);
/* 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);
}
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? */
(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;
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;
}
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. */
/* 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;
}
{
/* 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
{