diff options
author | Russ White <russ@riw.us> | 2022-03-16 00:58:16 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-16 00:58:16 +0100 |
commit | 5d97021ba36666884c7caed69e3565e01f73eaf5 (patch) | |
tree | d27dc8e34ceee6c63b333c91f8c57a7a95ecbe67 | |
parent | Merge pull request #10643 from Jafaral/ospf-multi-vrf (diff) | |
parent | zebra: cleanup protodown netlink logs (diff) | |
download | frr-5d97021ba36666884c7caed69e3565e01f73eaf5.tar.xz frr-5d97021ba36666884c7caed69e3565e01f73eaf5.zip |
Merge pull request #10427 from sworleys/Protodown-Reason-Upstream
Add Support for Setting Protodown Reason Code
-rw-r--r-- | doc/user/sharp.rst | 4 | ||||
-rw-r--r-- | doc/user/zebra.rst | 11 | ||||
-rw-r--r-- | include/linux/if_link.h | 13 | ||||
-rw-r--r-- | sharpd/sharp_vty.c | 64 | ||||
-rw-r--r-- | sharpd/sharp_zebra.c | 12 | ||||
-rw-r--r-- | sharpd/sharp_zebra.h | 2 | ||||
-rw-r--r-- | tests/topotests/zebra_rib/test_zebra_rib.py | 57 | ||||
-rw-r--r-- | zebra/debug_nl.c | 110 | ||||
-rw-r--r-- | zebra/dplane_fpm_nl.c | 3 | ||||
-rw-r--r-- | zebra/if_netlink.c | 276 | ||||
-rw-r--r-- | zebra/if_netlink.h | 25 | ||||
-rw-r--r-- | zebra/if_socket.c | 41 | ||||
-rw-r--r-- | zebra/interface.c | 349 | ||||
-rw-r--r-- | zebra/interface.h | 30 | ||||
-rw-r--r-- | zebra/kernel_netlink.c | 10 | ||||
-rw-r--r-- | zebra/kernel_socket.c | 6 | ||||
-rw-r--r-- | zebra/rt.h | 3 | ||||
-rw-r--r-- | zebra/rt_netlink.h | 2 | ||||
-rw-r--r-- | zebra/subdir.am | 1 | ||||
-rw-r--r-- | zebra/zapi_msg.c | 22 | ||||
-rw-r--r-- | zebra/zebra_dplane.c | 204 | ||||
-rw-r--r-- | zebra/zebra_dplane.h | 19 | ||||
-rw-r--r-- | zebra/zebra_errors.c | 9 | ||||
-rw-r--r-- | zebra/zebra_errors.h | 1 | ||||
-rw-r--r-- | zebra/zebra_evpn_mh.c | 52 | ||||
-rw-r--r-- | zebra/zebra_evpn_mh.h | 2 | ||||
-rw-r--r-- | zebra/zebra_nhg.c | 3 | ||||
-rw-r--r-- | zebra/zebra_rib.c | 8 | ||||
-rw-r--r-- | zebra/zebra_router.h | 16 | ||||
-rw-r--r-- | zebra/zebra_vty.c | 28 |
30 files changed, 1190 insertions, 193 deletions
diff --git a/doc/user/sharp.rst b/doc/user/sharp.rst index e9d4e2763..8d201a3c0 100644 --- a/doc/user/sharp.rst +++ b/doc/user/sharp.rst @@ -296,3 +296,7 @@ keyword. At present, no sharp commands will be preserved in the config. router# show sharp segment-routing srv6 (nothing) + +.. clicmd:: sharp interface IFNAME protodown + + Set an interface protodown. diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index 221e9c6fe..0244f7c58 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -255,6 +255,17 @@ Link Parameters Commands for InterASv2 link in OSPF (RFC5392). Note that this option is not yet supported for ISIS (RFC5316). +Global Commands +------------------------ + +.. clicmd:: zebra protodown reason-bit (0-31) + + This command is only supported for linux and a kernel > 5.1. + Change reason-bit frr uses for setting protodown. We default to 7, but + if another userspace app ever conflicts with this, you can change it here. + The descriptor for this bit should exist in :file:`/etc/iproute2/protodown_reasons.d/` + to display with :clicmd:`ip -d link show`. + Nexthop Tracking ================ diff --git a/include/linux/if_link.h b/include/linux/if_link.h index 22a45914a..e5cea2782 100644 --- a/include/linux/if_link.h +++ b/include/linux/if_link.h @@ -167,12 +167,25 @@ enum { IFLA_NEW_IFINDEX, IFLA_MIN_MTU, IFLA_MAX_MTU, + IFLA_PROP_LIST, + IFLA_ALT_IFNAME, /* Alternative ifname */ + IFLA_PERM_ADDRESS, + IFLA_PROTO_DOWN_REASON, __IFLA_MAX }; #define IFLA_MAX (__IFLA_MAX - 1) +enum { + IFLA_PROTO_DOWN_REASON_UNSPEC, + IFLA_PROTO_DOWN_REASON_MASK, /* u32, mask for reason bits */ + IFLA_PROTO_DOWN_REASON_VALUE, /* u32, reason bit value */ + + __IFLA_PROTO_DOWN_REASON_CNT, + IFLA_PROTO_DOWN_REASON_MAX = __IFLA_PROTO_DOWN_REASON_CNT - 1 +}; + /* backwards compatibility for userspace */ #ifndef __KERNEL__ #define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg)))) diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c index 78cc57cc4..889643f65 100644 --- a/sharpd/sharp_vty.c +++ b/sharpd/sharp_vty.c @@ -1258,6 +1258,67 @@ DEFPY (show_sharp_cspf, return CMD_SUCCESS; } +static struct interface *if_lookup_vrf_all(const char *ifname) +{ + struct interface *ifp; + struct vrf *vrf; + + RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) { + ifp = if_lookup_by_name(ifname, vrf->vrf_id); + if (ifp) + return ifp; + } + + return NULL; +} + +DEFPY (sharp_interface_protodown, + sharp_interface_protodown_cmd, + "sharp interface IFNAME$ifname protodown", + SHARP_STR + INTERFACE_STR + IFNAME_STR + "Set interface protodown\n") +{ + struct interface *ifp; + + ifp = if_lookup_vrf_all(ifname); + + if (!ifp) { + vty_out(vty, "%% Can't find interface %s\n", ifname); + return CMD_WARNING; + } + + if (sharp_zebra_send_interface_protodown(ifp, true) != 0) + return CMD_WARNING; + + return CMD_SUCCESS; +} + +DEFPY (no_sharp_interface_protodown, + no_sharp_interface_protodown_cmd, + "no sharp interface IFNAME$ifname protodown", + NO_STR + SHARP_STR + INTERFACE_STR + IFNAME_STR + "Set interface protodown\n") +{ + struct interface *ifp; + + ifp = if_lookup_vrf_all(ifname); + + if (!ifp) { + vty_out(vty, "%% Can't find interface %s\n", ifname); + return CMD_WARNING; + } + + if (sharp_zebra_send_interface_protodown(ifp, false) != 0) + return CMD_WARNING; + + return CMD_SUCCESS; +} + void sharp_vty_init(void) { install_element(ENABLE_NODE, &install_routes_data_dump_cmd); @@ -1290,5 +1351,8 @@ void sharp_vty_init(void) &sharp_srv6_manager_release_locator_chunk_cmd); install_element(ENABLE_NODE, &show_sharp_segment_routing_srv6_cmd); + install_element(ENABLE_NODE, &sharp_interface_protodown_cmd); + install_element(ENABLE_NODE, &no_sharp_interface_protodown_cmd); + return; } diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c index 5304b17f0..52364bff4 100644 --- a/sharpd/sharp_zebra.c +++ b/sharpd/sharp_zebra.c @@ -969,6 +969,18 @@ static int sharp_zebra_process_srv6_locator_chunk(ZAPI_CALLBACK_ARGS) return 0; } +int sharp_zebra_send_interface_protodown(struct interface *ifp, bool down) +{ + zlog_debug("Sending zebra to set %s protodown %s", ifp->name, + down ? "on" : "off"); + + if (zclient_send_interface_protodown(zclient, ifp->vrf->vrf_id, ifp, + down) == ZCLIENT_SEND_FAILURE) + return -1; + + return 0; +} + static zclient_handler *const sharp_handlers[] = { [ZEBRA_INTERFACE_ADDRESS_ADD] = interface_address_add, [ZEBRA_INTERFACE_ADDRESS_DELETE] = interface_address_delete, diff --git a/sharpd/sharp_zebra.h b/sharpd/sharp_zebra.h index 49f11a67e..d8ea67979 100644 --- a/sharpd/sharp_zebra.h +++ b/sharpd/sharp_zebra.h @@ -73,4 +73,6 @@ extern void sharp_install_seg6local_route_helper(struct prefix *p, enum seg6local_action_t act, struct seg6local_context *ctx); +extern int sharp_zebra_send_interface_protodown(struct interface *ifp, + bool down); #endif diff --git a/tests/topotests/zebra_rib/test_zebra_rib.py b/tests/topotests/zebra_rib/test_zebra_rib.py index ae891d906..b72691b71 100644 --- a/tests/topotests/zebra_rib/test_zebra_rib.py +++ b/tests/topotests/zebra_rib/test_zebra_rib.py @@ -31,6 +31,7 @@ import sys from functools import partial import pytest import json +import platform # Save the Current Working Directory to find configuration files. CWD = os.path.dirname(os.path.realpath(__file__)) @@ -45,6 +46,20 @@ from time import sleep pytestmark = [pytest.mark.sharpd] +krel = platform.release() + + +def config_macvlan(tgen, r_str, device, macvlan): + "Creates specified macvlan interace on physical device" + + if topotest.version_cmp(krel, "5.1") < 0: + return + + router = tgen.gears[r_str] + router.run( + "ip link add {} link {} type macvlan mode bridge".format(macvlan, device) + ) + router.run("ip link set {} up".format(macvlan)) def setup_module(mod): @@ -62,6 +77,8 @@ def setup_module(mod): TopoRouter.RD_SHARP, os.path.join(CWD, "{}/sharpd.conf".format(rname)) ) + # Macvlan interface for protodown func test */ + config_macvlan(tgen, "r1", "r1-eth0", "r1-eth0-macvlan") # Initialize all routers. tgen.start_router() @@ -269,6 +286,46 @@ def test_route_map_usage(): assert ok, result +def test_protodown(): + "Run protodown basic functionality test and report results." + pdown = False + count = 0 + tgen = get_topogen() + if topotest.version_cmp(krel, "5.1") < 0: + tgen.errors = "kernel 5.1 needed for protodown tests" + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + # Set interface protodown on + r1.vtysh_cmd("sharp interface r1-eth0-macvlan protodown") + + # Timeout to wait for dplane to handle it + while count < 10: + count += 1 + output = r1.vtysh_cmd("show interface r1-eth0-macvlan") + if re.search(r"protodown reasons:.*sharp", output): + pdown = True + break + sleep(1) + + assert pdown is True, "Interface r1-eth0-macvlan not set protodown" + + # Set interface protodown off + r1.vtysh_cmd("no sharp interface r1-eth0-macvlan protodown") + + # Timeout to wait for dplane to handle it + while count < 10: + count += 1 + output = r1.vtysh_cmd("show interface r1-eth0-macvlan") + if not re.search(r"protodown reasons:.*sharp", output): + pdown = False + break + sleep(1) + + assert pdown is False, "Interface r1-eth0-macvlan not set protodown off" + + def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() diff --git a/zebra/debug_nl.c b/zebra/debug_nl.c index 260ba30b3..b7d12bf53 100644 --- a/zebra/debug_nl.c +++ b/zebra/debug_nl.c @@ -255,6 +255,40 @@ const char *ifi_type2str(int type) } } +const char *ifla_pdr_type2str(int type) +{ + switch (type) { + case IFLA_PROTO_DOWN_REASON_UNSPEC: + return "UNSPEC"; + case IFLA_PROTO_DOWN_REASON_MASK: + return "MASK"; + case IFLA_PROTO_DOWN_REASON_VALUE: + return "VALUE"; + default: + return "UNKNOWN"; + } +} + +const char *ifla_info_type2str(int type) +{ + switch (type) { + case IFLA_INFO_UNSPEC: + return "UNSPEC"; + case IFLA_INFO_KIND: + return "KIND"; + case IFLA_INFO_DATA: + return "DATA"; + case IFLA_INFO_XSTATS: + return "XSTATS"; + case IFLA_INFO_SLAVE_KIND: + return "SLAVE_KIND"; + case IFLA_INFO_SLAVE_DATA: + return "SLAVE_DATA"; + default: + return "UNKNOWN"; + } +} + const char *rta_type2str(int type) { switch (type) { @@ -358,6 +392,8 @@ const char *rta_type2str(int type) case IFLA_EVENT: return "EVENT"; #endif /* IFLA_EVENT */ + case IFLA_PROTO_DOWN_REASON: + return "PROTO_DOWN_REASON"; default: return "UNKNOWN"; } @@ -838,6 +874,42 @@ const char *nh_flags2str(uint32_t flags, char *buf, size_t buflen) /* * Netlink abstractions. */ +static void nllink_pdr_dump(struct rtattr *rta, size_t msglen) +{ + size_t plen; + uint32_t u32v; + +next_rta: + /* Check the header for valid length and for outbound access. */ + if (RTA_OK(rta, msglen) == 0) + return; + + plen = RTA_PAYLOAD(rta); + zlog_debug(" linkinfo [len=%d (payload=%zu) type=(%d) %s]", + rta->rta_len, plen, rta->rta_type, + ifla_pdr_type2str(rta->rta_type)); + switch (rta->rta_type) { + case IFLA_PROTO_DOWN_REASON_MASK: + case IFLA_PROTO_DOWN_REASON_VALUE: + if (plen < sizeof(uint32_t)) { + zlog_debug(" invalid length"); + break; + } + + u32v = *(uint32_t *)RTA_DATA(rta); + zlog_debug(" %u", u32v); + break; + + default: + /* NOTHING: unhandled. */ + break; + } + + /* Get next pointer and start iteration again. */ + rta = RTA_NEXT(rta, msglen); + goto next_rta; +} + static void nllink_linkinfo_dump(struct rtattr *rta, size_t msglen) { size_t plen; @@ -851,7 +923,7 @@ next_rta: plen = RTA_PAYLOAD(rta); zlog_debug(" linkinfo [len=%d (payload=%zu) type=(%d) %s]", rta->rta_len, plen, rta->rta_type, - rta_type2str(rta->rta_type)); + ifla_info_type2str(rta->rta_type)); switch (rta->rta_type) { case IFLA_INFO_KIND: if (plen == 0) { @@ -888,8 +960,10 @@ static void nllink_dump(struct ifinfomsg *ifi, size_t msglen) struct rtattr *rta; size_t plen, it; uint32_t u32v; + uint8_t u8v; char bytestr[16]; char dbuf[128]; + unsigned short rta_type; /* Get the first attribute and go from there. */ rta = IFLA_RTA(ifi); @@ -899,10 +973,10 @@ next_rta: return; plen = RTA_PAYLOAD(rta); + rta_type = rta->rta_type & ~NLA_F_NESTED; zlog_debug(" rta [len=%d (payload=%zu) type=(%d) %s]", rta->rta_len, - plen, rta->rta_type, rta_type2str(rta->rta_type)); - switch (rta->rta_type) { - case IFLA_IFNAME: + plen, rta_type, rta_type2str(rta_type)); + switch (rta_type) { case IFLA_IFALIAS: if (plen == 0) { zlog_debug(" invalid length"); @@ -927,6 +1001,7 @@ next_rta: #endif /* IFLA_GSO_MAX_SIZE */ case IFLA_CARRIER_CHANGES: case IFLA_MASTER: + case IFLA_LINK: if (plen < sizeof(uint32_t)) { zlog_debug(" invalid length"); break; @@ -936,6 +1011,15 @@ next_rta: zlog_debug(" %u", u32v); break; + case IFLA_PROTO_DOWN: + if (plen < sizeof(uint8_t)) { + zlog_debug(" invalid length"); + break; + } + + u8v = *(uint8_t *)RTA_DATA(rta); + zlog_debug(" %u", u8v); + break; case IFLA_ADDRESS: datap = RTA_DATA(rta); dbuf[0] = 0; @@ -952,7 +1036,11 @@ next_rta: break; case IFLA_LINKINFO: - nllink_linkinfo_dump(RTA_DATA(rta), msglen); + nllink_linkinfo_dump(RTA_DATA(rta), plen); + break; + + case IFLA_PROTO_DOWN_REASON: + nllink_pdr_dump(RTA_DATA(rta), plen); break; default: @@ -1027,6 +1115,7 @@ static void nlneigh_dump(struct ndmsg *ndm, size_t msglen) uint16_t vid; char bytestr[16]; char dbuf[128]; + unsigned short rta_type; #ifndef NDA_RTA #define NDA_RTA(ndm) \ @@ -1043,9 +1132,10 @@ next_rta: return; plen = RTA_PAYLOAD(rta); + rta_type = rta->rta_type & ~NLA_F_NESTED; zlog_debug(" rta [len=%d (payload=%zu) type=(%d) %s]", rta->rta_len, - plen, rta->rta_type, neigh_rta2str(rta->rta_type)); - switch (rta->rta_type & ~ NLA_F_NESTED) { + plen, rta->rta_type, neigh_rta2str(rta_type)); + switch (rta_type) { case NDA_LLADDR: datap = RTA_DATA(rta); dbuf[0] = 0; @@ -1153,6 +1243,7 @@ static void nlnh_dump(struct nhmsg *nhm, size_t msglen) uint32_t u32v; unsigned long count, i; struct nexthop_grp *nhgrp; + unsigned short rta_type; rta = RTM_NHA(nhm); @@ -1162,9 +1253,10 @@ next_rta: return; plen = RTA_PAYLOAD(rta); + rta_type = rta->rta_type & ~NLA_F_NESTED; zlog_debug(" rta [len=%d (payload=%zu) type=(%d) %s]", rta->rta_len, - plen, rta->rta_type, nhm_rta2str(rta->rta_type)); - switch (rta->rta_type & ~NLA_F_NESTED) { + plen, rta->rta_type, nhm_rta2str(rta_type)); + switch (rta_type) { case NHA_ID: u32v = *(uint32_t *)RTA_DATA(rta); zlog_debug(" %u", u32v); diff --git a/zebra/dplane_fpm_nl.c b/zebra/dplane_fpm_nl.c index 4cd2bef30..ec4ea372f 100644 --- a/zebra/dplane_fpm_nl.c +++ b/zebra/dplane_fpm_nl.c @@ -812,6 +812,9 @@ static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx) case DPLANE_OP_INTF_ADDR_ADD: case DPLANE_OP_INTF_ADDR_DEL: case DPLANE_OP_INTF_NETCONFIG: + case DPLANE_OP_INTF_INSTALL: + case DPLANE_OP_INTF_UPDATE: + case DPLANE_OP_INTF_DELETE: case DPLANE_OP_NONE: break; diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index e3506ecb1..fca03e55b 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -77,6 +77,7 @@ #include "zebra/netconf_netlink.h" extern struct zebra_privs_t zserv_privs; +uint8_t frr_protodown_r_bit = FRR_PROTODOWN_REASON_DEFAULT_BIT; /* Note: on netlink systems, there should be a 1-to-1 mapping between interface names and ifindex values. */ @@ -814,33 +815,90 @@ static int netlink_bridge_interface(struct nlmsghdr *h, int len, ns_id_t ns_id, return 0; } -/* If the interface is an es bond member then it must follow EVPN's - * protodown setting +static bool is_if_protodown_reason_only_frr(uint32_t rc_bitfield) +{ + /* This shouldn't be possible */ + assert(frr_protodown_r_bit < 32); + return (rc_bitfield == (((uint32_t)1) << frr_protodown_r_bit)); +} + +/* + * Process interface protodown dplane update. + * + * If the interface is an es bond member then it must follow EVPN's + * protodown setting. */ static void netlink_proc_dplane_if_protodown(struct zebra_if *zif, - bool protodown) + struct rtattr **tb) { - bool zif_protodown; + bool protodown; + bool old_protodown; + uint32_t rc_bitfield = 0; + struct rtattr *pd_reason_info[IFLA_MAX + 1]; + + protodown = !!*(uint8_t *)RTA_DATA(tb[IFLA_PROTO_DOWN]); + + if (tb[IFLA_PROTO_DOWN_REASON]) { + netlink_parse_rtattr_nested(pd_reason_info, IFLA_INFO_MAX, + tb[IFLA_PROTO_DOWN_REASON]); - zif_protodown = !!(zif->flags & ZIF_FLAG_PROTODOWN); - if (protodown == zif_protodown) + if (pd_reason_info[IFLA_PROTO_DOWN_REASON_VALUE]) + rc_bitfield = *(uint32_t *)RTA_DATA( + pd_reason_info[IFLA_PROTO_DOWN_REASON_VALUE]); + } + + /* + * 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_KERNEL) zlog_debug("interface %s dplane change, protdown %s", zif->ifp->name, protodown ? "on" : "off"); + /* Set protodown, respectively */ + COND_FLAG(zif->flags, ZIF_FLAG_PROTODOWN, protodown); + if (zebra_evpn_is_es_bond_member(zif->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", + zif->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", + zif->ifp->name); + return; + } + if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_KERNEL) zlog_debug( - "bond mbr %s re-instate protdown %s in the dplane", - zif->ifp->name, zif_protodown ? "on" : "off"); - netlink_protodown(zif->ifp, zif_protodown); - } else { - if (protodown) - zif->flags |= ZIF_FLAG_PROTODOWN; + "bond mbr %s reinstate protodown %s in the dplane", + zif->ifp->name, old_protodown ? "on" : "off"); + + if (old_protodown) + SET_FLAG(zif->flags, ZIF_FLAG_SET_PROTODOWN); else - zif->flags &= ~ZIF_FLAG_PROTODOWN; + SET_FLAG(zif->flags, ZIF_FLAG_UNSET_PROTODOWN); + + dplane_intf_update(zif->ifp); } } @@ -859,6 +917,29 @@ static uint8_t netlink_parse_lacp_bypass(struct rtattr **linkinfo) } /* + * Only called at startup to cleanup leftover protodown reasons we may + * have not cleaned up. We leave protodown set though. + */ +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); +} + +/* * Called from interface_lookup_netlink(). This function is only used * during bootstrap. */ @@ -905,7 +986,8 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) /* Looking up interface name. */ memset(linkinfo, 0, sizeof(linkinfo)); - netlink_parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); + netlink_parse_rtattr_flags(tb, IFLA_MAX, IFLA_RTA(ifi), len, + NLA_F_NESTED); /* check for wireless messages to ignore */ if ((tb[IFLA_WIRELESS] != NULL) && (ifi->ifi_change == 0)) { @@ -1020,10 +1102,8 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) zebra_l2if_update_bond_slave(ifp, bond_ifindex, !!bypass); if (tb[IFLA_PROTO_DOWN]) { - uint8_t protodown; - - protodown = *(uint8_t *)RTA_DATA(tb[IFLA_PROTO_DOWN]); - netlink_proc_dplane_if_protodown(zif, !!protodown); + netlink_proc_dplane_if_protodown(zif, tb); + if_sweep_protodown(zif); } return 0; @@ -1244,6 +1324,41 @@ netlink_put_address_update_msg(struct nl_batch *bth, false); } +static ssize_t netlink_intf_msg_encoder(struct zebra_dplane_ctx *ctx, void *buf, + size_t buflen) +{ + enum dplane_op_e op; + int cmd = 0; + + op = dplane_ctx_get_op(ctx); + + switch (op) { + case DPLANE_OP_INTF_UPDATE: + cmd = RTM_SETLINK; + break; + case DPLANE_OP_INTF_INSTALL: + cmd = RTM_NEWLINK; + break; + case DPLANE_OP_INTF_DELETE: + cmd = RTM_DELLINK; + break; + default: + flog_err( + EC_ZEBRA_NHG_FIB_UPDATE, + "Context received for kernel interface update with incorrect OP code (%u)", + op); + return -1; + } + + return netlink_intf_msg_encode(cmd, ctx, buf, buflen); +} + +enum netlink_msg_status +netlink_put_intf_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx) +{ + return netlink_batch_add_msg(bth, ctx, netlink_intf_msg_encoder, false); +} + int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, int startup) { int len; @@ -1716,7 +1831,8 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) /* Looking up interface name. */ memset(linkinfo, 0, sizeof(linkinfo)); - netlink_parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); + netlink_parse_rtattr_flags(tb, IFLA_MAX, IFLA_RTA(ifi), len, + NLA_F_NESTED); /* check for wireless messages to ignore */ if ((tb[IFLA_WIRELESS] != NULL) && (ifi->ifi_change == 0)) { @@ -1856,14 +1972,9 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) zebra_l2if_update_bond_slave(ifp, bond_ifindex, !!bypass); - if (tb[IFLA_PROTO_DOWN]) { - uint8_t protodown; + if (tb[IFLA_PROTO_DOWN]) + netlink_proc_dplane_if_protodown(ifp->info, tb); - protodown = *(uint8_t *)RTA_DATA( - tb[IFLA_PROTO_DOWN]); - netlink_proc_dplane_if_protodown(ifp->info, - !!protodown); - } } else if (ifp->vrf->vrf_id != vrf_id) { /* VRF change for an interface. */ if (IS_ZEBRA_DEBUG_KERNEL) @@ -1910,14 +2021,8 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) netlink_to_zebra_link_type(ifi->ifi_type); netlink_interface_update_hw_addr(tb, ifp); - if (tb[IFLA_PROTO_DOWN]) { - uint8_t protodown; - - protodown = *(uint8_t *)RTA_DATA( - tb[IFLA_PROTO_DOWN]); - netlink_proc_dplane_if_protodown(zif, - !!protodown); - } + if (tb[IFLA_PROTO_DOWN]) + netlink_proc_dplane_if_protodown(ifp->info, tb); if (if_is_no_ptm_operative(ifp)) { bool is_up = if_is_operative(ifp); @@ -2049,30 +2154,72 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) return 0; } -int netlink_protodown(struct interface *ifp, bool down) -{ - struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); +/** + * Interface encoding helper function. + * + * \param[in] cmd netlink command. + * \param[in] ctx dataplane context (information snapshot). + * \param[out] buf buffer to hold the packet. + * \param[in] buflen amount of buffer bytes. + */ +ssize_t netlink_intf_msg_encode(uint16_t cmd, + const struct zebra_dplane_ctx *ctx, void *buf, + size_t buflen) +{ struct { struct nlmsghdr n; struct ifinfomsg ifa; - char buf[NL_PKT_BUF_SIZE]; - } req; + char buf[]; + } *req = buf; - memset(&req, 0, sizeof(req)); + struct rtattr *nest_protodown_reason; + ifindex_t ifindex = dplane_ctx_get_ifindex(ctx); + bool down = dplane_ctx_intf_is_protodown(ctx); + bool pd_reason_val = dplane_ctx_get_intf_pd_reason_val(ctx); + struct nlsock *nl = + kernel_netlink_nlsock_lookup(dplane_ctx_get_ns_sock(ctx)); - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); - req.n.nlmsg_flags = NLM_F_REQUEST; - req.n.nlmsg_type = RTM_SETLINK; - req.n.nlmsg_pid = zns->netlink_cmd.snl.nl_pid; + if (buflen < sizeof(*req)) + return 0; - req.ifa.ifi_index = ifp->ifindex; + memset(req, 0, sizeof(*req)); - nl_attr_put(&req.n, sizeof(req), IFLA_PROTO_DOWN, &down, sizeof(down)); - nl_attr_put32(&req.n, sizeof(req), IFLA_LINK, ifp->ifindex); + if (cmd != RTM_SETLINK) + flog_err( + EC_ZEBRA_INTF_UPDATE_FAILURE, + "Only RTM_SETLINK message type currently supported in dplane pthread"); - return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, - false); + req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req->n.nlmsg_flags = NLM_F_REQUEST; + req->n.nlmsg_type = cmd; + req->n.nlmsg_pid = nl->snl.nl_pid; + + req->ifa.ifi_index = ifindex; + + nl_attr_put8(&req->n, buflen, IFLA_PROTO_DOWN, down); + nl_attr_put32(&req->n, buflen, IFLA_LINK, ifindex); + + /* Reason info nest */ + nest_protodown_reason = + nl_attr_nest(&req->n, buflen, IFLA_PROTO_DOWN_REASON); + + if (!nest_protodown_reason) + return -1; + + nl_attr_put32(&req->n, buflen, IFLA_PROTO_DOWN_REASON_MASK, + (1 << frr_protodown_r_bit)); + nl_attr_put32(&req->n, buflen, IFLA_PROTO_DOWN_REASON_VALUE, + ((int)pd_reason_val) << frr_protodown_r_bit); + + nl_attr_nest_end(&req->n, nest_protodown_reason); + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s: %s, protodown=%d reason_val=%d ifindex=%u", + __func__, nl_msg_type_to_str(cmd), down, + pd_reason_val, ifindex); + + return NLMSG_ALIGN(req->n.nlmsg_len); } /* Interface information read by netlink. */ @@ -2088,4 +2235,35 @@ void interface_list(struct zebra_ns *zns) interface_addr_lookup_netlink(zns); } +void if_netlink_set_frr_protodown_r_bit(uint8_t bit) +{ + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug( + "Protodown reason bit index changed: bit-index %u -> bit-index %u", + frr_protodown_r_bit, bit); + + frr_protodown_r_bit = bit; +} + +void if_netlink_unset_frr_protodown_r_bit(void) +{ + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug( + "Protodown reason bit index changed: bit-index %u -> bit-index %u", + frr_protodown_r_bit, FRR_PROTODOWN_REASON_DEFAULT_BIT); + + frr_protodown_r_bit = FRR_PROTODOWN_REASON_DEFAULT_BIT; +} + + +bool if_netlink_frr_protodown_r_bit_is_set(void) +{ + return (frr_protodown_r_bit != FRR_PROTODOWN_REASON_DEFAULT_BIT); +} + +uint8_t if_netlink_get_frr_protodown_r_bit(void) +{ + return frr_protodown_r_bit; +} + #endif /* GNU_LINUX */ diff --git a/zebra/if_netlink.h b/zebra/if_netlink.h index a1ce7af8c..46eac2537 100644 --- a/zebra/if_netlink.h +++ b/zebra/if_netlink.h @@ -40,6 +40,9 @@ int netlink_interface_addr_dplane(struct nlmsghdr *h, ns_id_t ns_id, extern int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup); extern int interface_lookup_netlink(struct zebra_ns *zns); +extern ssize_t netlink_intf_msg_encode(uint16_t cmd, + const struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen); extern enum netlink_msg_status netlink_put_gre_set_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx); @@ -47,19 +50,19 @@ extern enum netlink_msg_status netlink_put_address_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx); -/* - * Set protodown status of interface. - * - * ifp - * Interface to set protodown on. - * - * down - * If true, set protodown on. If false, set protodown off. +extern enum netlink_msg_status +netlink_put_intf_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx); + +#define FRR_PROTODOWN_REASON_DEFAULT_BIT 7 +/* Protodown bit setter/getter * - * Returns: - * 0 + * Allow users to change the bit if it conflicts with another + * on their system. */ -int netlink_protodown(struct interface *ifp, bool down); +extern void if_netlink_set_frr_protodown_r_bit(uint8_t bit); +extern void if_netlink_unset_frr_protodown_r_bit(void); +extern bool if_netlink_frr_protodown_r_bit_is_set(void); +extern uint8_t if_netlink_get_frr_protodown_r_bit(void); #ifdef __cplusplus } diff --git a/zebra/if_socket.c b/zebra/if_socket.c new file mode 100644 index 000000000..309d5a3f3 --- /dev/null +++ b/zebra/if_socket.c @@ -0,0 +1,41 @@ +/* + * Zebra Interface interaction with the kernel using socket. + * Copyright (C) 2022 NVIDIA CORPORATION & AFFILIATES + * Stephen Worley + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include <zebra.h> + +#ifndef HAVE_NETLINK + +#include "lib_errors.h" + +#include "zebra/rt.h" +#include "zebra/zebra_dplane.h" +#include "zebra/zebra_errors.h" + +enum zebra_dplane_result kernel_intf_update(struct zebra_dplane_ctx *ctx) +{ + flog_err(EC_LIB_UNAVAILABLE, "%s not Implemented for this platform", + __func__); + return ZEBRA_DPLANE_REQUEST_FAILURE; +} + +#endif diff --git a/zebra/interface.c b/zebra/interface.c index a76f8741e..69d611e58 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -261,6 +261,13 @@ static int if_zebra_delete_hook(struct interface *ifp) 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); @@ -1229,58 +1236,130 @@ void zebra_if_update_all_links(struct zebra_ns *zns) } } -void zebra_if_set_protodown(struct interface *ifp, bool down) +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; + + /* 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 - netlink_protodown(ifp, down); + 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 addr event based on info in a dplane context object. + * 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. */ -void zebra_if_addr_update_ctx(struct zebra_dplane_ctx *ctx) +static void zebra_if_addr_update_ctx(struct zebra_dplane_ctx *ctx, + struct interface *ifp) { - struct interface *ifp; uint8_t flags = 0; const char *label = NULL; - ns_id_t ns_id; - struct zebra_ns *zns; uint32_t metric = METRIC_MAX; - ifindex_t ifindex; const struct prefix *addr, *dest = NULL; enum dplane_op_e op; op = dplane_ctx_get_op(ctx); - ns_id = dplane_ctx_get_ns_id(ctx); - - 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); - goto done; - } - - ifindex = dplane_ctx_get_ifindex(ctx); - - ifp = if_lookup_by_index_per_ns(zns, ifindex); - if (ifp == NULL) { - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("%s: can't find ifp at nsid %u index %d", - __func__, ns_id, ifindex); - goto done; - } - addr = dplane_ctx_get_intf_addr(ctx); if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("%s: %s: ifindex %u, addr %pFX", __func__, - dplane_op2str(op), ifindex, addr); + 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); @@ -1335,41 +1414,66 @@ void zebra_if_addr_update_ctx(struct zebra_dplane_ctx *ctx) */ 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; + + 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: - /* We're responsible for the ctx object */ - dplane_ctx_fini(&ctx); + /* 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. */ -int zebra_if_netconf_update_ctx(struct zebra_dplane_ctx *ctx) +static void zebra_if_netconf_update_ctx(struct zebra_dplane_ctx *ctx, + struct interface *ifp) { - struct zebra_ns *zns; - struct interface *ifp; struct zebra_if *zif; enum dplane_netconf_status_e mpls; - int ret = 0; - - zns = zebra_ns_lookup(dplane_ctx_get_netconf_ns_id(ctx)); - if (zns == NULL) { - ret = -1; - goto done; - } - - ifp = if_lookup_by_index_per_ns(zns, - dplane_ctx_get_netconf_ifindex(ctx)); - if (ifp == NULL) { - ret = -1; - goto done; - } zif = ifp->info; - if (zif == NULL) { - ret = -1; - goto done; + if (!zif) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s: if %s(%u) zebra info pointer is NULL", + __func__, ifp->name, ifp->ifindex); + return; } mpls = dplane_ctx_get_netconf_mpls(ctx); @@ -1383,12 +1487,105 @@ int zebra_if_netconf_update_ctx(struct zebra_dplane_ctx *ctx) zlog_debug("%s: if %s, ifindex %d, mpls %s", __func__, ifp->name, ifp->ifindex, (zif->mpls ? "ON" : "OFF")); +} +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); + + goto done; + } + + ifp = if_lookup_by_index_per_ns(zns, ifindex); + if (ifp == NULL) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s: can't find ifp at nsid %u index %d", + __func__, ns_id, ifindex); + + goto done; + } + + switch (op) { + case DPLANE_OP_INTF_ADDR_ADD: + case DPLANE_OP_INTF_ADDR_DEL: + zebra_if_addr_update_ctx(ctx, ifp); + break; + + case DPLANE_OP_INTF_INSTALL: + case DPLANE_OP_INTF_UPDATE: + case DPLANE_OP_INTF_DELETE: + zebra_if_update_ctx(ctx, ifp); + break; + + case DPLANE_OP_INTF_NETCONFIG: + zebra_if_netconf_update_ctx(ctx, ifp); + break; + + case DPLANE_OP_ROUTE_INSTALL: + case DPLANE_OP_ROUTE_UPDATE: + case DPLANE_OP_ROUTE_DELETE: + case DPLANE_OP_NH_DELETE: + case DPLANE_OP_NH_INSTALL: + case DPLANE_OP_NH_UPDATE: + case DPLANE_OP_ROUTE_NOTIFY: + case DPLANE_OP_LSP_INSTALL: + case DPLANE_OP_LSP_UPDATE: + case DPLANE_OP_LSP_DELETE: + case DPLANE_OP_LSP_NOTIFY: + case DPLANE_OP_PW_INSTALL: + case DPLANE_OP_PW_UNINSTALL: + case DPLANE_OP_SYS_ROUTE_ADD: + case DPLANE_OP_SYS_ROUTE_DELETE: + case DPLANE_OP_ADDR_INSTALL: + case DPLANE_OP_ADDR_UNINSTALL: + case DPLANE_OP_MAC_INSTALL: + case DPLANE_OP_MAC_DELETE: + case DPLANE_OP_NEIGH_INSTALL: + case DPLANE_OP_NEIGH_UPDATE: + case DPLANE_OP_NEIGH_DELETE: + case DPLANE_OP_NEIGH_IP_INSTALL: + case DPLANE_OP_NEIGH_IP_DELETE: + case DPLANE_OP_VTEP_ADD: + case DPLANE_OP_VTEP_DELETE: + case DPLANE_OP_RULE_ADD: + case DPLANE_OP_RULE_DELETE: + case DPLANE_OP_RULE_UPDATE: + case DPLANE_OP_NEIGH_DISCOVER: + case DPLANE_OP_BR_PORT_UPDATE: + case DPLANE_OP_NONE: + case DPLANE_OP_IPTABLE_ADD: + case DPLANE_OP_IPTABLE_DELETE: + case DPLANE_OP_IPSET_ADD: + case DPLANE_OP_IPSET_DELETE: + case DPLANE_OP_IPSET_ENTRY_ADD: + case DPLANE_OP_IPSET_ENTRY_DELETE: + case DPLANE_OP_NEIGH_TABLE_UPDATE: + case DPLANE_OP_GRE_SET: + break; /* should never hit here */ + } done: - /* Free the context */ dplane_ctx_fini(&ctx); - - return ret; } /* Dump if address information to vty. */ @@ -1651,28 +1848,34 @@ static void ifs_dump_brief_vty_json(json_object *json, struct vrf *vrf) } } -const char *zebra_protodown_rc_str(enum protodown_reasons protodown_rc, - char *pd_buf, uint32_t pd_buf_len) +const char *zebra_protodown_rc_str(uint32_t protodown_rc, char *pd_buf, + uint32_t pd_buf_len) { - bool first = true; - pd_buf[0] = '\0'; + size_t len; strlcat(pd_buf, "(", pd_buf_len); - if (protodown_rc & ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY) { - if (first) - first = false; - else - strlcat(pd_buf, ",", pd_buf_len); - strlcat(pd_buf, "startup-delay", pd_buf_len); - } + if (CHECK_FLAG(protodown_rc, ZEBRA_PROTODOWN_EXTERNAL)) + strlcat(pd_buf, "external,", pd_buf_len); - if (protodown_rc & ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN) { - if (!first) - strlcat(pd_buf, ",", pd_buf_len); - strlcat(pd_buf, "uplinks-down", 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); @@ -1878,7 +2081,7 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) zebra_evpn_if_es_print(vty, NULL, zebra_if); vty_out(vty, " protodown: %s %s\n", - (zebra_if->flags & ZIF_FLAG_PROTODOWN) ? "on" : "off", + (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", @@ -2229,7 +2432,7 @@ static void if_dump_vty_json(struct vty *vty, struct interface *ifp, if (if_is_protodown_applicable(ifp)) { json_object_string_add( json_if, "protodown", - (zebra_if->flags & ZIF_FLAG_PROTODOWN) ? "on" : "off"); + (ZEBRA_IF_IS_PROTODOWN(zebra_if)) ? "on" : "off"); if (zebra_if->protodown_rc) json_object_string_add( json_if, "protodownReason", diff --git a/zebra/interface.h b/zebra/interface.h index 315a3170d..4b06603e7 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -308,14 +308,22 @@ enum zebra_if_flags { /* Dataplane protodown-on */ ZIF_FLAG_PROTODOWN = (1 << 2), + /* Dataplane protodown-on Queued to the dplane */ + ZIF_FLAG_SET_PROTODOWN = (1 << 3), + /* Dataplane protodown-off Queued to the dplane */ + ZIF_FLAG_UNSET_PROTODOWN = (1 << 4), /* LACP bypass state is set by the dataplane on a bond member * and inherited by the bond (if one or more bond members are in * a bypass state the bond is placed in a bypass state) */ - ZIF_FLAG_LACP_BYPASS = (1 << 3) + ZIF_FLAG_LACP_BYPASS = (1 << 5) }; +#define ZEBRA_IF_IS_PROTODOWN(zif) ((zif)->flags & ZIF_FLAG_PROTODOWN) +#define ZEBRA_IF_IS_PROTODOWN_ONLY_EXTERNAL(zif) \ + ((zif)->protodown_rc == ZEBRA_PROTODOWN_EXTERNAL) + /* `zebra' daemon local interface structure. */ struct zebra_if { /* back pointer to the interface */ @@ -403,7 +411,7 @@ struct zebra_if { * in the dataplane. This results in a carrier/L1 down on the * physical device. */ - enum protodown_reasons protodown_rc; + uint32_t protodown_rc; /* list of zebra_mac entries using this interface as destination */ struct list *mac_list; @@ -497,7 +505,16 @@ extern void if_handle_vrf_change(struct interface *ifp, vrf_id_t vrf_id); extern void zebra_if_update_link(struct interface *ifp, ifindex_t link_ifindex, ns_id_t ns_id); extern void zebra_if_update_all_links(struct zebra_ns *zns); -extern void zebra_if_set_protodown(struct interface *ifp, bool down); +/** + * Directly update entire protodown & reason code bitfield. + */ +extern int zebra_if_update_protodown_rc(struct interface *ifp, bool new_down, + uint32_t new_protodown_rc); +/** + * Set protodown with single reason. + */ +extern int zebra_if_set_protodown(struct interface *ifp, bool down, + enum protodown_reasons new_reason); extern int if_ip_address_install(struct interface *ifp, struct prefix *prefix, const char *label, struct prefix *pp); extern int if_ipv6_address_install(struct interface *ifp, struct prefix *prefix, @@ -521,10 +538,9 @@ extern bool if_nhg_dependents_is_empty(const struct interface *ifp); extern void vrf_add_update(struct vrf *vrfp); extern void zebra_l2_map_slave_to_bond(struct zebra_if *zif, vrf_id_t vrf); extern void zebra_l2_unmap_slave_from_bond(struct zebra_if *zif); -extern const char *zebra_protodown_rc_str(enum protodown_reasons protodown_rc, - char *pd_buf, uint32_t pd_buf_len); -void zebra_if_addr_update_ctx(struct zebra_dplane_ctx *ctx); -int zebra_if_netconf_update_ctx(struct zebra_dplane_ctx *ctx); +extern const char *zebra_protodown_rc_str(uint32_t protodown_rc, char *pd_buf, + uint32_t pd_buf_len); +void zebra_if_dplane_result(struct zebra_dplane_ctx *ctx); #ifdef HAVE_PROC_NET_DEV extern void ifstat_update_proc(void); diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index d84b0c132..0dd76e325 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -94,6 +94,7 @@ static const struct message nlmsg_str[] = {{RTM_NEWROUTE, "RTM_NEWROUTE"}, {RTM_DELROUTE, "RTM_DELROUTE"}, {RTM_GETROUTE, "RTM_GETROUTE"}, {RTM_NEWLINK, "RTM_NEWLINK"}, + {RTM_SETLINK, "RTM_SETLINK"}, {RTM_DELLINK, "RTM_DELLINK"}, {RTM_GETLINK, "RTM_GETLINK"}, {RTM_NEWADDR, "RTM_NEWADDR"}, @@ -209,6 +210,10 @@ int netlink_config_write_helper(struct vty *vty) vty_out(vty, "zebra kernel netlink batch-tx-buf %u %u\n", size, threshold); + if (if_netlink_frr_protodown_r_bit_is_set()) + vty_out(vty, "zebra protodown reason-bit %u\n", + if_netlink_get_frr_protodown_r_bit()); + return 0; } @@ -1491,6 +1496,11 @@ static enum netlink_msg_status nl_put_msg(struct nl_batch *bth, case DPLANE_OP_INTF_NETCONFIG: case DPLANE_OP_NONE: return FRR_NETLINK_ERROR; + + case DPLANE_OP_INTF_INSTALL: + case DPLANE_OP_INTF_UPDATE: + case DPLANE_OP_INTF_DELETE: + return netlink_put_intf_update_msg(bth, ctx); } return FRR_NETLINK_ERROR; diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index ce1f17111..6583af0a5 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -1577,6 +1577,12 @@ void kernel_update_multi(struct dplane_ctx_q *ctx_list) res = kernel_pbr_rule_update(ctx); break; + case DPLANE_OP_INTF_INSTALL: + case DPLANE_OP_INTF_UPDATE: + case DPLANE_OP_INTF_DELETE: + res = kernel_intf_update(ctx); + break; + /* Ignore 'notifications' - no-op */ case DPLANE_OP_SYS_ROUTE_ADD: case DPLANE_OP_SYS_ROUTE_DELETE: diff --git a/zebra/rt.h b/zebra/rt.h index 5e626928d..4952c3eb1 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -66,6 +66,9 @@ enum zebra_dplane_result kernel_neigh_update_ctx(struct zebra_dplane_ctx *ctx); extern enum zebra_dplane_result kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx); +extern enum zebra_dplane_result +kernel_intf_update(struct zebra_dplane_ctx *ctx); + #endif /* !HAVE_NETLINK */ extern int kernel_neigh_update(int cmd, int ifindex, void *addr, char *lla, diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h index 0a7977170..b1af4b20e 100644 --- a/zebra/rt_netlink.h +++ b/zebra/rt_netlink.h @@ -128,6 +128,8 @@ const char *af_type2str(int type); const char *ifi_type2str(int type); const char *rta_type2str(int type); const char *rtm_type2str(int type); +const char *ifla_pdr_type2str(int type); +const char *ifla_info_type2str(int type); const char *rtm_protocol2str(int type); const char *rtm_scope2str(int type); const char *rtm_rta2str(int type); diff --git a/zebra/subdir.am b/zebra/subdir.am index 77e0898d8..8cb1237c2 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -60,6 +60,7 @@ zebra_zebra_SOURCES = \ zebra/debug.c \ zebra/if_ioctl.c \ zebra/if_netlink.c \ + zebra/if_socket.c \ zebra/if_sysctl.c \ zebra/interface.c \ zebra/ioctl.c \ diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 1b6f37ec6..e94aee5c1 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -1487,6 +1487,7 @@ static void zread_interface_set_protodown(ZAPI_HANDLER_ARGS) ifindex_t ifindex; struct interface *ifp; char down; + enum protodown_reasons reason; STREAM_GETL(msg, ifindex); STREAM_GETC(msg, down); @@ -1494,16 +1495,27 @@ static void zread_interface_set_protodown(ZAPI_HANDLER_ARGS) /* set ifdown */ ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), ifindex); - if (ifp) { - zlog_info("Setting interface %s (%u): protodown %s", ifp->name, - ifindex, down ? "on" : "off"); - zebra_if_set_protodown(ifp, down); - } else { + if (!ifp) { zlog_warn( "Cannot set protodown %s for interface %u; does not exist", down ? "on" : "off", ifindex); + + return; + } + + switch (client->proto) { + case ZEBRA_ROUTE_VRRP: + reason = ZEBRA_PROTODOWN_VRRP; + break; + case ZEBRA_ROUTE_SHARP: + reason = ZEBRA_PROTODOWN_SHARP; + break; + default: + reason = 0; + break; } + zebra_if_set_protodown(ifp, down, reason); stream_failure: return; diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 6de2be3ab..d034c8f30 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -193,6 +193,9 @@ struct dplane_intf_info { uint32_t metric; uint32_t flags; + bool protodown; + bool pd_reason_val; + #define DPLANE_INTF_CONNECTED (1 << 0) /* Connected peer, p2p */ #define DPLANE_INTF_SECONDARY (1 << 1) #define DPLANE_INTF_BROADCAST (1 << 2) @@ -526,6 +529,9 @@ static struct zebra_dplane_globals { _Atomic uint32_t dg_gre_set_in; _Atomic uint32_t dg_gre_set_errors; + _Atomic uint32_t dg_intfs_in; + _Atomic uint32_t dg_intf_errors; + /* Dataplane pthread */ struct frr_pthread *dg_pthread; @@ -760,6 +766,9 @@ static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx) case DPLANE_OP_NONE: case DPLANE_OP_IPSET_ADD: case DPLANE_OP_IPSET_DELETE: + case DPLANE_OP_INTF_INSTALL: + case DPLANE_OP_INTF_UPDATE: + case DPLANE_OP_INTF_DELETE: break; case DPLANE_OP_IPSET_ENTRY_ADD: @@ -1073,6 +1082,16 @@ const char *dplane_op2str(enum dplane_op_e op) case DPLANE_OP_INTF_NETCONFIG: return "INTF_NETCONFIG"; + + case DPLANE_OP_INTF_INSTALL: + ret = "INTF_INSTALL"; + break; + case DPLANE_OP_INTF_UPDATE: + ret = "INTF_UPDATE"; + break; + case DPLANE_OP_INTF_DELETE: + ret = "INTF_DELETE"; + break; } return ret; @@ -1771,6 +1790,27 @@ void dplane_ctx_set_intf_metric(struct zebra_dplane_ctx *ctx, uint32_t metric) ctx->u.intf.metric = metric; } +uint32_t dplane_ctx_get_intf_pd_reason_val(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.pd_reason_val; +} + +void dplane_ctx_set_intf_pd_reason_val(struct zebra_dplane_ctx *ctx, bool val) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.pd_reason_val = val; +} + +bool dplane_ctx_intf_is_protodown(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.protodown; +} + /* Is interface addr p2p? */ bool dplane_ctx_intf_is_connected(const struct zebra_dplane_ctx *ctx) { @@ -2638,6 +2678,73 @@ done: return ret; } +/** + * dplane_ctx_intf_init() - Initialize a context block for a inteface update + * + * @ctx: Dataplane context to init + * @op: Operation being performed + * @ifp: Interface + * + * Return: Result status + */ +int dplane_ctx_intf_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, + const struct interface *ifp) +{ + struct zebra_ns *zns; + struct zebra_if *zif; + int ret = EINVAL; + bool set_pdown, unset_pdown; + + if (!ctx || !ifp) + goto done; + + ctx->zd_op = op; + ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS; + ctx->zd_vrf_id = ifp->vrf->vrf_id; + + strlcpy(ctx->zd_ifname, ifp->name, sizeof(ctx->zd_ifname)); + ctx->zd_ifindex = ifp->ifindex; + + zns = zebra_ns_lookup(ifp->vrf->vrf_id); + dplane_ctx_ns_init(ctx, zns, false); + + + /* Copy over ifp info */ + ctx->u.intf.metric = ifp->metric; + ctx->u.intf.flags = ifp->flags; + + /* Copy over extra zebra info, if available */ + zif = (struct zebra_if *)ifp->info; + + if (zif) { + set_pdown = !!(zif->flags & ZIF_FLAG_SET_PROTODOWN); + unset_pdown = !!(zif->flags & ZIF_FLAG_UNSET_PROTODOWN); + + if (zif->protodown_rc && + ZEBRA_IF_IS_PROTODOWN_ONLY_EXTERNAL(zif) == false) + ctx->u.intf.pd_reason_val = true; + + /* + * See if we have new protodown state to set, otherwise keep + * current state + */ + if (set_pdown) + ctx->u.intf.protodown = true; + else if (unset_pdown) + ctx->u.intf.protodown = false; + else + ctx->u.intf.protodown = !!ZEBRA_IF_IS_PROTODOWN(zif); + } + + dplane_ctx_ns_init(ctx, zns, (op == DPLANE_OP_INTF_UPDATE)); + ctx->zd_is_update = (op == DPLANE_OP_INTF_UPDATE); + + ret = AOK; + +done: + return ret; +} + /* * Capture information for an LSP update in a dplane context. */ @@ -3824,6 +3931,85 @@ static enum zebra_dplane_result intf_addr_update_internal( return result; } +/** + * dplane_intf_update_internal() - Helper for enqueuing interface changes + * + * @ifp: Interface where the change occured + * @op: The operation to be enqued + * + * Return: Result of the change + */ +static enum zebra_dplane_result +dplane_intf_update_internal(const struct interface *ifp, enum dplane_op_e op) +{ + enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; + int ret = EINVAL; + struct zebra_dplane_ctx *ctx = NULL; + + /* Obtain context block */ + ctx = dplane_ctx_alloc(); + if (!ctx) { + ret = ENOMEM; + goto done; + } + + ret = dplane_ctx_intf_init(ctx, op, ifp); + if (ret == AOK) + ret = dplane_update_enqueue(ctx); + +done: + /* Update counter */ + atomic_fetch_add_explicit(&zdplane_info.dg_intfs_in, 1, + memory_order_relaxed); + + if (ret == AOK) + result = ZEBRA_DPLANE_REQUEST_QUEUED; + else { + atomic_fetch_add_explicit(&zdplane_info.dg_intf_errors, 1, + memory_order_relaxed); + if (ctx) + dplane_ctx_free(&ctx); + } + + return result; +} + +/* + * Enqueue a interface add for the dataplane. + */ +enum zebra_dplane_result dplane_intf_add(const struct interface *ifp) +{ + enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE; + + if (ifp) + ret = dplane_intf_update_internal(ifp, DPLANE_OP_INTF_INSTALL); + return ret; +} + +/* + * Enqueue a interface update for the dataplane. + */ +enum zebra_dplane_result dplane_intf_update(const struct interface *ifp) +{ + enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE; + + if (ifp) + ret = dplane_intf_update_internal(ifp, DPLANE_OP_INTF_UPDATE); + return ret; +} + +/* + * Enqueue a interface delete for the dataplane. + */ +enum zebra_dplane_result dplane_intf_delete(const struct interface *ifp) +{ + enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE; + + if (ifp) + ret = dplane_intf_update_internal(ifp, DPLANE_OP_INTF_DELETE); + return ret; +} + /* * Enqueue vxlan/evpn mac add (or update). */ @@ -5241,6 +5427,15 @@ static void kernel_dplane_log_detail(struct zebra_dplane_ctx *ctx) dplane_ctx_get_netconf_mpls(ctx), dplane_ctx_get_netconf_mcast(ctx)); break; + + case DPLANE_OP_INTF_INSTALL: + case DPLANE_OP_INTF_UPDATE: + case DPLANE_OP_INTF_DELETE: + zlog_debug("Dplane intf %s, idx %u, protodown %d", + dplane_op2str(dplane_ctx_get_op(ctx)), + dplane_ctx_get_ifindex(ctx), + dplane_ctx_intf_is_protodown(ctx)); + break; } } @@ -5375,6 +5570,15 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx) &zdplane_info.dg_gre_set_errors, 1, memory_order_relaxed); break; + + case DPLANE_OP_INTF_INSTALL: + case DPLANE_OP_INTF_UPDATE: + case DPLANE_OP_INTF_DELETE: + if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) + atomic_fetch_add_explicit(&zdplane_info.dg_intf_errors, + 1, memory_order_relaxed); + break; + /* Ignore 'notifications' - no-op */ case DPLANE_OP_SYS_ROUTE_ADD: case DPLANE_OP_SYS_ROUTE_DELETE: diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 29555d5b5..334d440a2 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -188,6 +188,11 @@ enum dplane_op_e { /* Incoming interface config events */ DPLANE_OP_INTF_NETCONFIG, + + /* Interface update */ + DPLANE_OP_INTF_INSTALL, + DPLANE_OP_INTF_UPDATE, + DPLANE_OP_INTF_DELETE, }; /* @@ -480,6 +485,9 @@ dplane_ctx_get_pw_backup_nhg(const struct zebra_dplane_ctx *ctx); /* Accessors for interface information */ uint32_t dplane_ctx_get_intf_metric(const struct zebra_dplane_ctx *ctx); void dplane_ctx_set_intf_metric(struct zebra_dplane_ctx *ctx, uint32_t metric); +uint32_t dplane_ctx_get_intf_pd_reason_val(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_intf_pd_reason_val(struct zebra_dplane_ctx *ctx, bool val); +bool dplane_ctx_intf_is_protodown(const struct zebra_dplane_ctx *ctx); /* Is interface addr p2p? */ bool dplane_ctx_intf_is_connected(const struct zebra_dplane_ctx *ctx); void dplane_ctx_intf_set_connected(struct zebra_dplane_ctx *ctx); @@ -677,6 +685,13 @@ enum zebra_dplane_result dplane_intf_addr_unset(const struct interface *ifp, const struct connected *ifc); /* + * Enqueue interface link changes for the dataplane. + */ +enum zebra_dplane_result dplane_intf_add(const struct interface *ifp); +enum zebra_dplane_result dplane_intf_update(const struct interface *ifp); +enum zebra_dplane_result dplane_intf_delete(const struct interface *ifp); + +/* * Link layer operations for the dataplane. */ enum zebra_dplane_result dplane_neigh_ip_update(enum dplane_op_e op, @@ -814,6 +829,10 @@ int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, int dplane_ctx_nexthop_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, struct nhg_hash_entry *nhe); +/* Encode interface information into data plane context. */ +int dplane_ctx_intf_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, + const struct interface *ifp); + /* Retrieve the limit on the number of pending, unprocessed updates. */ uint32_t dplane_get_in_queue_limit(void); diff --git a/zebra/zebra_errors.c b/zebra/zebra_errors.c index c3890f722..7549a3d5c 100644 --- a/zebra/zebra_errors.c +++ b/zebra/zebra_errors.c @@ -792,6 +792,15 @@ static struct log_ref ferr_zebra_err[] = { .suggestion = "Ignore this error.", }, { + .code = EC_ZEBRA_INTF_UPDATE_FAILURE, + .title = + "Zebra failed to update interface in the kernel", + .description = + "Zebra made an attempt to update an interfce in the kernel, but it was not successful.", + .suggestion = + "Wait for Zebra to reattempt update.", + }, + { .code = END_FERR, } }; diff --git a/zebra/zebra_errors.h b/zebra/zebra_errors.h index 540c6dd7d..5164de09d 100644 --- a/zebra/zebra_errors.h +++ b/zebra/zebra_errors.h @@ -136,6 +136,7 @@ enum zebra_log_refs { EC_ZEBRA_ES_CREATE, EC_ZEBRA_GRE_SET_UPDATE, EC_ZEBRA_SRV6M_UNRELEASED_LOCATOR_CHUNK, + EC_ZEBRA_INTF_UPDATE_FAILURE, }; void zebra_error_init(void); diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c index 50eecd31d..9099c066b 100644 --- a/zebra/zebra_evpn_mh.c +++ b/zebra/zebra_evpn_mh.c @@ -3463,11 +3463,13 @@ void zebra_evpn_mh_json(json_object *json) if (zmh_info->protodown_rc) { json_array = json_object_new_array(); - if (zmh_info->protodown_rc & ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY) + if (CHECK_FLAG(zmh_info->protodown_rc, + ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY)) json_object_array_add( json_array, json_object_new_string("startupDelay")); - if (zmh_info->protodown_rc & ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN) + if (CHECK_FLAG(zmh_info->protodown_rc, + ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN)) json_object_array_add( json_array, json_object_new_string("uplinkDown")); @@ -3623,10 +3625,10 @@ bool zebra_evpn_is_es_bond_member(struct interface *ifp) void zebra_evpn_mh_update_protodown_bond_mbr(struct zebra_if *zif, bool clear, const char *caller) { - bool old_protodown; bool new_protodown; - enum protodown_reasons old_protodown_rc = 0; - enum protodown_reasons protodown_rc = 0; + uint32_t old_protodown_rc = 0; + uint32_t new_protodown_rc = 0; + uint32_t protodown_rc = 0; if (!clear) { struct zebra_if *bond_zif; @@ -3635,32 +3637,23 @@ void zebra_evpn_mh_update_protodown_bond_mbr(struct zebra_if *zif, bool clear, protodown_rc = bond_zif->protodown_rc; } - old_protodown = !!(zif->flags & ZIF_FLAG_PROTODOWN); old_protodown_rc = zif->protodown_rc; - zif->protodown_rc &= ~ZEBRA_PROTODOWN_EVPN_ALL; - zif->protodown_rc |= (protodown_rc & ZEBRA_PROTODOWN_EVPN_ALL); - new_protodown = !!zif->protodown_rc; + new_protodown_rc = (old_protodown_rc & ~ZEBRA_PROTODOWN_EVPN_ALL); + new_protodown_rc |= (protodown_rc & ZEBRA_PROTODOWN_EVPN_ALL); + new_protodown = !!new_protodown_rc; - if (IS_ZEBRA_DEBUG_EVPN_MH_ES - && (zif->protodown_rc != old_protodown_rc)) + if (IS_ZEBRA_DEBUG_EVPN_MH_ES && (new_protodown_rc != old_protodown_rc)) zlog_debug( "%s bond mbr %s protodown_rc changed; old 0x%x new 0x%x", caller, zif->ifp->name, old_protodown_rc, - zif->protodown_rc); - - if (old_protodown == new_protodown) - return; + new_protodown_rc); - if (new_protodown) - zif->flags |= ZIF_FLAG_PROTODOWN; - else - zif->flags &= ~ZIF_FLAG_PROTODOWN; - - if (IS_ZEBRA_DEBUG_EVPN_MH_ES) - zlog_debug("%s protodown %s", zif->ifp->name, - new_protodown ? "on" : "off"); - - zebra_if_set_protodown(zif->ifp, new_protodown); + if (zebra_if_update_protodown_rc(zif->ifp, new_protodown, + new_protodown_rc) == 0) { + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("%s protodown %s", zif->ifp->name, + new_protodown ? "on" : "off"); + } } /* The bond members inherit the protodown reason code from the bond */ @@ -3683,7 +3676,7 @@ static void zebra_evpn_mh_update_protodown_es(struct zebra_evpn_es *es, bool resync_dplane) { struct zebra_if *zif; - enum protodown_reasons old_protodown_rc; + uint32_t old_protodown_rc; zif = es->zif; /* if the reason code is the same bail unless it is a new @@ -3714,7 +3707,7 @@ static void zebra_evpn_mh_update_protodown_es(struct zebra_evpn_es *es, static void zebra_evpn_mh_clear_protodown_es(struct zebra_evpn_es *es) { struct zebra_if *zif; - enum protodown_reasons old_protodown_rc; + uint32_t old_protodown_rc; zif = es->zif; if (!(zif->protodown_rc & ZEBRA_PROTODOWN_EVPN_ALL)) @@ -3742,10 +3735,9 @@ static void zebra_evpn_mh_update_protodown_es_all(void) zebra_evpn_mh_update_protodown_es(es, false /*resync_dplane*/); } -static void zebra_evpn_mh_update_protodown(enum protodown_reasons protodown_rc, - bool set) +static void zebra_evpn_mh_update_protodown(uint32_t protodown_rc, bool set) { - enum protodown_reasons old_protodown_rc = zmh_info->protodown_rc; + uint32_t old_protodown_rc = zmh_info->protodown_rc; if (set) { if ((protodown_rc & zmh_info->protodown_rc) == protodown_rc) diff --git a/zebra/zebra_evpn_mh.h b/zebra/zebra_evpn_mh.h index af6832092..ce7b920de 100644 --- a/zebra/zebra_evpn_mh.h +++ b/zebra/zebra_evpn_mh.h @@ -263,7 +263,7 @@ struct zebra_evpn_mh_info { uint32_t uplink_oper_up_cnt; /* These protodown bits are inherited by all ES bonds */ - enum protodown_reasons protodown_rc; + uint32_t protodown_rc; }; /* returns TRUE if the EVPN is ready to be sent to BGP */ diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 469a94a65..1b926dba5 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -2993,6 +2993,9 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx) case DPLANE_OP_INTF_ADDR_ADD: case DPLANE_OP_INTF_ADDR_DEL: case DPLANE_OP_INTF_NETCONFIG: + case DPLANE_OP_INTF_INSTALL: + case DPLANE_OP_INTF_UPDATE: + case DPLANE_OP_INTF_DELETE: break; } diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index e376d4b2a..c6840a503 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -4318,11 +4318,11 @@ static void rib_process_dplane_results(struct thread *thread) case DPLANE_OP_INTF_ADDR_ADD: case DPLANE_OP_INTF_ADDR_DEL: - zebra_if_addr_update_ctx(ctx); - break; - + case DPLANE_OP_INTF_INSTALL: + case DPLANE_OP_INTF_UPDATE: + case DPLANE_OP_INTF_DELETE: case DPLANE_OP_INTF_NETCONFIG: - zebra_if_netconf_update_ctx(ctx); + zebra_if_dplane_result(ctx); break; /* Some op codes not handled here */ diff --git a/zebra/zebra_router.h b/zebra/zebra_router.h index 63a61d529..7aca91959 100644 --- a/zebra/zebra_router.h +++ b/zebra/zebra_router.h @@ -68,19 +68,27 @@ enum multicast_mode { * physical device. */ enum protodown_reasons { + /* A process outside of FRR's control protodowned the interface */ + ZEBRA_PROTODOWN_EXTERNAL = (1 << 0), /* On startup local ESs are held down for some time to * allow the underlay to converge and EVPN routes to * get learnt */ - ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY = (1 << 0), + ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY = (1 << 1), /* If all the uplinks are down the switch has lost access * to the VxLAN overlay and must shut down the access * ports to allow servers to re-direct their traffic to * other switches on the Ethernet Segment */ - ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN = (1 << 1), - ZEBRA_PROTODOWN_EVPN_ALL = (ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN - | ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY) + ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN = (1 << 2), + ZEBRA_PROTODOWN_EVPN_ALL = (ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN | + ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY), + ZEBRA_PROTODOWN_VRRP = (1 << 3), + /* This reason used exclusively for testing */ + ZEBRA_PROTODOWN_SHARP = (1 << 4), + /* Just used to clear our fields on shutdown, externel not included */ + ZEBRA_PROTODOWN_ALL = (ZEBRA_PROTODOWN_EVPN_ALL | ZEBRA_PROTODOWN_VRRP | + ZEBRA_PROTODOWN_SHARP) }; #define ZEBRA_PROTODOWN_RC_STR_LEN 80 diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index cc1ba3d8f..32bbfd665 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -60,6 +60,7 @@ #include "northbound_cli.h" #include "zebra/zebra_nb.h" #include "zebra/kernel_netlink.h" +#include "zebra/if_netlink.h" #include "zebra/table_manager.h" #include "zebra/zebra_script.h" #include "zebra/rtadv.h" @@ -4357,6 +4358,31 @@ DEFUN_HIDDEN(no_zebra_kernel_netlink_batch_tx_buf, return CMD_SUCCESS; } +DEFPY (zebra_protodown_bit, + zebra_protodown_bit_cmd, + "zebra protodown reason-bit (0-31)$bit", + ZEBRA_STR + "Protodown Configuration\n" + "Reason Bit used in the kernel for application\n" + "Reason Bit range\n") +{ + if_netlink_set_frr_protodown_r_bit(bit); + return CMD_SUCCESS; +} + +DEFPY (no_zebra_protodown_bit, + no_zebra_protodown_bit_cmd, + "no zebra protodown reason-bit [(0-31)$bit]", + NO_STR + ZEBRA_STR + "Protodown Configuration\n" + "Reason Bit used in the kernel for setting protodown\n" + "Reason Bit Range\n") +{ + if_netlink_unset_frr_protodown_r_bit(); + return CMD_SUCCESS; +} + #endif /* HAVE_NETLINK */ DEFUN(ip_table_range, ip_table_range_cmd, @@ -4562,6 +4588,8 @@ void zebra_vty_init(void) #ifdef HAVE_NETLINK install_element(CONFIG_NODE, &zebra_kernel_netlink_batch_tx_buf_cmd); install_element(CONFIG_NODE, &no_zebra_kernel_netlink_batch_tx_buf_cmd); + install_element(CONFIG_NODE, &zebra_protodown_bit_cmd); + install_element(CONFIG_NODE, &no_zebra_protodown_bit_cmd); #endif /* HAVE_NETLINK */ #ifdef HAVE_SCRIPTING |