summaryrefslogtreecommitdiffstats
path: root/zebra/interface.c
diff options
context:
space:
mode:
Diffstat (limited to 'zebra/interface.c')
-rw-r--r--zebra/interface.c3952
1 files changed, 3952 insertions, 0 deletions
diff --git a/zebra/interface.c b/zebra/interface.c
new file mode 100644
index 00000000..b3adc448
--- /dev/null
+++ b/zebra/interface.c
@@ -0,0 +1,3952 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Interface function.
+ * Copyright (C) 1997, 1999 Kunihiro Ishiguro
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "lib_errors.h"
+#include "vty.h"
+#include "sockunion.h"
+#include "prefix.h"
+#include "command.h"
+#include "memory.h"
+#include "ioctl.h"
+#include "connected.h"
+#include "log.h"
+#include "zclient.h"
+#include "vrf.h"
+
+#include "zebra/rtadv.h"
+#include "zebra_ns.h"
+#include "zebra_vrf.h"
+#include "zebra/interface.h"
+#include "zebra/rib.h"
+#include "zebra/rt.h"
+#include "zebra/zebra_router.h"
+#include "zebra/redistribute.h"
+#include "zebra/debug.h"
+#include "zebra/irdp.h"
+#include "zebra/zebra_ptm.h"
+#include "zebra/rt_netlink.h"
+#include "zebra/if_netlink.h"
+#include "zebra/interface.h"
+#include "zebra/zebra_vxlan.h"
+#include "zebra/zebra_errors.h"
+#include "zebra/zebra_evpn_mh.h"
+
+DEFINE_MTYPE_STATIC(ZEBRA, ZINFO, "Zebra Interface Information");
+
+#define ZEBRA_PTM_SUPPORT
+
+DEFINE_HOOK(zebra_if_extra_info, (struct vty * vty, struct interface *ifp),
+ (vty, ifp));
+
+DEFINE_MTYPE(ZEBRA, ZIF_DESC, "Intf desc");
+
+static void if_down_del_nbr_connected(struct interface *ifp);
+
+static void if_zebra_speed_update(struct event *thread)
+{
+ struct interface *ifp = EVENT_ARG(thread);
+ struct zebra_if *zif = ifp->info;
+ uint32_t new_speed;
+ bool changed = false;
+ int error = 0;
+
+ new_speed = kernel_get_speed(ifp, &error);
+
+ /* error may indicate vrf not available or
+ * interfaces not available.
+ * note that loopback & virtual interfaces can return 0 as speed
+ */
+ if (error == INTERFACE_SPEED_ERROR_READ)
+ return;
+
+ if (new_speed != ifp->speed) {
+ zlog_info("%s: %s old speed: %u new speed: %u", __func__,
+ ifp->name, ifp->speed, new_speed);
+ ifp->speed = new_speed;
+ if_add_update(ifp);
+ changed = true;
+ }
+
+ if (changed || error == INTERFACE_SPEED_ERROR_UNKNOWN) {
+#define SPEED_UPDATE_SLEEP_TIME 5
+#define SPEED_UPDATE_COUNT_MAX (4 * 60 / SPEED_UPDATE_SLEEP_TIME)
+ /*
+ * Some interfaces never actually have an associated speed
+ * with them ( I am looking at you bridges ).
+ * So instead of iterating forever, let's give the
+ * system 4 minutes to try to figure out the speed
+ * if after that it it's probably never going to become
+ * useful.
+ * Since I don't know all the wonderful types of interfaces
+ * that may come into existence in the future I am going
+ * to not update the system to keep track of that. This
+ * is far simpler to just stop trying after 4 minutes
+ */
+ if (error == INTERFACE_SPEED_ERROR_UNKNOWN &&
+ zif->speed_update_count == SPEED_UPDATE_COUNT_MAX)
+ return;
+
+ zif->speed_update_count++;
+ event_add_timer(zrouter.master, if_zebra_speed_update, ifp,
+ SPEED_UPDATE_SLEEP_TIME, &zif->speed_update);
+ event_ignore_late_timer(zif->speed_update);
+ }
+}
+
+static void zebra_if_node_destroy(route_table_delegate_t *delegate,
+ struct route_table *table,
+ struct route_node *node)
+{
+ if (node->info)
+ list_delete((struct list **)&node->info);
+ route_node_destroy(delegate, table, node);
+}
+
+route_table_delegate_t zebra_if_table_delegate = {
+ .create_node = route_node_create,
+ .destroy_node = zebra_if_node_destroy};
+
+/* Called when new interface is added. */
+static int if_zebra_new_hook(struct interface *ifp)
+{
+ struct zebra_if *zebra_if;
+
+ zebra_if = XCALLOC(MTYPE_ZINFO, sizeof(struct zebra_if));
+ zebra_if->ifp = ifp;
+
+ zebra_if->multicast = IF_ZEBRA_DATA_UNSPEC;
+ zebra_if->mpls_config = IF_ZEBRA_DATA_UNSPEC;
+ zebra_if->shutdown = IF_ZEBRA_DATA_UNSPEC;
+
+ zebra_if->link_nsid = NS_UNKNOWN;
+
+ nhg_connected_tree_init(&zebra_if->nhg_dependents);
+
+ zebra_ptm_if_init(zebra_if);
+
+ ifp->ptm_enable = zebra_ptm_get_enable_state();
+
+ rtadv_if_init(zebra_if);
+
+ zebra_evpn_mh_if_init(zebra_if);
+
+ memset(&zebra_if->neigh_mac[0], 0, 6);
+
+ /* Initialize installed address chains tree. */
+ zebra_if->ipv4_subnets =
+ route_table_init_with_delegate(&zebra_if_table_delegate);
+
+ ifp->info = zebra_if;
+
+ /*
+ * Some platforms are telling us that the interface is
+ * up and ready to go. When we check the speed we
+ * sometimes get the wrong value. Wait a couple
+ * of seconds and ask again. Hopefully it's all settled
+ * down upon startup.
+ */
+ zebra_if->speed_update_count = 0;
+ event_add_timer(zrouter.master, if_zebra_speed_update, ifp, 15,
+ &zebra_if->speed_update);
+ event_ignore_late_timer(zebra_if->speed_update);
+
+ return 0;
+}
+
+static void if_down_nhg_dependents(const struct interface *ifp)
+{
+ struct nhg_connected *rb_node_dep = NULL;
+ struct zebra_if *zif = (struct zebra_if *)ifp->info;
+
+ frr_each(nhg_connected_tree, &zif->nhg_dependents, rb_node_dep)
+ zebra_nhg_check_valid(rb_node_dep->nhe);
+}
+
+static void if_nhg_dependents_release(const struct interface *ifp)
+{
+ struct nhg_connected *rb_node_dep = NULL;
+ struct zebra_if *zif = (struct zebra_if *)ifp->info;
+
+ frr_each(nhg_connected_tree, &zif->nhg_dependents, rb_node_dep) {
+ rb_node_dep->nhe->ifp = NULL; /* Null it out */
+ zebra_nhg_check_valid(rb_node_dep->nhe);
+ if (CHECK_FLAG(rb_node_dep->nhe->flags,
+ NEXTHOP_GROUP_KEEP_AROUND) &&
+ rb_node_dep->nhe->refcnt == 1)
+ zebra_nhg_decrement_ref(rb_node_dep->nhe);
+ }
+}
+
+/* Called when interface is deleted. */
+static int if_zebra_delete_hook(struct interface *ifp)
+{
+ struct zebra_if *zebra_if;
+ struct zebra_l2info_bond *bond;
+
+ if (ifp->info) {
+ zebra_if = ifp->info;
+
+ /* If we set protodown, clear our reason now from the kernel */
+ if (ZEBRA_IF_IS_PROTODOWN(zebra_if) && zebra_if->protodown_rc &&
+ !ZEBRA_IF_IS_PROTODOWN_ONLY_EXTERNAL(zebra_if))
+ zebra_if_update_protodown_rc(ifp, true,
+ (zebra_if->protodown_rc &
+ ~ZEBRA_PROTODOWN_ALL));
+
+ /* Free installed address chains tree. */
+ if (zebra_if->ipv4_subnets)
+ route_table_finish(zebra_if->ipv4_subnets);
+
+ rtadv_if_fini(zebra_if);
+
+ bond = &zebra_if->bond_info;
+ if (bond && bond->mbr_zifs)
+ list_delete(&bond->mbr_zifs);
+
+ zebra_l2_bridge_if_cleanup(ifp);
+ zebra_evpn_if_cleanup(zebra_if);
+ zebra_evpn_mac_ifp_del(ifp);
+
+ if_nhg_dependents_release(ifp);
+ nhg_connected_tree_free(&zebra_if->nhg_dependents);
+
+ XFREE(MTYPE_ZIF_DESC, zebra_if->desc);
+
+ EVENT_OFF(zebra_if->speed_update);
+
+ XFREE(MTYPE_ZINFO, zebra_if);
+ }
+
+ return 0;
+}
+
+/* Build the table key */
+static void if_build_key(uint32_t ifindex, struct prefix *p)
+{
+ p->family = AF_INET;
+ p->prefixlen = IPV4_MAX_BITLEN;
+ p->u.prefix4.s_addr = ifindex;
+}
+
+/* Link an interface in a per NS interface tree */
+struct interface *if_link_per_ns(struct zebra_ns *ns, struct interface *ifp)
+{
+ struct prefix p;
+ struct route_node *rn;
+
+ if (ifp->ifindex == IFINDEX_INTERNAL)
+ return NULL;
+
+ if_build_key(ifp->ifindex, &p);
+ rn = route_node_get(ns->if_table, &p);
+ if (rn->info) {
+ ifp = (struct interface *)rn->info;
+ route_unlock_node(rn); /* get */
+ return ifp;
+ }
+
+ rn->info = ifp;
+ ifp->node = rn;
+
+ return ifp;
+}
+
+/* Delete a VRF. This is called in vrf_terminate(). */
+void if_unlink_per_ns(struct interface *ifp)
+{
+ if (!ifp->node)
+ return;
+
+ ifp->node->info = NULL;
+ route_unlock_node(ifp->node);
+ ifp->node = NULL;
+}
+
+/* Look up an interface by identifier within a NS */
+struct interface *if_lookup_by_index_per_ns(struct zebra_ns *ns,
+ uint32_t ifindex)
+{
+ struct prefix p;
+ struct route_node *rn;
+ struct interface *ifp = NULL;
+
+ if_build_key(ifindex, &p);
+ rn = route_node_lookup(ns->if_table, &p);
+ if (rn) {
+ ifp = (struct interface *)rn->info;
+ route_unlock_node(rn); /* lookup */
+ }
+ return ifp;
+}
+
+/* Look up an interface by name within a NS */
+struct interface *if_lookup_by_name_per_ns(struct zebra_ns *ns,
+ const char *ifname)
+{
+ struct route_node *rn;
+ struct interface *ifp;
+
+ for (rn = route_top(ns->if_table); rn; rn = route_next(rn)) {
+ ifp = (struct interface *)rn->info;
+ if (ifp && strcmp(ifp->name, ifname) == 0) {
+ route_unlock_node(rn);
+ return (ifp);
+ }
+ }
+
+ return NULL;
+}
+
+struct interface *if_lookup_by_index_per_nsid(ns_id_t ns_id, uint32_t ifindex)
+{
+ struct zebra_ns *zns;
+
+ zns = zebra_ns_lookup(ns_id);
+ return zns ? if_lookup_by_index_per_ns(zns, ifindex) : NULL;
+}
+
+const char *ifindex2ifname_per_ns(struct zebra_ns *zns, unsigned int ifindex)
+{
+ struct interface *ifp;
+
+ return ((ifp = if_lookup_by_index_per_ns(zns, ifindex)) != NULL)
+ ? ifp->name
+ : "unknown";
+}
+
+/* Tie an interface address to its derived subnet list of addresses. */
+int if_subnet_add(struct interface *ifp, struct connected *ifc)
+{
+ struct route_node *rn;
+ struct zebra_if *zebra_if;
+ struct prefix cp;
+ struct list *addr_list;
+
+ assert(ifp && ifp->info && ifc);
+ zebra_if = ifp->info;
+
+ /* Get address derived subnet node and associated address list, while
+ marking
+ address secondary attribute appropriately. */
+ cp = *CONNECTED_PREFIX(ifc);
+ apply_mask(&cp);
+ rn = route_node_get(zebra_if->ipv4_subnets, &cp);
+
+ if ((addr_list = rn->info))
+ SET_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY);
+ else {
+ UNSET_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY);
+ rn->info = addr_list = list_new();
+ route_lock_node(rn);
+ }
+
+ /* Tie address at the tail of address list. */
+ listnode_add(addr_list, ifc);
+
+ /* Return list element count. */
+ return (addr_list->count);
+}
+
+/* Untie an interface address from its derived subnet list of addresses. */
+int if_subnet_delete(struct interface *ifp, struct connected *ifc)
+{
+ struct route_node *rn;
+ struct zebra_if *zebra_if;
+ struct list *addr_list;
+ struct prefix cp;
+
+ assert(ifp && ifp->info && ifc);
+ zebra_if = ifp->info;
+
+ cp = *CONNECTED_PREFIX(ifc);
+ apply_mask(&cp);
+
+ /* Get address derived subnet node. */
+ rn = route_node_lookup(zebra_if->ipv4_subnets, &cp);
+ if (!(rn && rn->info)) {
+ flog_warn(EC_ZEBRA_REMOVE_ADDR_UNKNOWN_SUBNET,
+ "Trying to remove an address from an unknown subnet. (please report this bug)");
+ return -1;
+ }
+ route_unlock_node(rn);
+
+ /* Untie address from subnet's address list. */
+ addr_list = rn->info;
+
+ /* Deleting an address that is not registered is a bug.
+ * In any case, we shouldn't decrement the lock counter if the address
+ * is unknown. */
+ if (!listnode_lookup(addr_list, ifc)) {
+ flog_warn(
+ EC_ZEBRA_REMOVE_UNREGISTERED_ADDR,
+ "Trying to remove an address from a subnet where it is not currently registered. (please report this bug)");
+ return -1;
+ }
+
+ listnode_delete(addr_list, ifc);
+ route_unlock_node(rn);
+
+ /* Return list element count, if not empty. */
+ if (addr_list->count) {
+ /* If deleted address is primary, mark subsequent one as such
+ * and distribute. */
+ if (!CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY)) {
+ ifc = listgetdata(
+ (struct listnode *)listhead(addr_list));
+ zebra_interface_address_delete_update(ifp, ifc);
+ UNSET_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY);
+ /* XXX: Linux kernel removes all the secondary addresses
+ * when the primary
+ * address is removed. We could try to work around that,
+ * though this is
+ * non-trivial. */
+ zebra_interface_address_add_update(ifp, ifc);
+ }
+
+ return addr_list->count;
+ }
+
+ /* Otherwise, free list and route node. */
+ list_delete(&addr_list);
+ rn->info = NULL;
+ route_unlock_node(rn);
+
+ return 0;
+}
+
+/* if_flags_mangle: A place for hacks that require mangling
+ * or tweaking the interface flags.
+ *
+ * ******************** Solaris flags hacks **************************
+ *
+ * Solaris IFF_UP flag reflects only the primary interface as the
+ * routing socket only sends IFINFO for the primary interface. Hence
+ * ~IFF_UP does not per se imply all the logical interfaces are also
+ * down - which we only know of as addresses. Instead we must determine
+ * whether the interface really is up or not according to how many
+ * addresses are still attached. (Solaris always sends RTM_DELADDR if
+ * an interface, logical or not, goes ~IFF_UP).
+ *
+ * Ie, we mangle IFF_UP to *additionally* reflect whether or not there
+ * are addresses left in struct connected, not just the actual underlying
+ * IFF_UP flag.
+ *
+ * We must hence remember the real state of IFF_UP, which we do in
+ * struct zebra_if.primary_state.
+ *
+ * Setting IFF_UP within zebra to administratively shutdown the
+ * interface will affect only the primary interface/address on Solaris.
+ ************************End Solaris flags hacks ***********************
+ */
+static void if_flags_mangle(struct interface *ifp, uint64_t *newflags)
+{
+ return;
+}
+
+/* Update the flags field of the ifp with the new flag set provided.
+ * Take whatever actions are required for any changes in flags we care
+ * about.
+ *
+ * newflags should be the raw value, as obtained from the OS.
+ */
+void if_flags_update(struct interface *ifp, uint64_t newflags)
+{
+ if_flags_mangle(ifp, &newflags);
+
+ if (if_is_no_ptm_operative(ifp)) {
+ /* operative -> inoperative? */
+ ifp->flags = newflags;
+ if (!if_is_operative(ifp))
+ if_down(ifp);
+ } else {
+ /* inoperative -> operative? */
+ ifp->flags = newflags;
+ if (if_is_operative(ifp))
+ if_up(ifp, true);
+ }
+}
+
+/* Wake up configured address if it is not in current kernel
+ address. */
+void if_addr_wakeup(struct interface *ifp)
+{
+ struct connected *ifc;
+ struct prefix *p;
+ enum zebra_dplane_result dplane_res;
+
+ frr_each_safe (if_connected, ifp->connected, ifc) {
+ p = ifc->address;
+
+ if (CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED)
+ && !CHECK_FLAG(ifc->conf, ZEBRA_IFC_QUEUED)) {
+ /* Address check. */
+ if (p->family == AF_INET) {
+ if (!if_is_up(ifp)) {
+ /* Assume zebra is configured like
+ * following:
+ *
+ * interface gre0
+ * ip addr 192.0.2.1/24
+ * !
+ *
+ * As soon as zebra becomes first aware
+ * that gre0 exists in the
+ * kernel, it will set gre0 up and
+ * configure its addresses.
+ *
+ * (This may happen at startup when the
+ * interface already exists
+ * or during runtime when the interface
+ * is added to the kernel)
+ *
+ * XXX: IRDP code is calling here via
+ * if_add_update - this seems
+ * somewhat weird.
+ * XXX: RUNNING is not a settable flag
+ * on any system
+ * I (paulj) am aware of.
+ */
+ if_set_flags(ifp, IFF_UP | IFF_RUNNING);
+ if_refresh(ifp);
+ }
+
+ dplane_res = dplane_intf_addr_set(ifp, ifc);
+ if (dplane_res ==
+ ZEBRA_DPLANE_REQUEST_FAILURE) {
+ flog_err_sys(
+ EC_ZEBRA_IFACE_ADDR_ADD_FAILED,
+ "Can't set interface's address: %s",
+ dplane_res2str(dplane_res));
+ continue;
+ }
+
+ SET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED);
+ /* The address will be advertised to zebra
+ * clients when the notification
+ * from the kernel has been received.
+ * It will also be added to the interface's
+ * subnet list then. */
+ }
+ if (p->family == AF_INET6) {
+ if (!if_is_up(ifp)) {
+ /* See long comment above */
+ if_set_flags(ifp, IFF_UP | IFF_RUNNING);
+ if_refresh(ifp);
+ }
+
+
+ dplane_res = dplane_intf_addr_set(ifp, ifc);
+ if (dplane_res ==
+ ZEBRA_DPLANE_REQUEST_FAILURE) {
+ flog_err_sys(
+ EC_ZEBRA_IFACE_ADDR_ADD_FAILED,
+ "Can't set interface's address: %s",
+ dplane_res2str(dplane_res));
+ continue;
+ }
+
+ SET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED);
+ /* The address will be advertised to zebra
+ * clients when the notification
+ * from the kernel has been received. */
+ }
+ }
+ }
+}
+
+/* Handle interface addition */
+void if_add_update(struct interface *ifp)
+{
+ struct zebra_if *if_data;
+ struct zebra_ns *zns;
+ struct zebra_vrf *zvrf = ifp->vrf->info;
+
+ /* case interface populate before vrf enabled */
+ if (zvrf->zns)
+ zns = zvrf->zns;
+ else
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ if_link_per_ns(zns, ifp);
+ if_data = ifp->info;
+ assert(if_data);
+
+ if (if_data->multicast == IF_ZEBRA_DATA_ON)
+ if_set_flags(ifp, IFF_MULTICAST);
+ else if (if_data->multicast == IF_ZEBRA_DATA_OFF)
+ if_unset_flags(ifp, IFF_MULTICAST);
+
+ zebra_ptm_if_set_ptm_state(ifp, if_data);
+
+ zebra_interface_add_update(ifp);
+
+ if (!CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) {
+ SET_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE);
+
+ if (if_data->shutdown == IF_ZEBRA_DATA_ON) {
+ if (IS_ZEBRA_DEBUG_KERNEL) {
+ zlog_debug(
+ "interface %s vrf %s(%u) index %d is shutdown. Won't wake it up.",
+ ifp->name, ifp->vrf->name,
+ ifp->vrf->vrf_id, ifp->ifindex);
+ }
+
+ return;
+ }
+
+ if_addr_wakeup(ifp);
+
+ if (if_data->mpls_config == IF_ZEBRA_DATA_ON)
+ dplane_intf_mpls_modify_state(ifp, true);
+ else if (if_data->mpls_config == IF_ZEBRA_DATA_OFF)
+ dplane_intf_mpls_modify_state(ifp, false);
+
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug(
+ "interface %s vrf %s(%u) index %d becomes active.",
+ ifp->name, ifp->vrf->name, ifp->vrf->vrf_id,
+ ifp->ifindex);
+
+ } else {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("interface %s vrf %s(%u) index %d is added.",
+ ifp->name, ifp->vrf->name, ifp->vrf->vrf_id,
+ ifp->ifindex);
+ }
+}
+
+/* Install connected routes corresponding to an interface. */
+static void if_install_connected(struct interface *ifp)
+{
+ struct connected *ifc;
+
+ frr_each (if_connected, ifp->connected, ifc) {
+ if (CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL))
+ zebra_interface_address_add_update(ifp, ifc);
+
+ connected_up(ifp, ifc);
+ }
+}
+
+/* Uninstall connected routes corresponding to an interface. */
+static void if_uninstall_connected(struct interface *ifp)
+{
+ struct connected *ifc;
+
+ frr_each_safe (if_connected, ifp->connected, ifc) {
+ zebra_interface_address_delete_update(ifp, ifc);
+ connected_down(ifp, ifc);
+ }
+}
+
+/* Uninstall and delete connected routes corresponding to an interface. */
+/* TODO - Check why IPv4 handling here is different from install or if_down */
+static void if_delete_connected(struct interface *ifp)
+{
+ struct connected *ifc, *ifc_next;
+ struct prefix cp;
+ struct route_node *rn;
+ struct zebra_if *zebra_if;
+
+ zebra_if = ifp->info;
+
+ for (ifc = if_connected_first(ifp->connected); ifc; ifc = ifc_next) {
+ ifc_next = if_connected_next(ifp->connected, ifc);
+
+ cp = *CONNECTED_PREFIX(ifc);
+ apply_mask(&cp);
+
+ if (cp.family == AF_INET
+ && (rn = route_node_lookup(zebra_if->ipv4_subnets, &cp))) {
+ struct listnode *anode;
+ struct listnode *next;
+ struct listnode *first;
+ struct list *addr_list;
+
+ route_unlock_node(rn);
+ addr_list = (struct list *)rn->info;
+
+ /* Remove addresses, secondaries first. */
+ first = listhead(addr_list);
+ if (first)
+ for (anode = first->next; anode || first;
+ anode = next) {
+ if (!anode) {
+ anode = first;
+ first = NULL;
+ }
+ next = anode->next;
+
+ ifc = listgetdata(anode);
+ connected_down(ifp, ifc);
+
+ /* XXX: We have to send notifications
+ * here explicitly, because we destroy
+ * the ifc before receiving the
+ * notification about the address being
+ * deleted.
+ */
+ zebra_interface_address_delete_update(
+ ifp, ifc);
+
+ UNSET_FLAG(ifc->conf, ZEBRA_IFC_REAL);
+ UNSET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED);
+
+ /* Remove from subnet chain. */
+ list_delete_node(addr_list, anode);
+ route_unlock_node(rn);
+
+ /* Remove from interface address list
+ * (unconditionally). */
+ if (!CHECK_FLAG(ifc->conf,
+ ZEBRA_IFC_CONFIGURED)) {
+ if (ifc == ifc_next)
+ ifc_next = if_connected_next(
+ ifp->connected,
+ ifc);
+
+ if_connected_del(ifp->connected,
+ ifc);
+ connected_free(&ifc);
+ }
+ }
+
+ /* Free chain list and respective route node. */
+ list_delete(&addr_list);
+ rn->info = NULL;
+ route_unlock_node(rn);
+ } else if (cp.family == AF_INET6) {
+ connected_down(ifp, ifc);
+
+ zebra_interface_address_delete_update(ifp, ifc);
+
+ UNSET_FLAG(ifc->conf, ZEBRA_IFC_REAL);
+ UNSET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED);
+
+ if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED)) {
+ if_connected_del(ifp->connected, ifc);
+ connected_free(&ifc);
+ }
+ }
+ }
+}
+
+/* Handle an interface delete event */
+void if_delete_update(struct interface **pifp)
+{
+ struct zebra_if *zif;
+ struct interface *ifp = *pifp;
+
+ if (if_is_up(ifp)) {
+ flog_err(
+ EC_LIB_INTERFACE,
+ "interface %s vrf %s(%u) index %d is still up while being deleted.",
+ ifp->name, ifp->vrf->name, ifp->vrf->vrf_id,
+ ifp->ifindex);
+ return;
+ }
+
+ if (!CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE))
+ return;
+
+ /* Mark interface as inactive */
+ UNSET_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE);
+
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("interface %s vrf %s(%u) index %d is now inactive.",
+ ifp->name, ifp->vrf->name, ifp->vrf->vrf_id,
+ ifp->ifindex);
+
+ /* Delete connected routes from the kernel. */
+ if_delete_connected(ifp);
+
+ /* if the ifp is in a vrf, move it to default so vrf can be deleted if
+ * desired. This operation is not done for netns implementation to avoid
+ * collision with interface with the same name in the default vrf (can
+ * occur with this implementation whereas it is not possible with
+ * vrf-lite).
+ */
+ if (ifp->vrf->vrf_id && !vrf_is_backend_netns())
+ if_handle_vrf_change(ifp, VRF_DEFAULT);
+
+ /* Send out notification on interface delete. */
+ zebra_interface_delete_update(ifp);
+
+ if_unlink_per_ns(ifp);
+
+ /* Update ifindex after distributing the delete message. This is in
+ case any client needs to have the old value of ifindex available
+ while processing the deletion. Each client daemon is responsible
+ for setting ifindex to IFINDEX_INTERNAL after processing the
+ interface deletion message. */
+ if_set_index(ifp, IFINDEX_INTERNAL);
+ ifp->node = NULL;
+
+ UNSET_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK);
+
+ /* Reset some zebra interface params to default values. */
+ zif = ifp->info;
+ if (zif) {
+ zebra_evpn_if_cleanup(zif);
+ zif->zif_type = ZEBRA_IF_OTHER;
+ zif->zif_slave_type = ZEBRA_IF_SLAVE_NONE;
+ memset(&zif->l2info, 0, sizeof(union zebra_l2if_info));
+ memset(&zif->brslave_info, 0,
+ sizeof(struct zebra_l2info_brslave));
+ zebra_evpn_mac_ifp_del(ifp);
+ }
+
+ if (!ifp->configured) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("interface %s is being deleted from the system",
+ ifp->name);
+ if_delete(pifp);
+ }
+}
+
+/* VRF change for an interface */
+void if_handle_vrf_change(struct interface *ifp, vrf_id_t vrf_id)
+{
+ vrf_id_t old_vrf_id;
+
+ old_vrf_id = ifp->vrf->vrf_id;
+
+ /* Uninstall connected routes. */
+ if_uninstall_connected(ifp);
+
+ /* Delete any IPv4 neighbors created to implement RFC 5549 */
+ if_nbr_ipv6ll_to_ipv4ll_neigh_del_all(ifp);
+
+ /* Delete all neighbor addresses learnt through IPv6 RA */
+ if_down_del_nbr_connected(ifp);
+
+ /* Send out notification on interface VRF change. */
+ /* This is to issue a DELETE, as appropriate. */
+ zebra_interface_vrf_update_del(ifp, vrf_id);
+
+ if (if_is_vrf(ifp))
+ return;
+
+ /* update VRF */
+ if_update_to_new_vrf(ifp, vrf_id);
+
+ /* Send out notification on interface VRF change. */
+ /* This is to issue an ADD, if needed. */
+ zebra_interface_vrf_update_add(ifp, old_vrf_id);
+}
+
+static void ipv6_ll_address_to_mac(struct in6_addr *address, uint8_t *mac)
+{
+ mac[0] = address->s6_addr[8] ^ 0x02;
+ mac[1] = address->s6_addr[9];
+ mac[2] = address->s6_addr[10];
+ mac[3] = address->s6_addr[13];
+ mac[4] = address->s6_addr[14];
+ mac[5] = address->s6_addr[15];
+}
+
+void if_nbr_mac_to_ipv4ll_neigh_update(struct interface *ifp,
+ char mac[6],
+ struct in6_addr *address,
+ int add)
+{
+ struct zebra_vrf *zvrf = ifp->vrf->info;
+ struct zebra_if *zif = ifp->info;
+ char buf[16] = "169.254.0.1";
+ struct in_addr ipv4_ll;
+ ns_id_t ns_id;
+
+ inet_pton(AF_INET, buf, &ipv4_ll);
+
+ ns_id = zvrf->zns->ns_id;
+
+ /*
+ * Remove and re-add any existing neighbor entry for this address,
+ * since Netlink doesn't currently offer update message types.
+ */
+ kernel_neigh_update(0, ifp->ifindex, (void *)&ipv4_ll.s_addr, mac, 6,
+ ns_id, AF_INET, true);
+
+ /* Add new neighbor entry.
+ *
+ * We force installation even if current neighbor entry is the same.
+ * Since this function is used to refresh our MAC entries after an
+ * interface flap, if we don't force in our custom entries with their
+ * state set to PERMANENT or REACHABLE then the kernel will attempt to
+ * resolve our leftover entries, fail, mark them unreachable and then
+ * they'll be useless to us.
+ */
+ if (add)
+ kernel_neigh_update(add, ifp->ifindex, (void *)&ipv4_ll.s_addr,
+ mac, 6, ns_id, AF_INET, true);
+
+ memcpy(&zif->neigh_mac[0], &mac[0], 6);
+
+ /*
+ * We need to note whether or not we originated a v6
+ * neighbor entry for this interface. So that when
+ * someone unwisely accidentally deletes this entry
+ * we can shove it back in.
+ */
+ zif->v6_2_v4_ll_neigh_entry = !!add;
+ memcpy(&zif->v6_2_v4_ll_addr6, address, sizeof(*address));
+
+ zvrf->neigh_updates++;
+}
+
+void if_nbr_ipv6ll_to_ipv4ll_neigh_update(struct interface *ifp,
+ struct in6_addr *address, int add)
+{
+
+ char mac[6];
+
+ ipv6_ll_address_to_mac(address, (uint8_t *)mac);
+ if_nbr_mac_to_ipv4ll_neigh_update(ifp, mac, address, add);
+}
+
+static void if_nbr_ipv6ll_to_ipv4ll_neigh_add_all(struct interface *ifp)
+{
+ if (listhead(ifp->nbr_connected)) {
+ struct nbr_connected *nbr_connected;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(ifp->nbr_connected, node,
+ nbr_connected))
+ if_nbr_ipv6ll_to_ipv4ll_neigh_update(
+ ifp, &nbr_connected->address->u.prefix6, 1);
+ }
+}
+
+void if_nbr_ipv6ll_to_ipv4ll_neigh_del_all(struct interface *ifp)
+{
+ if (listhead(ifp->nbr_connected)) {
+ struct nbr_connected *nbr_connected;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(ifp->nbr_connected, node,
+ nbr_connected))
+ if_nbr_ipv6ll_to_ipv4ll_neigh_update(
+ ifp, &nbr_connected->address->u.prefix6, 0);
+ }
+}
+
+static void if_down_del_nbr_connected(struct interface *ifp)
+{
+ struct nbr_connected *nbr_connected;
+ struct listnode *node, *nnode;
+
+ for (ALL_LIST_ELEMENTS(ifp->nbr_connected, node, nnode,
+ nbr_connected)) {
+ listnode_delete(ifp->nbr_connected, nbr_connected);
+ nbr_connected_free(nbr_connected);
+ }
+}
+
+/* Interface is up. */
+void if_up(struct interface *ifp, bool install_connected)
+{
+ struct zebra_if *zif;
+ struct interface *link_if;
+
+ zif = ifp->info;
+ zif->up_count++;
+ frr_timestamp(2, zif->up_last, sizeof(zif->up_last));
+
+ /* Notify the protocol daemons. */
+ if (ifp->ptm_enable && (ifp->ptm_status == ZEBRA_PTM_STATUS_DOWN)) {
+ flog_warn(EC_ZEBRA_PTM_NOT_READY,
+ "%s: interface %s hasn't passed ptm check",
+ __func__, ifp->name);
+ return;
+ }
+ zebra_interface_up_update(ifp);
+
+ if_nbr_ipv6ll_to_ipv4ll_neigh_add_all(ifp);
+
+ rtadv_if_up(zif);
+
+ /* Install connected routes to the kernel. */
+ if (install_connected)
+ if_install_connected(ifp);
+
+ /*
+ * Interface associated NHG's have been deleted on
+ * interface down events, now that this interface
+ * is coming back up, let's resync the zebra -> dplane
+ * nhg's so that they can be continued to be used.
+ */
+ zebra_interface_nhg_reinstall(ifp);
+
+ /* Handle interface up for specific types for EVPN. Non-VxLAN interfaces
+ * are checked to see if (remote) neighbor entries need to be installed
+ * on them for ARP suppression.
+ */
+ if (IS_ZEBRA_IF_VXLAN(ifp))
+ zebra_vxlan_if_up(ifp);
+ else if (IS_ZEBRA_IF_BRIDGE(ifp)) {
+ link_if = ifp;
+ zebra_vxlan_svi_up(ifp, link_if);
+ } else if (IS_ZEBRA_IF_VLAN(ifp)) {
+ link_if = zif->link;
+ if (link_if)
+ zebra_vxlan_svi_up(ifp, link_if);
+ } else if (IS_ZEBRA_IF_MACVLAN(ifp)) {
+ zebra_vxlan_macvlan_up(ifp);
+ }
+
+ if (zif->es_info.es)
+ zebra_evpn_es_if_oper_state_change(zif, true /*up*/);
+
+ if (zif->flags & ZIF_FLAG_EVPN_MH_UPLINK)
+ zebra_evpn_mh_uplink_oper_update(zif);
+
+ event_add_timer(zrouter.master, if_zebra_speed_update, ifp, 0,
+ &zif->speed_update);
+ event_ignore_late_timer(zif->speed_update);
+
+ if_addr_wakeup(ifp);
+}
+
+/* Interface goes down. We have to manage different behavior of based
+ OS. */
+void if_down(struct interface *ifp)
+{
+ struct zebra_if *zif;
+ struct interface *link_if;
+
+ zif = ifp->info;
+ zif->down_count++;
+ frr_timestamp(2, zif->down_last, sizeof(zif->down_last));
+
+ if_down_nhg_dependents(ifp);
+
+ /* Handle interface down for specific types for EVPN. Non-VxLAN
+ * interfaces
+ * are checked to see if (remote) neighbor entries need to be purged
+ * for ARP suppression.
+ */
+ if (IS_ZEBRA_IF_VXLAN(ifp))
+ zebra_vxlan_if_down(ifp);
+ else if (IS_ZEBRA_IF_BRIDGE(ifp)) {
+ link_if = ifp;
+ zebra_vxlan_svi_down(ifp, link_if);
+ } else if (IS_ZEBRA_IF_VLAN(ifp)) {
+ link_if = zif->link;
+ if (link_if)
+ zebra_vxlan_svi_down(ifp, link_if);
+ } else if (IS_ZEBRA_IF_MACVLAN(ifp)) {
+ zebra_vxlan_macvlan_down(ifp);
+ }
+
+ if (zif->es_info.es)
+ zebra_evpn_es_if_oper_state_change(zif, false /*up*/);
+
+ if (zif->flags & ZIF_FLAG_EVPN_MH_UPLINK)
+ zebra_evpn_mh_uplink_oper_update(zif);
+
+ /* Notify to the protocol daemons. */
+ zebra_interface_down_update(ifp);
+
+ /* Uninstall connected routes from the kernel. */
+ if_uninstall_connected(ifp);
+
+ if_nbr_ipv6ll_to_ipv4ll_neigh_del_all(ifp);
+
+ /* Delete all neighbor addresses learnt through IPv6 RA */
+ if_down_del_nbr_connected(ifp);
+}
+
+void if_refresh(struct interface *ifp)
+{
+#ifndef GNU_LINUX
+ if_get_flags(ifp);
+#endif
+}
+
+void zebra_if_update_link(struct interface *ifp, ifindex_t link_ifindex,
+ ns_id_t ns_id)
+{
+ struct zebra_if *zif;
+
+ zif = (struct zebra_if *)ifp->info;
+ zif->link_nsid = ns_id;
+ zif->link_ifindex = link_ifindex;
+ zif->link = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id),
+ link_ifindex);
+}
+
+/*
+ * during initial link dump kernel does not order lower devices before
+ * upper devices so we need to fixup link dependencies at the end of dump
+ */
+void zebra_if_update_all_links(struct zebra_ns *zns)
+{
+ struct route_node *rn;
+ struct interface *ifp;
+ struct zebra_if *zif;
+
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_info("fixup link dependencies");
+
+ for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
+ ifp = (struct interface *)rn->info;
+ if (!ifp)
+ continue;
+ zif = ifp->info;
+ /* update bond-member to bond linkages */
+ if ((IS_ZEBRA_IF_BOND_SLAVE(ifp))
+ && (zif->bondslave_info.bond_ifindex != IFINDEX_INTERNAL)
+ && !zif->bondslave_info.bond_if) {
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("bond mbr %s map to bond %d",
+ zif->ifp->name,
+ zif->bondslave_info.bond_ifindex);
+ zebra_l2_map_slave_to_bond(zif, ifp->vrf->vrf_id);
+ }
+
+ /* update SVI linkages */
+ if ((zif->link_ifindex != IFINDEX_INTERNAL) && !zif->link) {
+ zif->link = if_lookup_by_index_per_nsid(
+ zif->link_nsid, zif->link_ifindex);
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("interface %s/%d's lower fixup to %s/%d",
+ ifp->name, ifp->ifindex,
+ zif->link?zif->link->name:"unk",
+ zif->link_ifindex);
+ }
+
+ /* Update VLAN<=>SVI map */
+ if (IS_ZEBRA_IF_VLAN(ifp))
+ zebra_evpn_acc_bd_svi_set(zif, NULL,
+ !!if_is_operative(ifp));
+ }
+}
+
+static bool if_ignore_set_protodown(const struct interface *ifp, bool new_down,
+ uint32_t new_protodown_rc)
+{
+ struct zebra_if *zif;
+ bool old_down, old_set_down, old_unset_down;
+
+ zif = ifp->info;
+
+ /*
+ * FRR does not have enough data to make this request
+ */
+ if (ifp->ifindex == IFINDEX_INTERNAL)
+ return true;
+
+ /* Current state as we know it */
+ old_down = !!(ZEBRA_IF_IS_PROTODOWN(zif));
+ old_set_down = !!CHECK_FLAG(zif->flags, ZIF_FLAG_SET_PROTODOWN);
+ old_unset_down = !!CHECK_FLAG(zif->flags, ZIF_FLAG_UNSET_PROTODOWN);
+
+ if (new_protodown_rc == zif->protodown_rc) {
+ /* Early return if already down & reason bitfield matches */
+ if (new_down == old_down) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug(
+ "Ignoring request to set protodown %s for interface %s (%u): protodown %s is already set (reason bitfield: old 0x%x new 0x%x)",
+ new_down ? "on" : "off", ifp->name,
+ ifp->ifindex, new_down ? "on" : "off",
+ zif->protodown_rc, new_protodown_rc);
+
+ return true;
+ }
+
+ /* Early return if already set queued & reason bitfield matches
+ */
+ if (new_down && old_set_down) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug(
+ "Ignoring request to set protodown %s for interface %s (%u): protodown %s is already queued to dplane (reason bitfield: old 0x%x new 0x%x)",
+ new_down ? "on" : "off", ifp->name,
+ ifp->ifindex, new_down ? "on" : "off",
+ zif->protodown_rc, new_protodown_rc);
+
+ return true;
+ }
+
+ /* Early return if already unset queued & reason bitfield
+ * matches */
+ if (!new_down && old_unset_down) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug(
+ "Ignoring request to set protodown %s for interface %s (%u): protodown %s is already queued to dplane (reason bitfield: old 0x%x new 0x%x)",
+ new_down ? "on" : "off", ifp->name,
+ ifp->ifindex, new_down ? "on" : "off",
+ zif->protodown_rc, new_protodown_rc);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+int zebra_if_update_protodown_rc(struct interface *ifp, bool new_down,
+ uint32_t new_protodown_rc)
+{
+ struct zebra_if *zif;
+
+ zif = ifp->info;
+
+ /* Check if we already have this state or it's queued */
+ if (if_ignore_set_protodown(ifp, new_down, new_protodown_rc))
+ return 1;
+
+ zlog_info(
+ "Setting protodown %s - interface %s (%u): reason bitfield change from 0x%x --> 0x%x",
+ new_down ? "on" : "off", ifp->name, ifp->ifindex,
+ zif->protodown_rc, new_protodown_rc);
+
+ zif->protodown_rc = new_protodown_rc;
+
+ if (new_down)
+ SET_FLAG(zif->flags, ZIF_FLAG_SET_PROTODOWN);
+ else
+ SET_FLAG(zif->flags, ZIF_FLAG_UNSET_PROTODOWN);
+
+#ifdef HAVE_NETLINK
+ dplane_intf_update(ifp);
+#else
+ zlog_warn("Protodown is not supported on this platform");
+#endif
+ return 0;
+}
+
+int zebra_if_set_protodown(struct interface *ifp, bool new_down,
+ enum protodown_reasons new_reason)
+{
+ struct zebra_if *zif;
+ uint32_t new_protodown_rc;
+
+ zif = ifp->info;
+
+ if (new_down)
+ new_protodown_rc = zif->protodown_rc | new_reason;
+ else
+ new_protodown_rc = zif->protodown_rc & ~new_reason;
+
+ return zebra_if_update_protodown_rc(ifp, new_down, new_protodown_rc);
+}
+
+/*
+ * Handle an interface events based on info in a dplane context object.
+ * This runs in the main pthread, using the info in the context object to
+ * modify an interface.
+ */
+static void zebra_if_addr_update_ctx(struct zebra_dplane_ctx *ctx,
+ struct interface *ifp)
+{
+ uint8_t flags = 0;
+ const char *label = NULL;
+ uint32_t metric = METRIC_MAX;
+ const struct prefix *addr, *dest = NULL;
+ enum dplane_op_e op;
+
+ if (!ifp)
+ return;
+
+ op = dplane_ctx_get_op(ctx);
+ addr = dplane_ctx_get_intf_addr(ctx);
+
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: %s: ifindex %s(%u), addr %pFX", __func__,
+ dplane_op2str(dplane_ctx_get_op(ctx)), ifp->name,
+ ifp->ifindex, addr);
+
+ /* Is there a peer or broadcast address? */
+ dest = dplane_ctx_get_intf_dest(ctx);
+ if (dest->prefixlen == 0)
+ dest = NULL;
+
+ if (dplane_ctx_intf_is_connected(ctx))
+ SET_FLAG(flags, ZEBRA_IFA_PEER);
+
+ /* Flags. */
+ if (dplane_ctx_intf_is_secondary(ctx))
+ SET_FLAG(flags, ZEBRA_IFA_SECONDARY);
+
+ if (dplane_ctx_intf_is_noprefixroute(ctx))
+ SET_FLAG(flags, ZEBRA_IFA_NOPREFIXROUTE);
+
+ /* Label? */
+ if (dplane_ctx_intf_has_label(ctx))
+ label = dplane_ctx_get_intf_label(ctx);
+
+ if (label && strcmp(ifp->name, label) == 0)
+ label = NULL;
+
+ metric = dplane_ctx_get_intf_metric(ctx);
+
+ /* Register interface address to the interface. */
+ if (addr->family == AF_INET) {
+ if (op == DPLANE_OP_INTF_ADDR_ADD)
+ connected_add_ipv4(
+ ifp, flags, &addr->u.prefix4, addr->prefixlen,
+ dest ? &dest->u.prefix4 : NULL, label, metric);
+ else if (CHECK_FLAG(flags, ZEBRA_IFA_PEER)) {
+ /* Delete with a peer address */
+ connected_delete_ipv4(ifp, flags, &addr->u.prefix4,
+ addr->prefixlen,
+ &dest->u.prefix4);
+ } else
+ connected_delete_ipv4(ifp, flags, &addr->u.prefix4,
+ addr->prefixlen, NULL);
+ }
+
+ if (addr->family == AF_INET6) {
+ if (op == DPLANE_OP_INTF_ADDR_ADD) {
+ connected_add_ipv6(ifp, flags, &addr->u.prefix6,
+ dest ? &dest->u.prefix6 : NULL,
+ addr->prefixlen, label, metric);
+ } else
+ connected_delete_ipv6(ifp, &addr->u.prefix6, NULL,
+ addr->prefixlen);
+ }
+
+ /*
+ * Linux kernel does not send route delete on interface down/addr del
+ * so we have to re-process routes it owns (i.e. kernel routes)
+ */
+ if (op != DPLANE_OP_INTF_ADDR_ADD)
+ rib_update(RIB_UPDATE_KERNEL);
+}
+
+static void zebra_if_update_ctx(struct zebra_dplane_ctx *ctx,
+ struct interface *ifp)
+{
+ enum zebra_dplane_result dp_res;
+ struct zebra_if *zif;
+ bool pd_reason_val;
+ bool down;
+
+ if (!ifp) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: Can't find ifp", __func__);
+
+ return;
+ }
+
+ dp_res = dplane_ctx_get_status(ctx);
+ pd_reason_val = dplane_ctx_get_intf_pd_reason_val(ctx);
+ down = dplane_ctx_intf_is_protodown(ctx);
+
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: %s: if %s(%u) ctx-protodown %s ctx-reason %d",
+ __func__, dplane_op2str(dplane_ctx_get_op(ctx)),
+ ifp->name, ifp->ifindex, down ? "on" : "off",
+ pd_reason_val);
+
+ zif = ifp->info;
+ if (!zif) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: if %s(%u) zebra info pointer is NULL",
+ __func__, ifp->name, ifp->ifindex);
+ return;
+ }
+
+ if (dp_res != ZEBRA_DPLANE_REQUEST_SUCCESS) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: if %s(%u) dplane update failed",
+ __func__, ifp->name, ifp->ifindex);
+ goto done;
+ }
+
+ /* Update our info */
+ COND_FLAG(zif->flags, ZIF_FLAG_PROTODOWN, down);
+
+done:
+ /* Clear our dplane flags */
+ UNSET_FLAG(zif->flags, ZIF_FLAG_SET_PROTODOWN);
+ UNSET_FLAG(zif->flags, ZIF_FLAG_UNSET_PROTODOWN);
+}
+
+/*
+ * Handle netconf change from a dplane context object; runs in the main
+ * pthread so it can update zebra data structs.
+ */
+static void zebra_if_netconf_update_ctx(struct zebra_dplane_ctx *ctx,
+ struct interface *ifp,
+ ifindex_t ifindex)
+{
+ struct zebra_if *zif = NULL;
+ afi_t afi;
+ enum dplane_netconf_status_e mpls, mcast_on, linkdown;
+ bool *mcast_set, *linkdown_set;
+
+ if (!ifp && ifindex != -1 && ifindex != -2) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: Can't find ifp(%u)", __func__, ifindex);
+
+ return;
+ }
+
+ afi = dplane_ctx_get_afi(ctx);
+ mpls = dplane_ctx_get_netconf_mpls(ctx);
+ linkdown = dplane_ctx_get_netconf_linkdown(ctx);
+ mcast_on = dplane_ctx_get_netconf_mcast(ctx);
+
+ if (ifindex == DPLANE_NETCONF_IFINDEX_ALL) {
+ if (afi == AFI_IP) {
+ mcast_set = &zrouter.all_mc_forwardingv4;
+ linkdown_set = &zrouter.all_linkdownv4;
+ } else {
+ mcast_set = &zrouter.all_mc_forwardingv6;
+ linkdown_set = &zrouter.all_linkdownv6;
+ }
+ } else if (ifindex == DPLANE_NETCONF_IFINDEX_DEFAULT) {
+ if (afi == AFI_IP) {
+ mcast_set = &zrouter.default_mc_forwardingv4;
+ linkdown_set = &zrouter.default_linkdownv4;
+ } else {
+ mcast_set = &zrouter.default_mc_forwardingv6;
+ linkdown_set = &zrouter.default_linkdownv6;
+ }
+ } else {
+ zif = ifp->info;
+ if (!zif) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug(
+ "%s: if %s(%u) zebra info pointer is NULL",
+ __func__, ifp ? ifp->name : "(null)",
+ ifp ? ifp->ifindex : ifindex);
+ return;
+ }
+ if (afi == AFI_IP) {
+ mcast_set = &zif->v4mcast_on;
+ linkdown_set = &zif->linkdown;
+ } else {
+ mcast_set = &zif->v6mcast_on;
+ linkdown_set = &zif->linkdownv6;
+ }
+
+ /*
+ * mpls netconf data is neither v4 or v6 it's AF_MPLS!
+ */
+ if (mpls == DPLANE_NETCONF_STATUS_ENABLED) {
+ zif->mpls = true;
+ zebra_mpls_turned_on();
+ } else if (mpls == DPLANE_NETCONF_STATUS_DISABLED)
+ zif->mpls = false;
+ }
+
+ if (linkdown == DPLANE_NETCONF_STATUS_ENABLED)
+ *linkdown_set = true;
+ else if (linkdown == DPLANE_NETCONF_STATUS_DISABLED)
+ *linkdown_set = false;
+
+ if (mcast_on == DPLANE_NETCONF_STATUS_ENABLED)
+ *mcast_set = true;
+ else if (mcast_on == DPLANE_NETCONF_STATUS_DISABLED)
+ *mcast_set = false;
+
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug(
+ "%s: afi: %d if %s, ifindex %d, mpls %s mc_forwarding: %s linkdown %s",
+ __func__, afi, ifp ? ifp->name : "Global",
+ ifp ? ifp->ifindex : ifindex,
+ (zif ? (zif->mpls ? "ON" : "OFF") : "OFF"),
+ (*mcast_set ? "ON" : "OFF"),
+ (*linkdown_set ? "ON" : "OFF"));
+}
+
+static void interface_vrf_change(enum dplane_op_e op, ifindex_t ifindex,
+ const char *name, uint32_t tableid,
+ ns_id_t ns_id)
+{
+ struct vrf *vrf;
+ struct zebra_vrf *zvrf = NULL;
+
+ if (op == DPLANE_OP_INTF_DELETE) {
+ if (IS_ZEBRA_DEBUG_DPLANE)
+ zlog_debug("DPLANE_OP_INTF_DELETE for VRF %s(%u)", name,
+ ifindex);
+
+ vrf = vrf_lookup_by_id((vrf_id_t)ifindex);
+ if (!vrf) {
+ flog_warn(EC_ZEBRA_VRF_NOT_FOUND,
+ "%s(%u): vrf not found", name, ifindex);
+ return;
+ }
+
+ vrf_delete(vrf);
+ } else {
+ if (IS_ZEBRA_DEBUG_DPLANE)
+ zlog_debug(
+ "DPLANE_OP_INTF_UPDATE for VRF %s(%u) table %u",
+ name, ifindex, tableid);
+
+ /*
+ * For a given tableid, if there already exists a vrf and it
+ * is different from the current vrf to be operated, then there
+ * is a misconfiguration and zebra will exit.
+ */
+ vrf_id_t exist_id = zebra_vrf_lookup_by_table(tableid, ns_id);
+
+ if (exist_id != VRF_DEFAULT) {
+ vrf = vrf_lookup_by_id(exist_id);
+
+ if (!vrf_lookup_by_id((vrf_id_t)ifindex) && !vrf) {
+ flog_err(EC_ZEBRA_VRF_NOT_FOUND,
+ "VRF %s id %u does not exist", name,
+ ifindex);
+ exit(-1);
+ }
+
+ if (vrf && strcmp(name, vrf->name)) {
+ flog_err(EC_ZEBRA_VRF_MISCONFIGURED,
+ "VRF %s id %u table id overlaps existing vrf %s(%d), misconfiguration exiting",
+ name, ifindex, vrf->name, vrf->vrf_id);
+ exit(-1);
+ }
+ }
+
+ vrf = vrf_update((vrf_id_t)ifindex, name);
+ if (!vrf) {
+ flog_err(EC_LIB_INTERFACE, "VRF %s id %u not created",
+ name, ifindex);
+ return;
+ }
+
+ /*
+ * This is the only place that we get the actual kernel table_id
+ * being used. We need it to set the table_id of the routes
+ * we are passing to the kernel.... And to throw some totally
+ * awesome parties. that too.
+ *
+ * At this point we *must* have a zvrf because the vrf_create
+ * callback creates one. We *must* set the table id
+ * before the vrf_enable because of( at the very least )
+ * static routes being delayed for installation until
+ * during the vrf_enable callbacks.
+ */
+ zvrf = (struct zebra_vrf *)vrf->info;
+ zvrf->table_id = tableid;
+
+ /* Enable the created VRF. */
+ if (!vrf_enable(vrf)) {
+ flog_err(EC_LIB_INTERFACE,
+ "Failed to enable VRF %s id %u", name,
+ ifindex);
+ return;
+ }
+ }
+}
+
+/*
+ * Note: on netlink systems, there should be a 1-to-1 mapping
+ * between interface names and ifindex values.
+ */
+static void set_ifindex(struct interface *ifp, ifindex_t ifi_index,
+ struct zebra_ns *zns)
+{
+ struct interface *oifp;
+
+ oifp = if_lookup_by_index_per_ns(zns, ifi_index);
+ if ((oifp != NULL) && (oifp != ifp)) {
+ if (ifi_index == IFINDEX_INTERNAL)
+ flog_err(
+ EC_LIB_INTERFACE,
+ "Netlink is setting interface %s ifindex to reserved internal value %u",
+ ifp->name, ifi_index);
+ else {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug(
+ "interface index %d was renamed from %s to %s",
+ ifi_index, oifp->name, ifp->name);
+ if (if_is_up(oifp))
+ flog_err(
+ EC_LIB_INTERFACE,
+ "interface rename detected on up interface: index %d was renamed from %s to %s, results are uncertain!",
+ ifi_index, oifp->name, ifp->name);
+ if_delete_update(&oifp);
+ }
+ }
+ if_set_index(ifp, ifi_index);
+}
+
+static inline void zebra_if_set_ziftype(struct interface *ifp,
+ enum zebra_iftype zif_type,
+ enum zebra_slave_iftype zif_slave_type)
+{
+ struct zebra_if *zif;
+
+ zif = (struct zebra_if *)ifp->info;
+ zif->zif_slave_type = zif_slave_type;
+
+ if (zif->zif_type != zif_type) {
+ zif->zif_type = zif_type;
+ /* If the if_type has been set to bond initialize ES info
+ * against it. XXX - note that we don't handle the case where
+ * a zif changes from bond to non-bond; it is really
+ * an unexpected/error condition.
+ */
+ zebra_evpn_if_init(zif);
+ }
+}
+
+static void interface_update_hw_addr(struct zebra_dplane_ctx *ctx,
+ struct interface *ifp)
+{
+ int i;
+
+ ifp->hw_addr_len = dplane_ctx_get_ifp_hw_addr_len(ctx);
+ memcpy(ifp->hw_addr, dplane_ctx_get_ifp_hw_addr(ctx), ifp->hw_addr_len);
+
+ for (i = 0; i < ifp->hw_addr_len; i++)
+ if (ifp->hw_addr[i] != 0)
+ break;
+
+ if (i == ifp->hw_addr_len)
+ ifp->hw_addr_len = 0;
+}
+
+static void interface_update_l2info(struct zebra_dplane_ctx *ctx,
+ struct interface *ifp,
+ enum zebra_iftype zif_type, int add,
+ ns_id_t link_nsid)
+{
+ const struct zebra_l2info_vxlan *vxlan_info;
+ const struct zebra_l2info_gre *gre_info;
+
+ switch (zif_type) {
+ case ZEBRA_IF_BRIDGE:
+ zebra_l2_bridge_add_update(ifp,
+ dplane_ctx_get_ifp_bridge_info(ctx));
+ break;
+ case ZEBRA_IF_VLAN:
+ zebra_l2_vlanif_update(ifp, dplane_ctx_get_ifp_vlan_info(ctx));
+ zebra_evpn_acc_bd_svi_set(ifp->info, NULL,
+ !!if_is_operative(ifp));
+ break;
+ case ZEBRA_IF_VXLAN:
+ vxlan_info = dplane_ctx_get_ifp_vxlan_info(ctx);
+ zebra_l2_vxlanif_add_update(ifp, vxlan_info, add);
+ if (link_nsid != NS_UNKNOWN && vxlan_info->ifindex_link)
+ zebra_if_update_link(ifp, vxlan_info->ifindex_link,
+ link_nsid);
+ break;
+ case ZEBRA_IF_GRE:
+ gre_info = dplane_ctx_get_ifp_gre_info(ctx);
+ zebra_l2_greif_add_update(ifp, gre_info, add);
+ if (link_nsid != NS_UNKNOWN && gre_info->ifindex_link)
+ zebra_if_update_link(ifp, gre_info->ifindex_link,
+ link_nsid);
+ break;
+ case ZEBRA_IF_OTHER:
+ case ZEBRA_IF_VRF:
+ case ZEBRA_IF_MACVLAN:
+ case ZEBRA_IF_VETH:
+ case ZEBRA_IF_BOND:
+ break;
+ }
+}
+
+static bool is_if_protodown_reason_only_frr(uint32_t rc_bitfield)
+{
+ uint8_t frr_protodown_r_bit = if_netlink_get_frr_protodown_r_bit();
+
+ return (rc_bitfield == (((uint32_t)1) << frr_protodown_r_bit));
+}
+
+static void interface_if_protodown(struct interface *ifp, bool protodown,
+ uint32_t rc_bitfield)
+{
+ struct zebra_if *zif = ifp->info;
+ bool old_protodown, reason_extern;
+
+ reason_extern = !!CHECK_FLAG(zif->protodown_rc,
+ ZEBRA_PROTODOWN_EXTERNAL);
+ /*
+ * Set our reason code to note it wasn't us.
+ * If the reason we got from the kernel is ONLY frr though, don't
+ * set it.
+ */
+ COND_FLAG(zif->protodown_rc, ZEBRA_PROTODOWN_EXTERNAL,
+ protodown && rc_bitfield &&
+ !is_if_protodown_reason_only_frr(rc_bitfield));
+
+
+ old_protodown = !!ZEBRA_IF_IS_PROTODOWN(zif);
+ if (protodown == old_protodown)
+ return;
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_DPLANE)
+ zlog_debug("interface %s dplane change, protodown %s curr reason_extern %u",
+ ifp->name, protodown ? "on" : "off", reason_extern);
+
+ /* Set protodown, respectively */
+ COND_FLAG(zif->flags, ZIF_FLAG_PROTODOWN, protodown);
+
+ if (zebra_evpn_is_es_bond_member(ifp)) {
+ /* Check it's not already being sent to the dplane first */
+ if (protodown &&
+ CHECK_FLAG(zif->flags, ZIF_FLAG_SET_PROTODOWN)) {
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug(
+ "bond mbr %s protodown on recv'd but already sent protodown on to the dplane",
+ ifp->name);
+ return;
+ }
+
+ if (!protodown &&
+ CHECK_FLAG(zif->flags, ZIF_FLAG_UNSET_PROTODOWN)) {
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug(
+ "bond mbr %s protodown off recv'd but already sent protodown off to the dplane",
+ ifp->name);
+ return;
+ }
+
+ if (!protodown && reason_extern) {
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("bond member %s has protodown reason external and clear the reason, skip reinstall.",
+ ifp->name);
+ return;
+ }
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug(
+ "bond mbr %s reinstate protodown %s in the dplane",
+ ifp->name, old_protodown ? "on" : "off");
+
+ if (old_protodown)
+ SET_FLAG(zif->flags, ZIF_FLAG_SET_PROTODOWN);
+ else
+ SET_FLAG(zif->flags, ZIF_FLAG_UNSET_PROTODOWN);
+
+ dplane_intf_update(zif->ifp);
+ }
+}
+
+static void if_sweep_protodown(struct zebra_if *zif)
+{
+ bool protodown;
+
+ protodown = !!ZEBRA_IF_IS_PROTODOWN(zif);
+
+ if (!protodown)
+ return;
+
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("interface %s sweeping protodown %s reason 0x%x",
+ zif->ifp->name, protodown ? "on" : "off",
+ zif->protodown_rc);
+
+ /* Only clear our reason codes, leave external if it was set */
+ UNSET_FLAG(zif->protodown_rc, ZEBRA_PROTODOWN_ALL);
+ dplane_intf_update(zif->ifp);
+}
+
+static void
+interface_bridge_vxlan_vlan_vni_map_update(struct zebra_dplane_ctx *ctx,
+ struct interface *ifp)
+{
+ const struct zebra_vxlan_vni_array *vniarray =
+ dplane_ctx_get_ifp_vxlan_vni_array(ctx);
+ struct zebra_vxlan_vni vni_start, vni_end;
+ struct hash *vni_table = NULL;
+ struct zebra_vxlan_vni vni, *vnip;
+ vni_t vni_id;
+ vlanid_t vid;
+ int i;
+
+ if (vniarray == NULL)
+ return;
+
+ memset(&vni_start, 0, sizeof(vni_start));
+ memset(&vni_end, 0, sizeof(vni_end));
+
+ for (i = 0; i < vniarray->count; i++) {
+ uint16_t flags = vniarray->vnis[i].flags;
+
+ if (flags & DPLANE_BRIDGE_VLAN_INFO_RANGE_BEGIN) {
+ vni_start = vniarray->vnis[i];
+ continue;
+ }
+
+ if (flags & DPLANE_BRIDGE_VLAN_INFO_RANGE_END)
+ vni_end = vniarray->vnis[i];
+
+ if (!(flags & DPLANE_BRIDGE_VLAN_INFO_RANGE_END)) {
+ vni_start = vniarray->vnis[i];
+ vni_end = vniarray->vnis[i];
+ }
+
+ if (IS_ZEBRA_DEBUG_DPLANE)
+ zlog_debug(
+ "Vlan-Vni(%d:%d-%d:%d) update for VxLAN IF %s(%u)",
+ vni_start.access_vlan, vni_end.access_vlan,
+ vni_start.vni, vni_end.vni, ifp->name,
+ ifp->ifindex);
+
+ if (!vni_table) {
+ vni_table = zebra_vxlan_vni_table_create();
+ if (!vni_table)
+ return;
+ }
+
+ for (vid = vni_start.access_vlan, vni_id = vni_start.vni;
+ vid <= vni_end.access_vlan; vid++, vni_id++) {
+
+ memset(&vni, 0, sizeof(vni));
+ vni.vni = vni_id;
+ vni.access_vlan = vid;
+ vnip = hash_get(vni_table, &vni, zebra_vxlan_vni_alloc);
+ if (!vnip)
+ return;
+ }
+
+ memset(&vni_start, 0, sizeof(vni_start));
+ memset(&vni_end, 0, sizeof(vni_end));
+ }
+
+ if (vni_table)
+ zebra_vxlan_if_vni_table_add_update(ifp, vni_table);
+}
+
+static void interface_bridge_vxlan_update(struct zebra_dplane_ctx *ctx,
+ struct interface *ifp)
+{
+ struct zebra_if *zif = ifp->info;
+ const struct zebra_dplane_bridge_vlan_info *bvinfo;
+
+ if (dplane_ctx_get_ifp_no_afspec(ctx))
+ return;
+
+ if (IS_ZEBRA_VXLAN_IF_SVD(zif))
+ interface_bridge_vxlan_vlan_vni_map_update(ctx, ifp);
+
+ if (dplane_ctx_get_ifp_no_bridge_vlan_info(ctx))
+ return;
+
+ bvinfo = dplane_ctx_get_ifp_bridge_vlan_info(ctx);
+
+ if (!(bvinfo->flags & DPLANE_BRIDGE_VLAN_INFO_PVID))
+ return;
+
+ if (IS_ZEBRA_DEBUG_DPLANE)
+ zlog_debug("Access VLAN %u for VxLAN IF %s(%u)", bvinfo->vid,
+ ifp->name, ifp->ifindex);
+
+ zebra_l2_vxlanif_update_access_vlan(ifp, bvinfo->vid);
+}
+
+static void interface_bridge_vlan_update(struct zebra_dplane_ctx *ctx,
+ struct interface *ifp)
+{
+ struct zebra_if *zif = ifp->info;
+ const struct zebra_dplane_bridge_vlan_info_array *bvarray;
+ struct zebra_dplane_bridge_vlan_info bvinfo;
+ bitfield_t old_vlan_bitmap;
+ uint16_t vid_range_start = 0;
+ int32_t i;
+
+ /* cache the old bitmap addrs */
+ old_vlan_bitmap = zif->vlan_bitmap;
+ /* create a new bitmap space for re-eval */
+ bf_init(zif->vlan_bitmap, IF_VLAN_BITMAP_MAX);
+
+ /* Could we have multiple bridge vlan infos? */
+ bvarray = dplane_ctx_get_ifp_bridge_vlan_info_array(ctx);
+ if (!bvarray)
+ return;
+
+ for (i = 0; i < bvarray->count; i++) {
+ bvinfo = bvarray->array[i];
+
+ if (bvinfo.flags & DPLANE_BRIDGE_VLAN_INFO_RANGE_BEGIN) {
+ vid_range_start = bvinfo.vid;
+ continue;
+ }
+
+ if (!(bvinfo.flags & DPLANE_BRIDGE_VLAN_INFO_RANGE_END))
+ vid_range_start = bvinfo.vid;
+
+ zebra_vlan_bitmap_compute(ifp, vid_range_start, bvinfo.vid);
+ }
+
+ zebra_vlan_mbr_re_eval(ifp, old_vlan_bitmap);
+ bf_free(old_vlan_bitmap);
+}
+
+static void interface_bridge_handling(struct zebra_dplane_ctx *ctx,
+ struct interface *ifp,
+ enum zebra_iftype zif_type)
+{
+ struct zebra_if *zif;
+
+ if (!ifp) {
+ zlog_warn("Cannot find bridge if %s(%u)",
+ dplane_ctx_get_ifname(ctx),
+ dplane_ctx_get_ifindex(ctx));
+ return;
+ }
+
+ if (IS_ZEBRA_IF_VXLAN(ifp))
+ return interface_bridge_vxlan_update(ctx, ifp);
+
+ /*
+ * build vlan bitmap associated with this interface if that
+ * device type is interested in the vlans
+ */
+ zif = ifp->info;
+ if (bf_is_inited(zif->vlan_bitmap))
+ interface_bridge_vlan_update(ctx, ifp);
+}
+
+static void zebra_if_dplane_ifp_handling(struct zebra_dplane_ctx *ctx)
+{
+ enum dplane_op_e op = dplane_ctx_get_op(ctx);
+ const char *name = dplane_ctx_get_ifname(ctx);
+ ns_id_t ns_id = dplane_ctx_get_ns_id(ctx);
+ ifindex_t ifindex = dplane_ctx_get_ifindex(ctx);
+ ifindex_t bond_ifindex = dplane_ctx_get_ifp_bond_ifindex(ctx);
+ uint32_t tableid = dplane_ctx_get_ifp_table_id(ctx);
+ enum zebra_iftype zif_type = dplane_ctx_get_ifp_zif_type(ctx);
+ struct interface *ifp;
+ struct zebra_ns *zns;
+
+ zns = zebra_ns_lookup(ns_id);
+ if (!zns) {
+ zlog_err("Where is our namespace?");
+ return;
+ }
+
+ if (IS_ZEBRA_DEBUG_DPLANE)
+ zlog_debug("%s for %s(%u)", dplane_op2str(op), name, ifindex);
+
+ ifp = if_lookup_by_name_per_ns(zns, name);
+ if (op == DPLANE_OP_INTF_DELETE) {
+ /* Delete interface notification from kernel */
+ if (ifp == NULL) {
+ if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_debug(
+ "Delete LINK received for unknown interface %s(%u)",
+ name, ifindex);
+ return;
+ }
+
+ if (IS_ZEBRA_IF_BOND(ifp))
+ zebra_l2if_update_bond(ifp, false);
+ if (IS_ZEBRA_IF_BOND_SLAVE(ifp))
+ zebra_l2if_update_bond_slave(ifp, bond_ifindex, false);
+ /* Special handling for bridge or VxLAN interfaces. */
+ if (IS_ZEBRA_IF_BRIDGE(ifp))
+ zebra_l2_bridge_del(ifp);
+ else if (IS_ZEBRA_IF_VXLAN(ifp))
+ zebra_l2_vxlanif_del(ifp);
+
+ if_delete_update(&ifp);
+
+ if (zif_type == ZEBRA_IF_VRF && !vrf_is_backend_netns())
+ interface_vrf_change(op, ifindex, name, tableid, ns_id);
+ } else {
+ ifindex_t master_ifindex, bridge_ifindex, bond_ifindex,
+ link_ifindex;
+ enum zebra_slave_iftype zif_slave_type;
+ uint8_t bypass;
+ uint64_t flags;
+ vrf_id_t vrf_id;
+ uint32_t mtu;
+ ns_id_t link_nsid;
+ struct zebra_if *zif;
+ bool protodown, protodown_set, startup;
+ uint32_t rc_bitfield;
+ uint8_t old_hw_addr[INTERFACE_HWADDR_MAX];
+ char *desc;
+ uint8_t family;
+
+ /* If VRF, create or update the VRF structure itself. */
+ if (zif_type == ZEBRA_IF_VRF && !vrf_is_backend_netns())
+ interface_vrf_change(op, ifindex, name, tableid, ns_id);
+
+ master_ifindex = dplane_ctx_get_ifp_master_ifindex(ctx);
+ zif_slave_type = dplane_ctx_get_ifp_zif_slave_type(ctx);
+ bridge_ifindex = dplane_ctx_get_ifp_bridge_ifindex(ctx);
+ bond_ifindex = dplane_ctx_get_ifp_bond_ifindex(ctx);
+ bypass = dplane_ctx_get_ifp_bypass(ctx);
+ flags = dplane_ctx_get_ifp_flags(ctx);
+ vrf_id = dplane_ctx_get_ifp_vrf_id(ctx);
+ mtu = dplane_ctx_get_ifp_mtu(ctx);
+ link_ifindex = dplane_ctx_get_ifp_link_ifindex(ctx);
+ link_nsid = dplane_ctx_get_ifp_link_nsid(ctx);
+ protodown_set = dplane_ctx_get_ifp_protodown_set(ctx);
+ protodown = dplane_ctx_get_ifp_protodown(ctx);
+ rc_bitfield = dplane_ctx_get_ifp_rc_bitfield(ctx);
+ startup = dplane_ctx_get_ifp_startup(ctx);
+ desc = dplane_ctx_get_ifp_desc(ctx);
+ family = dplane_ctx_get_ifp_family(ctx);
+
+#ifndef AF_BRIDGE
+ /*
+ * Work around to make free bsd happy at the moment
+ */
+#define AF_BRIDGE 7
+#endif
+ if (family == AF_BRIDGE)
+ return interface_bridge_handling(ctx, ifp, zif_type);
+
+ if (ifp == NULL ||
+ !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) {
+ /* Add interface notification from kernel */
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug(
+ "RTM_NEWLINK ADD for %s(%u) vrf_id %u type %d sl_type %d master %u",
+ name, ifindex, vrf_id, zif_type,
+ zif_slave_type, master_ifindex);
+
+ if (ifp == NULL) {
+ /* unknown interface */
+ ifp = if_get_by_name(name, vrf_id, NULL);
+ } else {
+ /* pre-configured interface, learnt now */
+ if (ifp->vrf->vrf_id != vrf_id)
+ if_update_to_new_vrf(ifp, vrf_id);
+ }
+
+ zif = ifp->info;
+
+ /* Update interface information. */
+ set_ifindex(ifp, ifindex, zns);
+ ifp->flags = flags;
+ ifp->mtu6 = ifp->mtu = mtu;
+ ifp->metric = 0;
+ ifp->speed = kernel_get_speed(ifp, NULL);
+ ifp->ptm_status = ZEBRA_PTM_STATUS_UNKNOWN;
+ ifp->txqlen = dplane_ctx_get_intf_txqlen(ctx);
+
+ /* Set interface type */
+ zebra_if_set_ziftype(ifp, zif_type, zif_slave_type);
+ if (IS_ZEBRA_IF_VRF(ifp))
+ SET_FLAG(ifp->status,
+ ZEBRA_INTERFACE_VRF_LOOPBACK);
+
+ /* Update link. */
+ zebra_if_update_link(ifp, link_ifindex, link_nsid);
+
+ ifp->ll_type = dplane_ctx_get_ifp_zltype(ctx);
+ interface_update_hw_addr(ctx, ifp);
+
+ /* Inform clients, install any configured addresses. */
+ if_add_update(ifp);
+
+ /*
+ * Extract and save L2 interface information, take
+ * additional actions.
+ */
+ interface_update_l2info(ctx, ifp, zif_type, 1,
+ link_nsid);
+ if (IS_ZEBRA_IF_BOND(ifp))
+ zebra_l2if_update_bond(ifp, true);
+ if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp))
+ zebra_l2if_update_bridge_slave(
+ ifp, bridge_ifindex, ns_id,
+ ZEBRA_BRIDGE_NO_ACTION);
+ else if (IS_ZEBRA_IF_BOND_SLAVE(ifp))
+ zebra_l2if_update_bond_slave(ifp, bond_ifindex,
+ !!bypass);
+
+ if (protodown_set) {
+ interface_if_protodown(ifp, protodown,
+ rc_bitfield);
+ if (startup)
+ if_sweep_protodown(zif);
+ }
+
+ if (IS_ZEBRA_IF_BRIDGE(ifp)) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug(
+ "RTM_NEWLINK ADD for %s(%u), vlan-aware %d",
+ name, ifp->ifindex,
+ IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(
+ zif));
+ }
+ } else if (ifp->vrf->vrf_id != vrf_id) {
+ /* VRF change for an interface. */
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug(
+ "RTM_NEWLINK vrf-change for %s(%u) vrf_id %u -> %u",
+ name, ifp->ifindex, ifp->vrf->vrf_id,
+ vrf_id);
+
+ if_handle_vrf_change(ifp, vrf_id);
+ } else {
+ bool was_bridge_slave, was_bond_slave;
+ uint8_t chgflags = ZEBRA_BRIDGE_NO_ACTION;
+
+ zif = ifp->info;
+
+ /* Interface update. */
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug(
+ "RTM_NEWLINK update for %s(%u) sl_type %d master %u",
+ name, ifp->ifindex, zif_slave_type,
+ master_ifindex);
+
+ set_ifindex(ifp, ifindex, zns);
+ ifp->mtu6 = ifp->mtu = mtu;
+ ifp->metric = 0;
+ ifp->txqlen = dplane_ctx_get_intf_txqlen(ctx);
+
+ /*
+ * Update interface type - NOTE: Only slave_type can
+ * change.
+ */
+ was_bridge_slave = IS_ZEBRA_IF_BRIDGE_SLAVE(ifp);
+ was_bond_slave = IS_ZEBRA_IF_BOND_SLAVE(ifp);
+ zebra_if_set_ziftype(ifp, zif_type, zif_slave_type);
+
+ memcpy(old_hw_addr, ifp->hw_addr, INTERFACE_HWADDR_MAX);
+
+ /* Update link. */
+ zebra_if_update_link(ifp, link_ifindex, link_nsid);
+
+ ifp->ll_type = dplane_ctx_get_ifp_zltype(ctx);
+ interface_update_hw_addr(ctx, ifp);
+
+ if (protodown_set)
+ interface_if_protodown(ifp, protodown,
+ rc_bitfield);
+
+ if (if_is_no_ptm_operative(ifp)) {
+ bool is_up = if_is_operative(ifp);
+
+ ifp->flags = flags;
+ if (!if_is_no_ptm_operative(ifp) ||
+ CHECK_FLAG(zif->flags,
+ ZIF_FLAG_PROTODOWN)) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug(
+ "Intf %s(%u) has gone DOWN",
+ name, ifp->ifindex);
+ if_down(ifp);
+ rib_update(RIB_UPDATE_KERNEL);
+ } else if (if_is_operative(ifp)) {
+ bool mac_updated = false;
+
+ /*
+ * Must notify client daemons of new
+ * interface status.
+ */
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug(
+ "Intf %s(%u) PTM up, notifying clients",
+ name, ifp->ifindex);
+ if_up(ifp, !is_up);
+
+ /*
+ * Update EVPN VNI when SVI MAC change
+ */
+ if (memcmp(old_hw_addr, ifp->hw_addr,
+ INTERFACE_HWADDR_MAX))
+ mac_updated = true;
+ if (IS_ZEBRA_IF_VLAN(ifp) &&
+ mac_updated) {
+ struct interface *link_if;
+
+ link_if = if_lookup_by_index_per_ns(
+ zebra_ns_lookup(
+ NS_DEFAULT),
+ link_ifindex);
+ if (link_if)
+ zebra_vxlan_svi_up(
+ ifp, link_if);
+ } else if (mac_updated &&
+ IS_ZEBRA_IF_BRIDGE(ifp)) {
+ zlog_debug(
+ "Intf %s(%u) bridge changed MAC address",
+ name, ifp->ifindex);
+ chgflags =
+ ZEBRA_BRIDGE_MASTER_MAC_CHANGE;
+ }
+ }
+ } else {
+ ifp->flags = flags;
+ if (if_is_operative(ifp) &&
+ !CHECK_FLAG(zif->flags,
+ ZIF_FLAG_PROTODOWN)) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug(
+ "Intf %s(%u) has come UP",
+ name, ifp->ifindex);
+ if_up(ifp, true);
+ if (IS_ZEBRA_IF_BRIDGE(ifp))
+ chgflags =
+ ZEBRA_BRIDGE_MASTER_UP;
+ } else {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug(
+ "Intf %s(%u) has gone DOWN",
+ name, ifp->ifindex);
+ if_down(ifp);
+ rib_update(RIB_UPDATE_KERNEL);
+ }
+ }
+
+ /*
+ * Extract and save L2 interface information, take
+ * additional actions.
+ */
+ interface_update_l2info(ctx, ifp, zif_type, 0,
+ link_nsid);
+ if (IS_ZEBRA_IF_BRIDGE(ifp))
+ zebra_l2if_update_bridge(ifp, chgflags);
+ if (IS_ZEBRA_IF_BOND(ifp))
+ zebra_l2if_update_bond(ifp, true);
+ if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp) || was_bridge_slave)
+ zebra_l2if_update_bridge_slave(
+ ifp, bridge_ifindex, ns_id, chgflags);
+ else if (IS_ZEBRA_IF_BOND_SLAVE(ifp) || was_bond_slave)
+ zebra_l2if_update_bond_slave(ifp, bond_ifindex,
+ !!bypass);
+ if (IS_ZEBRA_IF_BRIDGE(ifp)) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug(
+ "RTM_NEWLINK update for %s(%u), vlan-aware %d",
+ name, ifp->ifindex,
+ IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(
+ zif));
+ }
+ }
+
+ zif = ifp->info;
+ if (zif) {
+ XFREE(MTYPE_ZIF_DESC, zif->desc);
+ if (desc[0])
+ zif->desc = XSTRDUP(MTYPE_ZIF_DESC, desc);
+ }
+ }
+}
+
+void zebra_if_dplane_result(struct zebra_dplane_ctx *ctx)
+{
+ struct zebra_ns *zns;
+ struct interface *ifp;
+ ns_id_t ns_id;
+ enum dplane_op_e op;
+ enum zebra_dplane_result dp_res;
+ ifindex_t ifindex;
+
+ ns_id = dplane_ctx_get_ns_id(ctx);
+ dp_res = dplane_ctx_get_status(ctx);
+ op = dplane_ctx_get_op(ctx);
+ ifindex = dplane_ctx_get_ifindex(ctx);
+
+ if (IS_ZEBRA_DEBUG_DPLANE_DETAIL || IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("Intf dplane ctx %p, op %s, ifindex (%u), result %s",
+ ctx, dplane_op2str(op), ifindex,
+ dplane_res2str(dp_res));
+
+ zns = zebra_ns_lookup(ns_id);
+ if (zns == NULL) {
+ /* No ns - deleted maybe? */
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: can't find zns id %u", __func__, ns_id);
+
+ return;
+ }
+
+ ifp = if_lookup_by_index_per_ns(zns, ifindex);
+
+ if (op == DPLANE_OP_INTF_ADDR_ADD || op == DPLANE_OP_INTF_ADDR_DEL) {
+ zebra_if_addr_update_ctx(ctx, ifp);
+ } else if (op == DPLANE_OP_INTF_INSTALL ||
+ op == DPLANE_OP_INTF_UPDATE || op == DPLANE_OP_INTF_DELETE) {
+ /*
+ * Queued from the dplane means it is something
+ * that we need to handle( create/delete the
+ * interface as needed )
+ */
+ if (dp_res == ZEBRA_DPLANE_REQUEST_QUEUED)
+ zebra_if_dplane_ifp_handling(ctx);
+ else
+ zebra_if_update_ctx(ctx, ifp);
+ } else if (op == DPLANE_OP_INTF_NETCONFIG) {
+ zebra_if_netconf_update_ctx(ctx, ifp, ifindex);
+ }
+}
+
+/* Dump if address information to vty. */
+static void connected_dump_vty(struct vty *vty, json_object *json,
+ struct connected *connected)
+{
+ struct prefix *p;
+ json_object *json_addr = NULL;
+
+ /* Print interface address. */
+ p = connected->address;
+
+ if (json) {
+ json_addr = json_object_new_object();
+ json_object_array_add(json, json_addr);
+ json_object_string_addf(json_addr, "address", "%pFX", p);
+ } else {
+ vty_out(vty, " %s %pFX", prefix_family_str(p), p);
+ }
+
+ /* If there is destination address, print it. */
+ if (CONNECTED_PEER(connected) && connected->destination) {
+ if (json) {
+ json_object_string_addf(json_addr, "peer", "%pFX",
+ connected->destination);
+ } else {
+ vty_out(vty, " peer %pFX", connected->destination);
+ }
+ }
+
+ if (json)
+ json_object_boolean_add(
+ json_addr, "secondary",
+ CHECK_FLAG(connected->flags, ZEBRA_IFA_SECONDARY));
+ else if (CHECK_FLAG(connected->flags, ZEBRA_IFA_SECONDARY))
+ vty_out(vty, " secondary");
+
+ if (json)
+ json_object_boolean_add(json_addr, "noPrefixRoute",
+ CHECK_FLAG(connected->flags, ZEBRA_IFA_NOPREFIXROUTE));
+ else if (CHECK_FLAG(connected->flags, ZEBRA_IFA_NOPREFIXROUTE))
+ vty_out(vty, " noprefixroute");
+
+ if (json)
+ json_object_boolean_add(
+ json_addr, "unnumbered",
+ CHECK_FLAG(connected->flags, ZEBRA_IFA_UNNUMBERED));
+ else if (CHECK_FLAG(connected->flags, ZEBRA_IFA_UNNUMBERED))
+ vty_out(vty, " unnumbered");
+
+ if (connected->label) {
+ if (json)
+ json_object_string_add(json_addr, "label",
+ connected->label);
+ else
+ vty_out(vty, " %s", connected->label);
+ }
+
+ if (!json)
+ vty_out(vty, "\n");
+}
+
+/* Dump interface neighbor address information to vty. */
+static void nbr_connected_dump_vty(struct vty *vty, json_object *json,
+ struct nbr_connected *connected)
+{
+ struct prefix *p;
+ char buf[PREFIX2STR_BUFFER];
+
+ /* Print interface address. */
+ p = connected->address;
+ if (json)
+ json_array_string_add(json, prefix2str(p, buf, sizeof(buf)));
+ else
+ vty_out(vty, " %s %pFX\n", prefix_family_str(p), p);
+}
+
+static const char *
+zebra_zifslavetype_2str(enum zebra_slave_iftype zif_slave_type)
+{
+ switch (zif_slave_type) {
+ case ZEBRA_IF_SLAVE_BRIDGE:
+ return "Bridge";
+ case ZEBRA_IF_SLAVE_VRF:
+ return "Vrf";
+ case ZEBRA_IF_SLAVE_BOND:
+ return "Bond";
+ case ZEBRA_IF_SLAVE_OTHER:
+ return "Other";
+ case ZEBRA_IF_SLAVE_NONE:
+ return "None";
+ }
+ return "None";
+}
+
+static const char *zebra_ziftype_2str(enum zebra_iftype zif_type)
+{
+ switch (zif_type) {
+ case ZEBRA_IF_OTHER:
+ return "Other";
+
+ case ZEBRA_IF_BRIDGE:
+ return "Bridge";
+
+ case ZEBRA_IF_VLAN:
+ return "Vlan";
+
+ case ZEBRA_IF_VXLAN:
+ return "Vxlan";
+
+ case ZEBRA_IF_VRF:
+ return "VRF";
+
+ case ZEBRA_IF_VETH:
+ return "VETH";
+
+ case ZEBRA_IF_BOND:
+ return "bond";
+
+ case ZEBRA_IF_MACVLAN:
+ return "macvlan";
+
+ case ZEBRA_IF_GRE:
+ return "GRE";
+
+ default:
+ return "Unknown";
+ }
+}
+
+/* Interface's brief information print out to vty interface. */
+static void ifs_dump_brief_vty(struct vty *vty, struct vrf *vrf)
+{
+ struct connected *connected;
+ struct listnode *node;
+ struct route_node *rn;
+ struct zebra_if *zebra_if;
+ struct prefix *p;
+ struct interface *ifp;
+ bool print_header = true;
+
+ FOR_ALL_INTERFACES (vrf, ifp) {
+ bool first_pfx_printed = false;
+
+ if (print_header) {
+ vty_out(vty, "%-16s%-8s%-16s%s\n", "Interface",
+ "Status", "VRF", "Addresses");
+ vty_out(vty, "%-16s%-8s%-16s%s\n", "---------",
+ "------", "---", "---------");
+ print_header = false; /* We have at least 1 iface */
+ }
+ zebra_if = ifp->info;
+
+ vty_out(vty, "%-16s", ifp->name);
+
+ if (if_is_up(ifp))
+ vty_out(vty, "%-8s", "up");
+ else
+ vty_out(vty, "%-8s", "down");
+
+ vty_out(vty, "%-16s", vrf->name);
+
+ for (rn = route_top(zebra_if->ipv4_subnets); rn;
+ rn = route_next(rn)) {
+ if (!rn->info)
+ continue;
+ uint32_t list_size = listcount((struct list *)rn->info);
+
+ for (ALL_LIST_ELEMENTS_RO((struct list *)rn->info, node,
+ connected)) {
+ if (!CHECK_FLAG(connected->flags,
+ ZEBRA_IFA_SECONDARY)) {
+ p = connected->address;
+ if (first_pfx_printed) {
+ /* padding to prepare row only
+ * for ip addr */
+ vty_out(vty, "%-40s", "");
+ if (list_size > 1)
+ vty_out(vty, "+ ");
+ vty_out(vty, "%pFX\n", p);
+ } else {
+ if (list_size > 1)
+ vty_out(vty, "+ ");
+ vty_out(vty, "%pFX\n", p);
+ }
+ first_pfx_printed = true;
+ break;
+ }
+ }
+ }
+
+ uint32_t v6_list_size = 0;
+ frr_each (if_connected, ifp->connected, connected) {
+ if (CHECK_FLAG(connected->conf, ZEBRA_IFC_REAL)
+ && (connected->address->family == AF_INET6))
+ v6_list_size++;
+ }
+ frr_each (if_connected, ifp->connected, connected) {
+ if (!CHECK_FLAG(connected->flags, ZEBRA_IFA_SECONDARY) &&
+ (connected->address->family == AF_INET6)) {
+ p = connected->address;
+ if (first_pfx_printed) {
+ vty_out(vty, "%-40s", "");
+ if (v6_list_size > 1)
+ vty_out(vty, "+ ");
+ vty_out(vty, "%pFX\n", p);
+ } else {
+ if (v6_list_size > 1)
+ vty_out(vty, "+ ");
+ vty_out(vty, "%pFX\n", p);
+ }
+ first_pfx_printed = true;
+ break;
+ }
+ }
+ if (!first_pfx_printed)
+ vty_out(vty, "\n");
+ }
+ vty_out(vty, "\n");
+}
+
+static void ifs_dump_brief_vty_json(json_object *json, struct vrf *vrf)
+{
+ struct connected *connected;
+ struct interface *ifp;
+
+ FOR_ALL_INTERFACES (vrf, ifp) {
+ json_object *json_if;
+ json_object *json_addrs;
+
+ json_if = json_object_new_object();
+ json_object_object_add(json, ifp->name, json_if);
+
+ json_object_string_add(json_if, "status",
+ if_is_up(ifp) ? "up" : "down");
+ json_object_string_add(json_if, "vrfName", vrf->name);
+
+ json_addrs = json_object_new_array();
+ json_object_object_add(json_if, "addresses", json_addrs);
+ frr_each (if_connected, ifp->connected, connected) {
+ if (!CHECK_FLAG(connected->flags, ZEBRA_IFA_SECONDARY)) {
+ char buf[PREFIX2STR_BUFFER];
+
+ json_array_string_add(
+ json_addrs,
+ prefix2str(connected->address, buf,
+ sizeof(buf)));
+ }
+ }
+ }
+}
+
+const char *zebra_protodown_rc_str(uint32_t protodown_rc, char *pd_buf,
+ uint32_t pd_buf_len)
+{
+ pd_buf[0] = '\0';
+ size_t len;
+
+ strlcat(pd_buf, "(", pd_buf_len);
+
+ if (CHECK_FLAG(protodown_rc, ZEBRA_PROTODOWN_EXTERNAL))
+ strlcat(pd_buf, "external,", pd_buf_len);
+
+ if (CHECK_FLAG(protodown_rc, ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY))
+ strlcat(pd_buf, "startup-delay,", pd_buf_len);
+
+ if (CHECK_FLAG(protodown_rc, ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN))
+ strlcat(pd_buf, "uplinks-down,", pd_buf_len);
+
+ if (CHECK_FLAG(protodown_rc, ZEBRA_PROTODOWN_VRRP))
+ strlcat(pd_buf, "vrrp,", pd_buf_len);
+
+ if (CHECK_FLAG(protodown_rc, ZEBRA_PROTODOWN_SHARP))
+ strlcat(pd_buf, "sharp,", pd_buf_len);
+
+ len = strnlen(pd_buf, pd_buf_len);
+
+ /* Remove trailing comma */
+ if (pd_buf[len - 1] == ',')
+ pd_buf[len - 1] = '\0';
+
+ strlcat(pd_buf, ")", pd_buf_len);
+
+ return pd_buf;
+}
+
+static inline bool if_is_protodown_applicable(struct interface *ifp)
+{
+ if (IS_ZEBRA_IF_BOND(ifp))
+ return false;
+
+ return true;
+}
+
+static void zebra_vxlan_if_vni_dump_vty(struct vty *vty,
+ struct zebra_vxlan_vni *vni)
+{
+ char str[INET6_ADDRSTRLEN];
+
+ vty_out(vty, " VxLAN Id %u", vni->vni);
+ if (vni->access_vlan)
+ vty_out(vty, " Access VLAN Id %u\n", vni->access_vlan);
+
+ if (vni->mcast_grp.s_addr != INADDR_ANY)
+ vty_out(vty, " Mcast Group %s",
+ inet_ntop(AF_INET, &vni->mcast_grp, str, sizeof(str)));
+}
+
+static void zebra_vxlan_if_vni_hash_dump_vty(struct hash_bucket *bucket,
+ void *ctxt)
+{
+ struct vty *vty;
+ struct zebra_vxlan_vni *vni;
+
+ vni = (struct zebra_vxlan_vni *)bucket->data;
+ vty = (struct vty *)ctxt;
+
+ zebra_vxlan_if_vni_dump_vty(vty, vni);
+}
+
+static void zebra_vxlan_if_dump_vty(struct vty *vty, struct zebra_if *zebra_if)
+{
+ struct zebra_l2info_vxlan *vxlan_info;
+ struct zebra_vxlan_vni_info *vni_info;
+
+ vxlan_info = &zebra_if->l2info.vxl;
+ vni_info = &vxlan_info->vni_info;
+
+ if (vxlan_info->vtep_ip.s_addr != INADDR_ANY)
+ vty_out(vty, " VTEP IP: %pI4", &vxlan_info->vtep_ip);
+
+ if (vxlan_info->ifindex_link && (vxlan_info->link_nsid != NS_UNKNOWN)) {
+ struct interface *ifp;
+
+ ifp = if_lookup_by_index_per_ns(
+ zebra_ns_lookup(vxlan_info->link_nsid),
+ vxlan_info->ifindex_link);
+ vty_out(vty, " Link Interface %s",
+ ifp == NULL ? "Unknown" : ifp->name);
+ }
+
+ if (IS_ZEBRA_VXLAN_IF_VNI(zebra_if)) {
+ zebra_vxlan_if_vni_dump_vty(vty, &vni_info->vni);
+ } else {
+ hash_iterate(vni_info->vni_table,
+ zebra_vxlan_if_vni_hash_dump_vty, vty);
+ }
+
+ vty_out(vty, "\n");
+}
+
+/* Interface's information print out to vty interface. */
+static void if_dump_vty(struct vty *vty, struct interface *ifp)
+{
+ struct connected *connected;
+ struct nbr_connected *nbr_connected;
+ struct listnode *node;
+ struct route_node *rn;
+ struct zebra_if *zebra_if;
+ char pd_buf[ZEBRA_PROTODOWN_RC_STR_LEN];
+
+ zebra_if = ifp->info;
+
+ vty_out(vty, "Interface %s is ", ifp->name);
+ if (if_is_up(ifp)) {
+ vty_out(vty, "up, line protocol ");
+
+ if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION)) {
+ if (if_is_running(ifp))
+ vty_out(vty, "is up\n");
+ else
+ vty_out(vty, "is down\n");
+ } else {
+ vty_out(vty, "detection is disabled\n");
+ }
+ } else {
+ vty_out(vty, "down\n");
+ }
+
+ vty_out(vty, " Link ups: %5u last: %s\n", zebra_if->up_count,
+ zebra_if->up_last[0] ? zebra_if->up_last : "(never)");
+ vty_out(vty, " Link downs: %5u last: %s\n", zebra_if->down_count,
+ zebra_if->down_last[0] ? zebra_if->down_last : "(never)");
+
+ zebra_ptm_show_status(vty, NULL, ifp);
+
+ vty_out(vty, " vrf: %s\n", ifp->vrf->name);
+
+ if (ifp->desc)
+ vty_out(vty, " Description: %s\n", ifp->desc);
+ if (zebra_if->desc)
+ vty_out(vty, " OS Description: %s\n", zebra_if->desc);
+
+ if (ifp->ifindex == IFINDEX_INTERNAL) {
+ vty_out(vty, " pseudo interface\n");
+ return;
+ } else if (!CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) {
+ vty_out(vty, " index %d inactive interface\n", ifp->ifindex);
+ return;
+ }
+
+ vty_out(vty, " index %d metric %d mtu %d speed %u txqlen %u",
+ ifp->ifindex, ifp->metric, ifp->mtu, ifp->speed, ifp->txqlen);
+ if (ifp->mtu6 != ifp->mtu)
+ vty_out(vty, "mtu6 %d ", ifp->mtu6);
+ vty_out(vty, "\n flags: %s\n", if_flag_dump(ifp->flags));
+
+ if (zebra_if->mpls)
+ vty_out(vty, " MPLS enabled\n");
+
+ if (zebra_if->linkdown)
+ vty_out(vty, " Ignore all v4 routes with linkdown\n");
+ if (zebra_if->linkdownv6)
+ vty_out(vty, " Ignore all v6 routes with linkdown\n");
+
+ if (zebra_if->v4mcast_on)
+ vty_out(vty, " v4 Multicast forwarding is on\n");
+ if (zebra_if->v6mcast_on)
+ vty_out(vty, " v6 Multicast forwarding is on\n");
+
+ /* Hardware address. */
+ vty_out(vty, " Type: %s\n", if_link_type_str(ifp->ll_type));
+ if (ifp->hw_addr_len != 0) {
+ int i;
+
+ vty_out(vty, " HWaddr: ");
+ for (i = 0; i < ifp->hw_addr_len; i++)
+ vty_out(vty, "%s%02x", i == 0 ? "" : ":",
+ ifp->hw_addr[i]);
+ vty_out(vty, "\n");
+ }
+
+ /* Bandwidth in Mbps */
+ if (ifp->bandwidth != 0) {
+ vty_out(vty, " bandwidth %u Mbps", ifp->bandwidth);
+ vty_out(vty, "\n");
+ }
+
+ for (rn = route_top(zebra_if->ipv4_subnets); rn; rn = route_next(rn)) {
+ if (!rn->info)
+ continue;
+
+ for (ALL_LIST_ELEMENTS_RO((struct list *)rn->info, node,
+ connected))
+ connected_dump_vty(vty, NULL, connected);
+ }
+
+ frr_each (if_connected, ifp->connected, connected) {
+ if (connected->address->family == AF_INET6)
+ connected_dump_vty(vty, NULL, connected);
+ }
+
+ vty_out(vty, " Interface Type %s\n",
+ zebra_ziftype_2str(zebra_if->zif_type));
+ vty_out(vty, " Interface Slave Type %s\n",
+ zebra_zifslavetype_2str(zebra_if->zif_slave_type));
+
+ if (IS_ZEBRA_IF_BRIDGE(ifp)) {
+ vty_out(vty, " Bridge VLAN-aware: %s\n",
+ IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(zebra_if) ? "yes" : "no");
+ } else if (IS_ZEBRA_IF_VLAN(ifp)) {
+ struct zebra_l2info_vlan *vlan_info;
+
+ vlan_info = &zebra_if->l2info.vl;
+ vty_out(vty, " VLAN Id %u\n", vlan_info->vid);
+ } else if (IS_ZEBRA_IF_VXLAN(ifp)) {
+ zebra_vxlan_if_dump_vty(vty, zebra_if);
+ } else if (IS_ZEBRA_IF_GRE(ifp)) {
+ struct zebra_l2info_gre *gre_info;
+
+ gre_info = &zebra_if->l2info.gre;
+ if (gre_info->vtep_ip.s_addr != INADDR_ANY) {
+ vty_out(vty, " VTEP IP: %pI4", &gre_info->vtep_ip);
+ if (gre_info->vtep_ip_remote.s_addr != INADDR_ANY)
+ vty_out(vty, " , remote %pI4",
+ &gre_info->vtep_ip_remote);
+ vty_out(vty, "\n");
+ }
+ if (gre_info->ifindex_link &&
+ (gre_info->link_nsid != NS_UNKNOWN)) {
+ struct interface *ifp;
+
+ ifp = if_lookup_by_index_per_ns(
+ zebra_ns_lookup(gre_info->link_nsid),
+ gre_info->ifindex_link);
+ vty_out(vty, " Link Interface %s\n",
+ ifp == NULL ? "Unknown" :
+ ifp->name);
+ }
+ }
+
+ if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp)) {
+ struct zebra_l2info_brslave *br_slave;
+
+ br_slave = &zebra_if->brslave_info;
+ if (br_slave->bridge_ifindex != IFINDEX_INTERNAL) {
+ if (br_slave->br_if)
+ vty_out(vty, " Master interface: %s\n",
+ br_slave->br_if->name);
+ else
+ vty_out(vty, " Master ifindex: %u\n",
+ br_slave->bridge_ifindex);
+ }
+ }
+
+ if (IS_ZEBRA_IF_BOND_SLAVE(ifp)) {
+ struct zebra_l2info_bondslave *bond_slave;
+
+ bond_slave = &zebra_if->bondslave_info;
+ if (bond_slave->bond_ifindex != IFINDEX_INTERNAL) {
+ if (bond_slave->bond_if)
+ vty_out(vty, " Master interface: %s\n",
+ bond_slave->bond_if->name);
+ else
+ vty_out(vty, " Master ifindex: %u\n",
+ bond_slave->bond_ifindex);
+ }
+ }
+
+ if (zebra_if->flags & ZIF_FLAG_LACP_BYPASS)
+ vty_out(vty, " LACP bypass: on\n");
+
+ zebra_evpn_if_es_print(vty, NULL, zebra_if);
+ vty_out(vty, " protodown: %s %s\n",
+ (ZEBRA_IF_IS_PROTODOWN(zebra_if)) ? "on" : "off",
+ if_is_protodown_applicable(ifp) ? "" : "(n/a)");
+ if (zebra_if->protodown_rc)
+ vty_out(vty, " protodown reasons: %s\n",
+ zebra_protodown_rc_str(zebra_if->protodown_rc, pd_buf,
+ sizeof(pd_buf)));
+
+ if (zebra_if->link_ifindex != IFINDEX_INTERNAL) {
+ if (zebra_if->link)
+ vty_out(vty, " Parent interface: %s\n", zebra_if->link->name);
+ else
+ vty_out(vty, " Parent ifindex: %d\n", zebra_if->link_ifindex);
+ }
+
+ if (HAS_LINK_PARAMS(ifp)) {
+ int i;
+ struct if_link_params *iflp = ifp->link_params;
+ vty_out(vty, " Traffic Engineering Link Parameters:\n");
+ if (IS_PARAM_SET(iflp, LP_TE_METRIC))
+ vty_out(vty, " TE metric %u\n", iflp->te_metric);
+ if (IS_PARAM_SET(iflp, LP_MAX_BW))
+ vty_out(vty, " Maximum Bandwidth %g (Byte/s)\n",
+ iflp->max_bw);
+ if (IS_PARAM_SET(iflp, LP_MAX_RSV_BW))
+ vty_out(vty,
+ " Maximum Reservable Bandwidth %g (Byte/s)\n",
+ iflp->max_rsv_bw);
+ if (IS_PARAM_SET(iflp, LP_UNRSV_BW)) {
+ vty_out(vty,
+ " Unreserved Bandwidth per Class Type in Byte/s:\n");
+ for (i = 0; i < MAX_CLASS_TYPE; i += 2)
+ vty_out(vty,
+ " [%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)\n",
+ i, iflp->unrsv_bw[i], i + 1,
+ iflp->unrsv_bw[i + 1]);
+ }
+
+ if (IS_PARAM_SET(iflp, LP_ADM_GRP))
+ vty_out(vty, " Administrative Group:%u\n",
+ iflp->admin_grp);
+ if (IS_PARAM_SET(iflp, LP_DELAY)) {
+ vty_out(vty, " Link Delay Average: %u (micro-sec.)",
+ iflp->av_delay);
+ if (IS_PARAM_SET(iflp, LP_MM_DELAY)) {
+ vty_out(vty, " Min: %u (micro-sec.)",
+ iflp->min_delay);
+ vty_out(vty, " Max: %u (micro-sec.)",
+ iflp->max_delay);
+ }
+ vty_out(vty, "\n");
+ }
+ if (IS_PARAM_SET(iflp, LP_DELAY_VAR))
+ vty_out(vty,
+ " Link Delay Variation %u (micro-sec.)\n",
+ iflp->delay_var);
+ if (IS_PARAM_SET(iflp, LP_PKT_LOSS))
+ vty_out(vty, " Link Packet Loss %f (in %%)\n",
+ (double)iflp->pkt_loss * LOSS_PRECISION);
+ if (IS_PARAM_SET(iflp, LP_AVA_BW))
+ vty_out(vty, " Available Bandwidth %g (Byte/s)\n",
+ iflp->ava_bw);
+ if (IS_PARAM_SET(iflp, LP_RES_BW))
+ vty_out(vty, " Residual Bandwidth %g (Byte/s)\n",
+ iflp->res_bw);
+ if (IS_PARAM_SET(iflp, LP_USE_BW))
+ vty_out(vty, " Utilized Bandwidth %g (Byte/s)\n",
+ iflp->use_bw);
+ if (IS_PARAM_SET(iflp, LP_RMT_AS))
+ vty_out(vty, " Neighbor ASBR IP: %pI4 AS: %u \n",
+ &iflp->rmt_ip, iflp->rmt_as);
+ }
+
+ hook_call(zebra_if_extra_info, vty, ifp);
+
+ if (listhead(ifp->nbr_connected))
+ vty_out(vty, " Neighbor address(s):\n");
+ for (ALL_LIST_ELEMENTS_RO(ifp->nbr_connected, node, nbr_connected))
+ nbr_connected_dump_vty(vty, NULL, nbr_connected);
+
+#ifdef HAVE_PROC_NET_DEV
+ /* Statistics print out using proc file system. */
+ vty_out(vty,
+ " %lu input packets (%lu multicast), %lu bytes, %lu dropped\n",
+ ifp->stats.rx_packets, ifp->stats.rx_multicast,
+ ifp->stats.rx_bytes, ifp->stats.rx_dropped);
+
+ vty_out(vty,
+ " %lu input errors, %lu length, %lu overrun, %lu CRC, %lu frame\n",
+ ifp->stats.rx_errors, ifp->stats.rx_length_errors,
+ ifp->stats.rx_over_errors, ifp->stats.rx_crc_errors,
+ ifp->stats.rx_frame_errors);
+
+ vty_out(vty, " %lu fifo, %lu missed\n", ifp->stats.rx_fifo_errors,
+ ifp->stats.rx_missed_errors);
+
+ vty_out(vty, " %lu output packets, %lu bytes, %lu dropped\n",
+ ifp->stats.tx_packets, ifp->stats.tx_bytes,
+ ifp->stats.tx_dropped);
+
+ vty_out(vty,
+ " %lu output errors, %lu aborted, %lu carrier, %lu fifo, %lu heartbeat\n",
+ ifp->stats.tx_errors, ifp->stats.tx_aborted_errors,
+ ifp->stats.tx_carrier_errors, ifp->stats.tx_fifo_errors,
+ ifp->stats.tx_heartbeat_errors);
+
+ vty_out(vty, " %lu window, %lu collisions\n",
+ ifp->stats.tx_window_errors, ifp->stats.collisions);
+#endif /* HAVE_PROC_NET_DEV */
+
+#ifdef HAVE_NET_RT_IFLIST
+ /* Statistics print out using sysctl (). */
+ vty_out(vty,
+ " input packets %llu, bytes %llu, dropped %llu, multicast packets %llu\n",
+ (unsigned long long)ifp->stats.ifi_ipackets,
+ (unsigned long long)ifp->stats.ifi_ibytes,
+ (unsigned long long)ifp->stats.ifi_iqdrops,
+ (unsigned long long)ifp->stats.ifi_imcasts);
+
+ vty_out(vty, " input errors %llu\n",
+ (unsigned long long)ifp->stats.ifi_ierrors);
+
+ vty_out(vty,
+ " output packets %llu, bytes %llu, multicast packets %llu\n",
+ (unsigned long long)ifp->stats.ifi_opackets,
+ (unsigned long long)ifp->stats.ifi_obytes,
+ (unsigned long long)ifp->stats.ifi_omcasts);
+
+ vty_out(vty, " output errors %llu\n",
+ (unsigned long long)ifp->stats.ifi_oerrors);
+
+ vty_out(vty, " collisions %llu\n",
+ (unsigned long long)ifp->stats.ifi_collisions);
+#endif /* HAVE_NET_RT_IFLIST */
+}
+
+static void zebra_vxlan_if_vni_dump_vty_json(json_object *json_if,
+ struct zebra_vxlan_vni *vni)
+{
+ json_object_int_add(json_if, "vxlanId", vni->vni);
+ if (vni->access_vlan)
+ json_object_int_add(json_if, "accessVlanId", vni->access_vlan);
+ if (vni->mcast_grp.s_addr != INADDR_ANY)
+ json_object_string_addf(json_if, "mcastGroup", "%pI4",
+ &vni->mcast_grp);
+}
+
+static void zebra_vxlan_if_vni_hash_dump_vty_json(struct hash_bucket *bucket,
+ void *ctxt)
+{
+ json_object *json_if;
+ struct zebra_vxlan_vni *vni;
+
+ vni = (struct zebra_vxlan_vni *)bucket->data;
+ json_if = (json_object *)ctxt;
+
+ zebra_vxlan_if_vni_dump_vty_json(json_if, vni);
+}
+
+static void zebra_vxlan_if_dump_vty_json(json_object *json_if,
+ struct zebra_if *zebra_if)
+{
+ struct zebra_l2info_vxlan *vxlan_info;
+ struct zebra_vxlan_vni_info *vni_info;
+
+ vxlan_info = &zebra_if->l2info.vxl;
+ vni_info = &vxlan_info->vni_info;
+
+ if (vxlan_info->vtep_ip.s_addr != INADDR_ANY)
+ json_object_string_addf(json_if, "vtepIp", "%pI4",
+ &vxlan_info->vtep_ip);
+
+ if (vxlan_info->ifindex_link && (vxlan_info->link_nsid != NS_UNKNOWN)) {
+ struct interface *ifp;
+
+ ifp = if_lookup_by_index_per_ns(
+ zebra_ns_lookup(vxlan_info->link_nsid),
+ vxlan_info->ifindex_link);
+ json_object_string_add(json_if, "linkInterface",
+ ifp == NULL ? "Unknown" : ifp->name);
+ }
+ if (IS_ZEBRA_VXLAN_IF_VNI(zebra_if)) {
+ zebra_vxlan_if_vni_dump_vty_json(json_if, &vni_info->vni);
+ } else {
+ hash_iterate(vni_info->vni_table,
+ zebra_vxlan_if_vni_hash_dump_vty_json, json_if);
+ }
+}
+
+static void if_dump_vty_json(struct vty *vty, struct interface *ifp,
+ json_object *json)
+{
+ struct connected *connected;
+ struct nbr_connected *nbr_connected;
+ struct listnode *node;
+ struct route_node *rn;
+ struct zebra_if *zebra_if;
+ char pd_buf[ZEBRA_PROTODOWN_RC_STR_LEN];
+ char buf[BUFSIZ];
+ json_object *json_if;
+ json_object *json_addrs;
+
+ json_if = json_object_new_object();
+ json_object_object_add(json, ifp->name, json_if);
+
+ if (if_is_up(ifp)) {
+ json_object_string_add(json_if, "administrativeStatus", "up");
+
+ if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION)) {
+ json_object_string_add(json_if, "operationalStatus",
+ if_is_running(ifp) ? "up"
+ : "down");
+ json_object_boolean_add(json_if, "linkDetection", true);
+ } else {
+ json_object_boolean_add(json_if, "linkDetection",
+ false);
+ }
+ } else {
+ json_object_string_add(json_if, "administrativeStatus", "down");
+ }
+
+ zebra_if = ifp->info;
+
+ json_object_int_add(json_if, "linkUps", zebra_if->up_count);
+ json_object_int_add(json_if, "linkDowns", zebra_if->down_count);
+ if (zebra_if->up_last[0])
+ json_object_string_add(json_if, "lastLinkUp",
+ zebra_if->up_last);
+ if (zebra_if->down_last[0])
+ json_object_string_add(json_if, "lastLinkDown",
+ zebra_if->down_last);
+
+ zebra_ptm_show_status(vty, json_if, ifp);
+
+ json_object_string_add(json_if, "vrfName", ifp->vrf->name);
+
+ if (ifp->desc)
+ json_object_string_add(json_if, "description", ifp->desc);
+ if (zebra_if->desc)
+ json_object_string_add(json_if, "OsDescription",
+ zebra_if->desc);
+
+ json_object_boolean_add(json_if, "mplsEnabled", zebra_if->mpls);
+ json_object_boolean_add(json_if, "linkDown", zebra_if->linkdown);
+ json_object_boolean_add(json_if, "linkDownV6", zebra_if->linkdownv6);
+ json_object_boolean_add(json_if, "mcForwardingV4",
+ zebra_if->v4mcast_on);
+ json_object_boolean_add(json_if, "mcForwardingV6",
+ zebra_if->v6mcast_on);
+
+ if (ifp->ifindex == IFINDEX_INTERNAL) {
+ json_object_boolean_add(json_if, "pseudoInterface", true);
+ return;
+ } else if (!CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) {
+ json_object_int_add(json_if, "index", ifp->ifindex);
+ return;
+ }
+
+ json_object_boolean_add(json_if, "pseudoInterface", false);
+ json_object_int_add(json_if, "index", ifp->ifindex);
+ json_object_int_add(json_if, "metric", ifp->metric);
+ json_object_int_add(json_if, "mtu", ifp->mtu);
+ if (ifp->mtu6 != ifp->mtu)
+ json_object_int_add(json_if, "mtu6", ifp->mtu6);
+ json_object_int_add(json_if, "speed", ifp->speed);
+ json_object_int_add(json_if, "txqlen", ifp->txqlen);
+ json_object_string_add(json_if, "flags", if_flag_dump(ifp->flags));
+
+ /* Hardware address. */
+ json_object_string_add(json_if, "type", if_link_type_str(ifp->ll_type));
+ if (ifp->hw_addr_len != 0) {
+ char hwbuf[BUFSIZ];
+
+ hwbuf[0] = '\0';
+ for (int i = 0; i < ifp->hw_addr_len; i++) {
+ snprintf(buf, sizeof(buf), "%s%02x", i == 0 ? "" : ":",
+ ifp->hw_addr[i]);
+ strlcat(hwbuf, buf, sizeof(hwbuf));
+ }
+ json_object_string_add(json_if, "hardwareAddress", hwbuf);
+ }
+
+ /* Bandwidth in Mbps */
+ if (ifp->bandwidth != 0)
+ json_object_int_add(json_if, "bandwidth", ifp->bandwidth);
+
+
+ /* IP addresses. */
+ json_addrs = json_object_new_array();
+ json_object_object_add(json_if, "ipAddresses", json_addrs);
+
+ for (rn = route_top(zebra_if->ipv4_subnets); rn; rn = route_next(rn)) {
+ if (!rn->info)
+ continue;
+
+ for (ALL_LIST_ELEMENTS_RO((struct list *)rn->info, node,
+ connected))
+ connected_dump_vty(vty, json_addrs, connected);
+ }
+
+ frr_each (if_connected, ifp->connected, connected) {
+ if (connected->address->family == AF_INET6)
+ connected_dump_vty(vty, json_addrs, connected);
+ }
+
+ json_object_string_add(json_if, "interfaceType",
+ zebra_ziftype_2str(zebra_if->zif_type));
+ json_object_string_add(
+ json_if, "interfaceSlaveType",
+ zebra_zifslavetype_2str(zebra_if->zif_slave_type));
+
+ if (IS_ZEBRA_IF_BRIDGE(ifp)) {
+ struct zebra_l2info_bridge *bridge_info;
+
+ bridge_info = &zebra_if->l2info.br;
+ json_object_boolean_add(json_if, "bridgeVlanAware",
+ bridge_info->bridge.vlan_aware);
+ } else if (IS_ZEBRA_IF_VLAN(ifp)) {
+ struct zebra_l2info_vlan *vlan_info;
+
+ vlan_info = &zebra_if->l2info.vl;
+ json_object_int_add(json_if, "vlanId", vlan_info->vid);
+ } else if (IS_ZEBRA_IF_VXLAN(ifp)) {
+ zebra_vxlan_if_dump_vty_json(json_if, zebra_if);
+
+ } else if (IS_ZEBRA_IF_GRE(ifp)) {
+ struct zebra_l2info_gre *gre_info;
+
+ gre_info = &zebra_if->l2info.gre;
+ if (gre_info->vtep_ip.s_addr != INADDR_ANY) {
+ json_object_string_addf(json_if, "vtepIp", "%pI4",
+ &gre_info->vtep_ip);
+ if (gre_info->vtep_ip_remote.s_addr != INADDR_ANY)
+ json_object_string_addf(
+ json_if, "vtepRemoteIp", "%pI4",
+ &gre_info->vtep_ip_remote);
+ }
+ if (gre_info->ifindex_link
+ && (gre_info->link_nsid != NS_UNKNOWN)) {
+ struct interface *ifp;
+
+ ifp = if_lookup_by_index_per_ns(
+ zebra_ns_lookup(gre_info->link_nsid),
+ gre_info->ifindex_link);
+ json_object_string_add(json_if, "linkInterface",
+ ifp == NULL ? "Unknown"
+ : ifp->name);
+ }
+ }
+
+ if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp)) {
+ struct zebra_l2info_brslave *br_slave;
+
+ br_slave = &zebra_if->brslave_info;
+ if (br_slave->bridge_ifindex != IFINDEX_INTERNAL) {
+ if (br_slave->br_if)
+ json_object_string_add(json_if,
+ "masterInterface",
+ br_slave->br_if->name);
+ else
+ json_object_int_add(json_if, "masterIfindex",
+ br_slave->bridge_ifindex);
+ }
+ }
+
+ if (IS_ZEBRA_IF_BOND_SLAVE(ifp)) {
+ struct zebra_l2info_bondslave *bond_slave;
+
+ bond_slave = &zebra_if->bondslave_info;
+ if (bond_slave->bond_ifindex != IFINDEX_INTERNAL) {
+ if (bond_slave->bond_if)
+ json_object_string_add(
+ json_if, "masterInterface",
+ bond_slave->bond_if->name);
+ else
+ json_object_int_add(json_if, "masterIfindex",
+ bond_slave->bond_ifindex);
+ }
+ }
+
+ json_object_boolean_add(
+ json_if, "lacpBypass",
+ CHECK_FLAG(zebra_if->flags, ZIF_FLAG_LACP_BYPASS));
+
+ zebra_evpn_if_es_print(vty, json_if, zebra_if);
+
+ if (if_is_protodown_applicable(ifp)) {
+ json_object_string_add(
+ json_if, "protodown",
+ (ZEBRA_IF_IS_PROTODOWN(zebra_if)) ? "on" : "off");
+ if (zebra_if->protodown_rc)
+ json_object_string_add(
+ json_if, "protodownReason",
+ zebra_protodown_rc_str(zebra_if->protodown_rc,
+ pd_buf, sizeof(pd_buf)));
+ }
+
+ if (zebra_if->link_ifindex != IFINDEX_INTERNAL) {
+ if (zebra_if->link)
+ json_object_string_add(json_if, "parentInterface",
+ zebra_if->link->name);
+ else
+ json_object_int_add(json_if, "parentIfindex",
+ zebra_if->link_ifindex);
+ }
+
+ if (HAS_LINK_PARAMS(ifp)) {
+ struct if_link_params *iflp = ifp->link_params;
+ json_object *json_te;
+
+ json_te = json_object_new_object();
+ json_object_object_add(
+ json_if, "trafficEngineeringLinkParameters", json_te);
+
+ if (IS_PARAM_SET(iflp, LP_TE_METRIC))
+ json_object_int_add(json_te, "teMetric",
+ iflp->te_metric);
+ if (IS_PARAM_SET(iflp, LP_MAX_BW))
+ json_object_double_add(json_te, "maximumBandwidth",
+ iflp->max_bw);
+ if (IS_PARAM_SET(iflp, LP_MAX_RSV_BW))
+ json_object_double_add(json_te,
+ "maximumReservableBandwidth",
+ iflp->max_rsv_bw);
+ if (IS_PARAM_SET(iflp, LP_UNRSV_BW)) {
+ json_object *json_bws;
+
+ json_bws = json_object_new_object();
+ json_object_object_add(json_te, "unreservedBandwidth",
+ json_bws);
+ for (unsigned int i = 0; i < MAX_CLASS_TYPE; ++i) {
+ char buf_ct[64];
+
+ snprintf(buf_ct, sizeof(buf_ct), "classType%u",
+ i);
+ json_object_double_add(json_bws, buf_ct,
+ iflp->unrsv_bw[i]);
+ }
+ }
+
+ if (IS_PARAM_SET(iflp, LP_ADM_GRP))
+ json_object_int_add(json_te, "administrativeGroup",
+ iflp->admin_grp);
+ if (IS_PARAM_SET(iflp, LP_DELAY)) {
+ json_object_int_add(json_te, "linkDelayAverage",
+ iflp->av_delay);
+ if (IS_PARAM_SET(iflp, LP_MM_DELAY)) {
+ json_object_int_add(json_te, "linkDelayMinimum",
+ iflp->min_delay);
+ json_object_int_add(json_te, "linkDelayMaximum",
+ iflp->max_delay);
+ }
+ }
+ if (IS_PARAM_SET(iflp, LP_DELAY_VAR))
+ json_object_int_add(json_te, "linkDelayVariation",
+ iflp->delay_var);
+ if (IS_PARAM_SET(iflp, LP_PKT_LOSS))
+ json_object_double_add(json_te, "linkPacketLoss",
+ (double)iflp->pkt_loss *
+ LOSS_PRECISION);
+ if (IS_PARAM_SET(iflp, LP_AVA_BW))
+ json_object_double_add(json_te, "availableBandwidth",
+ iflp->ava_bw);
+ if (IS_PARAM_SET(iflp, LP_RES_BW))
+ json_object_double_add(json_te, "residualBandwidth",
+ iflp->res_bw);
+ if (IS_PARAM_SET(iflp, LP_USE_BW))
+ json_object_double_add(json_te, "utilizedBandwidth",
+ iflp->use_bw);
+ if (IS_PARAM_SET(iflp, LP_RMT_AS))
+ json_object_string_addf(json_te, "neighborAsbrIp",
+ "%pI4", &iflp->rmt_ip);
+ json_object_int_add(json_te, "neighborAsbrAs", iflp->rmt_as);
+ }
+
+ if (listhead(ifp->nbr_connected)) {
+ json_object *json_nbr_addrs;
+
+ json_nbr_addrs = json_object_new_array();
+ json_object_object_add(json_if, "neighborIpAddresses",
+ json_nbr_addrs);
+
+ for (ALL_LIST_ELEMENTS_RO(ifp->nbr_connected, node,
+ nbr_connected))
+ nbr_connected_dump_vty(vty, json_nbr_addrs,
+ nbr_connected);
+ }
+
+#ifdef HAVE_PROC_NET_DEV
+ json_object_int_add(json_if, "inputPackets", stats.rx_packets);
+ json_object_int_add(json_if, "inputBytes", ifp->stats.rx_bytes);
+ json_object_int_add(json_if, "inputDropped", ifp->stats.rx_dropped);
+ json_object_int_add(json_if, "inputMulticastPackets",
+ ifp->stats.rx_multicast);
+ json_object_int_add(json_if, "inputErrors", ifp->stats.rx_errors);
+ json_object_int_add(json_if, "inputLengthErrors",
+ ifp->stats.rx_length_errors);
+ json_object_int_add(json_if, "inputOverrunErrors",
+ ifp->stats.rx_over_errors);
+ json_object_int_add(json_if, "inputCrcErrors",
+ ifp->stats.rx_crc_errors);
+ json_object_int_add(json_if, "inputFrameErrors",
+ ifp->stats.rx_frame_errors);
+ json_object_int_add(json_if, "inputFifoErrors",
+ ifp->stats.rx_fifo_errors);
+ json_object_int_add(json_if, "inputMissedErrors",
+ ifp->stats.rx_missed_errors);
+ json_object_int_add(json_if, "outputPackets", ifp->stats.tx_packets);
+ json_object_int_add(json_if, "outputBytes", ifp->stats.tx_bytes);
+ json_object_int_add(json_if, "outputDroppedPackets",
+ ifp->stats.tx_dropped);
+ json_object_int_add(json_if, "outputErrors", ifp->stats.tx_errors);
+ json_object_int_add(json_if, "outputAbortedErrors",
+ ifp->stats.tx_aborted_errors);
+ json_object_int_add(json_if, "outputCarrierErrors",
+ ifp->stats.tx_carrier_errors);
+ json_object_int_add(json_if, "outputFifoErrors",
+ ifp->stats.tx_fifo_errors);
+ json_object_int_add(json_if, "outputHeartbeatErrors",
+ ifp->stats.tx_heartbeat_errors);
+ json_object_int_add(json_if, "outputWindowErrors",
+ ifp->stats.tx_window_errors);
+ json_object_int_add(json_if, "collisions", ifp->stats.collisions);
+#endif /* HAVE_PROC_NET_DEV */
+
+#ifdef HAVE_NET_RT_IFLIST
+ json_object_int_add(json_if, "inputPackets", ifp->stats.ifi_ipackets);
+ json_object_int_add(json_if, "inputBytes", ifp->stats.ifi_ibytes);
+ json_object_int_add(json_if, "inputDropd", ifp->stats.ifi_iqdrops);
+ json_object_int_add(json_if, "inputMulticastPackets",
+ ifp->stats.ifi_imcasts);
+ json_object_int_add(json_if, "inputErrors", ifp->stats.ifi_ierrors);
+ json_object_int_add(json_if, "outputPackets", ifp->stats.ifi_opackets);
+ json_object_int_add(json_if, "outputBytes", ifp->stats.ifi_obytes);
+ json_object_int_add(json_if, "outputMulticastPackets",
+ ifp->stats.ifi_omcasts);
+ json_object_int_add(json_if, "outputErrors", ifp->stats.ifi_oerrors);
+ json_object_int_add(json_if, "collisions", ifp->stats.ifi_collisions);
+#endif /* HAVE_NET_RT_IFLIST */
+}
+
+static void interface_update_stats(void)
+{
+#ifdef HAVE_PROC_NET_DEV
+ /* If system has interface statistics via proc file system, update
+ statistics. */
+ ifstat_update_proc();
+#endif /* HAVE_PROC_NET_DEV */
+#ifdef HAVE_NET_RT_IFLIST
+ ifstat_update_sysctl();
+#endif /* HAVE_NET_RT_IFLIST */
+}
+
+#include "zebra/interface_clippy.c"
+/* Show all interfaces to vty. */
+DEFPY(show_interface, show_interface_cmd,
+ "show interface vrf NAME$vrf_name [brief$brief] [json$uj]",
+ SHOW_STR
+ "Interface status and configuration\n"
+ VRF_CMD_HELP_STR
+ "Interface status and configuration summary\n"
+ JSON_STR)
+{
+ struct vrf *vrf;
+ struct interface *ifp;
+ json_object *json = NULL;
+
+ interface_update_stats();
+
+ vrf = vrf_lookup_by_name(vrf_name);
+ if (!vrf) {
+ if (uj)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "%% VRF %s not found\n", vrf_name);
+ return CMD_WARNING;
+ }
+
+ if (uj)
+ json = json_object_new_object();
+
+ if (brief) {
+ if (json)
+ ifs_dump_brief_vty_json(json, vrf);
+ else
+ ifs_dump_brief_vty(vty, vrf);
+ } else {
+ FOR_ALL_INTERFACES (vrf, ifp) {
+ if (json)
+ if_dump_vty_json(vty, ifp, json);
+ else
+ if_dump_vty(vty, ifp);
+ }
+ }
+
+ if (json)
+ vty_json(vty, json);
+
+ return CMD_SUCCESS;
+}
+
+
+/* Show all interfaces to vty. */
+DEFPY (show_interface_vrf_all,
+ show_interface_vrf_all_cmd,
+ "show interface [vrf all] [brief$brief] [json$uj]",
+ SHOW_STR
+ "Interface status and configuration\n"
+ VRF_ALL_CMD_HELP_STR
+ "Interface status and configuration summary\n"
+ JSON_STR)
+{
+ struct vrf *vrf;
+ struct interface *ifp;
+ json_object *json = NULL;
+
+ interface_update_stats();
+
+ if (uj)
+ json = json_object_new_object();
+
+ /* All interface print. */
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+ if (brief) {
+ if (json)
+ ifs_dump_brief_vty_json(json, vrf);
+ else
+ ifs_dump_brief_vty(vty, vrf);
+ } else {
+ FOR_ALL_INTERFACES (vrf, ifp) {
+ if (json)
+ if_dump_vty_json(vty, ifp, json);
+ else
+ if_dump_vty(vty, ifp);
+ }
+ }
+ }
+
+ if (json)
+ vty_json(vty, json);
+
+ return CMD_SUCCESS;
+}
+
+/* Show specified interface to vty. */
+
+DEFPY (show_interface_name_vrf,
+ show_interface_name_vrf_cmd,
+ "show interface IFNAME$ifname vrf NAME$vrf_name [json$uj]",
+ SHOW_STR
+ "Interface status and configuration\n"
+ "Interface name\n"
+ VRF_CMD_HELP_STR
+ JSON_STR)
+{
+ struct interface *ifp;
+ struct vrf *vrf;
+ json_object *json = NULL;
+
+ interface_update_stats();
+
+ vrf = vrf_lookup_by_name(vrf_name);
+ if (!vrf) {
+ if (uj)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "%% VRF %s not found\n", vrf_name);
+ return CMD_WARNING;
+ }
+
+ ifp = if_lookup_by_name_vrf(ifname, vrf);
+ if (ifp == NULL) {
+ if (uj)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "%% Can't find interface %s\n", ifname);
+ return CMD_WARNING;
+ }
+
+ if (uj)
+ json = json_object_new_object();
+
+ if (json)
+ if_dump_vty_json(vty, ifp, json);
+ else
+ if_dump_vty(vty, ifp);
+
+ if (json)
+ vty_json(vty, json);
+
+ return CMD_SUCCESS;
+}
+
+/* Show specified interface to vty. */
+DEFPY (show_interface_name_vrf_all,
+ show_interface_name_vrf_all_cmd,
+ "show interface IFNAME$ifname [vrf all] [json$uj]",
+ SHOW_STR
+ "Interface status and configuration\n"
+ "Interface name\n"
+ VRF_ALL_CMD_HELP_STR
+ JSON_STR)
+{
+ struct interface *ifp = NULL;
+ struct interface *ifptmp;
+ struct vrf *vrf;
+ json_object *json = NULL;
+ int count = 0;
+
+ interface_update_stats();
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+ ifptmp = if_lookup_by_name_vrf(ifname, vrf);
+ if (ifptmp) {
+ ifp = ifptmp;
+ count++;
+ if (!vrf_is_backend_netns())
+ break;
+ }
+ }
+
+ if (ifp == NULL) {
+ if (uj)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "%% Can't find interface %s\n", ifname);
+ return CMD_WARNING;
+ }
+ if (count > 1) {
+ if (uj) {
+ vty_out(vty, "{}\n");
+ } else {
+ vty_out(vty,
+ "%% There are multiple interfaces with name %s\n",
+ ifname);
+ vty_out(vty, "%% You must specify the VRF name\n");
+ }
+ return CMD_WARNING;
+ }
+
+ if (uj)
+ json = json_object_new_object();
+
+ if (json)
+ if_dump_vty_json(vty, ifp, json);
+ else
+ if_dump_vty(vty, ifp);
+
+ if (json)
+ vty_json(vty, json);
+
+ return CMD_SUCCESS;
+}
+
+static void if_show_description(struct vty *vty, struct vrf *vrf)
+{
+ struct interface *ifp;
+
+ vty_out(vty, "Interface Status Protocol Description\n");
+ FOR_ALL_INTERFACES (vrf, ifp) {
+ int len;
+ struct zebra_if *zif;
+ bool intf_desc;
+
+ intf_desc = false;
+
+ len = vty_out(vty, "%s", ifp->name);
+ vty_out(vty, "%*s", (16 - len), " ");
+
+ if (if_is_up(ifp)) {
+ vty_out(vty, "up ");
+ if (CHECK_FLAG(ifp->status,
+ ZEBRA_INTERFACE_LINKDETECTION)) {
+ if (if_is_running(ifp))
+ vty_out(vty, "up ");
+ else
+ vty_out(vty, "down ");
+ } else {
+ vty_out(vty, "unknown ");
+ }
+ } else {
+ vty_out(vty, "down down ");
+ }
+
+ if (ifp->desc) {
+ intf_desc = true;
+ vty_out(vty, "%s", ifp->desc);
+ }
+ zif = ifp->info;
+ if (zif && zif->desc) {
+ vty_out(vty, "%s%s",
+ intf_desc
+ ? "\n "
+ : "",
+ zif->desc);
+ }
+
+ vty_out(vty, "\n");
+ }
+}
+
+DEFUN (show_interface_desc,
+ show_interface_desc_cmd,
+ "show interface description vrf NAME",
+ SHOW_STR
+ "Interface status and configuration\n"
+ "Interface description\n"
+ VRF_CMD_HELP_STR)
+{
+ struct vrf *vrf;
+
+ vrf = vrf_lookup_by_name(argv[4]->arg);
+ if (!vrf) {
+ vty_out(vty, "%% VRF %s not found\n", argv[4]->arg);
+ return CMD_WARNING;
+ }
+
+ if_show_description(vty, vrf);
+
+ return CMD_SUCCESS;
+}
+
+
+DEFUN (show_interface_desc_vrf_all,
+ show_interface_desc_vrf_all_cmd,
+ "show interface description [vrf all]",
+ SHOW_STR
+ "Interface status and configuration\n"
+ "Interface description\n"
+ VRF_ALL_CMD_HELP_STR)
+{
+ struct vrf *vrf;
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name)
+ if (!RB_EMPTY(if_name_head, &vrf->ifaces_by_name)) {
+ vty_out(vty, "\n\tVRF %s(%u)\n\n", VRF_LOGNAME(vrf),
+ vrf->vrf_id);
+ if_show_description(vty, vrf);
+ }
+
+ return CMD_SUCCESS;
+}
+
+void if_arp(struct interface *ifp, bool enable)
+{
+ int ret;
+
+ if (!CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE))
+ return;
+
+ if (enable)
+ ret = if_unset_flags(ifp, IFF_NOARP);
+ else
+ ret = if_set_flags(ifp, IFF_NOARP);
+
+ if (ret < 0) {
+ zlog_debug("Can't %sset noarp flag on interface %s",
+ enable ? "" : "un", ifp->name);
+ return;
+ }
+
+ if_refresh(ifp);
+}
+
+int if_multicast_set(struct interface *ifp)
+{
+ struct zebra_if *if_data;
+
+ if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) {
+ if (if_set_flags(ifp, IFF_MULTICAST) < 0) {
+ zlog_debug("Can't set multicast flag on interface %s",
+ ifp->name);
+ return -1;
+ }
+ if_refresh(ifp);
+ }
+ if_data = ifp->info;
+ if_data->multicast = IF_ZEBRA_DATA_ON;
+
+ return 0;
+}
+
+int if_multicast_unset(struct interface *ifp)
+{
+ struct zebra_if *if_data;
+
+ if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) {
+ if (if_unset_flags(ifp, IFF_MULTICAST) < 0) {
+ zlog_debug("Can't unset multicast flag on interface %s",
+ ifp->name);
+ return -1;
+ }
+ if_refresh(ifp);
+ }
+ if_data = ifp->info;
+ if_data->multicast = IF_ZEBRA_DATA_OFF;
+
+ return 0;
+}
+
+int if_linkdetect(struct interface *ifp, bool detect)
+{
+ int if_was_operative;
+
+ if_was_operative = if_is_no_ptm_operative(ifp);
+ if (detect) {
+ SET_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION);
+
+ /* When linkdetection is enabled, if might come down */
+ if (!if_is_no_ptm_operative(ifp) && if_was_operative)
+ if_down(ifp);
+ } else {
+ UNSET_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION);
+
+ /* Interface may come up after disabling link detection */
+ if (if_is_operative(ifp) && !if_was_operative)
+ if_up(ifp, true);
+ }
+ /* FIXME: Will defer status change forwarding if interface
+ does not come down! */
+ return 0;
+}
+
+int if_shutdown(struct interface *ifp)
+{
+ struct zebra_if *if_data;
+
+ if (ifp->ifindex != IFINDEX_INTERNAL) {
+ /* send RA lifetime of 0 before stopping. rfc4861/6.2.5 */
+ rtadv_stop_ra(ifp);
+ if (if_unset_flags(ifp, IFF_UP) < 0) {
+ zlog_debug("Can't shutdown interface %s", ifp->name);
+ return -1;
+ }
+ if_refresh(ifp);
+ }
+ if_data = ifp->info;
+ if_data->shutdown = IF_ZEBRA_DATA_ON;
+
+ return 0;
+}
+
+int if_no_shutdown(struct interface *ifp)
+{
+ struct zebra_if *if_data;
+
+ if (ifp->ifindex != IFINDEX_INTERNAL) {
+ if (if_set_flags(ifp, IFF_UP | IFF_RUNNING) < 0) {
+ zlog_debug("Can't up interface %s", ifp->name);
+ return -1;
+ }
+ if_refresh(ifp);
+
+ /* Some addresses (in particular, IPv6 addresses on Linux) get
+ * removed when the interface goes down. They need to be
+ * readded.
+ */
+ if_addr_wakeup(ifp);
+ }
+
+ if_data = ifp->info;
+ if_data->shutdown = IF_ZEBRA_DATA_OFF;
+
+ return 0;
+}
+
+void link_param_cmd_set_uint32(struct interface *ifp, uint32_t *field,
+ uint32_t type, uint32_t value)
+{
+ /* Update field as needed */
+ if (IS_PARAM_UNSET(ifp->link_params, type) || *field != value) {
+ *field = value;
+ SET_PARAM(ifp->link_params, type);
+ }
+}
+
+void link_param_cmd_set_float(struct interface *ifp, float *field,
+ uint32_t type, float value)
+{
+ /* Update field as needed */
+ if (IS_PARAM_UNSET(ifp->link_params, type) || *field != value) {
+ *field = value;
+ SET_PARAM(ifp->link_params, type);
+ }
+}
+
+void link_param_cmd_unset(struct interface *ifp, uint32_t type)
+{
+ if (ifp->link_params == NULL)
+ return;
+
+ /* Unset field */
+ UNSET_PARAM(ifp->link_params, type);
+}
+
+void if_ip_address_install(struct interface *ifp, struct prefix *prefix,
+ const char *label, struct prefix *pp)
+{
+ struct zebra_if *if_data;
+ struct connected *ifc;
+
+ if_data = ifp->info;
+
+ ifc = connected_check_ptp(ifp, prefix, pp);
+ if (!ifc) {
+ ifc = connected_new();
+ ifc->ifp = ifp;
+
+ /* Address. */
+ ifc->address = prefix_new();
+ prefix_copy(ifc->address, prefix);
+
+ if (pp) {
+ SET_FLAG(ifc->flags, ZEBRA_IFA_PEER);
+ ifc->destination = prefix_new();
+ prefix_copy(ifc->destination, pp);
+ }
+
+ /* Label. */
+ if (label)
+ ifc->label = XSTRDUP(MTYPE_CONNECTED_LABEL, label);
+
+ /* Add to linked list. */
+ if_connected_add_tail(ifp->connected, ifc);
+ }
+
+ /* This address is configured from zebra. */
+ if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED))
+ SET_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED);
+
+ /* In case of this route need to install kernel. */
+ if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_QUEUED) &&
+ CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE) &&
+ !(if_data && if_data->shutdown == IF_ZEBRA_DATA_ON)) {
+ /* Some system need to up the interface to set IP address. */
+ if (!if_is_up(ifp)) {
+ if_set_flags(ifp, IFF_UP | IFF_RUNNING);
+ if_refresh(ifp);
+ }
+
+ dplane_intf_addr_set(ifp, ifc);
+
+ SET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED);
+ /* The address will be advertised to zebra clients when the
+ * notification
+ * from the kernel has been received.
+ * It will also be added to the subnet chain list, then. */
+ }
+}
+
+void if_ip_address_uninstall(struct interface *ifp, struct prefix *prefix,
+ struct prefix *pp)
+{
+ struct connected *ifc;
+
+ ifc = connected_check_ptp(ifp, prefix, pp);
+ assert(ifc);
+
+ UNSET_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED);
+
+ /* This is not real address or interface is not active. */
+ if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_QUEUED)
+ || !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) {
+ if_connected_del(ifp->connected, ifc);
+ connected_free(&ifc);
+ return;
+ }
+
+ /* This is real route. */
+ dplane_intf_addr_unset(ifp, ifc);
+
+ UNSET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED);
+}
+
+void if_ipv6_address_install(struct interface *ifp, struct prefix *prefix)
+{
+ struct zebra_if *if_data;
+ struct connected *ifc;
+
+ if_data = ifp->info;
+
+ ifc = connected_check(ifp, prefix);
+ if (!ifc) {
+ ifc = connected_new();
+ ifc->ifp = ifp;
+
+ /* Address. */
+ ifc->address = prefix_new();
+ prefix_copy(ifc->address, prefix);
+
+ /* Add to linked list. */
+ if_connected_add_tail(ifp->connected, ifc);
+ }
+
+ /* This address is configured from zebra. */
+ if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED))
+ SET_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED);
+
+ /* In case of this route need to install kernel. */
+ if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_QUEUED) &&
+ CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE) &&
+ !(if_data && if_data->shutdown == IF_ZEBRA_DATA_ON)) {
+ /* Some system need to up the interface to set IP address. */
+ if (!if_is_up(ifp)) {
+ if_set_flags(ifp, IFF_UP | IFF_RUNNING);
+ if_refresh(ifp);
+ }
+
+ dplane_intf_addr_set(ifp, ifc);
+
+ SET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED);
+ /* The address will be advertised to zebra clients when the
+ * notification
+ * from the kernel has been received. */
+ }
+}
+
+void if_ipv6_address_uninstall(struct interface *ifp, struct prefix *prefix)
+{
+ struct connected *ifc;
+
+ ifc = connected_check(ifp, prefix);
+ assert(ifc);
+
+ UNSET_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED);
+
+ /* This is not real address or interface is not active. */
+ if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_QUEUED)
+ || !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) {
+ if_connected_del(ifp->connected, ifc);
+ connected_free(&ifc);
+ return;
+ }
+
+ /* This is real route. */
+ dplane_intf_addr_unset(ifp, ifc);
+
+ UNSET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED);
+}
+
+/* Allocate and initialize interface vector. */
+void zebra_if_init(void)
+{
+ /* Initialize interface and new hook. */
+ hook_register_prio(if_add, 0, if_zebra_new_hook);
+ hook_register_prio(if_del, 0, if_zebra_delete_hook);
+
+ install_element(VIEW_NODE, &show_interface_cmd);
+ install_element(VIEW_NODE, &show_interface_vrf_all_cmd);
+ install_element(VIEW_NODE, &show_interface_name_vrf_cmd);
+ install_element(VIEW_NODE, &show_interface_name_vrf_all_cmd);
+
+ install_element(ENABLE_NODE, &show_interface_desc_cmd);
+ install_element(ENABLE_NODE, &show_interface_desc_vrf_all_cmd);
+}