summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_mplsvpn.c3
-rw-r--r--bgpd/bgp_snmp_bgp4v2.c12
-rw-r--r--doc/accords/cli-colors44
-rw-r--r--doc/developer/fpm.rst16
-rw-r--r--doc/user/zebra.rst2
-rw-r--r--tests/topotests/bgp_vrf_leaking_5549_routes/ce1/bgpd.conf23
-rw-r--r--tests/topotests/bgp_vrf_leaking_5549_routes/ce1/zebra.conf13
-rw-r--r--tests/topotests/bgp_vrf_leaking_5549_routes/pe1/bgpd.conf41
-rw-r--r--tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/default_ipv4_vpn.json32
-rw-r--r--tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/vrf10_ipv4_unicast.json32
-rw-r--r--tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/vrf20_ipv4_unicast.json34
-rw-r--r--tests/topotests/bgp_vrf_leaking_5549_routes/pe1/zebra.conf10
-rwxr-xr-xtests/topotests/bgp_vrf_leaking_5549_routes/test_bgp_vrf_leaking.py121
-rw-r--r--zebra/dplane_fpm_nl.c137
-rw-r--r--zebra/rt_netlink.c30
-rw-r--r--zebra/rt_netlink.h4
-rw-r--r--zebra/zebra_dplane.c188
-rw-r--r--zebra/zebra_dplane.h18
-rw-r--r--zebra/zebra_rib.c117
-rw-r--r--zebra/zebra_router.c11
-rw-r--r--zebra/zebra_router.h8
-rw-r--r--zebra/zebra_vty.c370
22 files changed, 1015 insertions, 251 deletions
diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c
index 12b68f2607..0270695c2f 100644
--- a/bgpd/bgp_mplsvpn.c
+++ b/bgpd/bgp_mplsvpn.c
@@ -1505,7 +1505,8 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */
} else {
if (!CHECK_FLAG(from_bgp->af_flags[afi][SAFI_UNICAST],
BGP_CONFIG_VRF_TO_VRF_EXPORT)) {
- if (afi == AFI_IP) {
+ if (afi == AFI_IP &&
+ !BGP_ATTR_NEXTHOP_AFI_IP6(path_vrf->attr)) {
/*
* For ipv4, copy to multiprotocol
* nexthop field
diff --git a/bgpd/bgp_snmp_bgp4v2.c b/bgpd/bgp_snmp_bgp4v2.c
index 9c91a30804..2d70aa94d3 100644
--- a/bgpd/bgp_snmp_bgp4v2.c
+++ b/bgpd/bgp_snmp_bgp4v2.c
@@ -440,11 +440,11 @@ bgp4v2PathAttrLookup(struct variable *v, oid name[], size_t *length,
sockunion_init(&su);
if (exact) {
- if (*length - v->namelen != BGP_NLRI_ENTRY_OFFSET)
+ if (*length - namelen != BGP_NLRI_ENTRY_OFFSET)
return NULL;
/* Set OID offset for prefix */
- offset = name + v->namelen;
+ offset = name + namelen;
if (family == AF_INET)
oid2in_addr(offset, afi_len, &addr->u.prefix4);
else
@@ -477,8 +477,8 @@ bgp4v2PathAttrLookup(struct variable *v, oid name[], size_t *length,
return NULL;
}
- offset = name + v->namelen;
- offsetlen = *length - v->namelen;
+ offset = name + namelen;
+ offsetlen = *length - namelen;
len = offsetlen;
if (offsetlen == 0) {
@@ -560,9 +560,9 @@ bgp4v2PathAttrLookup(struct variable *v, oid name[], size_t *length,
if (min) {
const struct prefix *rn_p = bgp_dest_get_prefix(dest);
- *length = v->namelen + BGP_NLRI_ENTRY_OFFSET;
+ *length = namelen + BGP_NLRI_ENTRY_OFFSET;
- offset = name + v->namelen;
+ offset = name + namelen;
if (family == AF_INET)
oid_copy_in_addr(offset, &rn_p->u.prefix4);
diff --git a/doc/accords/cli-colors b/doc/accords/cli-colors
new file mode 100644
index 0000000000..04bdfc7fae
--- /dev/null
+++ b/doc/accords/cli-colors
@@ -0,0 +1,44 @@
+Adding colors to FRR CLI output
+===============================
+
+
+There were multiple approaches/attempts to get colored output for the CLI into
+FRR, most recently End of 2022 in PR #12497. After some discussion, some items
+crystallized out:
+
+First, generally speaking, colors (or other rich output formatting) must be
+used sparingly. In particular, e.g. "every IP address" is not something to
+color. The output formatting needs to have an actual purpose to improve UX,
+not turn it into a christmas tree.
+
+In the long run, the CLI will hopefully become a YANG frontend. In that case,
+the CLI frontend component is a great place to apply all kinds of output/UI/UX
+features. However, this is a long way off.
+
+That said, an implementation in the current vtysh+daemon ecosystem is not out
+of the question, especially if the use of colors/formatting is limited to
+important places (which is desirable anyway - see general statement above.)
+We don't want to litter formatting all over every single vty_out call.
+
+A color option on a per-command/DEFUN level (i.e. the way `[json]` is done) was
+rejected. The decision to color output must take information from vtysh's
+environment into account, notably the TERM environment variable, the NO_COLOR
+environment variable, and whether stdout is a terminal or not. An explicit
+`--color` switch (or `terminal color` vtysh command, or other similar things)
+is needed too. To be clear, the switch must not be on individual commands, it
+needs to be on the vtysh session level.
+
+Lastly, the output pager needs to work with this.
+
+
+Suggested implementation
+------------------------
+
+(not part of the consensus / accord, only to record discussion)
+
+As far as discussion went, the most promising approach to actually implement
+this is to put some type of unconditional formatting tag into daemon's vty_out
+calls. This would be some escape-like sequence - an actual ANSI color code
+itself is not particularly readable or pretty, though that would work as well.
+vtysh would then, while passing through the output from the daemons, replace or
+remove these tags according to terminal/user settings.
diff --git a/doc/developer/fpm.rst b/doc/developer/fpm.rst
index 9849869133..56d33671d2 100644
--- a/doc/developer/fpm.rst
+++ b/doc/developer/fpm.rst
@@ -101,3 +101,19 @@ Data
^^^^
The netlink or protobuf message payload.
+
+
+Route Status Notification from ASIC
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The dplane_fpm_nl has the ability to read route netlink messages
+from the underlying fpm implementation that can tell zebra
+whether or not the route has been Offloaded/Failed or Trapped.
+The end developer must send the data up the same socket that has
+been created to listen for FPM messages from Zebra. The data sent
+must have a Frame Header with Version set to 1, Message Type set to 1
+and an appropriate message Length. The message data must contain
+a RTM_NEWROUTE netlink message that sends the prefix and nexthops
+associated with the route. Finally rtm_flags must contain
+RTM_F_OFFLOAD, RTM_F_TRAP and or RTM_F_OFFLOAD_FAILED to signify
+what has happened to the route in the ASIC.
diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst
index 230a4f43b0..3608f828e8 100644
--- a/doc/user/zebra.rst
+++ b/doc/user/zebra.rst
@@ -1342,7 +1342,7 @@ zebra Terminal Mode Commands
total number of route nodes in the table. Which will be higher than
the actual number of routes that are held.
-.. clicmd:: show nexthop-group rib [ID] [vrf NAME] [singleton [ip|ip6]] [type]
+.. clicmd:: show nexthop-group rib [ID] [vrf NAME] [singleton [ip|ip6]] [type] [json]
Display nexthop groups created by zebra. The [vrf NAME] option
is only meaningful if you have started zebra with the --vrfwnetns
diff --git a/tests/topotests/bgp_vrf_leaking_5549_routes/ce1/bgpd.conf b/tests/topotests/bgp_vrf_leaking_5549_routes/ce1/bgpd.conf
new file mode 100644
index 0000000000..66493f0fea
--- /dev/null
+++ b/tests/topotests/bgp_vrf_leaking_5549_routes/ce1/bgpd.conf
@@ -0,0 +1,23 @@
+frr defaults traditional
+!
+hostname ce1
+password zebra
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+router bgp 65002
+ bgp router-id 192.0.2.2
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor eth0 interface
+ neighbor eth0 remote-as external
+ neighbor eth0 timers connect 1
+ !
+ address-family ipv4 unicast
+ neighbor eth0 activate
+ redistribute connected
+ exit-address-family
+ !
+!
diff --git a/tests/topotests/bgp_vrf_leaking_5549_routes/ce1/zebra.conf b/tests/topotests/bgp_vrf_leaking_5549_routes/ce1/zebra.conf
new file mode 100644
index 0000000000..a163295844
--- /dev/null
+++ b/tests/topotests/bgp_vrf_leaking_5549_routes/ce1/zebra.conf
@@ -0,0 +1,13 @@
+log file zebra.log
+!
+hostname ce1
+!
+interface lo
+ ip address 172.16.0.1/32
+!
+interface eth0
+ ipv6 nd ra-interval 1
+ no ipv6 nd suppress-ra
+!
+line vty
+!
diff --git a/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/bgpd.conf b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/bgpd.conf
new file mode 100644
index 0000000000..c5c99270e7
--- /dev/null
+++ b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/bgpd.conf
@@ -0,0 +1,41 @@
+frr defaults traditional
+!
+hostname pe1
+password zebra
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+router bgp 65001
+ bgp router-id 192.0.2.1
+ !
+!
+router bgp 65001 vrf vrf10
+ bgp router-id 192.0.2.1
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor eth0 interface
+ neighbor eth0 remote-as external
+ neighbor eth0 timers connect 1
+ !
+ address-family ipv4 unicast
+ neighbor eth0 activate
+ rd vpn export 65001:10
+ rt vpn both 0:10
+ import vpn
+ export vpn
+ exit-address-family
+ !
+!
+router bgp 65001 vrf vrf20
+ bgp router-id 192.0.2.1
+ !
+ address-family ipv4 unicast
+ rd vpn export 65001:20
+ rt vpn both 0:10
+ import vpn
+ export vpn
+ exit-address-family
+ !
+!
diff --git a/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/default_ipv4_vpn.json b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/default_ipv4_vpn.json
new file mode 100644
index 0000000000..9516016fc2
--- /dev/null
+++ b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/default_ipv4_vpn.json
@@ -0,0 +1,32 @@
+{
+ "vrfName": "default",
+ "routerId": "192.0.2.1",
+ "localAS": 65001,
+ "routes": {
+ "routeDistinguishers": {
+ "65001:10": {
+ "172.16.0.1/32": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "172.16.0.1",
+ "prefixLen": 32,
+ "network": "172.16.0.1\/32",
+ "path": "65002",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf10",
+ "nexthops": [
+ {
+ "hostname": "pe1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/vrf10_ipv4_unicast.json b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/vrf10_ipv4_unicast.json
new file mode 100644
index 0000000000..768bffbe9d
--- /dev/null
+++ b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/vrf10_ipv4_unicast.json
@@ -0,0 +1,32 @@
+{
+ "vrfName": "vrf10",
+ "routerId": "192.0.2.1",
+ "localAS": 65001,
+ "routes": {
+ "172.16.0.1/32": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "172.16.0.1",
+ "prefixLen": 32,
+ "network": "172.16.0.1\/32",
+ "path": "65002",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "hostname": "ce1",
+ "afi": "ipv6",
+ "scope": "global",
+ "used": true
+ },
+ {
+ "hostname": "ce1",
+ "afi": "ipv6",
+ "scope": "link-local"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/vrf20_ipv4_unicast.json b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/vrf20_ipv4_unicast.json
new file mode 100644
index 0000000000..1e93715270
--- /dev/null
+++ b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/vrf20_ipv4_unicast.json
@@ -0,0 +1,34 @@
+{
+ "vrfName": "vrf20",
+ "routerId": "192.0.2.1",
+ "localAS": 65001,
+ "routes": {
+ "172.16.0.1/32": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "172.16.0.1",
+ "prefixLen": 32,
+ "network": "172.16.0.1\/32",
+ "path": "65002",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf10",
+ "nexthops": [
+ {
+ "hostname": "pe1",
+ "afi": "ipv6",
+ "scope": "global",
+ "used": true
+ },
+ {
+ "hostname": "pe1",
+ "afi": "ipv6",
+ "scope": "link-local"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/zebra.conf b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/zebra.conf
new file mode 100644
index 0000000000..d40041ab3c
--- /dev/null
+++ b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/zebra.conf
@@ -0,0 +1,10 @@
+log file zebra.log
+!
+hostname pe1
+!
+interface eth0 vrf vrf10
+ ipv6 nd ra-interval 1
+ no ipv6 nd suppress-ra
+!
+line vty
+!
diff --git a/tests/topotests/bgp_vrf_leaking_5549_routes/test_bgp_vrf_leaking.py b/tests/topotests/bgp_vrf_leaking_5549_routes/test_bgp_vrf_leaking.py
new file mode 100755
index 0000000000..dd27ad3ed1
--- /dev/null
+++ b/tests/topotests/bgp_vrf_leaking_5549_routes/test_bgp_vrf_leaking.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2022, LINE Corporation
+# Authored by Ryoga Saito <ryoga.saito@linecorp.com>
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+import os
+import re
+import sys
+import json
+import functools
+import pytest
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+from lib.common_config import required_linux_kernel_version
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ tgen.add_router("pe1")
+ tgen.add_router("ce1")
+
+ tgen.add_link(tgen.gears["pe1"], tgen.gears["ce1"], "eth0", "eth0")
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ for rname, router in tgen.routers().items():
+ router.load_config(TopoRouter.RD_ZEBRA,
+ os.path.join(CWD, '{}/zebra.conf'.format(rname)))
+ router.load_config(TopoRouter.RD_BGP,
+ os.path.join(CWD, '{}/bgpd.conf'.format(rname)))
+
+ tgen.gears["pe1"].run("ip link add vrf10 type vrf table 10")
+ tgen.gears["pe1"].run("ip link set vrf10 up")
+ tgen.gears["pe1"].run("ip link add vrf20 type vrf table 20")
+ tgen.gears["pe1"].run("ip link set vrf20 up")
+ tgen.gears["pe1"].run("ip link set eth0 master vrf10")
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def open_json_file(path):
+ try:
+ with open(path, "r") as f:
+ return json.load(f)
+ except IOError:
+ assert False, "Could not read file {}".format(path)
+
+
+def check_vrf10_rib(output):
+ expected = open_json_file("%s/pe1/results/vrf10_ipv4_unicast.json" % CWD)
+ actual = json.loads(output)
+ return topotest.json_cmp(actual, expected)
+
+
+def check_default_vpn_rib(output):
+ expected = open_json_file("%s/pe1/results/default_ipv4_vpn.json" % CWD)
+ actual = json.loads(output)
+ return topotest.json_cmp(actual, expected)
+
+
+def check_vrf20_rib(output):
+ expected = open_json_file("%s/pe1/results/vrf20_ipv4_unicast.json" % CWD)
+ actual = json.loads(output)
+ return topotest.json_cmp(actual, expected)
+
+
+def check(name, command, checker):
+ tgen = get_topogen()
+ router = tgen.gears[name]
+
+ def _check():
+ try:
+ return checker(router.vtysh_cmd(command))
+ except:
+ return False
+
+ logger.info('[+] check {} "{}"'.format(name, command))
+ _, result = topotest.run_and_expect(_check, None, count=10, wait=0.5)
+ assert result is None, "Failed"
+
+
+def test_rib():
+ check("pe1", "show bgp vrf vrf10 ipv4 unicast json", check_vrf10_rib)
+ check("pe1", "show bgp ipv4 vpn json", check_default_vpn_rib)
+ check("pe1", "show bgp vrf vrf20 ipv4 unicast json", check_vrf20_rib)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/zebra/dplane_fpm_nl.c b/zebra/dplane_fpm_nl.c
index 6c95be29df..337113988e 100644
--- a/zebra/dplane_fpm_nl.c
+++ b/zebra/dplane_fpm_nl.c
@@ -51,6 +51,7 @@
#include "zebra/kernel_netlink.h"
#include "zebra/rt_netlink.h"
#include "zebra/debug.h"
+#include "fpm/fpm.h"
#define SOUTHBOUND_DEFAULT_ADDR INADDR_LOOPBACK
#define SOUTHBOUND_DEFAULT_PORT 2620
@@ -462,18 +463,17 @@ static void fpm_reconnect(struct fpm_nl_ctx *fnc)
static void fpm_read(struct thread *t)
{
struct fpm_nl_ctx *fnc = THREAD_ARG(t);
+ fpm_msg_hdr_t fpm;
ssize_t rv;
+ char buf[65535];
+ struct nlmsghdr *hdr;
+ struct zebra_dplane_ctx *ctx;
+ size_t available_bytes;
+ size_t hdr_available_bytes;
/* Let's ignore the input at the moment. */
rv = stream_read_try(fnc->ibuf, fnc->socket,
STREAM_WRITEABLE(fnc->ibuf));
- /* We've got an interruption. */
- if (rv == -2) {
- /* Schedule next read. */
- thread_add_read(fnc->fthread->master, fpm_read, fnc,
- fnc->socket, &fnc->t_read);
- return;
- }
if (rv == 0) {
atomic_fetch_add_explicit(&fnc->counters.connection_closes, 1,
memory_order_relaxed);
@@ -492,14 +492,131 @@ static void fpm_read(struct thread *t)
FPM_RECONNECT(fnc);
return;
}
- stream_reset(fnc->ibuf);
+
+ /* Schedule the next read */
+ thread_add_read(fnc->fthread->master, fpm_read, fnc, fnc->socket,
+ &fnc->t_read);
+
+ /* We've got an interruption. */
+ if (rv == -2)
+ return;
+
/* Account all bytes read. */
atomic_fetch_add_explicit(&fnc->counters.bytes_read, rv,
memory_order_relaxed);
- thread_add_read(fnc->fthread->master, fpm_read, fnc, fnc->socket,
- &fnc->t_read);
+ available_bytes = STREAM_READABLE(fnc->ibuf);
+ while (available_bytes) {
+ if (available_bytes < (ssize_t)FPM_MSG_HDR_LEN) {
+ stream_pulldown(fnc->ibuf);
+ return;
+ }
+
+ fpm.version = stream_getc(fnc->ibuf);
+ fpm.msg_type = stream_getc(fnc->ibuf);
+ fpm.msg_len = stream_getw(fnc->ibuf);
+
+ if (fpm.version != FPM_PROTO_VERSION &&
+ fpm.msg_type != FPM_MSG_TYPE_NETLINK) {
+ stream_reset(fnc->ibuf);
+ zlog_warn(
+ "%s: Received version/msg_type %u/%u, expected 1/1",
+ __func__, fpm.version, fpm.msg_type);
+
+ FPM_RECONNECT(fnc);
+ return;
+ }
+
+ /*
+ * If the passed in length doesn't even fill in the header
+ * something is wrong and reset.
+ */
+ if (fpm.msg_len < FPM_MSG_HDR_LEN) {
+ zlog_warn(
+ "%s: Received message length: %u that does not even fill the FPM header",
+ __func__, fpm.msg_len);
+ FPM_RECONNECT(fnc);
+ return;
+ }
+
+ /*
+ * If we have not received the whole payload, reset the stream
+ * back to the beginning of the header and move it to the
+ * top.
+ */
+ if (fpm.msg_len > available_bytes) {
+ stream_rewind_getp(fnc->ibuf, FPM_MSG_HDR_LEN);
+ stream_pulldown(fnc->ibuf);
+ return;
+ }
+
+ available_bytes -= FPM_MSG_HDR_LEN;
+
+ /*
+ * Place the data from the stream into a buffer
+ */
+ hdr = (struct nlmsghdr *)buf;
+ stream_get(buf, fnc->ibuf, fpm.msg_len - FPM_MSG_HDR_LEN);
+ hdr_available_bytes = fpm.msg_len - FPM_MSG_HDR_LEN;
+ available_bytes -= hdr_available_bytes;
+
+ /* Sanity check: must be at least header size. */
+ if (hdr->nlmsg_len < sizeof(*hdr)) {
+ zlog_warn(
+ "%s: [seq=%u] invalid message length %u (< %zu)",
+ __func__, hdr->nlmsg_seq, hdr->nlmsg_len,
+ sizeof(*hdr));
+ continue;
+ }
+ if (hdr->nlmsg_len > fpm.msg_len) {
+ zlog_warn(
+ "%s: Received a inner header length of %u that is greater than the fpm total length of %u",
+ __func__, hdr->nlmsg_len, fpm.msg_len);
+ FPM_RECONNECT(fnc);
+ }
+ /* Not enough bytes available. */
+ if (hdr->nlmsg_len > hdr_available_bytes) {
+ zlog_warn(
+ "%s: [seq=%u] invalid message length %u (> %zu)",
+ __func__, hdr->nlmsg_seq, hdr->nlmsg_len,
+ available_bytes);
+ continue;
+ }
+
+ if (!(hdr->nlmsg_flags & NLM_F_REQUEST)) {
+ if (IS_ZEBRA_DEBUG_FPM)
+ zlog_debug(
+ "%s: [seq=%u] not a request, skipping",
+ __func__, hdr->nlmsg_seq);
+
+ /*
+ * This request is a bust, go to the next one
+ */
+ continue;
+ }
+
+ switch (hdr->nlmsg_type) {
+ case RTM_NEWROUTE:
+ ctx = dplane_ctx_alloc();
+ dplane_ctx_set_op(ctx, DPLANE_OP_ROUTE_NOTIFY);
+ if (netlink_route_change_read_unicast_internal(
+ hdr, 0, false, ctx) != 1) {
+ dplane_ctx_fini(&ctx);
+ stream_pulldown(fnc->ibuf);
+ return;
+ }
+ break;
+ default:
+ if (IS_ZEBRA_DEBUG_FPM)
+ zlog_debug(
+ "%s: Received message type %u which is not currently handled",
+ __func__, hdr->nlmsg_type);
+ break;
+ }
+ }
+
+ stream_reset(fnc->ibuf);
}
static void fpm_write(struct thread *t)
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
index 2396dfe4d6..96ec90e549 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.c
@@ -692,8 +692,9 @@ static uint8_t parse_multipath_nexthops_unicast(ns_id_t ns_id,
}
/* Looking up routing table by netlink interface. */
-static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id,
- int startup)
+int netlink_route_change_read_unicast_internal(struct nlmsghdr *h,
+ ns_id_t ns_id, int startup,
+ struct zebra_dplane_ctx *ctx)
{
int len;
struct rtmsg *rtm;
@@ -768,9 +769,8 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id,
selfroute = is_selfroute(rtm->rtm_protocol);
- if (!startup && selfroute
- && h->nlmsg_type == RTM_NEWROUTE
- && !zrouter.asic_offloaded) {
+ if (!startup && selfroute && h->nlmsg_type == RTM_NEWROUTE &&
+ !zrouter.asic_offloaded && !ctx) {
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug("Route type: %d Received that we think we have originated, ignoring",
rtm->rtm_protocol);
@@ -988,8 +988,8 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id,
}
}
if (nhe_id || ng)
- rib_add_multipath(afi, SAFI_UNICAST, &p, &src_p, re, ng,
- startup);
+ dplane_rib_add_multipath(afi, SAFI_UNICAST, &p, &src_p,
+ re, ng, startup, ctx);
else {
/*
* I really don't see how this is possible
@@ -1004,6 +1004,13 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id,
XFREE(MTYPE_RE, re);
}
} else {
+ if (ctx) {
+ zlog_err(
+ "%s: %pFX RTM_DELROUTE received but received a context as well",
+ __func__, &p);
+ return 0;
+ }
+
if (nhe_id) {
rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, flags,
&p, &src_p, NULL, nhe_id, table, metric,
@@ -1028,7 +1035,14 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id,
}
}
- return 0;
+ return 1;
+}
+
+static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id,
+ int startup)
+{
+ return netlink_route_change_read_unicast_internal(h, ns_id, startup,
+ NULL);
}
static struct mcast_route_data *mroute = NULL;
diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h
index b1af4b20e1..fd2b79a2bf 100644
--- a/zebra/rt_netlink.h
+++ b/zebra/rt_netlink.h
@@ -122,6 +122,10 @@ netlink_put_lsp_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx);
extern enum netlink_msg_status
netlink_put_pw_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx);
+int netlink_route_change_read_unicast_internal(struct nlmsghdr *h,
+ ns_id_t ns_id, int startup,
+ struct zebra_dplane_ctx *ctx);
+
#ifdef NETLINK_DEBUG
const char *nlmsg_type2str(uint16_t type);
const char *af_type2str(int type);
diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c
index c189408b57..84dae7f2d6 100644
--- a/zebra/zebra_dplane.c
+++ b/zebra/zebra_dplane.c
@@ -135,6 +135,8 @@ struct dplane_route_info {
uint32_t zd_mtu;
uint32_t zd_nexthop_mtu;
+ uint32_t zd_flags;
+
/* Nexthop hash entry info */
struct dplane_nexthop_info nhe;
@@ -1430,6 +1432,20 @@ uint16_t dplane_ctx_get_old_instance(const struct zebra_dplane_ctx *ctx)
return ctx->u.rinfo.zd_old_instance;
}
+uint32_t dplane_ctx_get_flags(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.rinfo.zd_flags;
+}
+
+void dplane_ctx_set_flags(struct zebra_dplane_ctx *ctx, uint32_t flags)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.rinfo.zd_flags = flags;
+}
+
uint32_t dplane_ctx_get_metric(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
@@ -2766,25 +2782,16 @@ static int dplane_ctx_ns_init(struct zebra_dplane_ctx *ctx,
return AOK;
}
-/*
- * Initialize a context block for a route update from zebra data structs.
- */
-int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
- struct route_node *rn, struct route_entry *re)
+int dplane_ctx_route_init_basic(struct zebra_dplane_ctx *ctx,
+ enum dplane_op_e op, struct route_entry *re,
+ const struct prefix *p,
+ const struct prefix_ipv6 *src_p, afi_t afi,
+ safi_t safi)
{
int ret = EINVAL;
- const struct route_table *table = NULL;
- const struct rib_table_info *info;
- const struct prefix *p, *src_p;
- struct zebra_ns *zns;
- struct zebra_vrf *zvrf;
- struct nexthop *nexthop;
- struct zebra_l3vni *zl3vni;
- const struct interface *ifp;
- struct dplane_intf_extra *if_extra;
- if (!ctx || !rn || !re)
- goto done;
+ if (!ctx || !re)
+ return ret;
TAILQ_INIT(&ctx->u.rinfo.intf_extra_q);
@@ -2794,9 +2801,6 @@ int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
ctx->u.rinfo.zd_type = re->type;
ctx->u.rinfo.zd_old_type = re->type;
- /* Prefixes: dest, and optional source */
- srcdest_rnode_prefixes(rn, &p, &src_p);
-
prefix_copy(&(ctx->u.rinfo.zd_dest), p);
if (src_p)
@@ -2806,6 +2810,7 @@ int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
ctx->zd_table_id = re->table;
+ ctx->u.rinfo.zd_flags = re->flags;
ctx->u.rinfo.zd_metric = re->metric;
ctx->u.rinfo.zd_old_metric = re->metric;
ctx->zd_vrf_id = re->vrf_id;
@@ -2816,11 +2821,46 @@ int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
ctx->u.rinfo.zd_old_tag = re->tag;
ctx->u.rinfo.zd_distance = re->distance;
+ ctx->u.rinfo.zd_afi = afi;
+ ctx->u.rinfo.zd_safi = safi;
+
+ return AOK;
+}
+
+/*
+ * Initialize a context block for a route update from zebra data structs.
+ */
+int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
+ struct route_node *rn, struct route_entry *re)
+{
+ int ret = EINVAL;
+ const struct route_table *table = NULL;
+ const struct rib_table_info *info;
+ const struct prefix *p;
+ const struct prefix_ipv6 *src_p;
+ struct zebra_ns *zns;
+ struct zebra_vrf *zvrf;
+ struct nexthop *nexthop;
+ struct zebra_l3vni *zl3vni;
+ const struct interface *ifp;
+ struct dplane_intf_extra *if_extra;
+
+ if (!ctx || !rn || !re)
+ return ret;
+
+ /*
+ * Let's grab the data from the route_node
+ * so that we can call a helper function
+ */
+
+ /* Prefixes: dest, and optional source */
+ srcdest_rnode_prefixes(rn, &p, (const struct prefix **)&src_p);
table = srcdest_rnode_table(rn);
info = table->info;
- ctx->u.rinfo.zd_afi = info->afi;
- ctx->u.rinfo.zd_safi = info->safi;
+ if (dplane_ctx_route_init_basic(ctx, op, re, p, src_p, info->afi,
+ info->safi) != AOK)
+ return ret;
/* Copy nexthops; recursive info is included too */
copy_nexthops(&(ctx->u.rinfo.zd_ng.nexthop),
@@ -2875,8 +2915,7 @@ int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
/* Don't need some info when capturing a system notification */
if (op == DPLANE_OP_SYS_ROUTE_ADD ||
op == DPLANE_OP_SYS_ROUTE_DELETE) {
- ret = AOK;
- goto done;
+ return AOK;
}
/* Extract ns info - can't use pointers to 'core' structs */
@@ -2897,14 +2936,12 @@ int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
* If its a delete we only use the prefix anyway, so this only
* matters for INSTALL/UPDATE.
*/
- if (zebra_nhg_kernel_nexthops_enabled()
- && (((op == DPLANE_OP_ROUTE_INSTALL)
- || (op == DPLANE_OP_ROUTE_UPDATE))
- && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED)
- && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED))) {
- ret = ENOENT;
- goto done;
- }
+ if (zebra_nhg_kernel_nexthops_enabled() &&
+ (((op == DPLANE_OP_ROUTE_INSTALL) ||
+ (op == DPLANE_OP_ROUTE_UPDATE)) &&
+ !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED) &&
+ !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED)))
+ return ENOENT;
re->nhe_installed_id = nhe->id;
}
@@ -2916,10 +2953,7 @@ int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
re->dplane_sequence = zebra_router_get_next_sequence();
ctx->zd_seq = re->dplane_sequence;
- ret = AOK;
-
-done:
- return ret;
+ return AOK;
}
static int dplane_ctx_tc_qdisc_init(struct zebra_dplane_ctx *ctx,
@@ -3031,7 +3065,7 @@ int dplane_ctx_nexthop_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
int ret = EINVAL;
if (!ctx || !nhe)
- goto done;
+ return ret;
ctx->zd_op = op;
ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS;
@@ -3066,7 +3100,6 @@ int dplane_ctx_nexthop_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
ret = AOK;
-done:
return ret;
}
@@ -3088,7 +3121,7 @@ int dplane_ctx_intf_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
bool set_pdown, unset_pdown;
if (!ctx || !ifp)
- goto done;
+ return ret;
ctx->zd_op = op;
ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS;
@@ -3133,7 +3166,6 @@ int dplane_ctx_intf_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
ret = AOK;
-done:
return ret;
}
@@ -3161,10 +3193,8 @@ int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
/* This may be called to create/init a dplane context, not necessarily
* to copy an lsp object.
*/
- if (lsp == NULL) {
- ret = AOK;
- goto done;
- }
+ if (lsp == NULL)
+ return ret;
if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
zlog_debug("init dplane ctx %s: in-label %u ecmp# %d",
@@ -3207,7 +3237,7 @@ int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
}
if (ret != AOK)
- goto done;
+ return ret;
/* Capture backup nhlfes/nexthops */
frr_each(nhlfe_list, &lsp->backup_nhlfe_list, nhlfe) {
@@ -3228,11 +3258,6 @@ int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
new_nhlfe->nexthop->flags = nhlfe->nexthop->flags;
}
- /* On error the ctx will be cleaned-up, so we don't need to
- * deal with any allocated nhlfe or nexthop structs here.
- */
-done:
-
return ret;
}
@@ -3293,11 +3318,11 @@ static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx,
afi = (pw->af == AF_INET) ? AFI_IP : AFI_IP6;
table = zebra_vrf_table(afi, SAFI_UNICAST, pw->vrf_id);
if (table == NULL)
- goto done;
+ return ret;
rn = route_node_match(table, &p);
if (rn == NULL)
- goto done;
+ return ret;
re = NULL;
RNODE_FOREACH_RE(rn, re) {
@@ -3365,10 +3390,7 @@ static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx,
}
route_unlock_node(rn);
- ret = AOK;
-
-done:
- return ret;
+ return AOK;
}
/**
@@ -3943,12 +3965,11 @@ enum zebra_dplane_result dplane_route_add(struct route_node *rn,
enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
if (rn == NULL || re == NULL)
- goto done;
+ return ret;
ret = dplane_route_update_internal(rn, re, NULL,
DPLANE_OP_ROUTE_INSTALL);
-done:
return ret;
}
@@ -3962,11 +3983,11 @@ enum zebra_dplane_result dplane_route_update(struct route_node *rn,
enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
if (rn == NULL || re == NULL)
- goto done;
+ return ret;
ret = dplane_route_update_internal(rn, re, old_re,
DPLANE_OP_ROUTE_UPDATE);
-done:
+
return ret;
}
@@ -3979,12 +4000,11 @@ enum zebra_dplane_result dplane_route_delete(struct route_node *rn,
enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
if (rn == NULL || re == NULL)
- goto done;
+ return ret;
ret = dplane_route_update_internal(rn, re, NULL,
DPLANE_OP_ROUTE_DELETE);
-done:
return ret;
}
@@ -3997,18 +4017,16 @@ enum zebra_dplane_result dplane_sys_route_add(struct route_node *rn,
enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
/* Ignore this event unless a provider plugin has requested it. */
- if (!zdplane_info.dg_sys_route_notifs) {
- ret = ZEBRA_DPLANE_REQUEST_SUCCESS;
- goto done;
- }
+ if (!zdplane_info.dg_sys_route_notifs)
+ return ZEBRA_DPLANE_REQUEST_SUCCESS;
+
if (rn == NULL || re == NULL)
- goto done;
+ return ret;
ret = dplane_route_update_internal(rn, re, NULL,
DPLANE_OP_SYS_ROUTE_ADD);
-done:
return ret;
}
@@ -4021,18 +4039,15 @@ enum zebra_dplane_result dplane_sys_route_del(struct route_node *rn,
enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
/* Ignore this event unless a provider plugin has requested it. */
- if (!zdplane_info.dg_sys_route_notifs) {
- ret = ZEBRA_DPLANE_REQUEST_SUCCESS;
- goto done;
- }
+ if (!zdplane_info.dg_sys_route_notifs)
+ return ZEBRA_DPLANE_REQUEST_SUCCESS;
if (rn == NULL || re == NULL)
- goto done;
+ return ret;
ret = dplane_route_update_internal(rn, re, NULL,
DPLANE_OP_SYS_ROUTE_DELETE);
-done:
return ret;
}
@@ -6287,6 +6302,20 @@ kernel_dplane_process_ipset_entry(struct zebra_dplane_provider *prov,
dplane_provider_enqueue_out_ctx(prov, ctx);
}
+void dplane_rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p,
+ struct prefix_ipv6 *src_p, struct route_entry *re,
+ struct nexthop_group *ng, int startup,
+ struct zebra_dplane_ctx *ctx)
+{
+ if (!ctx)
+ rib_add_multipath(afi, safi, p, src_p, re, ng, startup);
+ else {
+ dplane_ctx_route_init_basic(ctx, dplane_ctx_get_op(ctx), re, p,
+ src_p, afi, safi);
+ dplane_provider_enqueue_to_zebra(ctx);
+ }
+}
+
/*
* Kernel provider callback
*/
@@ -6463,7 +6492,7 @@ int dplane_clean_ctx_queue(bool (*context_cb)(struct zebra_dplane_ctx *ctx,
TAILQ_INIT(&work_list);
if (context_cb == NULL)
- goto done;
+ return AOK;
/* Walk the pending context queue under the dplane lock. */
DPLANE_LOCK();
@@ -6487,9 +6516,7 @@ int dplane_clean_ctx_queue(bool (*context_cb)(struct zebra_dplane_ctx *ctx,
dplane_ctx_fini(&ctx);
}
-done:
-
- return 0;
+ return AOK;
}
/* Indicates zebra shutdown/exit is in progress. Some operations may be
@@ -6553,10 +6580,8 @@ static bool dplane_work_pending(void)
}
DPLANE_UNLOCK();
- if (ctx != NULL) {
- ret = true;
- goto done;
- }
+ if (ctx != NULL)
+ return true;
while (prov) {
@@ -6579,7 +6604,6 @@ static bool dplane_work_pending(void)
if (ctx != NULL)
ret = true;
-done:
return ret;
}
diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h
index b9fd176de7..51f6f3d897 100644
--- a/zebra/zebra_dplane.h
+++ b/zebra/zebra_dplane.h
@@ -380,6 +380,8 @@ route_tag_t dplane_ctx_get_old_tag(const struct zebra_dplane_ctx *ctx);
uint16_t dplane_ctx_get_instance(const struct zebra_dplane_ctx *ctx);
void dplane_ctx_set_instance(struct zebra_dplane_ctx *ctx, uint16_t instance);
uint16_t dplane_ctx_get_old_instance(const struct zebra_dplane_ctx *ctx);
+uint32_t dplane_ctx_get_flags(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_flags(struct zebra_dplane_ctx *ctx, uint32_t flags);
uint32_t dplane_ctx_get_metric(const struct zebra_dplane_ctx *ctx);
uint32_t dplane_ctx_get_old_metric(const struct zebra_dplane_ctx *ctx);
uint32_t dplane_ctx_get_mtu(const struct zebra_dplane_ctx *ctx);
@@ -908,6 +910,12 @@ dplane_pbr_ipset_entry_delete(struct zebra_pbr_ipset_entry *ipset);
int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
struct route_node *rn, struct route_entry *re);
+int dplane_ctx_route_init_basic(struct zebra_dplane_ctx *ctx,
+ enum dplane_op_e op, struct route_entry *re,
+ const struct prefix *p,
+ const struct prefix_ipv6 *src_p, afi_t afi,
+ safi_t safi);
+
/* Encode next hop information into data plane context. */
int dplane_ctx_nexthop_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
struct nhg_hash_entry *nhe);
@@ -1073,6 +1081,16 @@ void zebra_dplane_pre_finish(void);
void zebra_dplane_finish(void);
void zebra_dplane_shutdown(void);
+/*
+ * decision point for sending a routing update through the old
+ * straight to zebra master pthread or through the dplane to
+ * the master pthread for handling
+ */
+void dplane_rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p,
+ struct prefix_ipv6 *src_p, struct route_entry *re,
+ struct nexthop_group *ng, int startup,
+ struct zebra_dplane_ctx *ctx);
+
#ifdef __cplusplus
}
#endif
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index 656588bb82..b86780276b 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -1523,8 +1523,7 @@ static bool rib_route_match_ctx(const struct route_entry *re,
}
done:
-
- return (result);
+ return result;
}
static void zebra_rib_fixup_system(struct route_node *rn)
@@ -2261,10 +2260,8 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx)
}
/* Ensure we clear the QUEUED flag */
- if (!zrouter.asic_offloaded) {
- UNSET_FLAG(re->status, ROUTE_ENTRY_QUEUED);
- UNSET_FLAG(re->status, ROUTE_ENTRY_ROUTE_REPLACING);
- }
+ UNSET_FLAG(re->status, ROUTE_ENTRY_QUEUED);
+ UNSET_FLAG(re->status, ROUTE_ENTRY_ROUTE_REPLACING);
/* Is this a notification that ... matters? We mostly care about
* the route that is currently selected for installation; we may also
@@ -2307,6 +2304,19 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx)
dplane_ctx_get_type(ctx)));
}
goto done;
+ } else {
+ uint32_t flags = dplane_ctx_get_flags(ctx);
+
+ if (CHECK_FLAG(flags, ZEBRA_FLAG_OFFLOADED)) {
+ UNSET_FLAG(re->flags, ZEBRA_FLAG_OFFLOAD_FAILED);
+ SET_FLAG(re->flags, ZEBRA_FLAG_OFFLOADED);
+ }
+ if (CHECK_FLAG(flags, ZEBRA_FLAG_OFFLOAD_FAILED)) {
+ UNSET_FLAG(re->flags, ZEBRA_FLAG_OFFLOADED);
+ SET_FLAG(re->flags, ZEBRA_FLAG_OFFLOAD_FAILED);
+ }
+ if (CHECK_FLAG(flags, ZEBRA_FLAG_TRAPPED))
+ SET_FLAG(re->flags, ZEBRA_FLAG_TRAPPED);
}
/* We'll want to determine whether the installation status of the
@@ -2340,55 +2350,70 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx)
/* Various fib transitions: changed nexthops; from installed to
* not-installed; or not-installed to installed.
*/
- if (start_count > 0 && end_count > 0) {
- if (debug_p)
- zlog_debug(
- "%s(%u:%u):%pRN applied nexthop changes from dplane notification",
- VRF_LOGNAME(vrf), dplane_ctx_get_vrf(ctx),
- dplane_ctx_get_table(ctx), rn);
+ if (zrouter.asic_notification_nexthop_control) {
+ if (start_count > 0 && end_count > 0) {
+ if (debug_p)
+ zlog_debug(
+ "%s(%u:%u):%pRN applied nexthop changes from dplane notification",
+ VRF_LOGNAME(vrf),
+ dplane_ctx_get_vrf(ctx),
+ dplane_ctx_get_table(ctx), rn);
- /* Changed nexthops - update kernel/others */
- dplane_route_notif_update(rn, re,
- DPLANE_OP_ROUTE_UPDATE, ctx);
+ /* Changed nexthops - update kernel/others */
+ dplane_route_notif_update(rn, re,
+ DPLANE_OP_ROUTE_UPDATE, ctx);
- } else if (start_count == 0 && end_count > 0) {
- if (debug_p)
- zlog_debug(
- "%s(%u:%u):%pRN installed transition from dplane notification",
- VRF_LOGNAME(vrf), dplane_ctx_get_vrf(ctx),
- dplane_ctx_get_table(ctx), rn);
+ } else if (start_count == 0 && end_count > 0) {
+ if (debug_p)
+ zlog_debug(
+ "%s(%u:%u):%pRN installed transition from dplane notification",
+ VRF_LOGNAME(vrf),
+ dplane_ctx_get_vrf(ctx),
+ dplane_ctx_get_table(ctx), rn);
- /* We expect this to be the selected route, so we want
- * to tell others about this transition.
- */
- SET_FLAG(re->status, ROUTE_ENTRY_INSTALLED);
+ /* We expect this to be the selected route, so we want
+ * to tell others about this transition.
+ */
+ SET_FLAG(re->status, ROUTE_ENTRY_INSTALLED);
- /* Changed nexthops - update kernel/others */
- dplane_route_notif_update(rn, re, DPLANE_OP_ROUTE_UPDATE, ctx);
+ /* Changed nexthops - update kernel/others */
+ dplane_route_notif_update(rn, re,
+ DPLANE_OP_ROUTE_UPDATE, ctx);
- /* Redistribute, lsp, and nht update */
- redistribute_update(rn, re, NULL);
+ /* Redistribute, lsp, and nht update */
+ redistribute_update(rn, re, NULL);
- } else if (start_count > 0 && end_count == 0) {
- if (debug_p)
- zlog_debug(
- "%s(%u:%u):%pRN un-installed transition from dplane notification",
- VRF_LOGNAME(vrf), dplane_ctx_get_vrf(ctx),
- dplane_ctx_get_table(ctx), rn);
+ } else if (start_count > 0 && end_count == 0) {
+ if (debug_p)
+ zlog_debug(
+ "%s(%u:%u):%pRN un-installed transition from dplane notification",
+ VRF_LOGNAME(vrf),
+ dplane_ctx_get_vrf(ctx),
+ dplane_ctx_get_table(ctx), rn);
- /* Transition from _something_ installed to _nothing_
- * installed.
- */
- /* We expect this to be the selected route, so we want
- * to tell others about this transistion.
- */
- UNSET_FLAG(re->status, ROUTE_ENTRY_INSTALLED);
+ /* Transition from _something_ installed to _nothing_
+ * installed.
+ */
+ /* We expect this to be the selected route, so we want
+ * to tell others about this transistion.
+ */
+ UNSET_FLAG(re->status, ROUTE_ENTRY_INSTALLED);
- /* Changed nexthops - update kernel/others */
- dplane_route_notif_update(rn, re, DPLANE_OP_ROUTE_DELETE, ctx);
+ /* Changed nexthops - update kernel/others */
+ dplane_route_notif_update(rn, re,
+ DPLANE_OP_ROUTE_DELETE, ctx);
- /* Redistribute, lsp, and nht update */
- redistribute_delete(rn, re, NULL);
+ /* Redistribute, lsp, and nht update */
+ redistribute_delete(rn, re, NULL);
+ }
+ }
+
+ if (!zebra_router_notify_on_ack()) {
+ if (CHECK_FLAG(re->flags, ZEBRA_FLAG_OFFLOADED))
+ zsend_route_notify_owner_ctx(ctx, ZAPI_ROUTE_INSTALLED);
+ if (CHECK_FLAG(re->flags, ZEBRA_FLAG_OFFLOAD_FAILED))
+ zsend_route_notify_owner_ctx(ctx,
+ ZAPI_ROUTE_FAIL_INSTALL);
}
/* Make any changes visible for lsp and nexthop-tracking processing */
diff --git a/zebra/zebra_router.c b/zebra/zebra_router.c
index b8923ef57d..a9a7b66ce7 100644
--- a/zebra/zebra_router.c
+++ b/zebra/zebra_router.c
@@ -330,6 +330,17 @@ void zebra_router_init(bool asic_offload, bool notify_on_ack)
zrouter.asic_offloaded = asic_offload;
zrouter.notify_on_ack = notify_on_ack;
+ /*
+ * If you start using asic_notification_nexthop_control
+ * come talk to the FRR community about what you are doing
+ * We would like to know.
+ */
+#if CONFDATE > 20251231
+ CPP_NOTICE(
+ "Remove zrouter.asic_notification_nexthop_control as that it's not being maintained or used");
+#endif
+ zrouter.asic_notification_nexthop_control = false;
+
#ifdef HAVE_SCRIPTING
zebra_script_init();
#endif
diff --git a/zebra/zebra_router.h b/zebra/zebra_router.h
index 069437ef47..e0ef86f082 100644
--- a/zebra/zebra_router.h
+++ b/zebra/zebra_router.h
@@ -224,6 +224,14 @@ struct zebra_router {
bool asic_offloaded;
bool notify_on_ack;
+ /*
+ * If the asic is notifying us about successful nexthop
+ * allocation/control. Some developers have made their
+ * asic take control of how many nexthops/ecmp they can
+ * have and will report what is successfull or not
+ */
+ bool asic_notification_nexthop_control;
+
bool supports_nhgs;
bool all_mc_forwardingv4, default_mc_forwardingv4;
diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c
index 91a0c1dd31..8ed8abe304 100644
--- a/zebra/zebra_vty.c
+++ b/zebra/zebra_vty.c
@@ -630,8 +630,7 @@ static void show_route_nexthop_helper(struct vty *vty,
case NEXTHOP_TYPE_IFINDEX:
vty_out(vty, " is directly connected, %s",
- ifindex2ifname(nexthop->ifindex,
- nexthop->vrf_id));
+ ifindex2ifname(nexthop->ifindex, nexthop->vrf_id));
break;
case NEXTHOP_TYPE_BLACKHOLE:
vty_out(vty, " unreachable");
@@ -703,8 +702,10 @@ static void show_route_nexthop_helper(struct vty *vty,
seg6local_context2str(buf, sizeof(buf),
&nexthop->nh_srv6->seg6local_ctx,
nexthop->nh_srv6->seg6local_action);
- vty_out(vty, ", seg6local %s %s", seg6local_action2str(
- nexthop->nh_srv6->seg6local_action), buf);
+ vty_out(vty, ", seg6local %s %s",
+ seg6local_action2str(
+ nexthop->nh_srv6->seg6local_action),
+ buf);
inet_ntop(AF_INET6, &nexthop->nh_srv6->seg6_segs, buf,
sizeof(buf));
@@ -722,6 +723,7 @@ static void show_route_nexthop_helper(struct vty *vty,
}
}
+
/*
* Render a nexthop into a json object; the caller allocates and owns
* the json object memory.
@@ -806,9 +808,8 @@ static void show_nexthop_json_helper(json_object *json_nexthop,
json_nexthop, "reject");
break;
case BLACKHOLE_ADMINPROHIB:
- json_object_boolean_true_add(
- json_nexthop,
- "admin-prohibited");
+ json_object_boolean_true_add(json_nexthop,
+ "adminProhibited");
break;
case BLACKHOLE_NULL:
json_object_boolean_true_add(
@@ -827,7 +828,7 @@ static void show_nexthop_json_helper(json_object *json_nexthop,
if (nexthop->rparent)
json_object_boolean_true_add(json_nexthop, "resolver");
- if (nexthop->vrf_id != re->vrf_id)
+ if ((re == NULL || (nexthop->vrf_id != re->vrf_id)))
json_object_string_add(json_nexthop, "vrf",
vrf_id_to_name(nexthop->vrf_id));
@@ -840,8 +841,7 @@ static void show_nexthop_json_helper(json_object *json_nexthop,
"active");
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK))
- json_object_boolean_true_add(json_nexthop,
- "onLink");
+ json_object_boolean_true_add(json_nexthop, "onLink");
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_LINKDOWN))
json_object_boolean_true_add(json_nexthop, "linkDown");
@@ -1479,125 +1479,264 @@ DEFUN (ip_nht_default_route,
return CMD_SUCCESS;
}
-static void show_nexthop_group_out(struct vty *vty, struct nhg_hash_entry *nhe)
+static void show_nexthop_group_out(struct vty *vty, struct nhg_hash_entry *nhe,
+ json_object *json_nhe_hdr)
{
struct nexthop *nexthop = NULL;
struct nhg_connected *rb_node_dep = NULL;
struct nexthop_group *backup_nhg;
char up_str[MONOTIME_STRLEN];
char time_left[MONOTIME_STRLEN];
+ json_object *json_dependants = NULL;
+ json_object *json_depends = NULL;
+ json_object *json_nexthop_array = NULL;
+ json_object *json_nexthops = NULL;
+ json_object *json = NULL;
+ json_object *json_backup_nexthop_array = NULL;
+ json_object *json_backup_nexthops = NULL;
+
uptime2str(nhe->uptime, up_str, sizeof(up_str));
- vty_out(vty, "ID: %u (%s)\n", nhe->id, zebra_route_string(nhe->type));
- vty_out(vty, " RefCnt: %u", nhe->refcnt);
- if (thread_is_scheduled(nhe->timer))
- vty_out(vty, " Time to Deletion: %s",
- thread_timer_to_hhmmss(time_left, sizeof(time_left),
- nhe->timer));
- vty_out(vty, "\n");
+ if (json_nhe_hdr)
+ json = json_object_new_object();
+
+ if (json) {
+ json_object_string_add(json, "type",
+ zebra_route_string(nhe->type));
+ json_object_int_add(json, "refCount", nhe->refcnt);
+ if (thread_is_scheduled(nhe->timer))
+ json_object_string_add(
+ json, "timeToDeletion",
+ thread_timer_to_hhmmss(time_left,
+ sizeof(time_left),
+ nhe->timer));
+ json_object_string_add(json, "uptime", up_str);
+ json_object_string_add(json, "vrf",
+ vrf_id_to_name(nhe->vrf_id));
- vty_out(vty, " Uptime: %s\n", up_str);
- vty_out(vty, " VRF: %s\n", vrf_id_to_name(nhe->vrf_id));
+ } else {
+ vty_out(vty, "ID: %u (%s)\n", nhe->id,
+ zebra_route_string(nhe->type));
+ vty_out(vty, " RefCnt: %u", nhe->refcnt);
+ if (thread_is_scheduled(nhe->timer))
+ vty_out(vty, " Time to Deletion: %s",
+ thread_timer_to_hhmmss(time_left,
+ sizeof(time_left),
+ nhe->timer));
+ vty_out(vty, "\n");
+ vty_out(vty, " Uptime: %s\n", up_str);
+ vty_out(vty, " VRF: %s\n", vrf_id_to_name(nhe->vrf_id));
+ }
if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_VALID)) {
- vty_out(vty, " Valid");
- if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED))
- vty_out(vty, ", Installed");
- vty_out(vty, "\n");
+ if (json)
+ json_object_boolean_true_add(json, "valid");
+ else
+ vty_out(vty, " Valid");
+
+ if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED)) {
+ if (json)
+ json_object_boolean_true_add(json, "installed");
+ else
+ vty_out(vty, ", Installed");
+ }
+ if (!json)
+ vty_out(vty, "\n");
+ }
+ if (nhe->ifp) {
+ if (json)
+ json_object_int_add(json, "interfaceIndex",
+ nhe->ifp->ifindex);
+ else
+ vty_out(vty, " Interface Index: %d\n",
+ nhe->ifp->ifindex);
}
- if (nhe->ifp)
- vty_out(vty, " Interface Index: %d\n", nhe->ifp->ifindex);
if (!zebra_nhg_depends_is_empty(nhe)) {
- vty_out(vty, " Depends:");
+ if (json)
+ json_depends = json_object_new_array();
+ else
+ vty_out(vty, " Depends:");
frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) {
- vty_out(vty, " (%u)", rb_node_dep->nhe->id);
+ if (json_depends)
+ json_object_array_add(
+ json_depends,
+ json_object_new_int(
+ rb_node_dep->nhe->id));
+ else
+ vty_out(vty, " (%u)", rb_node_dep->nhe->id);
}
- vty_out(vty, "\n");
+ if (!json_depends)
+ vty_out(vty, "\n");
+ else
+ json_object_object_add(json, "depends", json_depends);
}
/* Output nexthops */
- for (ALL_NEXTHOPS(nhe->nhg, nexthop)) {
- if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
- vty_out(vty, " ");
- else
- /* Make recursive nexthops a bit more clear */
- vty_out(vty, " ");
+ if (json)
+ json_nexthop_array = json_object_new_array();
- show_route_nexthop_helper(vty, NULL, nexthop);
+
+ for (ALL_NEXTHOPS(nhe->nhg, nexthop)) {
+ if (json_nexthop_array) {
+ json_nexthops = json_object_new_object();
+ show_nexthop_json_helper(json_nexthops, nexthop, NULL);
+ } else {
+ if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+ vty_out(vty, " ");
+ else
+ /* Make recursive nexthops a bit more clear */
+ vty_out(vty, " ");
+ show_route_nexthop_helper(vty, NULL, nexthop);
+ }
if (nhe->backup_info == NULL || nhe->backup_info->nhe == NULL) {
if (CHECK_FLAG(nexthop->flags,
- NEXTHOP_FLAG_HAS_BACKUP))
- vty_out(vty, " [backup %d]",
- nexthop->backup_idx[0]);
+ NEXTHOP_FLAG_HAS_BACKUP)) {
+ if (json)
+ json_object_int_add(
+ json_nexthops, "backup",
+ nexthop->backup_idx[0]);
+ else
+ vty_out(vty, " [backup %d]",
+ nexthop->backup_idx[0]);
+ }
+
+ if (!json)
+ vty_out(vty, "\n");
+ else
+ json_object_array_add(json_nexthop_array,
+ json_nexthops);
- vty_out(vty, "\n");
continue;
}
- /* TODO -- print more useful backup info */
- if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
- int i;
-
- vty_out(vty, "[backup");
- for (i = 0; i < nexthop->backup_num; i++)
- vty_out(vty, " %d", nexthop->backup_idx[i]);
-
- vty_out(vty, "]");
+ if (!json) {
+ /* TODO -- print more useful backup info */
+ if (CHECK_FLAG(nexthop->flags,
+ NEXTHOP_FLAG_HAS_BACKUP)) {
+ int i;
+
+ vty_out(vty, "[backup");
+ for (i = 0; i < nexthop->backup_num; i++)
+ vty_out(vty, " %d",
+ nexthop->backup_idx[i]);
+ vty_out(vty, "]");
+ }
+ vty_out(vty, "\n");
+ } else {
+ json_object_array_add(json_nexthop_array,
+ json_nexthops);
}
-
- vty_out(vty, "\n");
}
+ if (json)
+ json_object_object_add(json, "nexthops", json_nexthop_array);
+
/* Output backup nexthops (if any) */
backup_nhg = zebra_nhg_get_backup_nhg(nhe);
if (backup_nhg) {
- vty_out(vty, " Backups:\n");
+ if (json)
+ json_backup_nexthop_array = json_object_new_array();
+ else
+ vty_out(vty, " Backups:\n");
for (ALL_NEXTHOPS_PTR(backup_nhg, nexthop)) {
- if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
- vty_out(vty, " ");
- else
- /* Make recursive nexthops a bit more clear */
- vty_out(vty, " ");
+ if (json_backup_nexthop_array) {
+ json_backup_nexthops = json_object_new_object();
+ show_nexthop_json_helper(json_backup_nexthops,
+ nexthop, NULL);
+ json_object_array_add(json_backup_nexthop_array,
+ json_backup_nexthops);
+ } else {
- show_route_nexthop_helper(vty, NULL, nexthop);
- vty_out(vty, "\n");
+ if (!CHECK_FLAG(nexthop->flags,
+ NEXTHOP_FLAG_RECURSIVE))
+ vty_out(vty, " ");
+ else
+ /* Make recursive nexthops a bit more
+ * clear
+ */
+ vty_out(vty, " ");
+ show_route_nexthop_helper(vty, NULL, nexthop);
+ vty_out(vty, "\n");
+ }
}
+
+ if (json)
+ json_object_object_add(json, "backupNexthops",
+ json_backup_nexthop_array);
}
if (!zebra_nhg_dependents_is_empty(nhe)) {
- vty_out(vty, " Dependents:");
+ if (json)
+ json_dependants = json_object_new_array();
+ else
+ vty_out(vty, " Dependents:");
frr_each(nhg_connected_tree, &nhe->nhg_dependents,
rb_node_dep) {
- vty_out(vty, " (%u)", rb_node_dep->nhe->id);
+ if (json)
+ json_object_array_add(
+ json_dependants,
+ json_object_new_int(
+ rb_node_dep->nhe->id));
+ else
+ vty_out(vty, " (%u)", rb_node_dep->nhe->id);
}
- vty_out(vty, "\n");
+ if (json)
+ json_object_object_add(json, "dependents",
+ json_dependants);
+ else
+ vty_out(vty, "\n");
}
- if (nhe->nhg.nhgr.buckets)
- vty_out(vty,
- " Buckets: %u Idle Timer: %u Unbalanced Timer: %u Unbalanced time: %" PRIu64 "\n",
- nhe->nhg.nhgr.buckets, nhe->nhg.nhgr.idle_timer,
- nhe->nhg.nhgr.unbalanced_timer,
- nhe->nhg.nhgr.unbalanced_time);
+ if (nhe->nhg.nhgr.buckets) {
+ if (json) {
+ json_object_int_add(json, "buckets",
+ nhe->nhg.nhgr.buckets);
+ json_object_int_add(json, "idleTimer",
+ nhe->nhg.nhgr.idle_timer);
+ json_object_int_add(json, "unbalancedTimer",
+ nhe->nhg.nhgr.unbalanced_timer);
+ json_object_int_add(json, "unbalancedTime",
+ nhe->nhg.nhgr.unbalanced_time);
+ } else {
+ vty_out(vty,
+ " Buckets: %u Idle Timer: %u Unbalanced Timer: %u Unbalanced time: %" PRIu64
+ "\n",
+ nhe->nhg.nhgr.buckets, nhe->nhg.nhgr.idle_timer,
+ nhe->nhg.nhgr.unbalanced_timer,
+ nhe->nhg.nhgr.unbalanced_time);
+ }
+ }
+
+ if (json_nhe_hdr)
+ json_object_object_addf(json_nhe_hdr, json, "%u", nhe->id);
}
-static int show_nexthop_group_id_cmd_helper(struct vty *vty, uint32_t id)
+static int show_nexthop_group_id_cmd_helper(struct vty *vty, uint32_t id,
+ json_object *json)
{
struct nhg_hash_entry *nhe = NULL;
nhe = zebra_nhg_lookup_id(id);
if (nhe)
- show_nexthop_group_out(vty, nhe);
+ show_nexthop_group_out(vty, nhe, json);
else {
- vty_out(vty, "Nexthop Group ID: %u does not exist\n", id);
+ if (json)
+ vty_json(vty, json);
+ else
+ vty_out(vty, "Nexthop Group ID: %u does not exist\n",
+ id);
return CMD_WARNING;
}
+
+ if (json)
+ vty_json(vty, json);
+
return CMD_SUCCESS;
}
@@ -1608,6 +1747,7 @@ struct nhe_show_context {
vrf_id_t vrf_id;
afi_t afi;
int type;
+ json_object *json;
};
static int nhe_show_walker(struct hash_bucket *bucket, void *arg)
@@ -1626,7 +1766,7 @@ static int nhe_show_walker(struct hash_bucket *bucket, void *arg)
if (ctx->type && nhe->type != ctx->type)
goto done;
- show_nexthop_group_out(ctx->vty, nhe);
+ show_nexthop_group_out(ctx->vty, nhe, ctx->json);
done:
return HASHWALK_CONTINUE;
@@ -1634,7 +1774,7 @@ done:
static void show_nexthop_group_cmd_helper(struct vty *vty,
struct zebra_vrf *zvrf, afi_t afi,
- int type)
+ int type, json_object *json)
{
struct nhe_show_context ctx;
@@ -1642,6 +1782,7 @@ static void show_nexthop_group_cmd_helper(struct vty *vty,
ctx.afi = afi;
ctx.vrf_id = zvrf->vrf->vrf_id;
ctx.type = type;
+ ctx.json = json;
hash_walk(zrouter.nhgs_id, nhe_show_walker, &ctx);
}
@@ -1659,7 +1800,7 @@ static void if_nexthop_group_dump_vty(struct vty *vty, struct interface *ifp)
frr_each(nhg_connected_tree, &zebra_if->nhg_dependents,
rb_node_dep) {
vty_out(vty, " ");
- show_nexthop_group_out(vty, rb_node_dep->nhe);
+ show_nexthop_group_out(vty, rb_node_dep->nhe, NULL);
}
}
}
@@ -1698,29 +1839,36 @@ DEFPY (show_interface_nexthop_group,
return CMD_SUCCESS;
}
-DEFPY (show_nexthop_group,
- show_nexthop_group_cmd,
- "show nexthop-group rib <(0-4294967295)$id|[singleton <ip$v4|ipv6$v6>] [<kernel|zebra|bgp|sharp>$type_str] [vrf <NAME$vrf_name|all$vrf_all>]>",
- SHOW_STR
- "Show Nexthop Groups\n"
- "RIB information\n"
- "Nexthop Group ID\n"
- "Show Singleton Nexthop-Groups\n"
- IP_STR
- IP6_STR
- "Kernel (not installed via the zebra RIB)\n"
- "Zebra (implicitly created by zebra)\n"
- "Border Gateway Protocol (BGP)\n"
- "Super Happy Advanced Routing Protocol (SHARP)\n"
- VRF_FULL_CMD_HELP_STR)
+DEFPY(show_nexthop_group,
+ show_nexthop_group_cmd,
+ "show nexthop-group rib <(0-4294967295)$id|[singleton <ip$v4|ipv6$v6>] [<kernel|zebra|bgp|sharp>$type_str] [vrf <NAME$vrf_name|all$vrf_all>]> [json]",
+ SHOW_STR
+ "Show Nexthop Groups\n"
+ "RIB information\n"
+ "Nexthop Group ID\n"
+ "Show Singleton Nexthop-Groups\n"
+ IP_STR
+ IP6_STR
+ "Kernel (not installed via the zebra RIB)\n"
+ "Zebra (implicitly created by zebra)\n"
+ "Border Gateway Protocol (BGP)\n"
+ "Super Happy Advanced Routing Protocol (SHARP)\n"
+ VRF_FULL_CMD_HELP_STR
+ JSON_STR)
{
struct zebra_vrf *zvrf = NULL;
afi_t afi = AFI_UNSPEC;
int type = 0;
+ bool uj = use_json(argc, argv);
+ json_object *json = NULL;
+ json_object *json_vrf = NULL;
+
+ if (uj)
+ json = json_object_new_object();
if (id)
- return show_nexthop_group_id_cmd_helper(vty, id);
+ return show_nexthop_group_id_cmd_helper(vty, id, json);
if (v4)
afi = AFI_IP;
@@ -1736,8 +1884,11 @@ DEFPY (show_nexthop_group,
}
if (!vrf_is_backend_netns() && (vrf_name || vrf_all)) {
- vty_out(vty,
- "VRF subcommand does not make any sense in l3mdev based vrf's\n");
+ if (uj)
+ vty_json(vty, json);
+ else
+ vty_out(vty,
+ "VRF subcommand does not make any sense in l3mdev based vrf's\n");
return CMD_WARNING;
}
@@ -1750,11 +1901,21 @@ DEFPY (show_nexthop_group,
zvrf = vrf->info;
if (!zvrf)
continue;
+ if (uj)
+ json_vrf = json_object_new_object();
+ else
+ vty_out(vty, "VRF: %s\n", vrf->name);
- vty_out(vty, "VRF: %s\n", vrf->name);
- show_nexthop_group_cmd_helper(vty, zvrf, afi, type);
+ show_nexthop_group_cmd_helper(vty, zvrf, afi, type,
+ json_vrf);
+ if (uj)
+ json_object_object_add(json, vrf->name,
+ json_vrf);
}
+ if (uj)
+ vty_json(vty, json);
+
return CMD_SUCCESS;
}
@@ -1764,12 +1925,18 @@ DEFPY (show_nexthop_group,
zvrf = zebra_vrf_lookup_by_name(VRF_DEFAULT_NAME);
if (!zvrf) {
- vty_out(vty, "%% VRF '%s' specified does not exist\n",
- vrf_name);
+ if (uj)
+ vty_json(vty, json);
+ else
+ vty_out(vty, "%% VRF '%s' specified does not exist\n",
+ vrf_name);
return CMD_WARNING;
}
- show_nexthop_group_cmd_helper(vty, zvrf, afi, type);
+ show_nexthop_group_cmd_helper(vty, zvrf, afi, type, json);
+
+ if (uj)
+ vty_json(vty, json);
return CMD_SUCCESS;
}
@@ -4073,6 +4240,15 @@ DEFUN (show_zebra,
ttable_add_row(table, "ASIC offload|%s",
zrouter.asic_offloaded ? "Used" : "Unavailable");
+ /*
+ * Do not display this unless someone is actually using it
+ *
+ * Why this distinction? I think this is effectively dead code
+ * and should not be exposed. Maybe someone proves me wrong.
+ */
+ if (zrouter.asic_notification_nexthop_control)
+ ttable_add_row(table, "ASIC offload and nexthop control|Used");
+
ttable_add_row(table, "RA|%s",
rtadv_compiled_in() ? "Compiled in" : "Not Compiled in");
ttable_add_row(table, "RFC 5549|%s",