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 zebra_neigh_t *zvni_neigh_add(zebra_vni_t *zvni, struct ipaddr *ip,
+ struct ethaddr *mac);
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,
json_object_string_add(json, "mac", buf1);
}
if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) {
- if (json == NULL)
+ if (json == NULL) {
vty_out(vty, " Remote VTEP: %s",
inet_ntoa(n->r_vtep_ip));
- else
+ vty_out(vty, " State: %s", IS_ZEBRA_NEIGH_ACTIVE(n)
+ ? "Active"
+ : "Inactive");
+ } else
json_object_string_add(json, "remoteVtep",
inet_ntoa(n->r_vtep_ip));
}
static void zvni_print_mac(zebra_mac_t *mac, void *ctxt)
{
struct vty *vty;
+ zebra_neigh_t *n = NULL;
+ struct listnode *node = NULL;
char buf1[20];
+ char buf2[INET6_ADDRSTRLEN];
vty = (struct vty *)ctxt;
vty_out(vty, "MAC: %s",
vty_out(vty, " Intf: %s(%u)", ifp->name, ifindex);
if (mac->fwd_info.local.vid)
vty_out(vty, " VLAN: %u", mac->fwd_info.local.vid);
- } else {
+ } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
vty_out(vty, " Remote VTEP: %s",
inet_ntoa(mac->fwd_info.r_vtep_ip));
+ } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) {
+ vty_out(vty, " Auto Mac ");
+ }
+ vty_out(vty, " ARP ref: %u\n", mac->neigh_refcnt);
+
+ /* print all the associated neigh */
+ vty_out(vty, " Neighbors:\n");
+ if (!listcount(mac->neigh_list))
+ vty_out(vty, " No Neighbors\n");
+ else {
+ for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, n)) {
+ vty_out(vty, " %s %s\n",
+ ipaddr2str(&n->ip, buf2, sizeof(buf2)),
+ CHECK_FLAG(n->flags, ZEBRA_MAC_LOCAL)
+ ? (IS_ZEBRA_NEIGH_ACTIVE(n)
+ ? "Active"
+ : "Inactive")
+ : "");
+ }
}
- vty_out(vty, " ARP ref: %u", mac->neigh_refcnt);
+
vty_out(vty, "\n");
}
else
json_object_object_add(json_mac_hdr, buf1, json_mac);
wctx->count++;
- } else {
+ } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
if (wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) {
if (IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip,
&wctx->r_vtep_ip)) {
/*
* Add neighbor entry.
*/
-static zebra_neigh_t *zvni_neigh_add(zebra_vni_t *zvni, struct ipaddr *ip)
+static zebra_neigh_t *zvni_neigh_add(zebra_vni_t *zvni, struct ipaddr *ip,
+ struct ethaddr *mac)
{
zebra_neigh_t tmp_n;
zebra_neigh_t *n = NULL;
+ zebra_mac_t *zmac = NULL;
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);
+ memcpy(&n->emac, mac, ETH_ALEN);
+ n->state = ZEBRA_NEIGH_INACTIVE;
+
+ /* Associate the neigh to mac */
+ zmac = zvni_mac_lookup(zvni, mac);
+ if (zmac)
+ listnode_add_sort(zmac->neigh_list, n);
+
return n;
}
static int zvni_neigh_del(zebra_vni_t *zvni, zebra_neigh_t *n)
{
zebra_neigh_t *tmp_n;
+ zebra_mac_t *zmac = NULL;
+
+ zmac = zvni_mac_lookup(zvni, &n->emac);
+ if (zmac)
+ listnode_delete(zmac->neigh_list, n);
/* Free the VNI hash entry and allocated memory. */
tmp_n = hash_release(zvni->neigh_table, n);
return n;
}
+/* Process all neigh associated to a mac upon local mac add event */
+static void zvni_process_neigh_on_local_mac_add(struct zebra_vrf *zvrf,
+ zebra_vni_t *zvni,
+ zebra_mac_t *zmac)
+{
+ zebra_neigh_t *n = NULL;
+ struct listnode *node = NULL;
+ char buf[ETHER_ADDR_STRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+
+ for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) {
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
+ /* MAC is learnt locally, program all inactive neigh
+ * pointing to this mac */
+ if (IS_ZEBRA_NEIGH_INACTIVE(n)) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "%u: neigh %s (MAC %s) on VNI %u is now ACTIVE",
+ zvrf_id(zvrf),
+ ipaddr2str(&n->ip, buf2,
+ sizeof(buf2)),
+ prefix_mac2str(&n->emac, buf,
+ sizeof(buf)),
+ zvni->vni);
+
+ ZEBRA_NEIGH_SET_ACTIVE(n);
+ zvni_neigh_send_add_to_client(
+ zvrf, zvni->vni, &n->ip, &n->emac, 0);
+ } else {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "%u: neigh %s (MAC %s) on VNI %u should NOT be ACTIVE",
+ zvrf_id(zvrf),
+ ipaddr2str(&n->ip, buf2,
+ sizeof(buf2)),
+ prefix_mac2str(&n->emac, buf,
+ sizeof(buf)),
+ zvni->vni);
+ }
+ } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) {
+ /* TODO: assume the neigh has moved too ?? */
+ }
+ }
+}
+
+/* Process all neigh associated to a mac upon local mac del event */
+static void zvni_process_neigh_on_local_mac_del(struct zebra_vrf *zvrf,
+ zebra_vni_t *zvni,
+ zebra_mac_t *zmac)
+{
+ zebra_neigh_t *n = NULL;
+ struct listnode *node = NULL;
+ char buf[ETHER_ADDR_STRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+
+ for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) {
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
+ if (IS_ZEBRA_NEIGH_ACTIVE(n)) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "%u: neigh %s (MAC %s) on VNI %u is now INACTIVE",
+ zvrf_id(zvrf),
+ ipaddr2str(&n->ip, buf2,
+ sizeof(buf2)),
+ prefix_mac2str(&n->emac, buf,
+ sizeof(buf)),
+ zvni->vni);
+
+ ZEBRA_NEIGH_SET_INACTIVE(n);
+ zvni_neigh_send_del_to_client(
+ zvrf, zvni->vni, &n->ip, &n->emac, 0);
+ }
+ } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_err(
+ "%u: local MAC %s getting deleted on VNI %u has remote neigh %s",
+ zvrf_id(zvrf),
+ prefix_mac2str(&n->emac, buf,
+ sizeof(buf)),
+ zvni->vni,
+ ipaddr2str(&n->ip, buf2, sizeof(buf2)));
+ }
+ }
+}
+
+/* process all neigh associated to a mac entry upon remote mac add */
+static void zvni_process_neigh_on_remote_mac_add(struct zebra_vrf *zvrf,
+ zebra_vni_t *zvni,
+ zebra_mac_t *zmac)
+{
+ zebra_neigh_t *n = NULL;
+ struct listnode *node = NULL;
+ char buf[ETHER_ADDR_STRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+
+ for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) {
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
+ if (IS_ZEBRA_NEIGH_ACTIVE(n)) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "%u: neigh %s (MAC %s) on VNI %u INACTIVE",
+ zvrf_id(zvrf),
+ ipaddr2str(&n->ip, buf2,
+ sizeof(buf2)),
+ prefix_mac2str(&n->emac, buf,
+ sizeof(buf)),
+ zvni->vni);
+
+ ZEBRA_NEIGH_SET_INACTIVE(n);
+ zvni_neigh_send_del_to_client(
+ zvrf, zvni->vni, &n->ip, &n->emac, 0);
+ }
+ }
+ }
+}
+
+/* process all neigh associated to mac entry upon remote mac del */
+static void zvni_process_neigh_on_remote_mac_del(struct zebra_vrf *zvrf,
+ zebra_vni_t *zvni,
+ zebra_mac_t *zmac)
+{
+ zebra_neigh_t *n = NULL;
+ struct listnode *node = NULL;
+ char buf[ETHER_ADDR_STRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+
+ for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) {
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_err(
+ "%u: remote MAC %s getting deleted on VNI %u has local neigh %s",
+ zvrf_id(zvrf),
+ prefix_mac2str(&n->emac, buf,
+ sizeof(buf)),
+ zvni->vni,
+ ipaddr2str(&n->ip, buf2, sizeof(buf2)));
+ }
+ }
+}
+
/*
* Inform BGP about local neighbor addition.
*/
n = zvni_neigh_lookup(zvni, ip);
if (!n) {
- n = zvni_neigh_add(zvni, ip);
+ n = zvni_neigh_add(zvni, ip, macaddr);
if (!n) {
zlog_err(
"%u:Failed to add neighbor %s MAC %s intf %s(%u) -> VNI %u",
mac = hash_get(zvni->mac_table, &tmp_mac, zvni_mac_alloc);
assert(mac);
+ mac->neigh_list = list_new();
+ mac->neigh_list->cmp = (int (*)(void *, void *))neigh_cmp;
+
return mac;
}
{
zebra_mac_t *tmp_mac;
+ list_delete(mac->neigh_list);
+
/* Free the VNI hash entry and allocated memory. */
tmp_mac = hash_release(zvni->mac_table, mac);
if (tmp_mac)
zebra_neigh_t *n;
struct zebra_vrf *zvrf;
char buf[INET6_ADDRSTRLEN];
+ char buf2[ETHER_ADDR_STRLEN];
+ zebra_mac_t *zmac;
/* We are only interested in neighbors on an SVI that resides on top
* of a VxLAN bridge.
if (!n)
return 0;
+ zmac = zvni_mac_lookup(zvni, &n->emac);
+ if (!zmac) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_err(
+ "%u: trying to del a neigh %s without a mac %s on VNI %u",
+ ifp->vrf_id, ipaddr2str(ip, buf, sizeof(buf)),
+ prefix_mac2str(&n->emac, buf2, sizeof(buf2)),
+ zvni->vni);
+
+ 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.
*/
assert(zvrf);
/* Remove neighbor from BGP. */
- zvni_neigh_send_del_to_client(zvrf, zvni->vni, &n->ip, &n->emac, 0);
+ if (IS_ZEBRA_NEIGH_ACTIVE(n))
+ zvni_neigh_send_del_to_client(zvrf, zvni->vni, &n->ip, &n->emac,
+ 0);
/* Delete this neighbor entry. */
zvni_neigh_del(zvni, n);
+ /* see if the AUTO mac needs to be deleted */
+ if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_AUTO)
+ || !listcount(zmac->neigh_list))
+ zvni_mac_del(zvni, zmac);
+
return 0;
}
zebra_vni_t *zvni;
zebra_neigh_t *n;
struct zebra_vrf *zvrf;
+ zebra_mac_t *zmac;
char buf[ETHER_ADDR_STRLEN];
char buf2[INET6_ADDRSTRLEN];
int send_upd = 1, send_del = 0;
ifp->ifindex, state, ext_learned ? "ext-learned " : "",
zvni->vni);
+ /* create a dummy MAC if the MAC is not already present */
+ zmac = zvni_mac_lookup(zvni, macaddr);
+ if (!zmac) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "%u: AUTO MAC %s created for neigh %s on VNI %u",
+ ifp->vrf_id,
+ prefix_mac2str(macaddr, buf, sizeof(buf)),
+ ipaddr2str(ip, buf2, sizeof(buf2)), zvni->vni);
+
+ zmac = zvni_mac_add(zvni, macaddr);
+ if (!zmac) {
+ zlog_warn("%u:Failed to add MAC %s VNI %u",
+ zvrf_id(zvrf),
+ prefix_mac2str(macaddr, buf, sizeof(buf)),
+ zvni->vni);
+ return -1;
+ }
+
+ memset(&zmac->fwd_info, 0, sizeof(zmac->fwd_info));
+ memset(&zmac->flags, 0, sizeof(u_int32_t));
+ SET_FLAG(zmac->flags, ZEBRA_MAC_AUTO);
+ }
+
/* If same entry already exists, it might be a change or it might be a
* move from remote to local.
*/
n->r_vtep_ip.s_addr = 0;
}
} else {
- n = zvni_neigh_add(zvni, ip);
+ n = zvni_neigh_add(zvni, ip, macaddr);
if (!n) {
zlog_err(
"%u:Failed to add neighbor %s MAC %s intf %s(%u) -> VNI %u",
/* Set "local" forwarding info. */
SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL);
- memcpy(&n->emac, macaddr, ETH_ALEN);
n->ifindex = ifp->ifindex;
+ /* Before we program this in BGP, we need to check if MAC is locally
+ * learnt as well */
+ if (!CHECK_FLAG(zmac->flags, ZEBRA_MAC_LOCAL)) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "%u: Skipping neigh %s add to client as MAC %s is not local on VNI %u",
+ ifp->vrf_id, ipaddr2str(ip, buf2, sizeof(buf2)),
+ prefix_mac2str(macaddr, buf, sizeof(buf)),
+ zvni->vni);
+
+ return 0;
+ }
+
/* Inform BGP if required. */
- if (send_upd)
+ if (send_upd) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "%u: neigh %s (MAC %s) is now ACTIVE on VNI %u",
+ ifp->vrf_id, ipaddr2str(ip, buf2, sizeof(buf2)),
+ prefix_mac2str(macaddr, buf, sizeof(buf)),
+ zvni->vni);
+
+ ZEBRA_NEIGH_SET_ACTIVE(n);
return zvni_neigh_send_add_to_client(zvrf, zvni->vni, ip,
macaddr, 0);
+ }
return 0;
}
}
} else {
if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
+ zvni_process_neigh_on_remote_mac_del(zvrf, zvni,
+ mac);
+
if (!mac->neigh_refcnt) {
zvni_mac_uninstall(zvni, mac, 0);
zvni_mac_del(zvni, mac);
else
UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
+ zvni_process_neigh_on_remote_mac_add(zvrf, zvni, mac);
+
/* Install the entry. */
zvni_mac_install(zvni, mac);
}
if (update_neigh) {
if (!n) {
- n = zvni_neigh_add(zvni, &ip);
+ n = zvni_neigh_add(zvni, &ip, &macaddr);
if (!n) {
zlog_warn(
"%u:Failed to add Neigh %s MAC %s VNI %u Remote VTEP %s",
if (old_mac)
zvni_deref_ip2mac(zvni, old_mac, 1);
mac->neigh_refcnt++;
+ memcpy(&n->emac, &macaddr, ETH_ALEN);
}
/* Set "remote" forwarding info. */
UNSET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL);
/* TODO: Handle MAC change. */
- memcpy(&n->emac, &macaddr, ETH_ALEN);
n->r_vtep_ip = vtep_ip;
SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE);
zvni_mac_send_del_to_client(zvrf, zvni->vni, macaddr,
(sticky ? ZEBRA_MAC_TYPE_STICKY : 0));
- /* Delete this MAC entry. */
- zvni_mac_del(zvni, mac);
+ /*
+ * If there are no neigh associated with the mac delete the mac
+ * else mark it as AUTO for forward reference
+ */
+ if (!listcount(mac->neigh_list)) {
+ zvni_mac_del(zvni, mac);
+ } else {
+ UNSET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
+ SET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
+ }
return 0;
}
zvni_mac_send_del_to_client(zvrf, zvni->vni, macaddr,
(sticky ? ZEBRA_MAC_TYPE_STICKY : 0));
- /* Delete this MAC entry. */
- zvni_mac_del(zvni, mac);
+ /* Update all the neigh entries associated with this mac */
+ zvni_process_neigh_on_local_mac_del(zvrf, zvni, mac);
+
+ /*
+ * If there are no neigh associated with the mac delete the mac
+ * else mark it as AUTO for forward reference
+ */
+ if (!listcount(mac->neigh_list)) {
+ zvni_mac_del(zvni, mac);
+ } else {
+ UNSET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
+ SET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
+ }
return 0;
}
? 1
: 0;
+
/*
* return if nothing has changed.
* inform bgp if sticky flag has changed
ifp->name, ifp->ifindex, vid,
zvni->vni);
return 0;
- } else if (mac_sticky != sticky)
+ } else if (mac_sticky != sticky) {
add = 1;
- else
+ } else {
add = 0; /* This is an update of local
interface. */
+ }
} else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
/*
* If we have already learned the MAC as a remote sticky
/* Set "local" forwarding info. */
UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE);
- memset(&mac->fwd_info, 0, sizeof(mac->fwd_info));
+ UNSET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
+ memset(&mac->fwd_info, 0, sizeof(mac->fwd_info));
mac->fwd_info.local.ifindex = ifp->ifindex;
mac->fwd_info.local.vid = vid;
UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
/* Inform BGP if required. */
- if (add)
- return zvni_mac_send_add_to_client(
- zvrf, zvni->vni, macaddr,
- (sticky ? ZEBRA_MAC_TYPE_STICKY : 0));
+ if (add) {
+ zvni_process_neigh_on_local_mac_add(zvrf, zvni, mac);
+ return zvni_mac_send_add_to_client(zvrf, zvni->vni, macaddr,
+ sticky);
+ }
return 0;
}