Merge pull request #6755 from xThaid/dplane_batching

zebra: dataplane batching
This commit is contained in:
Donald Sharp 2020-08-11 13:59:12 -04:00 committed by GitHub
commit 659d56e13f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 3912 additions and 487 deletions

View File

@ -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.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,2 @@
int r1-eth0
ip address 192.168.1.1/24

View File

@ -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))

View File

@ -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)

View File

@ -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.
*

View File

@ -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)

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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);
}
/*

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}
/*

View File

@ -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

View File

@ -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;

View File

@ -90,7 +90,6 @@ enum zebra_dplane_result {
ZEBRA_DPLANE_REQUEST_QUEUED,
ZEBRA_DPLANE_REQUEST_SUCCESS,
ZEBRA_DPLANE_REQUEST_FAILURE,
ZEBRA_DPLANE_REQUEST_PENDING,
};
/*

View File

@ -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;
}
}
}

View File

@ -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)

View File

@ -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.
*/

View File

@ -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;

View File

@ -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);
}