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