summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/developer/zebra.rst70
-rw-r--r--tests/topotests/zebra_netlink/__init__.py0
-rw-r--r--tests/topotests/zebra_netlink/r1/sharpd.conf0
-rw-r--r--tests/topotests/zebra_netlink/r1/v4_route.json2802
-rw-r--r--tests/topotests/zebra_netlink/r1/zebra.conf2
-rwxr-xr-xtests/topotests/zebra_netlink/test_zebra_netlink.py131
-rw-r--r--zebra/if_netlink.c55
-rw-r--r--zebra/if_netlink.h4
-rw-r--r--zebra/kernel_netlink.c405
-rw-r--r--zebra/kernel_netlink.h46
-rw-r--r--zebra/kernel_socket.c94
-rw-r--r--zebra/rt.h12
-rw-r--r--zebra/rt_netlink.c226
-rw-r--r--zebra/rt_netlink.h19
-rw-r--r--zebra/rt_socket.c14
-rw-r--r--zebra/rule_netlink.c85
-rw-r--r--zebra/rule_netlink.h3
-rw-r--r--zebra/zebra_dplane.c291
-rw-r--r--zebra/zebra_dplane.h1
-rw-r--r--zebra/zebra_mpls.c11
-rw-r--r--zebra/zebra_mpls_netlink.c34
-rw-r--r--zebra/zebra_pbr.h7
-rw-r--r--zebra/zebra_rib.c6
-rw-r--r--zebra/zebra_vty.c49
24 files changed, 3896 insertions, 471 deletions
diff --git a/doc/developer/zebra.rst b/doc/developer/zebra.rst
index 6a73803d01..edd8b9ae29 100644
--- a/doc/developer/zebra.rst
+++ b/doc/developer/zebra.rst
@@ -382,3 +382,73 @@ Zebra Protocol Commands
+------------------------------------+-------+
| ZEBRA_OPAQUE_UNREGISTER | 109 |
+------------------------------------+-------+
+
+Dataplane batching
+=========================
+
+Dataplane batching is an optimization feature that reduces the processing
+time involved in the user space to kernel space transition for every message we
+want to send.
+
+Design
+-----------
+
+With our dataplane abstraction, we create a queue of dataplane context objects
+for the messages we want to send to the kernel. In a separate pthread, we
+loop over this queue and send the context objects to the appropriate
+dataplane. A batching enhancement tightly integrates with the dataplane
+context objects so they are able to be batch sent to dataplanes that support
+it.
+
+There is one main change in the dataplane code. It does not call
+kernel-dependent functions one-by-one, but instead it hands a list of work down
+to the kernel level for processing.
+
+Netlink
+^^^^^^^
+
+At the moment, this is the only dataplane that allows for batch sending
+messages to it.
+
+When messages must be sent to the kernel, they are consecutively added
+to the batch represented by the `struct nl_batch`. Context objects are firstly
+encoded to their binary representation. All the encoding functions use the same
+interface: take a context object, a buffer and a size of the buffer as an
+argument. It is important that they should handle a situation in which a message
+wouldn't fit in the buffer and return a proper error. To achieve a zero-copy
+(in the user space only) messages are encoded to the same buffer which will
+be passed to the kernel. Hence, we can theoretically hit the boundary of the
+buffer.
+
+Messages stored in the batch are sent if one of the conditions occurs:
+
+- When an encoding function returns the buffer overflow error. The context
+ object that caused this error is re-added to the new, empty batch.
+
+- When the size of the batch hits certain limit.
+
+- When the namespace of a currently being processed context object is
+ different from all the previous ones. They have to be sent through
+ distinct sockets, so the messages cannot share the same buffer.
+
+- After the last message from the list is processed.
+
+As mentioned earlier, there is a special threshold which is smaller than
+the size of the underlying buffer. It prevents the overflow error and thus
+eliminates the case, in which a message is encoded twice.
+
+The buffer used in the batching is global, since allocating that big amount of
+memory every time wouldn't be most effective. However, its size can be changed
+dynamically, using hidden vtysh command:
+``zebra kernel netlink batch-tx-buf (1-1048576) (1-1048576)``. This feature is
+only used in tests and shouldn't be utilized in any other place.
+
+For every failed message in the batch, the kernel responds with an error
+message. Error messages are kept in the same order as they were sent, so parsing the
+response is straightforward. We use the two pointer technique to match
+requests with responses and then set appropriate status of dataplane context
+objects. There is also a global receive buffer and it is assumed that whatever
+the kernel sends it will fit in this buffer. The payload of netlink error messages
+consists of a error code and the original netlink message of the request, so
+the batch response won't be bigger than the batch request increased by
+some space for the headers.
diff --git a/tests/topotests/zebra_netlink/__init__.py b/tests/topotests/zebra_netlink/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/zebra_netlink/__init__.py
diff --git a/tests/topotests/zebra_netlink/r1/sharpd.conf b/tests/topotests/zebra_netlink/r1/sharpd.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/zebra_netlink/r1/sharpd.conf
diff --git a/tests/topotests/zebra_netlink/r1/v4_route.json b/tests/topotests/zebra_netlink/r1/v4_route.json
new file mode 100644
index 0000000000..61e9bb240b
--- /dev/null
+++ b/tests/topotests/zebra_netlink/r1/v4_route.json
@@ -0,0 +1,2802 @@
+{
+ "2.1.3.7\/32":[
+ {
+ "prefix":"2.1.3.7\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.8\/32":[
+ {
+ "prefix":"2.1.3.8\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.9\/32":[
+ {
+ "prefix":"2.1.3.9\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.10\/32":[
+ {
+ "prefix":"2.1.3.10\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.11\/32":[
+ {
+ "prefix":"2.1.3.11\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.12\/32":[
+ {
+ "prefix":"2.1.3.12\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.13\/32":[
+ {
+ "prefix":"2.1.3.13\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.14\/32":[
+ {
+ "prefix":"2.1.3.14\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.15\/32":[
+ {
+ "prefix":"2.1.3.15\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.16\/32":[
+ {
+ "prefix":"2.1.3.16\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.17\/32":[
+ {
+ "prefix":"2.1.3.17\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.18\/32":[
+ {
+ "prefix":"2.1.3.18\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.19\/32":[
+ {
+ "prefix":"2.1.3.19\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.20\/32":[
+ {
+ "prefix":"2.1.3.20\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.21\/32":[
+ {
+ "prefix":"2.1.3.21\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.22\/32":[
+ {
+ "prefix":"2.1.3.22\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.23\/32":[
+ {
+ "prefix":"2.1.3.23\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.24\/32":[
+ {
+ "prefix":"2.1.3.24\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.25\/32":[
+ {
+ "prefix":"2.1.3.25\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.26\/32":[
+ {
+ "prefix":"2.1.3.26\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.27\/32":[
+ {
+ "prefix":"2.1.3.27\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.28\/32":[
+ {
+ "prefix":"2.1.3.28\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.29\/32":[
+ {
+ "prefix":"2.1.3.29\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.30\/32":[
+ {
+ "prefix":"2.1.3.30\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.31\/32":[
+ {
+ "prefix":"2.1.3.31\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.32\/32":[
+ {
+ "prefix":"2.1.3.32\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.33\/32":[
+ {
+ "prefix":"2.1.3.33\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.34\/32":[
+ {
+ "prefix":"2.1.3.34\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.35\/32":[
+ {
+ "prefix":"2.1.3.35\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.36\/32":[
+ {
+ "prefix":"2.1.3.36\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.37\/32":[
+ {
+ "prefix":"2.1.3.37\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.38\/32":[
+ {
+ "prefix":"2.1.3.38\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.39\/32":[
+ {
+ "prefix":"2.1.3.39\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.40\/32":[
+ {
+ "prefix":"2.1.3.40\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.41\/32":[
+ {
+ "prefix":"2.1.3.41\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.42\/32":[
+ {
+ "prefix":"2.1.3.42\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.43\/32":[
+ {
+ "prefix":"2.1.3.43\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.44\/32":[
+ {
+ "prefix":"2.1.3.44\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.45\/32":[
+ {
+ "prefix":"2.1.3.45\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.46\/32":[
+ {
+ "prefix":"2.1.3.46\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.47\/32":[
+ {
+ "prefix":"2.1.3.47\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.48\/32":[
+ {
+ "prefix":"2.1.3.48\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.49\/32":[
+ {
+ "prefix":"2.1.3.49\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.50\/32":[
+ {
+ "prefix":"2.1.3.50\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.51\/32":[
+ {
+ "prefix":"2.1.3.51\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.52\/32":[
+ {
+ "prefix":"2.1.3.52\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.53\/32":[
+ {
+ "prefix":"2.1.3.53\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.54\/32":[
+ {
+ "prefix":"2.1.3.54\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.55\/32":[
+ {
+ "prefix":"2.1.3.55\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.56\/32":[
+ {
+ "prefix":"2.1.3.56\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.57\/32":[
+ {
+ "prefix":"2.1.3.57\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.58\/32":[
+ {
+ "prefix":"2.1.3.58\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.59\/32":[
+ {
+ "prefix":"2.1.3.59\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.60\/32":[
+ {
+ "prefix":"2.1.3.60\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.61\/32":[
+ {
+ "prefix":"2.1.3.61\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.62\/32":[
+ {
+ "prefix":"2.1.3.62\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.63\/32":[
+ {
+ "prefix":"2.1.3.63\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.64\/32":[
+ {
+ "prefix":"2.1.3.64\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.65\/32":[
+ {
+ "prefix":"2.1.3.65\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.66\/32":[
+ {
+ "prefix":"2.1.3.66\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.67\/32":[
+ {
+ "prefix":"2.1.3.67\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.68\/32":[
+ {
+ "prefix":"2.1.3.68\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.69\/32":[
+ {
+ "prefix":"2.1.3.69\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.70\/32":[
+ {
+ "prefix":"2.1.3.70\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.71\/32":[
+ {
+ "prefix":"2.1.3.71\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.72\/32":[
+ {
+ "prefix":"2.1.3.72\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.73\/32":[
+ {
+ "prefix":"2.1.3.73\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.74\/32":[
+ {
+ "prefix":"2.1.3.74\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.75\/32":[
+ {
+ "prefix":"2.1.3.75\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.76\/32":[
+ {
+ "prefix":"2.1.3.76\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.77\/32":[
+ {
+ "prefix":"2.1.3.77\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.78\/32":[
+ {
+ "prefix":"2.1.3.78\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.79\/32":[
+ {
+ "prefix":"2.1.3.79\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.80\/32":[
+ {
+ "prefix":"2.1.3.80\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.81\/32":[
+ {
+ "prefix":"2.1.3.81\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.82\/32":[
+ {
+ "prefix":"2.1.3.82\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.83\/32":[
+ {
+ "prefix":"2.1.3.83\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.84\/32":[
+ {
+ "prefix":"2.1.3.84\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.85\/32":[
+ {
+ "prefix":"2.1.3.85\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.86\/32":[
+ {
+ "prefix":"2.1.3.86\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.87\/32":[
+ {
+ "prefix":"2.1.3.87\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.88\/32":[
+ {
+ "prefix":"2.1.3.88\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.89\/32":[
+ {
+ "prefix":"2.1.3.89\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.90\/32":[
+ {
+ "prefix":"2.1.3.90\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.91\/32":[
+ {
+ "prefix":"2.1.3.91\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.92\/32":[
+ {
+ "prefix":"2.1.3.92\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.93\/32":[
+ {
+ "prefix":"2.1.3.93\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.94\/32":[
+ {
+ "prefix":"2.1.3.94\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.95\/32":[
+ {
+ "prefix":"2.1.3.95\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.96\/32":[
+ {
+ "prefix":"2.1.3.96\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.97\/32":[
+ {
+ "prefix":"2.1.3.97\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.98\/32":[
+ {
+ "prefix":"2.1.3.98\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.99\/32":[
+ {
+ "prefix":"2.1.3.99\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.100\/32":[
+ {
+ "prefix":"2.1.3.100\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.101\/32":[
+ {
+ "prefix":"2.1.3.101\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.102\/32":[
+ {
+ "prefix":"2.1.3.102\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.103\/32":[
+ {
+ "prefix":"2.1.3.103\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.104\/32":[
+ {
+ "prefix":"2.1.3.104\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.105\/32":[
+ {
+ "prefix":"2.1.3.105\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.106\/32":[
+ {
+ "prefix":"2.1.3.106\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/zebra_netlink/r1/zebra.conf b/tests/topotests/zebra_netlink/r1/zebra.conf
new file mode 100644
index 0000000000..786be19ad4
--- /dev/null
+++ b/tests/topotests/zebra_netlink/r1/zebra.conf
@@ -0,0 +1,2 @@
+int r1-eth0
+ ip address 192.168.1.1/24 \ No newline at end of file
diff --git a/tests/topotests/zebra_netlink/test_zebra_netlink.py b/tests/topotests/zebra_netlink/test_zebra_netlink.py
new file mode 100755
index 0000000000..7b692c75ab
--- /dev/null
+++ b/tests/topotests/zebra_netlink/test_zebra_netlink.py
@@ -0,0 +1,131 @@
+#!/usr/bin/env python
+
+#
+# test_zebra_netlink.py
+#
+# Copyright (c) 2020 by
+#
+# 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.
+#
+
+"""
+test_zebra_netlink.py: Test some basic interactions with kernel using Netlink
+
+"""
+
+import os
+import re
+import sys
+import pytest
+import json
+import platform
+from functools import partial
+
+# Save the Current Working Directory to find configuration files.
+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 shutdown_bringup_interface
+
+# Required to instantiate the topology builder class.
+from mininet.topo import Topo
+
+#####################################################
+##
+## Network Topology Definition
+##
+#####################################################
+
+
+class ZebraTopo(Topo):
+ "Test topology builder"
+
+ def build(self, *_args, **_opts):
+ "Build function"
+ tgen = get_topogen(self)
+
+ tgen.add_router("r1")
+
+ # Create a empty network for router 1
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+
+
+#####################################################
+##
+## Tests starting
+##
+#####################################################
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(ZebraTopo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ for rname, router in router_list.iteritems():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+
+ router.load_config(
+ TopoRouter.RD_SHARP, os.path.join(CWD, "{}/sharpd.conf".format(rname))
+ )
+
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+
+
+def test_zebra_netlink_batching():
+ "Test the situation where dataplane fills netlink send buffer entirely."
+ logger.info(
+ "Test the situation where dataplane fills netlink send buffer entirely."
+ )
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ ptyest.skip("skipped because of preview test failure")
+ r1 = tgen.gears["r1"]
+
+ # Reduce the size of the buffer to hit the limit.
+ r1.vtysh_cmd("conf t\nzebra kernel netlink batch-tx-buf 256 256")
+
+ r1.vtysh_cmd("sharp install routes 2.1.3.7 nexthop 192.168.1.1 100")
+ json_file = "{}/r1/v4_route.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(topotest.router_json_cmp, r1, "show ip route json", expected,)
+ _, result = topotest.run_and_expect(test_func, None, count=2, wait=0.5)
+ assertmsg = '"r1" JSON output mismatches'
+ assert result is None, assertmsg
+
+ r1.vtysh_cmd("sharp remove routes 2.1.3.7 100")
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c
index 81f77d4f9b..5cd3e69299 100644
--- a/zebra/if_netlink.c
+++ b/zebra/if_netlink.c
@@ -987,7 +987,8 @@ int kernel_interface_set_master(struct interface *master,
}
/* Interface address modification. */
-static int netlink_address_ctx(const struct zebra_dplane_ctx *ctx)
+static ssize_t netlink_address_msg_encoder(struct zebra_dplane_ctx *ctx,
+ void *buf, size_t buflen)
{
int bytelen;
const struct prefix *p;
@@ -997,64 +998,72 @@ static int netlink_address_ctx(const struct zebra_dplane_ctx *ctx)
struct {
struct nlmsghdr n;
struct ifaddrmsg ifa;
- char buf[NL_PKT_BUF_SIZE];
- } req;
+ char buf[0];
+ } *req = buf;
+
+ if (buflen < sizeof(*req))
+ return 0;
p = dplane_ctx_get_intf_addr(ctx);
- memset(&req, 0, sizeof(req) - NL_PKT_BUF_SIZE);
+ memset(req, 0, sizeof(*req));
bytelen = (p->family == AF_INET ? 4 : 16);
- req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
- req.n.nlmsg_flags = NLM_F_REQUEST;
+ req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+ req->n.nlmsg_flags = NLM_F_REQUEST;
if (dplane_ctx_get_op(ctx) == DPLANE_OP_ADDR_INSTALL)
cmd = RTM_NEWADDR;
else
cmd = RTM_DELADDR;
- req.n.nlmsg_type = cmd;
- req.ifa.ifa_family = p->family;
+ req->n.nlmsg_type = cmd;
+ req->ifa.ifa_family = p->family;
- req.ifa.ifa_index = dplane_ctx_get_ifindex(ctx);
+ req->ifa.ifa_index = dplane_ctx_get_ifindex(ctx);
- nl_attr_put(&req.n, sizeof(req), IFA_LOCAL, &p->u.prefix, bytelen);
+ if (!nl_attr_put(&req->n, buflen, IFA_LOCAL, &p->u.prefix, bytelen))
+ return 0;
if (p->family == AF_INET) {
if (dplane_ctx_intf_is_connected(ctx)) {
p = dplane_ctx_get_intf_dest(ctx);
- nl_attr_put(&req.n, sizeof(req), IFA_ADDRESS,
- &p->u.prefix, bytelen);
+ if (!nl_attr_put(&req->n, buflen, IFA_ADDRESS,
+ &p->u.prefix, bytelen))
+ return 0;
} else if (cmd == RTM_NEWADDR) {
struct in_addr broad = {
.s_addr = ipv4_broadcast_addr(p->u.prefix4.s_addr,
p->prefixlen)
};
- nl_attr_put(&req.n, sizeof(req), IFA_BROADCAST, &broad,
- bytelen);
+ if (!nl_attr_put(&req->n, buflen, IFA_BROADCAST, &broad,
+ bytelen))
+ return 0;
}
}
/* p is now either address or destination/bcast addr */
- req.ifa.ifa_prefixlen = p->prefixlen;
+ req->ifa.ifa_prefixlen = p->prefixlen;
if (dplane_ctx_intf_is_secondary(ctx))
- SET_FLAG(req.ifa.ifa_flags, IFA_F_SECONDARY);
+ SET_FLAG(req->ifa.ifa_flags, IFA_F_SECONDARY);
if (dplane_ctx_intf_has_label(ctx)) {
label = dplane_ctx_get_intf_label(ctx);
- nl_attr_put(&req.n, sizeof(req), IFA_LABEL, label,
- strlen(label) + 1);
+ if (!nl_attr_put(&req->n, buflen, IFA_LABEL, label,
+ strlen(label) + 1))
+ return 0;
}
- return netlink_talk_info(netlink_talk_filter, &req.n,
- dplane_ctx_get_ns(ctx), 0);
+ return NLMSG_ALIGN(req->n.nlmsg_len);
}
-enum zebra_dplane_result kernel_address_update_ctx(struct zebra_dplane_ctx *ctx)
+enum netlink_msg_status
+netlink_put_address_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx)
{
- return (netlink_address_ctx(ctx) == 0 ?
- ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE);
+ return netlink_batch_add_msg(bth, ctx, netlink_address_msg_encoder,
+ false);
}
int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, int startup)
diff --git a/zebra/if_netlink.h b/zebra/if_netlink.h
index 29fd2aca35..0bbba81ca6 100644
--- a/zebra/if_netlink.h
+++ b/zebra/if_netlink.h
@@ -32,6 +32,10 @@ extern int netlink_interface_addr(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 enum netlink_msg_status
+netlink_put_address_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx);
+
/*
* Set protodown status of interface.
*
diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c
index a4d22c12a4..b04076b945 100644
--- a/zebra/kernel_netlink.c
+++ b/zebra/kernel_netlink.c
@@ -84,6 +84,27 @@
#define RTPROT_MROUTED 17
#endif
+#define NL_DEFAULT_BATCH_BUFSIZE (16 * NL_PKT_BUF_SIZE)
+
+/*
+ * We limit the batch's size to a number smaller than the length of the
+ * underlying buffer since the last message that wouldn't fit the batch would go
+ * over the upper boundary and then it would have to be encoded again into a new
+ * buffer. If the difference between the limit and the length of the buffer is
+ * big enough (bigger than the biggest Netlink message) then this situation
+ * won't occur.
+ */
+#define NL_DEFAULT_BATCH_SEND_THRESHOLD (15 * NL_PKT_BUF_SIZE)
+
+/*
+ * For every request sent to the kernel that has failed we get an error message,
+ * which contains a standard netlink message header and the payload consisting
+ * of an error code and the original netlink mesage. So the receiving buffer
+ * must be at least as big as the transmitting buffer increased by some space
+ * for headers.
+ */
+#define NL_BATCH_RX_BUFSIZE (NL_DEFAULT_BATCH_BUFSIZE + NL_PKT_BUF_SIZE)
+
static const struct message nlmsg_str[] = {{RTM_NEWROUTE, "RTM_NEWROUTE"},
{RTM_DELROUTE, "RTM_DELROUTE"},
{RTM_GETROUTE, "RTM_GETROUTE"},
@@ -151,6 +172,62 @@ extern uint32_t nl_rcvbufsize;
extern struct zebra_privs_t zserv_privs;
+DEFINE_MTYPE_STATIC(ZEBRA, NL_BUF, "Zebra Netlink buffers")
+
+size_t nl_batch_tx_bufsize;
+char *nl_batch_tx_buf;
+
+char nl_batch_rx_buf[NL_BATCH_RX_BUFSIZE];
+
+_Atomic uint32_t nl_batch_bufsize = NL_DEFAULT_BATCH_BUFSIZE;
+_Atomic uint32_t nl_batch_send_threshold = NL_DEFAULT_BATCH_SEND_THRESHOLD;
+
+struct nl_batch {
+ void *buf;
+ size_t bufsiz;
+ size_t limit;
+
+ void *buf_head;
+ size_t curlen;
+ size_t msgcnt;
+
+ const struct zebra_dplane_info *zns;
+
+ struct dplane_ctx_q ctx_list;
+
+ /*
+ * Pointer to the queue of completed contexts outbound back
+ * towards the dataplane module.
+ */
+ struct dplane_ctx_q *ctx_out_q;
+};
+
+int netlink_config_write_helper(struct vty *vty)
+{
+ uint32_t size =
+ atomic_load_explicit(&nl_batch_bufsize, memory_order_relaxed);
+ uint32_t threshold = atomic_load_explicit(&nl_batch_send_threshold,
+ memory_order_relaxed);
+
+ if (size != NL_DEFAULT_BATCH_BUFSIZE
+ || threshold != NL_DEFAULT_BATCH_SEND_THRESHOLD)
+ vty_out(vty, "zebra kernel netlink batch-tx-buf %u %u\n", size,
+ threshold);
+
+ return 0;
+}
+
+void netlink_set_batch_buffer_size(uint32_t size, uint32_t threshold, bool set)
+{
+ if (!set) {
+ size = NL_DEFAULT_BATCH_BUFSIZE;
+ threshold = NL_DEFAULT_BATCH_SEND_THRESHOLD;
+ }
+
+ atomic_store_explicit(&nl_batch_bufsize, size, memory_order_relaxed);
+ atomic_store_explicit(&nl_batch_send_threshold, threshold,
+ memory_order_relaxed);
+}
int netlink_talk_filter(struct nlmsghdr *h, ns_id_t ns_id, int startup)
{
@@ -1008,9 +1085,10 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int),
* startup -> Are we reading in under startup conditions
* This is passed through eventually to filter.
*/
-int netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, int startup),
- struct nlmsghdr *n,
- const struct zebra_dplane_info *dp_info, int startup)
+static int
+netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, int startup),
+ struct nlmsghdr *n, const struct zebra_dplane_info *dp_info,
+ int startup)
{
const struct nlsock *nl;
@@ -1080,6 +1158,327 @@ int netlink_request(struct nlsock *nl, void *req)
return 0;
}
+static int nl_batch_read_resp(struct nl_batch *bth)
+{
+ struct nlmsghdr *h;
+ struct sockaddr_nl snl;
+ struct msghdr msg;
+ int status, seq;
+ const struct nlsock *nl;
+ struct zebra_dplane_ctx *ctx;
+ bool ignore_msg;
+
+ nl = &(bth->zns->nls);
+
+ msg.msg_name = (void *)&snl;
+ msg.msg_namelen = sizeof(snl);
+
+ status = netlink_recv_msg(nl, msg, nl_batch_rx_buf,
+ sizeof(nl_batch_rx_buf));
+ if (status == -1 || status == 0)
+ return status;
+
+ for (h = (struct nlmsghdr *)nl_batch_rx_buf;
+ (status >= 0 && NLMSG_OK(h, (unsigned int)status));
+ h = NLMSG_NEXT(h, status)) {
+ ignore_msg = false;
+ seq = h->nlmsg_seq;
+ /*
+ * Find the corresponding context object. Received responses are
+ * in the same order as requests we sent, so we can simply
+ * iterate over the context list and match responses with
+ * requests at same time.
+ */
+ while (true) {
+ ctx = dplane_ctx_dequeue(&(bth->ctx_list));
+ if (ctx == NULL)
+ break;
+
+ dplane_ctx_enqueue_tail(bth->ctx_out_q, ctx);
+
+ /* We have found corresponding context object. */
+ if (dplane_ctx_get_ns(ctx)->nls.seq == seq)
+ break;
+
+ /*
+ * 'update' context objects take two consecutive
+ * sequence numbers.
+ */
+ if (dplane_ctx_is_update(ctx)
+ && dplane_ctx_get_ns(ctx)->nls.seq + 1 == seq) {
+ /*
+ * This is the situation where we get a response
+ * to a message that should be ignored.
+ */
+ ignore_msg = true;
+ break;
+ }
+ }
+
+ if (ignore_msg)
+ continue;
+
+ /*
+ * We received a message with the sequence number that isn't
+ * associated with any dplane context object.
+ */
+ if (ctx == NULL) {
+ zlog_debug(
+ "%s: skipping unassociated response, seq number %d NS %u",
+ __func__, h->nlmsg_seq, bth->zns->ns_id);
+ continue;
+ }
+
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ int err = netlink_parse_error(nl, h, bth->zns, 0);
+
+ if (err == -1)
+ dplane_ctx_set_status(
+ ctx, ZEBRA_DPLANE_REQUEST_FAILURE);
+
+ zlog_debug("%s: netlink error message seq=%d ",
+ __func__, h->nlmsg_seq);
+ continue;
+ }
+
+ /*
+ * If we get here then we did not receive neither the ack nor
+ * the error and instead received some other message in an
+ * unexpected way.
+ */
+ zlog_debug("%s: ignoring message type 0x%04x(%s) NS %u",
+ __func__, h->nlmsg_type,
+ nl_msg_type_to_str(h->nlmsg_type), bth->zns->ns_id);
+ }
+
+ return 0;
+}
+
+static void nl_batch_reset(struct nl_batch *bth)
+{
+ bth->buf_head = bth->buf;
+ bth->curlen = 0;
+ bth->msgcnt = 0;
+ bth->zns = NULL;
+
+ TAILQ_INIT(&(bth->ctx_list));
+}
+
+static void nl_batch_init(struct nl_batch *bth, struct dplane_ctx_q *ctx_out_q)
+{
+ /*
+ * If the size of the buffer has changed, free and then allocate a new
+ * one.
+ */
+ size_t bufsize =
+ atomic_load_explicit(&nl_batch_bufsize, memory_order_relaxed);
+ if (bufsize != nl_batch_tx_bufsize) {
+ if (nl_batch_tx_buf)
+ XFREE(MTYPE_NL_BUF, nl_batch_tx_buf);
+
+ nl_batch_tx_buf = XCALLOC(MTYPE_NL_BUF, bufsize);
+ nl_batch_tx_bufsize = bufsize;
+ }
+
+ bth->buf = nl_batch_tx_buf;
+ bth->bufsiz = bufsize;
+ bth->limit = atomic_load_explicit(&nl_batch_send_threshold,
+ memory_order_relaxed);
+
+ bth->ctx_out_q = ctx_out_q;
+
+ nl_batch_reset(bth);
+}
+
+static void nl_batch_send(struct nl_batch *bth)
+{
+ struct zebra_dplane_ctx *ctx;
+ bool err = false;
+
+ if (bth->curlen != 0 && bth->zns != NULL) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: %s, batch size=%zu, msg cnt=%zu",
+ __func__, bth->zns->nls.name, bth->curlen,
+ bth->msgcnt);
+
+ if (netlink_send_msg(&(bth->zns->nls), bth->buf, bth->curlen)
+ == -1)
+ err = true;
+
+ if (!err) {
+ if (nl_batch_read_resp(bth) == -1)
+ err = true;
+ }
+ }
+
+ /* Move remaining contexts to the outbound queue. */
+ while (true) {
+ ctx = dplane_ctx_dequeue(&(bth->ctx_list));
+ if (ctx == NULL)
+ break;
+
+ if (err)
+ dplane_ctx_set_status(ctx,
+ ZEBRA_DPLANE_REQUEST_FAILURE);
+
+ dplane_ctx_enqueue_tail(bth->ctx_out_q, ctx);
+ }
+
+ nl_batch_reset(bth);
+}
+
+enum netlink_msg_status netlink_batch_add_msg(
+ struct nl_batch *bth, struct zebra_dplane_ctx *ctx,
+ ssize_t (*msg_encoder)(struct zebra_dplane_ctx *, void *, size_t),
+ bool ignore_res)
+{
+ int seq;
+ ssize_t size;
+ struct nlmsghdr *msgh;
+
+ size = (*msg_encoder)(ctx, bth->buf_head, bth->bufsiz - bth->curlen);
+
+ /*
+ * If there was an error while encoding the message (other than buffer
+ * overflow) then return an error.
+ */
+ if (size < 0)
+ return FRR_NETLINK_ERROR;
+
+ /*
+ * If the message doesn't fit entirely in the buffer then send the batch
+ * and retry.
+ */
+ if (size == 0) {
+ nl_batch_send(bth);
+ size = (*msg_encoder)(ctx, bth->buf_head,
+ bth->bufsiz - bth->curlen);
+ /*
+ * If the message doesn't fit in the empty buffer then just
+ * return an error.
+ */
+ if (size <= 0)
+ return FRR_NETLINK_ERROR;
+ }
+
+ seq = dplane_ctx_get_ns(ctx)->nls.seq;
+ if (ignore_res)
+ seq++;
+
+ msgh = (struct nlmsghdr *)bth->buf_head;
+ msgh->nlmsg_seq = seq;
+ msgh->nlmsg_pid = dplane_ctx_get_ns(ctx)->nls.snl.nl_pid;
+
+ bth->zns = dplane_ctx_get_ns(ctx);
+ bth->buf_head = ((char *)bth->buf_head) + size;
+ bth->curlen += size;
+ bth->msgcnt++;
+
+ return FRR_NETLINK_QUEUED;
+}
+
+static enum netlink_msg_status nl_put_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx)
+{
+ if (dplane_ctx_is_skip_kernel(ctx))
+ return FRR_NETLINK_SUCCESS;
+
+ switch (dplane_ctx_get_op(ctx)) {
+
+ case DPLANE_OP_ROUTE_INSTALL:
+ case DPLANE_OP_ROUTE_UPDATE:
+ case DPLANE_OP_ROUTE_DELETE:
+ return netlink_put_route_update_msg(bth, ctx);
+
+ case DPLANE_OP_NH_INSTALL:
+ case DPLANE_OP_NH_UPDATE:
+ case DPLANE_OP_NH_DELETE:
+ return netlink_put_nexthop_update_msg(bth, ctx);
+
+ case DPLANE_OP_LSP_INSTALL:
+ case DPLANE_OP_LSP_UPDATE:
+ case DPLANE_OP_LSP_DELETE:
+ return netlink_put_lsp_update_msg(bth, ctx);
+
+ case DPLANE_OP_PW_INSTALL:
+ case DPLANE_OP_PW_UNINSTALL:
+ return netlink_put_pw_update_msg(bth, ctx);
+
+ case DPLANE_OP_ADDR_INSTALL:
+ case DPLANE_OP_ADDR_UNINSTALL:
+ return netlink_put_address_update_msg(bth, ctx);
+
+ case DPLANE_OP_MAC_INSTALL:
+ case DPLANE_OP_MAC_DELETE:
+ return netlink_put_mac_update_msg(bth, ctx);
+
+ case DPLANE_OP_NEIGH_INSTALL:
+ case DPLANE_OP_NEIGH_UPDATE:
+ case DPLANE_OP_NEIGH_DELETE:
+ case DPLANE_OP_VTEP_ADD:
+ case DPLANE_OP_VTEP_DELETE:
+ return netlink_put_neigh_update_msg(bth, ctx);
+
+ case DPLANE_OP_RULE_ADD:
+ case DPLANE_OP_RULE_DELETE:
+ case DPLANE_OP_RULE_UPDATE:
+ return netlink_put_rule_update_msg(bth, ctx);
+
+ case DPLANE_OP_SYS_ROUTE_ADD:
+ case DPLANE_OP_SYS_ROUTE_DELETE:
+ case DPLANE_OP_ROUTE_NOTIFY:
+ case DPLANE_OP_LSP_NOTIFY:
+ return FRR_NETLINK_SUCCESS;
+
+ case DPLANE_OP_NONE:
+ return FRR_NETLINK_ERROR;
+ }
+
+ return FRR_NETLINK_ERROR;
+}
+
+void kernel_update_multi(struct dplane_ctx_q *ctx_list)
+{
+ struct nl_batch batch;
+ struct zebra_dplane_ctx *ctx;
+ struct dplane_ctx_q handled_list;
+ enum netlink_msg_status res;
+
+ TAILQ_INIT(&handled_list);
+ nl_batch_init(&batch, &handled_list);
+
+ while (true) {
+ ctx = dplane_ctx_dequeue(ctx_list);
+ if (ctx == NULL)
+ break;
+
+ if (batch.zns != NULL
+ && batch.zns->ns_id != dplane_ctx_get_ns(ctx)->ns_id)
+ nl_batch_send(&batch);
+
+ /*
+ * Assume all messages will succeed and then mark only the ones
+ * that failed.
+ */
+ dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS);
+
+ res = nl_put_msg(&batch, ctx);
+
+ dplane_ctx_enqueue_tail(&(batch.ctx_list), ctx);
+ if (res == FRR_NETLINK_ERROR)
+ dplane_ctx_set_status(ctx,
+ ZEBRA_DPLANE_REQUEST_FAILURE);
+
+ if (batch.curlen > batch.limit)
+ nl_batch_send(&batch);
+ }
+
+ nl_batch_send(&batch);
+
+ TAILQ_INIT(ctx_list);
+ dplane_ctx_list_append(ctx_list, &handled_list);
+}
+
/* Exported interface function. This function simply calls
netlink_socket (). */
void kernel_init(struct zebra_ns *zns)
diff --git a/zebra/kernel_netlink.h b/zebra/kernel_netlink.h
index bd8159faf3..c02e16480b 100644
--- a/zebra/kernel_netlink.h
+++ b/zebra/kernel_netlink.h
@@ -98,13 +98,49 @@ extern int netlink_talk_filter(struct nlmsghdr *h, ns_id_t ns, int startup);
extern int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup),
struct nlmsghdr *n, struct nlsock *nl,
struct zebra_ns *zns, int startup);
-/* Version with 'info' struct only */
-int netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, int startup),
- struct nlmsghdr *n,
- const struct zebra_dplane_info *dp_info, int startup);
-
extern int netlink_request(struct nlsock *nl, void *req);
+enum netlink_msg_status {
+ FRR_NETLINK_SUCCESS,
+ FRR_NETLINK_ERROR,
+ FRR_NETLINK_QUEUED,
+};
+
+struct nl_batch;
+
+/*
+ * netlink_batch_add_msg - add message to the netlink batch using dplane
+ * context object.
+ *
+ * @ctx: Dataplane context
+ * @msg_encoder: A function that encodes dplane context object into
+ * netlink message. Should take dplane context object,
+ * pointer to a buffer and buffer's length as parameters
+ * and should return -1 on error, 0 on buffer overflow or
+ * size of the encoded message.
+ * @ignore_res: Whether the result of this message should be ignored.
+ * This should be used in some 'update' cases where we
+ * need to send two messages for one context object.
+ *
+ * Return: Status of the message.
+ */
+extern enum netlink_msg_status netlink_batch_add_msg(
+ struct nl_batch *bth, struct zebra_dplane_ctx *ctx,
+ ssize_t (*msg_encoder)(struct zebra_dplane_ctx *, void *, size_t),
+ bool ignore_res);
+
+/*
+ * Vty/cli apis
+ */
+extern int netlink_config_write_helper(struct vty *vty);
+
+/*
+ * Configure size of the batch buffer and sending threshold. If 'unset', reset
+ * to default value.
+ */
+extern void netlink_set_batch_buffer_size(uint32_t size, uint32_t threshold,
+ bool set);
+
#endif /* HAVE_NETLINK */
#ifdef __cplusplus
diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c
index 873d221149..9e56e2988d 100644
--- a/zebra/kernel_socket.c
+++ b/zebra/kernel_socket.c
@@ -1464,4 +1464,98 @@ void kernel_terminate(struct zebra_ns *zns, bool complete)
return;
}
+void kernel_update_multi(struct dplane_ctx_q *ctx_list)
+{
+ struct zebra_dplane_ctx *ctx;
+ struct dplane_ctx_q handled_list;
+ enum zebra_dplane_result res;
+
+ TAILQ_INIT(&handled_list);
+
+ while (true) {
+ ctx = dplane_ctx_dequeue(ctx_list);
+ if (ctx == NULL)
+ break;
+
+ /*
+ * A previous provider plugin may have asked to skip the
+ * kernel update.
+ */
+ if (dplane_ctx_is_skip_kernel(ctx)) {
+ res = ZEBRA_DPLANE_REQUEST_SUCCESS;
+ goto skip_one;
+ }
+
+ switch (dplane_ctx_get_op(ctx)) {
+
+ case DPLANE_OP_ROUTE_INSTALL:
+ case DPLANE_OP_ROUTE_UPDATE:
+ case DPLANE_OP_ROUTE_DELETE:
+ res = kernel_route_update(ctx);
+ break;
+
+ case DPLANE_OP_NH_INSTALL:
+ case DPLANE_OP_NH_UPDATE:
+ case DPLANE_OP_NH_DELETE:
+ res = kernel_nexthop_update(ctx);
+ break;
+
+ case DPLANE_OP_LSP_INSTALL:
+ case DPLANE_OP_LSP_UPDATE:
+ case DPLANE_OP_LSP_DELETE:
+ res = kernel_lsp_update(ctx);
+ break;
+
+ case DPLANE_OP_PW_INSTALL:
+ case DPLANE_OP_PW_UNINSTALL:
+ res = kernel_pw_update(ctx);
+ break;
+
+ case DPLANE_OP_ADDR_INSTALL:
+ case DPLANE_OP_ADDR_UNINSTALL:
+ res = kernel_address_update_ctx(ctx);
+ break;
+
+ case DPLANE_OP_MAC_INSTALL:
+ case DPLANE_OP_MAC_DELETE:
+ res = kernel_mac_update_ctx(ctx);
+ break;
+
+ case DPLANE_OP_NEIGH_INSTALL:
+ case DPLANE_OP_NEIGH_UPDATE:
+ case DPLANE_OP_NEIGH_DELETE:
+ case DPLANE_OP_VTEP_ADD:
+ case DPLANE_OP_VTEP_DELETE:
+ res = kernel_neigh_update_ctx(ctx);
+ break;
+
+ case DPLANE_OP_RULE_ADD:
+ case DPLANE_OP_RULE_DELETE:
+ case DPLANE_OP_RULE_UPDATE:
+ res = kernel_pbr_rule_update(ctx);
+ break;
+
+ /* Ignore 'notifications' - no-op */
+ case DPLANE_OP_SYS_ROUTE_ADD:
+ case DPLANE_OP_SYS_ROUTE_DELETE:
+ case DPLANE_OP_ROUTE_NOTIFY:
+ case DPLANE_OP_LSP_NOTIFY:
+ res = ZEBRA_DPLANE_REQUEST_SUCCESS;
+ break;
+
+ default:
+ res = ZEBRA_DPLANE_REQUEST_FAILURE;
+ break;
+ }
+
+ skip_one:
+ dplane_ctx_set_status(ctx, res);
+
+ dplane_ctx_enqueue_tail(&handled_list, ctx);
+ }
+
+ TAILQ_INIT(ctx_list);
+ dplane_ctx_list_append(ctx_list, &handled_list);
+}
+
#endif /* !HAVE_NETLINK */
diff --git a/zebra/rt.h b/zebra/rt.h
index 143e16b3ea..48f1df2868 100644
--- a/zebra/rt.h
+++ b/zebra/rt.h
@@ -40,7 +40,7 @@ extern "C" {
#define RSYSTEM_ROUTE(type) \
((RKERNEL_ROUTE(type)) || (type) == ZEBRA_ROUTE_CONNECT)
-
+#ifndef HAVE_NETLINK
/*
* Update or delete a route, nexthop, LSP, pseudowire, or vxlan MAC from the
* kernel, using info from a dataplane context.
@@ -63,6 +63,11 @@ enum zebra_dplane_result kernel_mac_update_ctx(struct zebra_dplane_ctx *ctx);
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);
+
+#endif /* !HAVE_NETLINK */
+
extern int kernel_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla,
int llalen, ns_id_t ns_id);
extern int kernel_interface_set_master(struct interface *master,
@@ -97,6 +102,11 @@ extern int kernel_upd_mac_nhg(uint32_t nhg_id, uint32_t nh_cnt,
struct nh_grp *nh_ids);
extern int kernel_del_mac_nhg(uint32_t nhg_id);
+/*
+ * Message batching interface.
+ */
+extern void kernel_update_multi(struct dplane_ctx_q *ctx_list);
+
#ifdef __cplusplus
}
#endif
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
index ea15de6bdb..190af3f59d 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.c
@@ -2230,19 +2230,11 @@ nexthop_done:
return NLMSG_ALIGN(req->n.nlmsg_len);
}
-/**
- * kernel_nexthop_update() - Update/delete a nexthop from the kernel
- *
- * @ctx: Dataplane context
- *
- * Return: Dataplane result flag
- */
-enum zebra_dplane_result kernel_nexthop_update(struct zebra_dplane_ctx *ctx)
+static ssize_t netlink_nexthop_msg_encoder(struct zebra_dplane_ctx *ctx,
+ void *buf, size_t buflen)
{
enum dplane_op_e op;
int cmd = 0;
- int ret = 0;
- char buf[NL_PKT_BUF_SIZE];
op = dplane_ctx_get_op(ctx);
if (op == DPLANE_OP_NH_INSTALL || op == DPLANE_OP_NH_UPDATE)
@@ -2253,33 +2245,43 @@ enum zebra_dplane_result kernel_nexthop_update(struct zebra_dplane_ctx *ctx)
flog_err(EC_ZEBRA_NHG_FIB_UPDATE,
"Context received for kernel nexthop update with incorrect OP code (%u)",
op);
- return ZEBRA_DPLANE_REQUEST_FAILURE;
+ return -1;
}
+ return netlink_nexthop_msg_encode(cmd, ctx, buf, buflen);
+}
+
+enum netlink_msg_status
+netlink_put_nexthop_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx)
+{
/* Nothing to do if the kernel doesn't support nexthop objects */
if (!kernel_nexthops_supported())
- return ZEBRA_DPLANE_REQUEST_SUCCESS;
+ return FRR_NETLINK_SUCCESS;
- if (netlink_nexthop_msg_encode(cmd, ctx, buf, sizeof(buf)) > 0)
- ret = netlink_talk_info(netlink_talk_filter, (void *)&buf,
- dplane_ctx_get_ns(ctx), 0);
- else
- ret = 0;
+ return netlink_batch_add_msg(bth, ctx, netlink_nexthop_msg_encoder,
+ false);
+}
- return (ret == 0 ? ZEBRA_DPLANE_REQUEST_SUCCESS
- : ZEBRA_DPLANE_REQUEST_FAILURE);
+static ssize_t netlink_newroute_msg_encoder(struct zebra_dplane_ctx *ctx,
+ void *buf, size_t buflen)
+{
+ return netlink_route_multipath_msg_encode(RTM_NEWROUTE, ctx, buf,
+ buflen, false, false);
}
-/*
- * Update or delete a prefix from the kernel,
- * using info from a dataplane context.
- */
-enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx)
+static ssize_t netlink_delroute_msg_encoder(struct zebra_dplane_ctx *ctx,
+ void *buf, size_t buflen)
{
- int cmd, ret;
+ return netlink_route_multipath_msg_encode(RTM_DELROUTE, ctx, buf,
+ buflen, false, false);
+}
+
+enum netlink_msg_status
+netlink_put_route_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx)
+{
+ int cmd;
const struct prefix *p = dplane_ctx_get_dest(ctx);
- struct nexthop *nexthop;
- uint8_t nl_pkt[NL_PKT_BUF_SIZE];
if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_DELETE) {
cmd = RTM_DELROUTE;
@@ -2289,7 +2291,6 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx)
if (p->family == AF_INET || v6_rr_semantics) {
/* Single 'replace' operation */
- cmd = RTM_NEWROUTE;
/*
* With route replace semantics in place
@@ -2299,17 +2300,11 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx)
* route should cause us to withdraw from
* the kernel the old non-system route
*/
- if (RSYSTEM_ROUTE(dplane_ctx_get_type(ctx)) &&
- !RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx))) {
- if (netlink_route_multipath_msg_encode(
- RTM_DELROUTE, ctx, nl_pkt,
- sizeof(nl_pkt), false, false)
- > 0)
- netlink_talk_info(
- netlink_talk_filter,
- (struct nlmsghdr *)nl_pkt,
- dplane_ctx_get_ns(ctx), 0);
- }
+ if (RSYSTEM_ROUTE(dplane_ctx_get_type(ctx))
+ && !RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx)))
+ netlink_batch_add_msg(
+ bth, ctx, netlink_delroute_msg_encoder,
+ true);
} else {
/*
* So v6 route replace semantics are not in
@@ -2323,51 +2318,24 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx)
* of the route delete. If that happens yeah we're
* screwed.
*/
- if (!RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx))) {
- if (netlink_route_multipath_msg_encode(
- RTM_DELROUTE, ctx, nl_pkt,
- sizeof(nl_pkt), false, false)
- > 0)
- netlink_talk_info(
- netlink_talk_filter,
- (struct nlmsghdr *)nl_pkt,
- dplane_ctx_get_ns(ctx), 0);
- }
- cmd = RTM_NEWROUTE;
+ if (!RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx)))
+ netlink_batch_add_msg(
+ bth, ctx, netlink_delroute_msg_encoder,
+ true);
}
- } else {
- return ZEBRA_DPLANE_REQUEST_FAILURE;
- }
-
- if (!RSYSTEM_ROUTE(dplane_ctx_get_type(ctx))) {
- if (netlink_route_multipath_msg_encode(
- cmd, ctx, nl_pkt, sizeof(nl_pkt), false, false)
- > 0)
- ret = netlink_talk_info(netlink_talk_filter,
- (struct nlmsghdr *)nl_pkt,
- dplane_ctx_get_ns(ctx), 0);
- else
- ret = -1;
-
+ cmd = RTM_NEWROUTE;
} else
- ret = 0;
- if ((cmd == RTM_NEWROUTE) && (ret == 0)) {
- /* Update installed nexthops to signal which have been
- * installed.
- */
- for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) {
- if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
- continue;
+ return FRR_NETLINK_ERROR;
- if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) {
- SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
- }
- }
- }
+ if (RSYSTEM_ROUTE(dplane_ctx_get_type(ctx)))
+ return FRR_NETLINK_SUCCESS;
- return (ret == 0 ?
- ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE);
+ return netlink_batch_add_msg(bth, ctx,
+ cmd == RTM_NEWROUTE
+ ? netlink_newroute_msg_encoder
+ : netlink_delroute_msg_encoder,
+ false);
}
/**
@@ -2783,23 +2751,16 @@ static ssize_t netlink_neigh_update_msg_encode(
* Add remote VTEP to the flood list for this VxLAN interface (VNI). This
* is done by adding an FDB entry with a MAC of 00:00:00:00:00:00.
*/
-static int netlink_vxlan_flood_update_ctx(const struct zebra_dplane_ctx *ctx,
- int cmd)
+static ssize_t
+netlink_vxlan_flood_update_ctx(const struct zebra_dplane_ctx *ctx, int cmd,
+ void *buf, size_t buflen)
{
struct ethaddr dst_mac = {.octet = {0}};
- uint8_t nl_pkt[NL_PKT_BUF_SIZE];
-
- if (netlink_neigh_update_msg_encode(
- ctx, cmd, &dst_mac, dplane_ctx_neigh_get_ipaddr(ctx), false,
- PF_BRIDGE, 0, NTF_SELF, (NUD_NOARP | NUD_PERMANENT),
- 0 /*nhg*/, false /*nfy*/, 0 /*nfy_flags*/,
- nl_pkt, sizeof(nl_pkt))
- <= 0)
- return -1;
- return netlink_talk_info(netlink_talk_filter,
- (struct nlmsghdr *)nl_pkt,
- dplane_ctx_get_ns(ctx), 0);
+ return netlink_neigh_update_msg_encode(
+ ctx, cmd, &dst_mac, dplane_ctx_neigh_get_ipaddr(ctx), false,
+ PF_BRIDGE, 0, NTF_SELF, (NUD_NOARP | NUD_PERMANENT), 0 /*nhg*/,
+ false /*nfy*/, 0 /*nfy_flags*/, buf, buflen);
}
#ifndef NDA_RTA
@@ -3148,9 +3109,8 @@ int netlink_macfdb_read_specific_mac(struct zebra_ns *zns,
/*
* Netlink-specific handler for MAC updates using dataplane context object.
*/
-ssize_t
-netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, uint8_t *data,
- size_t datalen)
+ssize_t netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, void *data,
+ size_t datalen)
{
struct ipaddr vtep_ip;
vlanid_t vid;
@@ -3626,15 +3586,14 @@ int netlink_neigh_change(struct nlmsghdr *h, ns_id_t ns_id)
/*
* Utility neighbor-update function, using info from dplane context.
*/
-static int netlink_neigh_update_ctx(const struct zebra_dplane_ctx *ctx,
- int cmd)
+static ssize_t netlink_neigh_update_ctx(const struct zebra_dplane_ctx *ctx,
+ int cmd, void *buf, size_t buflen)
{
const struct ipaddr *ip;
const struct ethaddr *mac;
uint8_t flags;
uint16_t state;
uint8_t family;
- uint8_t nl_pkt[NL_PKT_BUF_SIZE];
ip = dplane_ctx_neigh_get_ipaddr(ctx);
mac = dplane_ctx_neigh_get_mac(ctx);
@@ -3659,60 +3618,55 @@ static int netlink_neigh_update_ctx(const struct zebra_dplane_ctx *ctx,
flags, state);
}
- if (netlink_neigh_update_msg_encode(ctx, cmd, mac, ip, true, family,
- RTN_UNICAST, flags, state,
- 0 /*nhg*/, false /*nfy*/, 0 /*nfy_flags*/,
- nl_pkt, sizeof(nl_pkt))
- <= 0)
- return -1;
-
- return netlink_talk_info(netlink_talk_filter, (struct nlmsghdr *)nl_pkt,
- dplane_ctx_get_ns(ctx), 0);
-}
-
-/*
- * Update MAC, using dataplane context object.
- */
-enum zebra_dplane_result kernel_mac_update_ctx(struct zebra_dplane_ctx *ctx)
-{
- uint8_t nl_pkt[NL_PKT_BUF_SIZE];
- ssize_t rv;
-
- rv = netlink_macfdb_update_ctx(ctx, nl_pkt, sizeof(nl_pkt));
- if (rv <= 0)
- return ZEBRA_DPLANE_REQUEST_FAILURE;
-
- rv = netlink_talk_info(netlink_talk_filter, (struct nlmsghdr *)nl_pkt,
- dplane_ctx_get_ns(ctx), 0);
-
- return rv == 0 ?
- ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE;
+ return netlink_neigh_update_msg_encode(
+ ctx, cmd, mac, ip, true, family, RTN_UNICAST, flags, state,
+ 0 /*nhg*/, false /*nfy*/, 0 /*nfy_flags*/, buf, buflen);
}
-enum zebra_dplane_result kernel_neigh_update_ctx(struct zebra_dplane_ctx *ctx)
+static ssize_t netlink_neigh_msg_encoder(struct zebra_dplane_ctx *ctx,
+ void *buf, size_t buflen)
{
- int ret = -1;
+ ssize_t ret;
switch (dplane_ctx_get_op(ctx)) {
case DPLANE_OP_NEIGH_INSTALL:
case DPLANE_OP_NEIGH_UPDATE:
- ret = netlink_neigh_update_ctx(ctx, RTM_NEWNEIGH);
+ ret = netlink_neigh_update_ctx(ctx, RTM_NEWNEIGH, buf, buflen);
break;
case DPLANE_OP_NEIGH_DELETE:
- ret = netlink_neigh_update_ctx(ctx, RTM_DELNEIGH);
+ ret = netlink_neigh_update_ctx(ctx, RTM_DELNEIGH, buf, buflen);
break;
case DPLANE_OP_VTEP_ADD:
- ret = netlink_vxlan_flood_update_ctx(ctx, RTM_NEWNEIGH);
+ ret = netlink_vxlan_flood_update_ctx(ctx, RTM_NEWNEIGH, buf,
+ buflen);
break;
case DPLANE_OP_VTEP_DELETE:
- ret = netlink_vxlan_flood_update_ctx(ctx, RTM_DELNEIGH);
+ ret = netlink_vxlan_flood_update_ctx(ctx, RTM_DELNEIGH, buf,
+ buflen);
break;
default:
- break;
+ ret = -1;
}
- return (ret == 0 ?
- ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE);
+ return ret;
+}
+
+/*
+ * Update MAC, using dataplane context object.
+ */
+
+enum netlink_msg_status netlink_put_mac_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx)
+{
+ return netlink_batch_add_msg(bth, ctx, netlink_macfdb_update_ctx,
+ false);
+}
+
+enum netlink_msg_status
+netlink_put_neigh_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx)
+{
+ return netlink_batch_add_msg(bth, ctx, netlink_neigh_msg_encoder,
+ false);
}
/*
diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h
index 05c0d76427..e1bb844785 100644
--- a/zebra/rt_netlink.h
+++ b/zebra/rt_netlink.h
@@ -74,7 +74,7 @@ extern ssize_t netlink_route_multipath_msg_encode(int cmd,
uint8_t *data, size_t datalen,
bool fpm, bool force_nhg);
extern ssize_t netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx,
- uint8_t *data, size_t datalen);
+ void *data, size_t datalen);
extern int netlink_route_change(struct nlmsghdr *h, ns_id_t ns_id, int startup);
extern int netlink_route_read(struct zebra_ns *zns);
@@ -101,6 +101,23 @@ extern int netlink_neigh_read_specific_ip(struct ipaddr *ip,
struct interface *vlan_if);
extern vrf_id_t vrf_lookup_by_table(uint32_t table_id, ns_id_t ns_id);
+struct nl_batch;
+extern enum netlink_msg_status
+netlink_put_route_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx);
+extern enum netlink_msg_status
+netlink_put_nexthop_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx);
+extern enum netlink_msg_status
+netlink_put_mac_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx);
+extern enum netlink_msg_status
+netlink_put_neigh_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx);
+extern enum netlink_msg_status
+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);
+
#ifdef __cplusplus
}
#endif
diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c
index 0271dc7f41..a2e15cbd2b 100644
--- a/zebra/rt_socket.c
+++ b/zebra/rt_socket.c
@@ -358,20 +358,6 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx)
}
} /* Elevated privs */
- if (RSYSTEM_ROUTE(type)
- && dplane_ctx_get_op(ctx) != DPLANE_OP_ROUTE_DELETE) {
- struct nexthop *nexthop;
-
- for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) {
- if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
- continue;
-
- if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) {
- SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
- }
- }
- }
-
return res;
}
diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c
index 648e9eabe1..3a3baab4ca 100644
--- a/zebra/rule_netlink.c
+++ b/zebra/rule_netlink.c
@@ -78,6 +78,8 @@ netlink_rule_msg_encode(int cmd, const struct zebra_dplane_ctx *ctx,
char buf1[PREFIX_STRLEN];
char buf2[PREFIX_STRLEN];
+ if (buflen < sizeof(*req))
+ return 0;
memset(req, 0, sizeof(*req));
family = PREFIX_FAMILY(src_ip);
bytelen = (family == AF_INET ? 4 : 16);
@@ -148,53 +150,55 @@ netlink_rule_msg_encode(int cmd, const struct zebra_dplane_ctx *ctx,
return NLMSG_ALIGN(req->n.nlmsg_len);
}
-/* Install or uninstall specified rule for a specific interface.
- * Form netlink message and ship it.
- */
-static int netlink_rule_update_internal(
- int cmd, const struct zebra_dplane_ctx *ctx, uint32_t filter_bm,
- uint32_t priority, uint32_t table, const struct prefix *src_ip,
- const struct prefix *dst_ip, uint32_t fwmark, uint8_t dsfield)
+static ssize_t netlink_rule_msg_encoder(struct zebra_dplane_ctx *ctx, void *buf,
+ size_t buflen)
{
- char buf[NL_PKT_BUF_SIZE];
+ int cmd = RTM_NEWRULE;
+
+ if (dplane_ctx_get_op(ctx) == DPLANE_OP_RULE_DELETE)
+ cmd = RTM_DELRULE;
- netlink_rule_msg_encode(cmd, ctx, filter_bm, priority, table, src_ip,
- dst_ip, fwmark, dsfield, buf, sizeof(buf));
- return netlink_talk_info(netlink_talk_filter, (void *)&buf,
- dplane_ctx_get_ns(ctx), 0);
+ return netlink_rule_msg_encode(
+ cmd, ctx, dplane_ctx_rule_get_filter_bm(ctx),
+ dplane_ctx_rule_get_priority(ctx),
+ dplane_ctx_rule_get_table(ctx), dplane_ctx_rule_get_src_ip(ctx),
+ dplane_ctx_rule_get_dst_ip(ctx),
+ dplane_ctx_rule_get_fwmark(ctx),
+ dplane_ctx_rule_get_dsfield(ctx), buf, buflen);
+}
+
+static ssize_t netlink_oldrule_msg_encoder(struct zebra_dplane_ctx *ctx,
+ void *buf, size_t buflen)
+{
+ return netlink_rule_msg_encode(
+ RTM_DELRULE, ctx, dplane_ctx_rule_get_old_filter_bm(ctx),
+ dplane_ctx_rule_get_old_priority(ctx),
+ dplane_ctx_rule_get_old_table(ctx),
+ dplane_ctx_rule_get_old_src_ip(ctx),
+ dplane_ctx_rule_get_old_dst_ip(ctx),
+ dplane_ctx_rule_get_old_fwmark(ctx),
+ dplane_ctx_rule_get_old_dsfield(ctx), buf, buflen);
}
+
/* Public functions */
-/*
- * Add, update or delete a rule from the
- * kernel, using info from a dataplane context.
- */
-enum zebra_dplane_result kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx)
+enum netlink_msg_status
+netlink_put_rule_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx)
{
enum dplane_op_e op;
- int cmd;
- int ret;
+ enum netlink_msg_status ret;
op = dplane_ctx_get_op(ctx);
- if (op == DPLANE_OP_RULE_ADD || op == DPLANE_OP_RULE_UPDATE)
- cmd = RTM_NEWRULE;
- else if (op == DPLANE_OP_RULE_DELETE)
- cmd = RTM_DELRULE;
- else {
+ if (!(op == DPLANE_OP_RULE_ADD || op == DPLANE_OP_RULE_UPDATE
+ || op == DPLANE_OP_RULE_DELETE)) {
flog_err(
EC_ZEBRA_PBR_RULE_UPDATE,
"Context received for kernel rule update with incorrect OP code (%u)",
op);
- return ZEBRA_DPLANE_REQUEST_FAILURE;
+ return FRR_NETLINK_ERROR;
}
- ret = netlink_rule_update_internal(
- cmd, ctx, dplane_ctx_rule_get_filter_bm(ctx),
- dplane_ctx_rule_get_priority(ctx),
- dplane_ctx_rule_get_table(ctx), dplane_ctx_rule_get_src_ip(ctx),
- dplane_ctx_rule_get_dst_ip(ctx),
- dplane_ctx_rule_get_fwmark(ctx),
- dplane_ctx_rule_get_dsfield(ctx));
+ ret = netlink_batch_add_msg(bth, ctx, netlink_rule_msg_encoder, false);
/**
* Delete the old one.
@@ -202,19 +206,10 @@ enum zebra_dplane_result kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx)
* Don't care about this result right?
*/
if (op == DPLANE_OP_RULE_UPDATE)
- netlink_rule_update_internal(
- RTM_DELRULE, ctx,
- dplane_ctx_rule_get_old_filter_bm(ctx),
- dplane_ctx_rule_get_old_priority(ctx),
- dplane_ctx_rule_get_old_table(ctx),
- dplane_ctx_rule_get_old_src_ip(ctx),
- dplane_ctx_rule_get_old_dst_ip(ctx),
- dplane_ctx_rule_get_old_fwmark(ctx),
- dplane_ctx_rule_get_old_dsfield(ctx));
-
-
- return (ret == 0 ? ZEBRA_DPLANE_REQUEST_SUCCESS
- : ZEBRA_DPLANE_REQUEST_FAILURE);
+ netlink_batch_add_msg(bth, ctx, netlink_oldrule_msg_encoder,
+ true);
+
+ return ret;
}
/*
diff --git a/zebra/rule_netlink.h b/zebra/rule_netlink.h
index 8c4741dc06..cf4d978e78 100644
--- a/zebra/rule_netlink.h
+++ b/zebra/rule_netlink.h
@@ -40,6 +40,9 @@ extern int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup);
*/
extern int netlink_rules_read(struct zebra_ns *zns);
+extern enum netlink_msg_status
+netlink_put_rule_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx);
+
#ifdef __cplusplus
}
#endif
diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c
index 5dcf76db15..d34e1433ce 100644
--- a/zebra/zebra_dplane.c
+++ b/zebra/zebra_dplane.c
@@ -858,8 +858,6 @@ const char *dplane_res2str(enum zebra_dplane_result res)
case ZEBRA_DPLANE_REQUEST_SUCCESS:
ret = "SUCCESS";
break;
- case ZEBRA_DPLANE_REQUEST_PENDING:
- ret = "PENDING";
}
return ret;
@@ -1979,6 +1977,7 @@ int dplane_ctx_nexthop_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
* it probably won't require two messages
*/
dplane_ctx_ns_init(ctx, zns, (op == DPLANE_OP_NH_UPDATE));
+ ctx->zd_is_update = (op == DPLANE_OP_NH_UPDATE);
ret = AOK;
@@ -2001,6 +2000,7 @@ int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
/* Capture namespace info */
dplane_ctx_ns_init(ctx, zebra_ns_lookup(NS_DEFAULT),
(op == DPLANE_OP_LSP_UPDATE));
+ ctx->zd_is_update = (op == DPLANE_OP_LSP_UPDATE);
memset(&ctx->u.lsp, 0, sizeof(ctx->u.lsp));
@@ -2222,6 +2222,7 @@ static int dplane_ctx_rule_init(struct zebra_dplane_ctx *ctx,
dplane_ctx_ns_init(ctx, zebra_ns_lookup(NS_DEFAULT),
op == DPLANE_OP_RULE_UPDATE);
+ ctx->zd_is_update = (op == DPLANE_OP_RULE_UPDATE);
ctx->zd_vrf_id = new_rule->vrf_id;
memcpy(ctx->zd_ifname, new_rule->ifname, sizeof(new_rule->ifname));
@@ -3717,149 +3718,99 @@ void dplane_provider_enqueue_to_zebra(struct zebra_dplane_ctx *ctx)
* Kernel dataplane provider
*/
-/*
- * Handler for kernel LSP updates
- */
-static enum zebra_dplane_result
-kernel_dplane_lsp_update(struct zebra_dplane_ctx *ctx)
+static void kernel_dplane_log_detail(struct zebra_dplane_ctx *ctx)
{
- return kernel_lsp_update(ctx);
-}
+ char buf[PREFIX_STRLEN];
-/*
- * Handler for kernel pseudowire updates
- */
-static enum zebra_dplane_result
-kernel_dplane_pw_update(struct zebra_dplane_ctx *ctx)
-{
- if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
- zlog_debug("Dplane pw %s: op %s af %d loc: %u rem: %u",
- dplane_ctx_get_ifname(ctx),
- dplane_op2str(ctx->zd_op),
- dplane_ctx_get_pw_af(ctx),
- dplane_ctx_get_pw_local_label(ctx),
- dplane_ctx_get_pw_remote_label(ctx));
-
- return kernel_pw_update(ctx);
-}
-
-/*
- * Handler for kernel route updates
- */
-static enum zebra_dplane_result
-kernel_dplane_route_update(struct zebra_dplane_ctx *ctx)
-{
- if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
- char dest_str[PREFIX_STRLEN];
+ switch (dplane_ctx_get_op(ctx)) {
- prefix2str(dplane_ctx_get_dest(ctx),
- dest_str, sizeof(dest_str));
+ case DPLANE_OP_ROUTE_INSTALL:
+ case DPLANE_OP_ROUTE_UPDATE:
+ case DPLANE_OP_ROUTE_DELETE:
+ prefix2str(dplane_ctx_get_dest(ctx), buf, sizeof(buf));
zlog_debug("%u:%s Dplane route update ctx %p op %s",
- dplane_ctx_get_vrf(ctx), dest_str,
- ctx, dplane_op2str(dplane_ctx_get_op(ctx)));
- }
-
- return kernel_route_update(ctx);
-}
-
-/*
- * Handler for kernel-facing interface address updates
- */
-static enum zebra_dplane_result
-kernel_dplane_address_update(struct zebra_dplane_ctx *ctx)
-{
- if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
- char dest_str[PREFIX_STRLEN];
-
- prefix2str(dplane_ctx_get_intf_addr(ctx), dest_str,
- sizeof(dest_str));
-
- zlog_debug("Dplane intf %s, idx %u, addr %s",
- dplane_op2str(dplane_ctx_get_op(ctx)),
- dplane_ctx_get_ifindex(ctx), dest_str);
- }
-
- return kernel_address_update_ctx(ctx);
-}
+ dplane_ctx_get_vrf(ctx), buf, ctx,
+ dplane_op2str(dplane_ctx_get_op(ctx)));
+ break;
-/**
- * kernel_dplane_nexthop_update() - Handler for kernel nexthop updates
- *
- * @ctx: Dataplane context
- *
- * Return: Dataplane result flag
- */
-static enum zebra_dplane_result
-kernel_dplane_nexthop_update(struct zebra_dplane_ctx *ctx)
-{
- if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
+ case DPLANE_OP_NH_INSTALL:
+ case DPLANE_OP_NH_UPDATE:
+ case DPLANE_OP_NH_DELETE:
zlog_debug("ID (%u) Dplane nexthop update ctx %p op %s",
dplane_ctx_get_nhe_id(ctx), ctx,
dplane_op2str(dplane_ctx_get_op(ctx)));
- }
+ break;
- return kernel_nexthop_update(ctx);
-}
+ case DPLANE_OP_LSP_INSTALL:
+ case DPLANE_OP_LSP_UPDATE:
+ case DPLANE_OP_LSP_DELETE:
+ break;
-/*
- * Handler for kernel-facing EVPN MAC address updates
- */
-static enum zebra_dplane_result
-kernel_dplane_mac_update(struct zebra_dplane_ctx *ctx)
-{
- if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
- char buf[ETHER_ADDR_STRLEN];
+ case DPLANE_OP_PW_INSTALL:
+ case DPLANE_OP_PW_UNINSTALL:
+ zlog_debug("Dplane pw %s: op %s af %d loc: %u rem: %u",
+ dplane_ctx_get_ifname(ctx),
+ dplane_op2str(ctx->zd_op), dplane_ctx_get_pw_af(ctx),
+ dplane_ctx_get_pw_local_label(ctx),
+ dplane_ctx_get_pw_remote_label(ctx));
+ break;
+
+ case DPLANE_OP_ADDR_INSTALL:
+ case DPLANE_OP_ADDR_UNINSTALL:
+ prefix2str(dplane_ctx_get_intf_addr(ctx), buf, sizeof(buf));
+
+ zlog_debug("Dplane intf %s, idx %u, addr %s",
+ dplane_op2str(dplane_ctx_get_op(ctx)),
+ dplane_ctx_get_ifindex(ctx), buf);
+ break;
+ case DPLANE_OP_MAC_INSTALL:
+ case DPLANE_OP_MAC_DELETE:
prefix_mac2str(dplane_ctx_mac_get_addr(ctx), buf,
sizeof(buf));
zlog_debug("Dplane %s, mac %s, ifindex %u",
dplane_op2str(dplane_ctx_get_op(ctx)),
buf, dplane_ctx_get_ifindex(ctx));
- }
-
- return kernel_mac_update_ctx(ctx);
-}
-
-/*
- * Handler for kernel-facing EVPN neighbor updates
- */
-static enum zebra_dplane_result
-kernel_dplane_neigh_update(struct zebra_dplane_ctx *ctx)
-{
- if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
- char buf[PREFIX_STRLEN];
+ break;
+ case DPLANE_OP_NEIGH_INSTALL:
+ case DPLANE_OP_NEIGH_UPDATE:
+ case DPLANE_OP_NEIGH_DELETE:
+ case DPLANE_OP_VTEP_ADD:
+ case DPLANE_OP_VTEP_DELETE:
ipaddr2str(dplane_ctx_neigh_get_ipaddr(ctx), buf,
sizeof(buf));
zlog_debug("Dplane %s, ip %s, ifindex %u",
dplane_op2str(dplane_ctx_get_op(ctx)),
buf, dplane_ctx_get_ifindex(ctx));
- }
-
- return kernel_neigh_update_ctx(ctx);
-}
+ break;
-/*
- * Handler for kernel PBR rule updates
- */
-static enum zebra_dplane_result
-kernel_dplane_rule_update(struct zebra_dplane_ctx *ctx)
-{
- if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
+ case DPLANE_OP_RULE_ADD:
+ case DPLANE_OP_RULE_DELETE:
+ case DPLANE_OP_RULE_UPDATE:
zlog_debug("Dplane rule update op %s, if %s(%u), ctx %p",
dplane_op2str(dplane_ctx_get_op(ctx)),
dplane_ctx_get_ifname(ctx),
dplane_ctx_get_ifindex(ctx), ctx);
+ break;
+
+ case DPLANE_OP_SYS_ROUTE_ADD:
+ case DPLANE_OP_SYS_ROUTE_DELETE:
+ case DPLANE_OP_ROUTE_NOTIFY:
+ case DPLANE_OP_LSP_NOTIFY:
- return kernel_pbr_rule_update(ctx);
+ case DPLANE_OP_NONE:
+ break;
+ }
}
-static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx,
- enum zebra_dplane_result res)
+static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx)
{
+ enum zebra_dplane_result res = dplane_ctx_get_status(ctx);
+
switch (dplane_ctx_get_op(ctx)) {
case DPLANE_OP_ROUTE_INSTALL:
@@ -3868,6 +3819,27 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx,
if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
atomic_fetch_add_explicit(&zdplane_info.dg_route_errors,
1, memory_order_relaxed);
+
+ if ((dplane_ctx_get_op(ctx) != DPLANE_OP_ROUTE_DELETE)
+ && (res == ZEBRA_DPLANE_REQUEST_SUCCESS)) {
+ struct nexthop *nexthop;
+
+ /* Update installed nexthops to signal which have been
+ * installed.
+ */
+ for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx),
+ nexthop)) {
+ if (CHECK_FLAG(nexthop->flags,
+ NEXTHOP_FLAG_RECURSIVE))
+ continue;
+
+ if (CHECK_FLAG(nexthop->flags,
+ NEXTHOP_FLAG_ACTIVE)) {
+ SET_FLAG(nexthop->flags,
+ NEXTHOP_FLAG_FIB);
+ }
+ }
+ }
break;
case DPLANE_OP_NH_INSTALL:
@@ -3932,11 +3904,14 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx,
case DPLANE_OP_SYS_ROUTE_DELETE:
case DPLANE_OP_ROUTE_NOTIFY:
case DPLANE_OP_LSP_NOTIFY:
+ break;
+
case DPLANE_OP_NONE:
+ if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
+ atomic_fetch_add_explicit(&zdplane_info.dg_other_errors,
+ 1, memory_order_relaxed);
break;
}
-
- dplane_ctx_set_status(ctx, res);
}
/*
@@ -3944,7 +3919,6 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx,
*/
static int kernel_dplane_process_func(struct zebra_dplane_provider *prov)
{
- enum zebra_dplane_result res;
struct zebra_dplane_ctx *ctx, *tctx;
struct dplane_ctx_q work_list;
int counter, limit;
@@ -3958,97 +3932,21 @@ static int kernel_dplane_process_func(struct zebra_dplane_provider *prov)
dplane_provider_get_name(prov));
for (counter = 0; counter < limit; counter++) {
-
ctx = dplane_provider_dequeue_in_ctx(prov);
if (ctx == NULL)
break;
- /* A previous provider plugin may have asked to skip the
- * kernel update.
- */
- if (dplane_ctx_is_skip_kernel(ctx)) {
- res = ZEBRA_DPLANE_REQUEST_SUCCESS;
- goto skip_one;
- }
-
- /* Dispatch to appropriate kernel-facing apis */
- switch (dplane_ctx_get_op(ctx)) {
-
- case DPLANE_OP_ROUTE_INSTALL:
- case DPLANE_OP_ROUTE_UPDATE:
- case DPLANE_OP_ROUTE_DELETE:
- res = kernel_dplane_route_update(ctx);
- break;
-
- case DPLANE_OP_NH_INSTALL:
- case DPLANE_OP_NH_UPDATE:
- case DPLANE_OP_NH_DELETE:
- res = kernel_dplane_nexthop_update(ctx);
- break;
-
- case DPLANE_OP_LSP_INSTALL:
- case DPLANE_OP_LSP_UPDATE:
- case DPLANE_OP_LSP_DELETE:
- res = kernel_dplane_lsp_update(ctx);
- break;
-
- case DPLANE_OP_PW_INSTALL:
- case DPLANE_OP_PW_UNINSTALL:
- res = kernel_dplane_pw_update(ctx);
- break;
-
- case DPLANE_OP_ADDR_INSTALL:
- case DPLANE_OP_ADDR_UNINSTALL:
- res = kernel_dplane_address_update(ctx);
- break;
-
- case DPLANE_OP_MAC_INSTALL:
- case DPLANE_OP_MAC_DELETE:
- res = kernel_dplane_mac_update(ctx);
- break;
-
- case DPLANE_OP_NEIGH_INSTALL:
- case DPLANE_OP_NEIGH_UPDATE:
- case DPLANE_OP_NEIGH_DELETE:
- case DPLANE_OP_VTEP_ADD:
- case DPLANE_OP_VTEP_DELETE:
- res = kernel_dplane_neigh_update(ctx);
- break;
-
- case DPLANE_OP_RULE_ADD:
- case DPLANE_OP_RULE_DELETE:
- case DPLANE_OP_RULE_UPDATE:
- res = kernel_dplane_rule_update(ctx);
- break;
-
- /* Ignore 'notifications' - no-op */
- case DPLANE_OP_SYS_ROUTE_ADD:
- case DPLANE_OP_SYS_ROUTE_DELETE:
- case DPLANE_OP_ROUTE_NOTIFY:
- case DPLANE_OP_LSP_NOTIFY:
- res = ZEBRA_DPLANE_REQUEST_SUCCESS;
- break;
-
- default:
- atomic_fetch_add_explicit(
- &zdplane_info.dg_other_errors, 1,
- memory_order_relaxed);
-
- res = ZEBRA_DPLANE_REQUEST_FAILURE;
- break;
- }
-
- skip_one:
- /* If the request isn't pending, we can handle the result right
- * away.
- */
- if (res != ZEBRA_DPLANE_REQUEST_PENDING)
- kernel_dplane_handle_result(ctx, res);
+ if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
+ kernel_dplane_log_detail(ctx);
TAILQ_INSERT_TAIL(&work_list, ctx, zd_q_entries);
}
+ kernel_update_multi(&work_list);
+
TAILQ_FOREACH_SAFE (ctx, &work_list, zd_q_entries, tctx) {
+ kernel_dplane_handle_result(ctx);
+
TAILQ_REMOVE(&work_list, ctx, zd_q_entries);
dplane_provider_enqueue_out_ctx(prov, ctx);
}
@@ -4093,7 +3991,6 @@ static int test_dplane_process_func(struct zebra_dplane_provider *prov)
limit = dplane_provider_get_work_limit(prov);
for (counter = 0; counter < limit; counter++) {
-
ctx = dplane_provider_dequeue_in_ctx(prov);
if (ctx == NULL)
break;
diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h
index 32032ed77d..3b5eda2486 100644
--- a/zebra/zebra_dplane.h
+++ b/zebra/zebra_dplane.h
@@ -90,7 +90,6 @@ enum zebra_dplane_result {
ZEBRA_DPLANE_REQUEST_QUEUED,
ZEBRA_DPLANE_REQUEST_SUCCESS,
ZEBRA_DPLANE_REQUEST_FAILURE,
- ZEBRA_DPLANE_REQUEST_PENDING,
};
/*
diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c
index a30cea6a1e..03b8c8de1f 100644
--- a/zebra/zebra_mpls.c
+++ b/zebra/zebra_mpls.c
@@ -956,9 +956,6 @@ static wq_item_status lsp_process(struct work_queue *wq, void *data)
case ZEBRA_DPLANE_REQUEST_SUCCESS:
zvrf->lsp_installs++;
break;
- /* Should never happen */
- case ZEBRA_DPLANE_REQUEST_PENDING:
- break;
}
}
} else {
@@ -984,10 +981,6 @@ static wq_item_status lsp_process(struct work_queue *wq, void *data)
case ZEBRA_DPLANE_REQUEST_SUCCESS:
zvrf->lsp_removals++;
break;
-
- /* Should never happen */
- case ZEBRA_DPLANE_REQUEST_PENDING:
- break;
}
} else if (CHECK_FLAG(lsp->flags, LSP_FLAG_CHANGED)) {
zebra_nhlfe_t *nhlfe;
@@ -1031,10 +1024,6 @@ static wq_item_status lsp_process(struct work_queue *wq, void *data)
case ZEBRA_DPLANE_REQUEST_SUCCESS:
zvrf->lsp_installs++;
break;
-
- /* Should never happen */
- case ZEBRA_DPLANE_REQUEST_PENDING:
- break;
}
}
}
diff --git a/zebra/zebra_mpls_netlink.c b/zebra/zebra_mpls_netlink.c
index c95a021639..3b2279c66c 100644
--- a/zebra/zebra_mpls_netlink.c
+++ b/zebra/zebra_mpls_netlink.c
@@ -28,13 +28,9 @@
#include "zebra/zebra_mpls.h"
#include "zebra/kernel_netlink.h"
-/*
- * LSP forwarding update using dataplane context information.
- */
-enum zebra_dplane_result kernel_lsp_update(struct zebra_dplane_ctx *ctx)
+static ssize_t netlink_lsp_msg_encoder(struct zebra_dplane_ctx *ctx, void *buf,
+ size_t buflen)
{
- uint8_t nl_pkt[NL_PKT_BUF_SIZE];
- ssize_t ret = -1;
int cmd;
/* Call to netlink layer based on type of update */
@@ -48,26 +44,21 @@ enum zebra_dplane_result kernel_lsp_update(struct zebra_dplane_ctx *ctx)
if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_MPLS)
zlog_debug("LSP in-label %u: update fails, no best NHLFE",
dplane_ctx_get_in_label(ctx));
- goto done;
+ return -1;
}
cmd = RTM_NEWROUTE;
} else
/* Invalid op? */
- goto done;
-
- ret = netlink_mpls_multipath_msg_encode(cmd, ctx, nl_pkt,
- sizeof(nl_pkt));
- if (ret <= 0)
- return ZEBRA_DPLANE_REQUEST_FAILURE;
-
- ret = netlink_talk_info(netlink_talk_filter, (struct nlmsghdr *)nl_pkt,
- dplane_ctx_get_ns(ctx), 0);
+ return -1;
-done:
+ return netlink_mpls_multipath_msg_encode(cmd, ctx, buf, buflen);
+}
- return (ret == 0 ?
- ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE);
+enum netlink_msg_status netlink_put_lsp_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx)
+{
+ return netlink_batch_add_msg(bth, ctx, netlink_lsp_msg_encoder, false);
}
/*
@@ -75,9 +66,10 @@ done:
* but note that the default has been to report 'success' for pw updates
* on unsupported platforms.
*/
-enum zebra_dplane_result kernel_pw_update(struct zebra_dplane_ctx *ctx)
+enum netlink_msg_status netlink_put_pw_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx)
{
- return ZEBRA_DPLANE_REQUEST_SUCCESS;
+ return FRR_NETLINK_SUCCESS;
}
int mpls_kernel_init(void)
diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h
index dd2d7a190d..888d2fcfa0 100644
--- a/zebra/zebra_pbr.h
+++ b/zebra/zebra_pbr.h
@@ -172,13 +172,6 @@ void zebra_pbr_add_iptable(struct zebra_pbr_iptable *iptable);
void zebra_pbr_del_iptable(struct zebra_pbr_iptable *iptable);
/*
- * Add, update or delete a rule from the
- * kernel, using info from a dataplane context.
- */
-extern enum zebra_dplane_result
-kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx);
-
-/*
* Get to know existing PBR rules in the kernel - typically called at startup.
*/
extern void kernel_read_pbr_rules(struct zebra_ns *zns);
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index afae449e7c..55dccc4a12 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -497,9 +497,6 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re,
if (zvrf)
zvrf->installs++;
break;
- /* Should never happen */
- case ZEBRA_DPLANE_REQUEST_PENDING:
- break;
}
return;
@@ -544,9 +541,6 @@ void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re)
if (zvrf)
zvrf->removals++;
break;
- /* Should never happen */
- case ZEBRA_DPLANE_REQUEST_PENDING:
- break;
}
return;
diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c
index fbe6ce70a0..b6d0b26125 100644
--- a/zebra/zebra_vty.c
+++ b/zebra/zebra_vty.c
@@ -57,6 +57,7 @@
#include "zebra/interface.h"
#include "northbound_cli.h"
#include "zebra/zebra_nb.h"
+#include "zebra/kernel_netlink.h"
extern int allow_delete;
@@ -3397,6 +3398,11 @@ static int config_write_protocol(struct vty *vty)
if (!zebra_nhg_kernel_nexthops_enabled())
vty_out(vty, "no zebra nexthop kernel enable\n");
+#ifdef HAVE_NETLINK
+ /* Include netlink info */
+ netlink_config_write_helper(vty);
+#endif /* HAVE_NETLINK */
+
return 1;
}
@@ -3719,6 +3725,44 @@ DEFUN_HIDDEN (show_frr,
return CMD_SUCCESS;
}
+#ifdef HAVE_NETLINK
+DEFUN_HIDDEN(zebra_kernel_netlink_batch_tx_buf,
+ zebra_kernel_netlink_batch_tx_buf_cmd,
+ "zebra kernel netlink batch-tx-buf (1-1048576) (1-1048576)",
+ ZEBRA_STR
+ "Zebra kernel interface\n"
+ "Set Netlink parameters\n"
+ "Set batch buffer size and send threshold\n"
+ "Size of the buffer\n"
+ "Send threshold\n")
+{
+ uint32_t bufsize = 0, threshold = 0;
+
+ bufsize = strtoul(argv[4]->arg, NULL, 10);
+ threshold = strtoul(argv[5]->arg, NULL, 10);
+
+ netlink_set_batch_buffer_size(bufsize, threshold, true);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_HIDDEN(no_zebra_kernel_netlink_batch_tx_buf,
+ no_zebra_kernel_netlink_batch_tx_buf_cmd,
+ "no zebra kernel netlink batch-tx-buf [(0-1048576)] [(0-1048576)]",
+ NO_STR ZEBRA_STR
+ "Zebra kernel interface\n"
+ "Set Netlink parameters\n"
+ "Set batch buffer size and send threshold\n"
+ "Size of the buffer\n"
+ "Send threshold\n")
+{
+ netlink_set_batch_buffer_size(0, 0, false);
+
+ return CMD_SUCCESS;
+}
+
+#endif /* HAVE_NETLINK */
+
/* IP node for static routes. */
static int zebra_ip_config(struct vty *vty);
static struct cmd_node ip_node = {
@@ -3856,5 +3900,10 @@ void zebra_vty_init(void)
install_element(CONFIG_NODE, &zebra_dplane_queue_limit_cmd);
install_element(CONFIG_NODE, &no_zebra_dplane_queue_limit_cmd);
+#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);
+#endif /* HAVE_NETLINK */
+
install_element(VIEW_NODE, &zebra_show_routing_tables_summary_cmd);
}