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 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 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; } - /* Nothing to do if the kernel doesn't support nexthop objects */ - if (!kernel_nexthops_supported()) - return ZEBRA_DPLANE_REQUEST_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 (ret == 0 ? ZEBRA_DPLANE_REQUEST_SUCCESS - : ZEBRA_DPLANE_REQUEST_FAILURE); + return netlink_nexthop_msg_encode(cmd, ctx, buf, buflen); } -/* - * 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) +enum netlink_msg_status +netlink_put_nexthop_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx) { - int cmd, ret; + /* Nothing to do if the kernel doesn't support nexthop objects */ + if (!kernel_nexthops_supported()) + return FRR_NETLINK_SUCCESS; + + return netlink_batch_add_msg(bth, ctx, netlink_nexthop_msg_encoder, + false); +} + +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); +} + +static ssize_t netlink_delroute_msg_encoder(struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) +{ + 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_neigh_update_msg_encode( + ctx, cmd, mac, ip, true, family, RTN_UNICAST, flags, state, + 0 /*nhg*/, false /*nfy*/, 0 /*nfy_flags*/, buf, buflen); +} - return netlink_talk_info(netlink_talk_filter, (struct nlmsghdr *)nl_pkt, - dplane_ctx_get_ns(ctx), 0); +static ssize_t netlink_neigh_msg_encoder(struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) +{ + 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, buf, buflen); + break; + case DPLANE_OP_NEIGH_DELETE: + 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, buf, + buflen); + break; + case DPLANE_OP_VTEP_DELETE: + ret = netlink_vxlan_flood_update_ctx(ctx, RTM_DELNEIGH, buf, + buflen); + break; + default: + ret = -1; + } + + return ret; } /* * Update MAC, using dataplane context object. */ -enum zebra_dplane_result kernel_mac_update_ctx(struct zebra_dplane_ctx *ctx) + +enum netlink_msg_status netlink_put_mac_update_msg(struct nl_batch *bth, + 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_batch_add_msg(bth, ctx, netlink_macfdb_update_ctx, + false); } -enum zebra_dplane_result kernel_neigh_update_ctx(struct zebra_dplane_ctx *ctx) +enum netlink_msg_status +netlink_put_neigh_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx) { - int ret = -1; - - switch (dplane_ctx_get_op(ctx)) { - case DPLANE_OP_NEIGH_INSTALL: - case DPLANE_OP_NEIGH_UPDATE: - ret = netlink_neigh_update_ctx(ctx, RTM_NEWNEIGH); - break; - case DPLANE_OP_NEIGH_DELETE: - ret = netlink_neigh_update_ctx(ctx, RTM_DELNEIGH); - break; - case DPLANE_OP_VTEP_ADD: - ret = netlink_vxlan_flood_update_ctx(ctx, RTM_NEWNEIGH); - break; - case DPLANE_OP_VTEP_DELETE: - ret = netlink_vxlan_flood_update_ctx(ctx, RTM_DELNEIGH); - break; - default: - break; - } - - return (ret == 0 ? - ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE); + 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; - 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); -} -/* 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 dplane_op_e op; - int cmd; - int 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) + if (dplane_ctx_get_op(ctx) == DPLANE_OP_RULE_DELETE) cmd = RTM_DELRULE; - else { - flog_err( - EC_ZEBRA_PBR_RULE_UPDATE, - "Context received for kernel rule update with incorrect OP code (%u)", - op); - return ZEBRA_DPLANE_REQUEST_FAILURE; - } - ret = netlink_rule_update_internal( + 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)); + 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 */ + +enum netlink_msg_status +netlink_put_rule_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx) +{ + enum dplane_op_e op; + enum netlink_msg_status ret; + + op = dplane_ctx_get_op(ctx); + 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 FRR_NETLINK_ERROR; + } + + 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)); + netlink_batch_add_msg(bth, ctx, netlink_oldrule_msg_encoder, + true); - - return (ret == 0 ? ZEBRA_DPLANE_REQUEST_SUCCESS - : ZEBRA_DPLANE_REQUEST_FAILURE); + 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)); + switch (dplane_ctx_get_op(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]; - - 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))); - } + dplane_ctx_get_vrf(ctx), buf, ctx, + dplane_op2str(dplane_ctx_get_op(ctx))); + break; - 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); -} - -/** - * 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)); - } + break; - return kernel_neigh_update_ctx(ctx); -} - -/* - * 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; - return kernel_pbr_rule_update(ctx); + case DPLANE_OP_SYS_ROUTE_ADD: + case DPLANE_OP_SYS_ROUTE_DELETE: + case DPLANE_OP_ROUTE_NOTIFY: + case DPLANE_OP_LSP_NOTIFY: + + 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; + return -1; - ret = netlink_mpls_multipath_msg_encode(cmd, ctx, nl_pkt, - sizeof(nl_pkt)); - if (ret <= 0) - return ZEBRA_DPLANE_REQUEST_FAILURE; + return netlink_mpls_multipath_msg_encode(cmd, ctx, buf, buflen); +} - ret = netlink_talk_info(netlink_talk_filter, (struct nlmsghdr *)nl_pkt, - dplane_ctx_get_ns(ctx), 0); - -done: - - 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 @@ -171,13 +171,6 @@ void zebra_pbr_del_ipset_entry(struct zebra_pbr_ipset_entry *ipset); 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. */ 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); }