summaryrefslogtreecommitdiffstats
path: root/zebra/interface.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--zebra/interface.c5731
1 files changed, 5731 insertions, 0 deletions
diff --git a/zebra/interface.c b/zebra/interface.c
new file mode 100644
index 0000000..1afd9d5
--- /dev/null
+++ b/zebra/interface.c
@@ -0,0 +1,5731 @@
+// 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 "lib/northbound_cli.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_HOOK(zebra_if_config_wr, (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 < 0)
+ 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 || new_speed == UINT32_MAX) {
+#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 (new_speed == UINT32_MAX &&
+ 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);
+}
+
+static void zebra_if_nhg_dependents_free(struct zebra_if *zebra_if)
+{
+ nhg_connected_tree_free(&zebra_if->nhg_dependents);
+}
+
+static void zebra_if_nhg_dependents_init(struct zebra_if *zebra_if)
+{
+ nhg_connected_tree_init(&zebra_if->nhg_dependents);
+}
+
+
+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_OFF;
+
+ zebra_if->link_nsid = NS_UNKNOWN;
+
+ zebra_if_nhg_dependents_init(zebra_if);
+
+ zebra_ptm_if_init(zebra_if);
+
+ ifp->ptm_enable = zebra_ptm_get_enable_state();
+
+ rtadv_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_nhg_dependents_check_valid(struct nhg_hash_entry *nhe)
+{
+ zebra_nhg_check_valid(nhe);
+}
+
+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)
+ if_nhg_dependents_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 */
+ if_nhg_dependents_check_valid(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);
+ zebra_if_nhg_dependents_free(zebra_if);
+
+ 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 listnode *node, *nnode;
+ struct connected *ifc;
+ struct prefix *p;
+ enum zebra_dplane_result dplane_res;
+
+ for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, 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 listnode *node;
+ struct listnode *next;
+ struct connected *ifc;
+
+ if (ifp->connected) {
+ for (ALL_LIST_ELEMENTS(ifp->connected, node, next, 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 listnode *node;
+ struct listnode *next;
+ struct connected *ifc;
+
+ if (ifp->connected) {
+ for (ALL_LIST_ELEMENTS(ifp->connected, node, next, 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;
+ struct prefix cp;
+ struct route_node *rn;
+ struct zebra_if *zebra_if;
+ struct listnode *node;
+ struct listnode *last = NULL;
+
+ zebra_if = ifp->info;
+
+ if (!ifp->connected)
+ return;
+
+ while ((node = (last ? last->next : listhead(ifp->connected)))) {
+ ifc = listgetdata(node);
+
+ 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)) {
+ listnode_delete(ifp->connected,
+ ifc);
+ connected_free(&ifc);
+ } else
+ last = node;
+ }
+
+ /* 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))
+ last = node;
+ else {
+ listnode_delete(ifp->connected, ifc);
+ connected_free(&ifc);
+ }
+ } else {
+ last = node;
+ }
+ }
+}
+
+/* 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);
+ }
+}
+
+void if_nhg_dependents_add(struct interface *ifp, struct nhg_hash_entry *nhe)
+{
+ if (ifp->info) {
+ struct zebra_if *zif = (struct zebra_if *)ifp->info;
+
+ nhg_connected_tree_add_nhe(&zif->nhg_dependents, nhe);
+ }
+}
+
+void if_nhg_dependents_del(struct interface *ifp, struct nhg_hash_entry *nhe)
+{
+ if (ifp->info) {
+ struct zebra_if *zif = (struct zebra_if *)ifp->info;
+
+ nhg_connected_tree_del_nhe(&zif->nhg_dependents, nhe);
+ }
+}
+
+unsigned int if_nhg_dependents_count(const struct interface *ifp)
+{
+ if (ifp->info) {
+ struct zebra_if *zif = (struct zebra_if *)ifp->info;
+
+ return nhg_connected_tree_count(&zif->nhg_dependents);
+ }
+
+ return 0;
+}
+
+
+bool if_nhg_dependents_is_empty(const struct interface *ifp)
+{
+ if (ifp->info) {
+ struct zebra_if *zif = (struct zebra_if *)ifp->info;
+
+ return nhg_connected_tree_is_empty(&zif->nhg_dependents);
+ }
+
+ return false;
+}
+
+/* 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);
+
+ /* 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);
+}
+
+/* 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);
+
+ /* 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);
+
+ if (!vrf_lookup_by_id((vrf_id_t)ifindex)) {
+ vrf_id_t exist_id;
+
+ exist_id = zebra_vrf_lookup_by_table(tableid, ns_id);
+ if (exist_id != VRF_DEFAULT) {
+ vrf = vrf_lookup_by_id(exist_id);
+
+ if (vrf)
+ 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);
+ else
+ flog_err(EC_ZEBRA_VRF_NOT_FOUND,
+ "VRF %s id %u does not exist",
+ name, ifindex);
+
+ 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;
+
+ /*
+ * 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",
+ ifp->name, protodown ? "on" : "off");
+
+ /* 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 (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;
+
+ 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);
+
+ switch (op) {
+ case DPLANE_OP_INTF_ADDR_ADD:
+ case DPLANE_OP_INTF_ADDR_DEL:
+ zebra_if_addr_update_ctx(ctx, ifp);
+ break;
+
+ case DPLANE_OP_INTF_INSTALL:
+ case DPLANE_OP_INTF_UPDATE:
+ case 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);
+ break;
+
+ case DPLANE_OP_INTF_NETCONFIG:
+ zebra_if_netconf_update_ctx(ctx, ifp, ifindex);
+ break;
+
+ case DPLANE_OP_ROUTE_INSTALL:
+ case DPLANE_OP_ROUTE_UPDATE:
+ case DPLANE_OP_ROUTE_DELETE:
+ case DPLANE_OP_NH_DELETE:
+ case DPLANE_OP_NH_INSTALL:
+ case DPLANE_OP_NH_UPDATE:
+ case DPLANE_OP_ROUTE_NOTIFY:
+ case DPLANE_OP_LSP_INSTALL:
+ case DPLANE_OP_LSP_UPDATE:
+ case DPLANE_OP_LSP_DELETE:
+ case DPLANE_OP_LSP_NOTIFY:
+ case DPLANE_OP_PW_INSTALL:
+ case DPLANE_OP_PW_UNINSTALL:
+ case DPLANE_OP_SYS_ROUTE_ADD:
+ case DPLANE_OP_SYS_ROUTE_DELETE:
+ case DPLANE_OP_ADDR_INSTALL:
+ case DPLANE_OP_ADDR_UNINSTALL:
+ case DPLANE_OP_MAC_INSTALL:
+ case DPLANE_OP_MAC_DELETE:
+ case DPLANE_OP_NEIGH_INSTALL:
+ case DPLANE_OP_NEIGH_UPDATE:
+ case DPLANE_OP_NEIGH_DELETE:
+ case DPLANE_OP_NEIGH_IP_INSTALL:
+ case DPLANE_OP_NEIGH_IP_DELETE:
+ case DPLANE_OP_VTEP_ADD:
+ case DPLANE_OP_VTEP_DELETE:
+ case DPLANE_OP_RULE_ADD:
+ case DPLANE_OP_RULE_DELETE:
+ case DPLANE_OP_RULE_UPDATE:
+ case DPLANE_OP_NEIGH_DISCOVER:
+ case DPLANE_OP_BR_PORT_UPDATE:
+ case DPLANE_OP_NONE:
+ case DPLANE_OP_IPTABLE_ADD:
+ case DPLANE_OP_IPTABLE_DELETE:
+ case DPLANE_OP_IPSET_ADD:
+ case DPLANE_OP_IPSET_DELETE:
+ case DPLANE_OP_IPSET_ENTRY_ADD:
+ case DPLANE_OP_IPSET_ENTRY_DELETE:
+ case DPLANE_OP_NEIGH_TABLE_UPDATE:
+ case DPLANE_OP_GRE_SET:
+ case DPLANE_OP_TC_QDISC_INSTALL:
+ case DPLANE_OP_TC_QDISC_UNINSTALL:
+ case DPLANE_OP_TC_CLASS_ADD:
+ case DPLANE_OP_TC_CLASS_DELETE:
+ case DPLANE_OP_TC_CLASS_UPDATE:
+ case DPLANE_OP_TC_FILTER_ADD:
+ case DPLANE_OP_TC_FILTER_DELETE:
+ case DPLANE_OP_TC_FILTER_UPDATE:
+ case DPLANE_OP_STARTUP_STAGE:
+ break; /* should never hit here */
+ }
+}
+
+/* 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, "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;
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, connected)) {
+ if (CHECK_FLAG(connected->conf, ZEBRA_IFC_REAL)
+ && (connected->address->family == AF_INET6))
+ v6_list_size++;
+ }
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, connected)) {
+ if (CHECK_FLAG(connected->conf, ZEBRA_IFC_REAL)
+ && !CHECK_FLAG(connected->flags,
+ ZEBRA_IFA_SECONDARY)
+ && (connected->address->family == AF_INET6)) {
+ p = connected->address;
+ /* Don't print link local pfx */
+ if (!IN6_IS_ADDR_LINKLOCAL(&p->u.prefix6)) {
+ if (first_pfx_printed) {
+ /* padding to prepare row only
+ * for ip addr */
+ 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 listnode *node;
+ 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);
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, connected)) {
+ if (CHECK_FLAG(connected->conf, ZEBRA_IFC_REAL)
+ && !CHECK_FLAG(connected->flags,
+ ZEBRA_IFA_SECONDARY)
+ && !(connected->address->family == AF_INET6
+ && IN6_IS_ADDR_LINKLOCAL(
+ &connected->address->u.prefix6))) {
+ 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);
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, connected)) {
+ if (CHECK_FLAG(connected->conf, ZEBRA_IFC_REAL)
+ && (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 %g (in %%)\n",
+ iflp->pkt_loss);
+ 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);
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, connected)) {
+ if (CHECK_FLAG(connected->conf, ZEBRA_IFC_REAL)
+ && (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",
+ iflp->pkt_loss);
+ 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;
+}
+
+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;
+}
+
+DEFUN (multicast,
+ multicast_cmd,
+ "multicast",
+ "Set multicast flag to interface\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ int ret;
+ struct zebra_if *if_data;
+
+ if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) {
+ ret = if_set_flags(ifp, IFF_MULTICAST);
+ if (ret < 0) {
+ vty_out(vty, "Can't set multicast flag\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if_refresh(ifp);
+ }
+ if_data = ifp->info;
+ if_data->multicast = IF_ZEBRA_DATA_ON;
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (mpls,
+ mpls_cmd,
+ "[no] mpls <enable$on|disable$off>",
+ NO_STR
+ MPLS_STR
+ "Set mpls to be on for the interface\n"
+ "Set mpls to be off for the interface\n")
+{
+ if (!no)
+ nb_cli_enqueue_change(vty, "./frr-zebra:zebra/mpls",
+ NB_OP_CREATE, on ? "true" : "false");
+ else
+ nb_cli_enqueue_change(vty, "./frr-zebra:zebra/mpls",
+ NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+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;
+}
+
+DEFUN (no_multicast,
+ no_multicast_cmd,
+ "no multicast",
+ NO_STR
+ "Unset multicast flag to interface\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ int ret;
+ struct zebra_if *if_data;
+
+ if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) {
+ ret = if_unset_flags(ifp, IFF_MULTICAST);
+ if (ret < 0) {
+ vty_out(vty, "Can't unset multicast flag\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if_refresh(ifp);
+ }
+ if_data = ifp->info;
+ if_data->multicast = IF_ZEBRA_DATA_OFF;
+
+ return CMD_SUCCESS;
+}
+
+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;
+}
+
+DEFUN(linkdetect, linkdetect_cmd, "link-detect",
+ "Enable link detection on interface\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+
+ if_linkdetect(ifp, true);
+
+ return CMD_SUCCESS;
+}
+
+
+DEFUN (no_linkdetect,
+ no_linkdetect_cmd,
+ "no link-detect",
+ NO_STR
+ "Disable link detection on interface\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+
+ if_linkdetect(ifp, false);
+
+ return CMD_SUCCESS;
+}
+
+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;
+}
+
+DEFUN (shutdown_if,
+ shutdown_if_cmd,
+ "shutdown",
+ "Shutdown the selected interface\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ int ret;
+ 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);
+ ret = if_unset_flags(ifp, IFF_UP);
+ if (ret < 0) {
+ vty_out(vty, "Can't shutdown interface\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if_refresh(ifp);
+ }
+ if_data = ifp->info;
+ if_data->shutdown = IF_ZEBRA_DATA_ON;
+
+ return CMD_SUCCESS;
+}
+
+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;
+}
+
+DEFUN (no_shutdown_if,
+ no_shutdown_if_cmd,
+ "no shutdown",
+ NO_STR
+ "Shutdown the selected interface\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ int ret;
+ struct zebra_if *if_data;
+
+ if (ifp->ifindex != IFINDEX_INTERNAL) {
+ ret = if_set_flags(ifp, IFF_UP | IFF_RUNNING);
+ if (ret < 0) {
+ vty_out(vty, "Can't up interface\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ 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 CMD_SUCCESS;
+}
+
+DEFUN (bandwidth_if,
+ bandwidth_if_cmd,
+ "bandwidth (1-100000)",
+ "Set bandwidth informational parameter\n"
+ "Bandwidth in megabits\n")
+{
+ int idx_number = 1;
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ unsigned int bandwidth;
+
+ bandwidth = strtol(argv[idx_number]->arg, NULL, 10);
+
+ /* bandwidth range is <1-100000> */
+ if (bandwidth < 1 || bandwidth > 100000) {
+ vty_out(vty, "Bandwidth is invalid\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ ifp->bandwidth = bandwidth;
+
+ /* force protocols to recalculate routes due to cost change */
+ if (if_is_operative(ifp))
+ zebra_interface_up_update(ifp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bandwidth_if,
+ no_bandwidth_if_cmd,
+ "no bandwidth [(1-100000)]",
+ NO_STR
+ "Set bandwidth informational parameter\n"
+ "Bandwidth in megabits\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+
+ ifp->bandwidth = 0;
+
+ /* force protocols to recalculate routes due to cost change */
+ if (if_is_operative(ifp))
+ zebra_interface_up_update(ifp);
+
+ return CMD_SUCCESS;
+}
+
+
+struct cmd_node link_params_node = {
+ .name = "link-params",
+ .node = LINK_PARAMS_NODE,
+ .parent_node = INTERFACE_NODE,
+ .prompt = "%s(config-link-params)# ",
+ .no_xpath = true,
+};
+
+static 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);
+
+ /* force protocols to update LINK STATE due to parameters change
+ */
+ if (if_is_operative(ifp))
+ zebra_interface_parameters_update(ifp);
+ }
+}
+static 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);
+
+ /* force protocols to update LINK STATE due to parameters change
+ */
+ if (if_is_operative(ifp))
+ zebra_interface_parameters_update(ifp);
+ }
+}
+
+static 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);
+
+ /* force protocols to update LINK STATE due to parameters change */
+ if (if_is_operative(ifp))
+ zebra_interface_parameters_update(ifp);
+}
+
+DEFUN_NOSH (link_params,
+ link_params_cmd,
+ "link-params",
+ LINK_PARAMS_STR)
+{
+ /* vty->qobj_index stays the same @ interface pointer */
+ vty->node = LINK_PARAMS_NODE;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_NOSH (exit_link_params,
+ exit_link_params_cmd,
+ "exit-link-params",
+ "Exit from Link Params configuration mode\n")
+{
+ if (vty->node == LINK_PARAMS_NODE)
+ vty->node = INTERFACE_NODE;
+ return CMD_SUCCESS;
+}
+
+/* Specific Traffic Engineering parameters commands */
+DEFUN (link_params_enable,
+ link_params_enable_cmd,
+ "enable",
+ "Activate link parameters on this interface\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+
+ /* This command could be issue at startup, when activate MPLS TE */
+ /* on a new interface or after a ON / OFF / ON toggle */
+ /* In all case, TE parameters are reset to their default factory */
+ if (IS_ZEBRA_DEBUG_EVENT || IS_ZEBRA_DEBUG_MPLS)
+ zlog_debug(
+ "Link-params: enable TE link parameters on interface %s",
+ ifp->name);
+
+ if (!if_link_params_get(ifp))
+ if_link_params_enable(ifp);
+
+ /* force protocols to update LINK STATE due to parameters change */
+ if (if_is_operative(ifp))
+ zebra_interface_parameters_update(ifp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_link_params_enable,
+ no_link_params_enable_cmd,
+ "no enable",
+ NO_STR
+ "Disable link parameters on this interface\n")
+{
+ char xpath[XPATH_MAXLEN];
+ int ret;
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+
+ if (IS_ZEBRA_DEBUG_EVENT || IS_ZEBRA_DEBUG_MPLS)
+ zlog_debug("MPLS-TE: disable TE link parameters on interface %s",
+ ifp->name);
+
+ if_link_params_free(ifp);
+
+ snprintf(
+ xpath, sizeof(xpath),
+ "/frr-interface:lib/interface[name='%s']/frr-zebra:zebra/link-params/affinities",
+ ifp->name);
+ if (yang_dnode_exists(running_config->dnode, xpath))
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ ret = nb_cli_apply_changes(vty, NULL);
+
+ if (ret != CMD_SUCCESS)
+ return ret;
+
+ /* force protocols to update LINK STATE due to parameters change */
+ if (if_is_operative(ifp))
+ zebra_interface_parameters_update(ifp);
+
+ return CMD_SUCCESS;
+}
+
+/* STANDARD TE metrics */
+DEFUN (link_params_metric,
+ link_params_metric_cmd,
+ "metric (0-4294967295)",
+ "Link metric for MPLS-TE purpose\n"
+ "Metric value in decimal\n")
+{
+ int idx_number = 1;
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct if_link_params *iflp = if_link_params_get(ifp);
+ uint32_t metric;
+
+ metric = strtoul(argv[idx_number]->arg, NULL, 10);
+
+ if (!iflp)
+ iflp = if_link_params_enable(ifp);
+
+ /* Update TE metric if needed */
+ link_param_cmd_set_uint32(ifp, &iflp->te_metric, LP_TE_METRIC, metric);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_link_params_metric,
+ no_link_params_metric_cmd,
+ "no metric",
+ NO_STR
+ "Disable Link Metric on this interface\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+
+ /* Unset TE Metric */
+ link_param_cmd_unset(ifp, LP_TE_METRIC);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (link_params_maxbw,
+ link_params_maxbw_cmd,
+ "max-bw BANDWIDTH",
+ "Maximum bandwidth that can be used\n"
+ "Bytes/second (IEEE floating point format)\n")
+{
+ int idx_bandwidth = 1;
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct if_link_params *iflp = if_link_params_get(ifp);
+
+ float bw;
+
+ if (sscanf(argv[idx_bandwidth]->arg, "%g", &bw) != 1) {
+ vty_out(vty, "link_params_maxbw: fscanf: %s\n",
+ safe_strerror(errno));
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Check that Maximum bandwidth is not lower than other bandwidth
+ * parameters */
+ if (iflp && ((bw <= iflp->max_rsv_bw) || (bw <= iflp->unrsv_bw[0]) ||
+ (bw <= iflp->unrsv_bw[1]) || (bw <= iflp->unrsv_bw[2]) ||
+ (bw <= iflp->unrsv_bw[3]) || (bw <= iflp->unrsv_bw[4]) ||
+ (bw <= iflp->unrsv_bw[5]) || (bw <= iflp->unrsv_bw[6]) ||
+ (bw <= iflp->unrsv_bw[7]) || (bw <= iflp->ava_bw) ||
+ (bw <= iflp->res_bw) || (bw <= iflp->use_bw))) {
+ vty_out(vty,
+ "Maximum Bandwidth could not be lower than others bandwidth\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (!iflp)
+ iflp = if_link_params_enable(ifp);
+
+ /* Update Maximum Bandwidth if needed */
+ link_param_cmd_set_float(ifp, &iflp->max_bw, LP_MAX_BW, bw);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (link_params_max_rsv_bw,
+ link_params_max_rsv_bw_cmd,
+ "max-rsv-bw BANDWIDTH",
+ "Maximum bandwidth that may be reserved\n"
+ "Bytes/second (IEEE floating point format)\n")
+{
+ int idx_bandwidth = 1;
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct if_link_params *iflp = if_link_params_get(ifp);
+ float bw;
+
+ if (sscanf(argv[idx_bandwidth]->arg, "%g", &bw) != 1) {
+ vty_out(vty, "link_params_max_rsv_bw: fscanf: %s\n",
+ safe_strerror(errno));
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Check that bandwidth is not greater than maximum bandwidth parameter
+ */
+ if (iflp && bw > iflp->max_bw) {
+ vty_out(vty,
+ "Maximum Reservable Bandwidth could not be greater than Maximum Bandwidth (%g)\n",
+ iflp->max_bw);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (!iflp)
+ iflp = if_link_params_enable(ifp);
+
+ /* Update Maximum Reservable Bandwidth if needed */
+ link_param_cmd_set_float(ifp, &iflp->max_rsv_bw, LP_MAX_RSV_BW, bw);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (link_params_unrsv_bw,
+ link_params_unrsv_bw_cmd,
+ "unrsv-bw (0-7) BANDWIDTH",
+ "Unreserved bandwidth at each priority level\n"
+ "Priority\n"
+ "Bytes/second (IEEE floating point format)\n")
+{
+ int idx_number = 1;
+ int idx_bandwidth = 2;
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct if_link_params *iflp = if_link_params_get(ifp);
+ int priority;
+ float bw;
+
+ /* We don't have to consider about range check here. */
+ if (sscanf(argv[idx_number]->arg, "%d", &priority) != 1) {
+ vty_out(vty, "link_params_unrsv_bw: fscanf: %s\n",
+ safe_strerror(errno));
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (sscanf(argv[idx_bandwidth]->arg, "%g", &bw) != 1) {
+ vty_out(vty, "link_params_unrsv_bw: fscanf: %s\n",
+ safe_strerror(errno));
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Check that bandwidth is not greater than maximum bandwidth parameter
+ */
+ if (iflp && bw > iflp->max_bw) {
+ vty_out(vty,
+ "UnReserved Bandwidth could not be greater than Maximum Bandwidth (%g)\n",
+ iflp->max_bw);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (!iflp)
+ iflp = if_link_params_enable(ifp);
+
+ /* Update Unreserved Bandwidth if needed */
+ link_param_cmd_set_float(ifp, &iflp->unrsv_bw[priority], LP_UNRSV_BW,
+ bw);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY_YANG(link_params_admin_grp, link_params_admin_grp_cmd,
+ "admin-grp BITPATTERN",
+ "Administrative group membership\n"
+ "32-bit Hexadecimal value (e.g. 0xa1)\n")
+{
+ char xpath[XPATH_MAXLEN];
+ int idx_bitpattern = 1;
+ unsigned long value;
+ char value_str[11];
+
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+
+ snprintf(
+ xpath, sizeof(xpath),
+ "/frr-interface:lib/interface[name='%s']/frr-zebra:zebra/link-params/affinities",
+ ifp->name);
+ if (yang_dnode_exists(running_config->dnode, xpath)) {
+ vty_out(vty,
+ "cannot use the admin-grp command when affinity is set\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (sscanf(argv[idx_bitpattern]->arg, "0x%lx", &value) != 1) {
+ vty_out(vty, "link_params_admin_grp: fscanf: %s\n",
+ safe_strerror(errno));
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (value > 0xFFFFFFFF) {
+ vty_out(vty, "value must be not be superior to 0xFFFFFFFF\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ snprintf(value_str, sizeof(value_str), "%ld", value);
+
+ nb_cli_enqueue_change(
+ vty, "./frr-zebra:zebra/link-params/legacy-admin-group",
+ NB_OP_MODIFY, value_str);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG(no_link_params_admin_grp, no_link_params_admin_grp_cmd,
+ "no admin-grp",
+ NO_STR "Disable Administrative group membership on this interface\n")
+{
+ nb_cli_enqueue_change(
+ vty, "./frr-zebra:zebra/link-params/legacy-admin-group",
+ NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+/* RFC5392 & RFC5316: INTER-AS */
+DEFUN (link_params_inter_as,
+ link_params_inter_as_cmd,
+ "neighbor A.B.C.D as (1-4294967295)",
+ "Configure remote ASBR information (Neighbor IP address and AS number)\n"
+ "Remote IP address in dot decimal A.B.C.D\n"
+ "Remote AS number\n"
+ "AS number in the range <1-4294967295>\n")
+{
+ int idx_ipv4 = 1;
+ int idx_number = 3;
+
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct if_link_params *iflp = if_link_params_get(ifp);
+ struct in_addr addr;
+ uint32_t as;
+
+ if (!inet_aton(argv[idx_ipv4]->arg, &addr)) {
+ vty_out(vty, "Please specify Router-Addr by A.B.C.D\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (!iflp)
+ iflp = if_link_params_enable(ifp);
+
+ as = strtoul(argv[idx_number]->arg, NULL, 10);
+
+ /* Update Remote IP and Remote AS fields if needed */
+ if (IS_PARAM_UNSET(iflp, LP_RMT_AS) || iflp->rmt_as != as
+ || iflp->rmt_ip.s_addr != addr.s_addr) {
+
+ iflp->rmt_as = as;
+ iflp->rmt_ip.s_addr = addr.s_addr;
+ SET_PARAM(iflp, LP_RMT_AS);
+
+ /* force protocols to update LINK STATE due to parameters change
+ */
+ if (if_is_operative(ifp))
+ zebra_interface_parameters_update(ifp);
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_link_params_inter_as,
+ no_link_params_inter_as_cmd,
+ "no neighbor",
+ NO_STR
+ "Remove Neighbor IP address and AS number for Inter-AS TE\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct if_link_params *iflp = if_link_params_get(ifp);
+
+ if (!iflp)
+ return CMD_SUCCESS;
+
+ /* Reset Remote IP and AS neighbor */
+ iflp->rmt_as = 0;
+ iflp->rmt_ip.s_addr = 0;
+ UNSET_PARAM(iflp, LP_RMT_AS);
+
+ /* force protocols to update LINK STATE due to parameters change */
+ if (if_is_operative(ifp))
+ zebra_interface_parameters_update(ifp);
+
+ return CMD_SUCCESS;
+}
+
+/* RFC7471: OSPF Traffic Engineering (TE) Metric extensions &
+ * draft-ietf-isis-metric-extensions-07.txt */
+DEFUN (link_params_delay,
+ link_params_delay_cmd,
+ "delay (0-16777215) [min (0-16777215) max (0-16777215)]",
+ "Unidirectional Average Link Delay\n"
+ "Average delay in micro-second as decimal (0...16777215)\n"
+ "Minimum delay\n"
+ "Minimum delay in micro-second as decimal (0...16777215)\n"
+ "Maximum delay\n"
+ "Maximum delay in micro-second as decimal (0...16777215)\n")
+{
+ /* Get and Check new delay values */
+ uint32_t delay = 0, low = 0, high = 0;
+ delay = strtoul(argv[1]->arg, NULL, 10);
+ if (argc == 6) {
+ low = strtoul(argv[3]->arg, NULL, 10);
+ high = strtoul(argv[5]->arg, NULL, 10);
+ }
+
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct if_link_params *iflp = if_link_params_get(ifp);
+ uint8_t update = 0;
+
+ if (argc == 2) {
+ /*
+ * Check new delay value against old Min and Max delays if set
+ *
+ * RFC 7471 Section 4.2.7:
+ * It is possible for min delay and max delay to be
+ * the same value.
+ *
+ * Therefore, it is also allowed that the average
+ * delay be equal to the min delay or max delay.
+ */
+ if (iflp && IS_PARAM_SET(iflp, LP_MM_DELAY) &&
+ (delay < iflp->min_delay || delay > iflp->max_delay)) {
+ vty_out(vty,
+ "Average delay should be in range Min (%d) - Max (%d) delay\n",
+ iflp->min_delay, iflp->max_delay);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (!iflp)
+ iflp = if_link_params_enable(ifp);
+
+ /* Update delay if value is not set or change */
+ if (IS_PARAM_UNSET(iflp, LP_DELAY) || iflp->av_delay != delay) {
+ iflp->av_delay = delay;
+ SET_PARAM(iflp, LP_DELAY);
+ update = 1;
+ }
+ /* Unset Min and Max delays if already set */
+ if (IS_PARAM_SET(iflp, LP_MM_DELAY)) {
+ iflp->min_delay = 0;
+ iflp->max_delay = 0;
+ UNSET_PARAM(iflp, LP_MM_DELAY);
+ update = 1;
+ }
+ } else {
+ /*
+ * Check new delays value coherency. See above note
+ * regarding average delay equal to min/max allowed
+ */
+ if (delay < low || delay > high) {
+ vty_out(vty,
+ "Average delay should be in range Min (%d) - Max (%d) delay\n",
+ low, high);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (!iflp)
+ iflp = if_link_params_enable(ifp);
+
+ /* Update Delays if needed */
+ if (IS_PARAM_UNSET(iflp, LP_DELAY)
+ || IS_PARAM_UNSET(iflp, LP_MM_DELAY)
+ || iflp->av_delay != delay || iflp->min_delay != low
+ || iflp->max_delay != high) {
+ iflp->av_delay = delay;
+ SET_PARAM(iflp, LP_DELAY);
+ iflp->min_delay = low;
+ iflp->max_delay = high;
+ SET_PARAM(iflp, LP_MM_DELAY);
+ update = 1;
+ }
+ }
+
+ /* force protocols to update LINK STATE due to parameters change */
+ if (update == 1 && if_is_operative(ifp))
+ zebra_interface_parameters_update(ifp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_link_params_delay,
+ no_link_params_delay_cmd,
+ "no delay",
+ NO_STR
+ "Disable Unidirectional Average, Min & Max Link Delay on this interface\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct if_link_params *iflp = if_link_params_get(ifp);
+
+ if (!iflp)
+ return CMD_SUCCESS;
+
+ /* Unset Delays */
+ iflp->av_delay = 0;
+ UNSET_PARAM(iflp, LP_DELAY);
+ iflp->min_delay = 0;
+ iflp->max_delay = 0;
+ UNSET_PARAM(iflp, LP_MM_DELAY);
+
+ /* force protocols to update LINK STATE due to parameters change */
+ if (if_is_operative(ifp))
+ zebra_interface_parameters_update(ifp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (link_params_delay_var,
+ link_params_delay_var_cmd,
+ "delay-variation (0-16777215)",
+ "Unidirectional Link Delay Variation\n"
+ "delay variation in micro-second as decimal (0...16777215)\n")
+{
+ int idx_number = 1;
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct if_link_params *iflp = if_link_params_get(ifp);
+ uint32_t value;
+
+ value = strtoul(argv[idx_number]->arg, NULL, 10);
+
+ if (!iflp)
+ iflp = if_link_params_enable(ifp);
+
+ /* Update Delay Variation if needed */
+ link_param_cmd_set_uint32(ifp, &iflp->delay_var, LP_DELAY_VAR, value);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_link_params_delay_var,
+ no_link_params_delay_var_cmd,
+ "no delay-variation",
+ NO_STR
+ "Disable Unidirectional Delay Variation on this interface\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+
+ /* Unset Delay Variation */
+ link_param_cmd_unset(ifp, LP_DELAY_VAR);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (link_params_pkt_loss,
+ link_params_pkt_loss_cmd,
+ "packet-loss PERCENTAGE",
+ "Unidirectional Link Packet Loss\n"
+ "percentage of total traffic by 0.000003% step and less than 50.331642%\n")
+{
+ int idx_percentage = 1;
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct if_link_params *iflp = if_link_params_get(ifp);
+ float fval;
+
+ if (sscanf(argv[idx_percentage]->arg, "%g", &fval) != 1) {
+ vty_out(vty, "link_params_pkt_loss: fscanf: %s\n",
+ safe_strerror(errno));
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (fval > MAX_PKT_LOSS)
+ fval = MAX_PKT_LOSS;
+
+ if (!iflp)
+ iflp = if_link_params_enable(ifp);
+
+ /* Update Packet Loss if needed */
+ link_param_cmd_set_float(ifp, &iflp->pkt_loss, LP_PKT_LOSS, fval);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_link_params_pkt_loss,
+ no_link_params_pkt_loss_cmd,
+ "no packet-loss",
+ NO_STR
+ "Disable Unidirectional Link Packet Loss on this interface\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+
+ /* Unset Packet Loss */
+ link_param_cmd_unset(ifp, LP_PKT_LOSS);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (link_params_res_bw,
+ link_params_res_bw_cmd,
+ "res-bw BANDWIDTH",
+ "Unidirectional Residual Bandwidth\n"
+ "Bytes/second (IEEE floating point format)\n")
+{
+ int idx_bandwidth = 1;
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct if_link_params *iflp = if_link_params_get(ifp);
+ float bw;
+
+ if (sscanf(argv[idx_bandwidth]->arg, "%g", &bw) != 1) {
+ vty_out(vty, "link_params_res_bw: fscanf: %s\n",
+ safe_strerror(errno));
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Check that bandwidth is not greater than maximum bandwidth parameter
+ */
+ if (iflp && bw > iflp->max_bw) {
+ vty_out(vty,
+ "Residual Bandwidth could not be greater than Maximum Bandwidth (%g)\n",
+ iflp->max_bw);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (!iflp)
+ iflp = if_link_params_enable(ifp);
+
+ /* Update Residual Bandwidth if needed */
+ link_param_cmd_set_float(ifp, &iflp->res_bw, LP_RES_BW, bw);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_link_params_res_bw,
+ no_link_params_res_bw_cmd,
+ "no res-bw",
+ NO_STR
+ "Disable Unidirectional Residual Bandwidth on this interface\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+
+ /* Unset Residual Bandwidth */
+ link_param_cmd_unset(ifp, LP_RES_BW);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (link_params_ava_bw,
+ link_params_ava_bw_cmd,
+ "ava-bw BANDWIDTH",
+ "Unidirectional Available Bandwidth\n"
+ "Bytes/second (IEEE floating point format)\n")
+{
+ int idx_bandwidth = 1;
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct if_link_params *iflp = if_link_params_get(ifp);
+ float bw;
+
+ if (sscanf(argv[idx_bandwidth]->arg, "%g", &bw) != 1) {
+ vty_out(vty, "link_params_ava_bw: fscanf: %s\n",
+ safe_strerror(errno));
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Check that bandwidth is not greater than maximum bandwidth parameter
+ */
+ if (iflp && bw > iflp->max_bw) {
+ vty_out(vty,
+ "Available Bandwidth could not be greater than Maximum Bandwidth (%g)\n",
+ iflp->max_bw);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (!iflp)
+ iflp = if_link_params_enable(ifp);
+
+ /* Update Residual Bandwidth if needed */
+ link_param_cmd_set_float(ifp, &iflp->ava_bw, LP_AVA_BW, bw);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_link_params_ava_bw,
+ no_link_params_ava_bw_cmd,
+ "no ava-bw",
+ NO_STR
+ "Disable Unidirectional Available Bandwidth on this interface\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+
+ /* Unset Available Bandwidth */
+ link_param_cmd_unset(ifp, LP_AVA_BW);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (link_params_use_bw,
+ link_params_use_bw_cmd,
+ "use-bw BANDWIDTH",
+ "Unidirectional Utilised Bandwidth\n"
+ "Bytes/second (IEEE floating point format)\n")
+{
+ int idx_bandwidth = 1;
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct if_link_params *iflp = if_link_params_get(ifp);
+ float bw;
+
+ if (sscanf(argv[idx_bandwidth]->arg, "%g", &bw) != 1) {
+ vty_out(vty, "link_params_use_bw: fscanf: %s\n",
+ safe_strerror(errno));
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Check that bandwidth is not greater than maximum bandwidth parameter
+ */
+ if (iflp && bw > iflp->max_bw) {
+ vty_out(vty,
+ "Utilised Bandwidth could not be greater than Maximum Bandwidth (%g)\n",
+ iflp->max_bw);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (!iflp)
+ iflp = if_link_params_enable(ifp);
+
+ /* Update Utilized Bandwidth if needed */
+ link_param_cmd_set_float(ifp, &iflp->use_bw, LP_USE_BW, bw);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_link_params_use_bw,
+ no_link_params_use_bw_cmd,
+ "no use-bw",
+ NO_STR
+ "Disable Unidirectional Utilised Bandwidth on this interface\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+
+ /* Unset Utilised Bandwidth */
+ link_param_cmd_unset(ifp, LP_USE_BW);
+
+ return CMD_SUCCESS;
+}
+
+static int ag_change(struct vty *vty, int argc, struct cmd_token **argv,
+ const char *xpath, bool no, int start_idx)
+{
+ for (int i = start_idx; i < argc; i++)
+ nb_cli_enqueue_change(vty, xpath,
+ no ? NB_OP_DESTROY : NB_OP_CREATE,
+ argv[i]->arg);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-zebra:zebra/link-params/affinities/affinity
+ */
+DEFPY_YANG(link_params_affinity, link_params_affinity_cmd,
+ "[no] affinity NAME...",
+ NO_STR
+ "Interface affinities\n"
+ "Affinity names\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(
+ xpath, sizeof(xpath),
+ "/frr-interface:lib/interface[name='%s']/frr-zebra:zebra/link-params/legacy-admin-group",
+ ifp->name);
+ if (yang_dnode_exists(running_config->dnode, xpath)) {
+ vty_out(vty,
+ "cannot use the affinity command when admin-grp is set\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return ag_change(vty, argc, argv,
+ "./frr-zebra:zebra/link-params/affinities/affinity",
+ no, no ? 2 : 1);
+}
+
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-zebra:zebra/link-params/affinities/affinity-mode
+ */
+DEFPY_YANG(link_params_affinity_mode, link_params_affinity_mode_cmd,
+ "affinity-mode <standard|extended|both>$affmode",
+ "Interface affinity mode\n"
+ "Standard Admin-Group only RFC3630,5305,5329 (default)\n"
+ "Extended Admin-Group only RFC7308\n"
+ "Standard and extended Admin-Group format\n")
+{
+ const char *xpath = "./frr-zebra:zebra/link-params/affinity-mode";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, affmode);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG(no_link_params_affinity_mode, no_link_params_affinity_mode_cmd,
+ "no affinity-mode [<standard|extended|both>]",
+ NO_STR
+ "Interface affinity mode\n"
+ "Standard Admin-Group only RFC3630,5305,5329 (default)\n"
+ "Extended Admin-Group only RFC7308\n"
+ "Standard and extended Admin-Group format\n")
+{
+ const char *xpath = "./frr-zebra:zebra/link-params/affinity-mode";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, "standard");
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+static int ag_iter_cb(const struct lyd_node *dnode, void *arg)
+{
+ struct vty *vty = (struct vty *)arg;
+
+ vty_out(vty, " %s", yang_dnode_get_string(dnode, "."));
+ return YANG_ITER_CONTINUE;
+}
+
+void cli_show_legacy_admin_group(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ if (!yang_dnode_exists(dnode, "./legacy-admin-group"))
+ return;
+
+ vty_out(vty, " admin-group 0x%x\n",
+ yang_dnode_get_uint32(dnode, "./legacy-admin-group"));
+}
+
+void cli_show_affinity_mode(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ enum affinity_mode affinity_mode = yang_dnode_get_enum(dnode, ".");
+
+ if (affinity_mode == AFFINITY_MODE_STANDARD)
+ vty_out(vty, " affinity-mode standard\n");
+ else if (affinity_mode == AFFINITY_MODE_BOTH)
+ vty_out(vty, " affinity-mode both\n");
+}
+
+void cli_show_affinity(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ if (!yang_dnode_exists(dnode, "./affinity"))
+ return;
+
+ vty_out(vty, " affinity");
+ yang_dnode_iterate(ag_iter_cb, vty, dnode, "./affinity");
+ vty_out(vty, "\n");
+}
+
+int if_ip_address_install(struct interface *ifp, struct prefix *prefix,
+ const char *label, struct prefix *pp)
+{
+ struct zebra_if *if_data;
+ struct prefix_ipv4 lp;
+ struct prefix_ipv4 *p;
+ struct connected *ifc;
+ enum zebra_dplane_result dplane_res;
+
+ if_data = ifp->info;
+
+ lp.family = prefix->family;
+ lp.prefix = prefix->u.prefix4;
+ lp.prefixlen = prefix->prefixlen;
+ apply_mask_ipv4(&lp);
+
+ ifc = connected_check_ptp(ifp, &lp, pp ? pp : NULL);
+ if (!ifc) {
+ ifc = connected_new();
+ ifc->ifp = ifp;
+
+ /* Address. */
+ p = prefix_ipv4_new();
+ *p = lp;
+ ifc->address = (struct prefix *)p;
+
+ if (pp) {
+ SET_FLAG(ifc->flags, ZEBRA_IFA_PEER);
+ p = prefix_ipv4_new();
+ *p = *(struct prefix_ipv4 *)pp;
+ ifc->destination = (struct prefix *)p;
+ }
+
+ /* Label. */
+ if (label)
+ ifc->label = XSTRDUP(MTYPE_CONNECTED_LABEL, label);
+
+ /* Add to linked list. */
+ listnode_add(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_res = dplane_intf_addr_set(ifp, ifc);
+ if (dplane_res == ZEBRA_DPLANE_REQUEST_FAILURE) {
+ zlog_debug(
+ "dplane can't set interface IP address: %s.",
+ dplane_res2str(dplane_res));
+ return NB_ERR;
+ }
+
+ 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. */
+ }
+
+ return 0;
+}
+
+static int ip_address_install(struct vty *vty, struct interface *ifp,
+ const char *addr_str, const char *peer_str,
+ const char *label)
+{
+ struct zebra_if *if_data;
+ struct prefix_ipv4 lp, pp;
+ struct connected *ifc;
+ struct prefix_ipv4 *p;
+ int ret;
+ enum zebra_dplane_result dplane_res;
+
+ if_data = ifp->info;
+
+ ret = str2prefix_ipv4(addr_str, &lp);
+ if (ret <= 0) {
+ vty_out(vty, "%% Malformed address \n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (ipv4_martian(&lp.prefix)) {
+ vty_out(vty, "%% Invalid address\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (peer_str) {
+ if (lp.prefixlen != IPV4_MAX_BITLEN) {
+ vty_out(vty,
+ "%% Local prefix length for P-t-P address must be /32\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ ret = str2prefix_ipv4(peer_str, &pp);
+ if (ret <= 0) {
+ vty_out(vty, "%% Malformed peer address\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
+
+ ifc = connected_check_ptp(ifp, &lp, peer_str ? &pp : NULL);
+ if (!ifc) {
+ ifc = connected_new();
+ ifc->ifp = ifp;
+
+ /* Address. */
+ p = prefix_ipv4_new();
+ *p = lp;
+ ifc->address = (struct prefix *)p;
+
+ if (peer_str) {
+ SET_FLAG(ifc->flags, ZEBRA_IFA_PEER);
+ p = prefix_ipv4_new();
+ *p = pp;
+ ifc->destination = (struct prefix *)p;
+ }
+
+ /* Label. */
+ if (label)
+ ifc->label = XSTRDUP(MTYPE_CONNECTED_LABEL, label);
+
+ /* Add to linked list. */
+ listnode_add(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_res = dplane_intf_addr_set(ifp, ifc);
+ if (dplane_res == ZEBRA_DPLANE_REQUEST_FAILURE) {
+ vty_out(vty, "%% Can't set interface IP address: %s.\n",
+ dplane_res2str(dplane_res));
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ 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. */
+ }
+
+ return CMD_SUCCESS;
+}
+
+int if_ip_address_uinstall(struct interface *ifp, struct prefix *prefix)
+{
+ struct connected *ifc = NULL;
+ enum zebra_dplane_result dplane_res;
+
+ if (prefix->family == AF_INET) {
+ /* Check current interface address. */
+ ifc = connected_check_ptp(ifp, prefix, NULL);
+ if (!ifc) {
+ zlog_debug("interface %s Can't find address",
+ ifp->name);
+ return -1;
+ }
+
+ } else if (prefix->family == AF_INET6) {
+ /* Check current interface address. */
+ ifc = connected_check(ifp, prefix);
+ }
+
+ if (!ifc) {
+ zlog_debug("interface %s Can't find address", ifp->name);
+ return -1;
+ }
+ 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)) {
+ listnode_delete(ifp->connected, ifc);
+ connected_free(&ifc);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* This is real route. */
+ dplane_res = dplane_intf_addr_unset(ifp, ifc);
+ if (dplane_res == ZEBRA_DPLANE_REQUEST_FAILURE) {
+ zlog_debug("Can't unset interface IP address: %s.",
+ dplane_res2str(dplane_res));
+ return -1;
+ }
+ UNSET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED);
+
+ return 0;
+}
+
+static int ip_address_uninstall(struct vty *vty, struct interface *ifp,
+ const char *addr_str, const char *peer_str,
+ const char *label)
+{
+ struct prefix_ipv4 lp, pp;
+ struct connected *ifc;
+ int ret;
+ enum zebra_dplane_result dplane_res;
+
+ /* Convert to prefix structure. */
+ ret = str2prefix_ipv4(addr_str, &lp);
+ if (ret <= 0) {
+ vty_out(vty, "%% Malformed address \n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (peer_str) {
+ if (lp.prefixlen != IPV4_MAX_BITLEN) {
+ vty_out(vty,
+ "%% Local prefix length for P-t-P address must be /32\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ ret = str2prefix_ipv4(peer_str, &pp);
+ if (ret <= 0) {
+ vty_out(vty, "%% Malformed peer address\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
+
+ /* Check current interface address. */
+ ifc = connected_check_ptp(ifp, &lp, peer_str ? &pp : NULL);
+ if (!ifc) {
+ vty_out(vty, "%% Can't find address\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* This is not configured address. */
+ if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED))
+ return CMD_WARNING_CONFIG_FAILED;
+
+ 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)) {
+ listnode_delete(ifp->connected, ifc);
+ connected_free(&ifc);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* This is real route. */
+ dplane_res = dplane_intf_addr_unset(ifp, ifc);
+ if (dplane_res == ZEBRA_DPLANE_REQUEST_FAILURE) {
+ vty_out(vty, "%% Can't unset interface IP address: %s.\n",
+ dplane_res2str(dplane_res));
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ UNSET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED);
+ /* we will receive a kernel notification about this route being removed.
+ * this will trigger its removal from the connected list. */
+ return CMD_SUCCESS;
+}
+
+DEFUN (ip_address,
+ ip_address_cmd,
+ "ip address A.B.C.D/M",
+ "Interface Internet Protocol config commands\n"
+ "Set the IP address of an interface\n"
+ "IP address (e.g. 10.0.0.1/8)\n")
+{
+ int idx_ipv4_prefixlen = 2;
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ return ip_address_install(vty, ifp, argv[idx_ipv4_prefixlen]->arg, NULL,
+ NULL);
+}
+
+DEFUN (no_ip_address,
+ no_ip_address_cmd,
+ "no ip address A.B.C.D/M",
+ NO_STR
+ "Interface Internet Protocol config commands\n"
+ "Set the IP address of an interface\n"
+ "IP Address (e.g. 10.0.0.1/8)\n")
+{
+ int idx_ipv4_prefixlen = 3;
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ return ip_address_uninstall(vty, ifp, argv[idx_ipv4_prefixlen]->arg,
+ NULL, NULL);
+}
+
+DEFUN(ip_address_peer,
+ ip_address_peer_cmd,
+ "ip address A.B.C.D peer A.B.C.D/M",
+ "Interface Internet Protocol config commands\n"
+ "Set the IP address of an interface\n"
+ "Local IP (e.g. 10.0.0.1) for P-t-P address\n"
+ "Specify P-t-P address\n"
+ "Peer IP address (e.g. 10.0.0.1/8)\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ return ip_address_install(vty, ifp, argv[2]->arg, argv[4]->arg, NULL);
+}
+
+DEFUN(no_ip_address_peer,
+ no_ip_address_peer_cmd,
+ "no ip address A.B.C.D peer A.B.C.D/M",
+ NO_STR
+ "Interface Internet Protocol config commands\n"
+ "Set the IP address of an interface\n"
+ "Local IP (e.g. 10.0.0.1) for P-t-P address\n"
+ "Specify P-t-P address\n"
+ "Peer IP address (e.g. 10.0.0.1/8)\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ return ip_address_uninstall(vty, ifp, argv[3]->arg, argv[5]->arg, NULL);
+}
+
+#ifdef HAVE_NETLINK
+DEFUN (ip_address_label,
+ ip_address_label_cmd,
+ "ip address A.B.C.D/M label LINE",
+ "Interface Internet Protocol config commands\n"
+ "Set the IP address of an interface\n"
+ "IP address (e.g. 10.0.0.1/8)\n"
+ "Label of this address\n"
+ "Label\n")
+{
+ int idx_ipv4_prefixlen = 2;
+ int idx_line = 4;
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ return ip_address_install(vty, ifp, argv[idx_ipv4_prefixlen]->arg, NULL,
+ argv[idx_line]->arg);
+}
+
+DEFUN (no_ip_address_label,
+ no_ip_address_label_cmd,
+ "no ip address A.B.C.D/M label LINE",
+ NO_STR
+ "Interface Internet Protocol config commands\n"
+ "Set the IP address of an interface\n"
+ "IP address (e.g. 10.0.0.1/8)\n"
+ "Label of this address\n"
+ "Label\n")
+{
+ int idx_ipv4_prefixlen = 3;
+ int idx_line = 5;
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ return ip_address_uninstall(vty, ifp, argv[idx_ipv4_prefixlen]->arg,
+ NULL, argv[idx_line]->arg);
+}
+#endif /* HAVE_NETLINK */
+
+int if_ipv6_address_install(struct interface *ifp, struct prefix *prefix,
+ const char *label)
+{
+ struct zebra_if *if_data;
+ struct prefix_ipv6 cp;
+ struct connected *ifc;
+ struct prefix_ipv6 *p;
+ enum zebra_dplane_result dplane_res;
+
+ if_data = ifp->info;
+
+ cp.family = prefix->family;
+ cp.prefixlen = prefix->prefixlen;
+ cp.prefix = prefix->u.prefix6;
+ apply_mask_ipv6(&cp);
+
+ ifc = connected_check(ifp, (struct prefix *)&cp);
+ if (!ifc) {
+ ifc = connected_new();
+ ifc->ifp = ifp;
+
+ /* Address. */
+ p = prefix_ipv6_new();
+ *p = cp;
+ ifc->address = (struct prefix *)p;
+
+ /* Label. */
+ if (label)
+ ifc->label = XSTRDUP(MTYPE_CONNECTED_LABEL, label);
+
+ /* Add to linked list. */
+ listnode_add(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_res = dplane_intf_addr_set(ifp, ifc);
+ if (dplane_res == ZEBRA_DPLANE_REQUEST_FAILURE) {
+ zlog_debug(
+ "dplane can't set interface IP address: %s.",
+ dplane_res2str(dplane_res));
+ return NB_ERR;
+ }
+
+ SET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED);
+ /* The address will be advertised to zebra clients when the
+ * notification
+ * from the kernel has been received. */
+ }
+
+ return 0;
+}
+
+static int ipv6_address_install(struct vty *vty, struct interface *ifp,
+ const char *addr_str, const char *peer_str,
+ const char *label)
+{
+ struct zebra_if *if_data;
+ struct prefix_ipv6 cp;
+ struct connected *ifc;
+ struct prefix_ipv6 *p;
+ int ret;
+ enum zebra_dplane_result dplane_res;
+
+ if_data = ifp->info;
+
+ ret = str2prefix_ipv6(addr_str, &cp);
+ if (ret <= 0) {
+ vty_out(vty, "%% Malformed address \n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (ipv6_martian(&cp.prefix)) {
+ vty_out(vty, "%% Invalid address\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ ifc = connected_check(ifp, (struct prefix *)&cp);
+ if (!ifc) {
+ ifc = connected_new();
+ ifc->ifp = ifp;
+
+ /* Address. */
+ p = prefix_ipv6_new();
+ *p = cp;
+ ifc->address = (struct prefix *)p;
+
+ /* Label. */
+ if (label)
+ ifc->label = XSTRDUP(MTYPE_CONNECTED_LABEL, label);
+
+ /* Add to linked list. */
+ listnode_add(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_res = dplane_intf_addr_set(ifp, ifc);
+ if (dplane_res == ZEBRA_DPLANE_REQUEST_FAILURE) {
+ vty_out(vty, "%% Can't set interface IP address: %s.\n",
+ dplane_res2str(dplane_res));
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ SET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED);
+ /* The address will be advertised to zebra clients when the
+ * notification
+ * from the kernel has been received. */
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* Return true if an ipv6 address is configured on ifp */
+int ipv6_address_configured(struct interface *ifp)
+{
+ struct connected *connected;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, connected))
+ if (CHECK_FLAG(connected->conf, ZEBRA_IFC_REAL)
+ && (connected->address->family == AF_INET6))
+ return 1;
+
+ return 0;
+}
+
+static int ipv6_address_uninstall(struct vty *vty, struct interface *ifp,
+ const char *addr_str, const char *peer_str,
+ const char *label)
+{
+ struct prefix_ipv6 cp;
+ struct connected *ifc;
+ int ret;
+ enum zebra_dplane_result dplane_res;
+
+ /* Convert to prefix structure. */
+ ret = str2prefix_ipv6(addr_str, &cp);
+ if (ret <= 0) {
+ vty_out(vty, "%% Malformed address \n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Check current interface address. */
+ ifc = connected_check(ifp, (struct prefix *)&cp);
+ if (!ifc) {
+ vty_out(vty, "%% Can't find address\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* This is not configured address. */
+ if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED))
+ return CMD_WARNING_CONFIG_FAILED;
+
+ 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)) {
+ listnode_delete(ifp->connected, ifc);
+ connected_free(&ifc);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* This is real route. */
+ dplane_res = dplane_intf_addr_unset(ifp, ifc);
+ if (dplane_res == ZEBRA_DPLANE_REQUEST_FAILURE) {
+ vty_out(vty, "%% Can't unset interface IP address: %s.\n",
+ dplane_res2str(dplane_res));
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ UNSET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED);
+ /* This information will be propagated to the zclients when the
+ * kernel notification is received. */
+ return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_address,
+ ipv6_address_cmd,
+ "ipv6 address X:X::X:X/M",
+ "Interface IPv6 config commands\n"
+ "Set the IP address of an interface\n"
+ "IPv6 address (e.g. 3ffe:506::1/48)\n")
+{
+ int idx_ipv6_prefixlen = 2;
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ return ipv6_address_install(vty, ifp, argv[idx_ipv6_prefixlen]->arg,
+ NULL, NULL);
+}
+
+DEFUN (no_ipv6_address,
+ no_ipv6_address_cmd,
+ "no ipv6 address X:X::X:X/M",
+ NO_STR
+ "Interface IPv6 config commands\n"
+ "Set the IP address of an interface\n"
+ "IPv6 address (e.g. 3ffe:506::1/48)\n")
+{
+ int idx_ipv6_prefixlen = 3;
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ return ipv6_address_uninstall(vty, ifp, argv[idx_ipv6_prefixlen]->arg,
+ NULL, NULL);
+}
+
+static int link_params_config_write(struct vty *vty, struct interface *ifp)
+{
+ const struct lyd_node *dnode;
+ char xpath[XPATH_MAXLEN];
+ int i;
+
+ if ((ifp == NULL) || !HAS_LINK_PARAMS(ifp))
+ return -1;
+
+ struct if_link_params *iflp = ifp->link_params;
+
+ vty_out(vty, " link-params\n");
+ vty_out(vty, " enable\n");
+ if (IS_PARAM_SET(iflp, LP_TE_METRIC) && iflp->te_metric != ifp->metric)
+ vty_out(vty, " metric %u\n", iflp->te_metric);
+ if (IS_PARAM_SET(iflp, LP_MAX_BW) && iflp->max_bw != iflp->default_bw)
+ vty_out(vty, " max-bw %g\n", iflp->max_bw);
+ if (IS_PARAM_SET(iflp, LP_MAX_RSV_BW)
+ && iflp->max_rsv_bw != iflp->default_bw)
+ vty_out(vty, " max-rsv-bw %g\n", iflp->max_rsv_bw);
+ if (IS_PARAM_SET(iflp, LP_UNRSV_BW)) {
+ for (i = 0; i < 8; i++)
+ if (iflp->unrsv_bw[i] != iflp->default_bw)
+ vty_out(vty, " unrsv-bw %d %g\n", i,
+ iflp->unrsv_bw[i]);
+ }
+
+ snprintf(
+ xpath, sizeof(xpath),
+ "/frr-interface:lib/interface[name='%s']/frr-zebra:zebra/link-params",
+ ifp->name);
+ dnode = yang_dnode_get(running_config->dnode, xpath);
+ if (dnode)
+ nb_cli_show_dnode_cmds(vty, dnode, false);
+
+ if (IS_PARAM_SET(iflp, LP_DELAY)) {
+ vty_out(vty, " delay %u", iflp->av_delay);
+ if (IS_PARAM_SET(iflp, LP_MM_DELAY)) {
+ vty_out(vty, " min %u", iflp->min_delay);
+ vty_out(vty, " max %u", iflp->max_delay);
+ }
+ vty_out(vty, "\n");
+ }
+ if (IS_PARAM_SET(iflp, LP_DELAY_VAR))
+ vty_out(vty, " delay-variation %u\n", iflp->delay_var);
+ if (IS_PARAM_SET(iflp, LP_PKT_LOSS))
+ vty_out(vty, " packet-loss %g\n", iflp->pkt_loss);
+ if (IS_PARAM_SET(iflp, LP_AVA_BW))
+ vty_out(vty, " ava-bw %g\n", iflp->ava_bw);
+ if (IS_PARAM_SET(iflp, LP_RES_BW))
+ vty_out(vty, " res-bw %g\n", iflp->res_bw);
+ if (IS_PARAM_SET(iflp, LP_USE_BW))
+ vty_out(vty, " use-bw %g\n", iflp->use_bw);
+ if (IS_PARAM_SET(iflp, LP_RMT_AS))
+ vty_out(vty, " neighbor %pI4 as %u\n", &iflp->rmt_ip,
+ iflp->rmt_as);
+
+ vty_out(vty, " exit-link-params\n");
+ return 0;
+}
+
+static int if_config_write(struct vty *vty)
+{
+ struct vrf *vrf;
+ struct interface *ifp;
+
+ zebra_ptm_write(vty);
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name)
+ FOR_ALL_INTERFACES (vrf, ifp) {
+ struct zebra_if *if_data;
+ struct listnode *addrnode;
+ struct connected *ifc;
+ struct prefix *p;
+
+ if_data = ifp->info;
+
+ if_vty_config_start(vty, ifp);
+
+ if (if_data) {
+ if (if_data->shutdown == IF_ZEBRA_DATA_ON)
+ vty_out(vty, " shutdown\n");
+
+ zebra_ptm_if_write(vty, if_data);
+ }
+
+ if (ifp->desc)
+ vty_out(vty, " description %s\n", ifp->desc);
+
+ /* Assign bandwidth here to avoid unnecessary interface
+ flap
+ while processing config script */
+ if (ifp->bandwidth != 0)
+ vty_out(vty, " bandwidth %u\n", ifp->bandwidth);
+
+ if (!CHECK_FLAG(ifp->status,
+ ZEBRA_INTERFACE_LINKDETECTION))
+ vty_out(vty, " no link-detect\n");
+
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, addrnode,
+ ifc)) {
+ if (CHECK_FLAG(ifc->conf,
+ ZEBRA_IFC_CONFIGURED)) {
+ char buf[INET6_ADDRSTRLEN];
+ p = ifc->address;
+ vty_out(vty, " ip%s address %s",
+ p->family == AF_INET ? ""
+ : "v6",
+ inet_ntop(p->family,
+ &p->u.prefix, buf,
+ sizeof(buf)));
+ if (CONNECTED_PEER(ifc)) {
+ p = ifc->destination;
+ vty_out(vty, " peer %s",
+ inet_ntop(p->family,
+ &p->u.prefix,
+ buf,
+ sizeof(buf)));
+ }
+ vty_out(vty, "/%d", p->prefixlen);
+
+ if (ifc->label)
+ vty_out(vty, " label %s",
+ ifc->label);
+
+ vty_out(vty, "\n");
+ }
+ }
+
+ if (if_data) {
+ if (if_data->multicast != IF_ZEBRA_DATA_UNSPEC)
+ vty_out(vty, " %smulticast\n",
+ if_data->multicast ==
+ IF_ZEBRA_DATA_ON
+ ? ""
+ : "no ");
+
+ if (if_data->mpls_config == IF_ZEBRA_DATA_ON)
+ vty_out(vty, " mpls enable\n");
+ else if (if_data->mpls_config ==
+ IF_ZEBRA_DATA_OFF)
+ vty_out(vty, " mpls disable\n");
+ }
+
+ hook_call(zebra_if_config_wr, vty, ifp);
+ zebra_evpn_mh_if_write(vty, ifp);
+ link_params_config_write(vty, ifp);
+
+ if_vty_config_end(vty);
+ }
+ return 0;
+}
+
+/* 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 configuration write function. */
+ if_cmd_init(if_config_write);
+ install_node(&link_params_node);
+ /*
+ * This is *intentionally* setting this to NULL, signaling
+ * that interface creation for zebra acts differently
+ */
+ if_zapi_callbacks(NULL, NULL, NULL, NULL);
+
+ 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);
+ install_element(INTERFACE_NODE, &multicast_cmd);
+ install_element(INTERFACE_NODE, &no_multicast_cmd);
+ install_element(INTERFACE_NODE, &mpls_cmd);
+ install_element(INTERFACE_NODE, &linkdetect_cmd);
+ install_element(INTERFACE_NODE, &no_linkdetect_cmd);
+ install_element(INTERFACE_NODE, &shutdown_if_cmd);
+ install_element(INTERFACE_NODE, &no_shutdown_if_cmd);
+ install_element(INTERFACE_NODE, &bandwidth_if_cmd);
+ install_element(INTERFACE_NODE, &no_bandwidth_if_cmd);
+ install_element(INTERFACE_NODE, &ip_address_cmd);
+ install_element(INTERFACE_NODE, &no_ip_address_cmd);
+ install_element(INTERFACE_NODE, &ip_address_peer_cmd);
+ install_element(INTERFACE_NODE, &no_ip_address_peer_cmd);
+ install_element(INTERFACE_NODE, &ipv6_address_cmd);
+ install_element(INTERFACE_NODE, &no_ipv6_address_cmd);
+#ifdef HAVE_NETLINK
+ install_element(INTERFACE_NODE, &ip_address_label_cmd);
+ install_element(INTERFACE_NODE, &no_ip_address_label_cmd);
+#endif /* HAVE_NETLINK */
+ install_element(INTERFACE_NODE, &link_params_cmd);
+ install_default(LINK_PARAMS_NODE);
+ install_element(LINK_PARAMS_NODE, &link_params_enable_cmd);
+ install_element(LINK_PARAMS_NODE, &no_link_params_enable_cmd);
+ install_element(LINK_PARAMS_NODE, &link_params_metric_cmd);
+ install_element(LINK_PARAMS_NODE, &no_link_params_metric_cmd);
+ install_element(LINK_PARAMS_NODE, &link_params_maxbw_cmd);
+ install_element(LINK_PARAMS_NODE, &link_params_max_rsv_bw_cmd);
+ install_element(LINK_PARAMS_NODE, &link_params_unrsv_bw_cmd);
+ install_element(LINK_PARAMS_NODE, &link_params_admin_grp_cmd);
+ install_element(LINK_PARAMS_NODE, &no_link_params_admin_grp_cmd);
+ install_element(LINK_PARAMS_NODE, &link_params_inter_as_cmd);
+ install_element(LINK_PARAMS_NODE, &no_link_params_inter_as_cmd);
+ install_element(LINK_PARAMS_NODE, &link_params_delay_cmd);
+ install_element(LINK_PARAMS_NODE, &no_link_params_delay_cmd);
+ install_element(LINK_PARAMS_NODE, &link_params_delay_var_cmd);
+ install_element(LINK_PARAMS_NODE, &no_link_params_delay_var_cmd);
+ install_element(LINK_PARAMS_NODE, &link_params_pkt_loss_cmd);
+ install_element(LINK_PARAMS_NODE, &no_link_params_pkt_loss_cmd);
+ install_element(LINK_PARAMS_NODE, &link_params_ava_bw_cmd);
+ install_element(LINK_PARAMS_NODE, &no_link_params_ava_bw_cmd);
+ install_element(LINK_PARAMS_NODE, &link_params_res_bw_cmd);
+ install_element(LINK_PARAMS_NODE, &no_link_params_res_bw_cmd);
+ install_element(LINK_PARAMS_NODE, &link_params_use_bw_cmd);
+ install_element(LINK_PARAMS_NODE, &no_link_params_use_bw_cmd);
+ install_element(LINK_PARAMS_NODE, &link_params_affinity_cmd);
+ install_element(LINK_PARAMS_NODE, &link_params_affinity_mode_cmd);
+ install_element(LINK_PARAMS_NODE, &no_link_params_affinity_mode_cmd);
+ install_element(LINK_PARAMS_NODE, &exit_link_params_cmd);
+
+ /* setup EVPN MH elements */
+ zebra_evpn_interface_init();
+}