zebra: move iptable handling in zebra_dplane

The iptable processing was not handled in remote dataplane, and was
directly processed by the thread in charge of zapi calls. Now that call
can be handled in the zebra_dplane separate thread. once a
zebra_dplane_ctx is allocated for iptable handling, the hook call is
performed later. Subsequently, a return code may be triggered to zclient
interface if any problem occurs when calling the hook call.

Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
This commit is contained in:
Philippe Guibert 2021-02-16 15:29:29 +01:00
parent c103ac43de
commit 5162e00045
9 changed files with 232 additions and 47 deletions

View File

@ -1350,6 +1350,10 @@ static enum netlink_msg_status nl_put_msg(struct nl_batch *bth,
case DPLANE_OP_BR_PORT_UPDATE:
return FRR_NETLINK_SUCCESS;
case DPLANE_OP_IPTABLE_ADD:
case DPLANE_OP_IPTABLE_DELETE:
return FRR_NETLINK_ERROR;
case DPLANE_OP_NONE:
return FRR_NETLINK_ERROR;
}

View File

@ -886,7 +886,7 @@ void zsend_ipset_notify_owner(struct zebra_pbr_ipset *ipset,
s = stream_new(ZEBRA_MAX_PACKET_SIZ);
zclient_create_header(s, ZEBRA_IPSET_NOTIFY_OWNER, VRF_DEFAULT);
zclient_create_header(s, cmd, VRF_DEFAULT);
stream_put(s, &note, sizeof(note));
stream_putl(s, ipset->unique);
stream_put(s, ipset->ipset_name, ZEBRA_IPSET_NAME_SIZE);
@ -924,18 +924,24 @@ void zsend_ipset_entry_notify_owner(struct zebra_pbr_ipset_entry *ipset,
zserv_send_message(client, s);
}
void zsend_iptable_notify_owner(struct zebra_pbr_iptable *iptable,
enum zapi_iptable_notify_owner note)
void zsend_iptable_notify_owner(const struct zebra_dplane_ctx *ctx,
uint16_t note)
{
struct listnode *node;
struct zserv *client;
struct stream *s;
struct zebra_pbr_iptable ipt;
uint16_t cmd = ZEBRA_IPTABLE_NOTIFY_OWNER;
if (!dplane_ctx_get_pbr_iptable(ctx, &ipt))
return;
if (IS_ZEBRA_DEBUG_PACKET)
zlog_debug("%s: Notifying %u", __func__, iptable->unique);
zlog_debug("%s: Notifying %s id %u note %u", __func__,
zserv_command_string(cmd), ipt.unique, note);
for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) {
if (iptable->sock == client->sock)
if (ipt.sock == client->sock)
break;
}
@ -944,9 +950,9 @@ void zsend_iptable_notify_owner(struct zebra_pbr_iptable *iptable,
s = stream_new(ZEBRA_MAX_PACKET_SIZ);
zclient_create_header(s, ZEBRA_IPTABLE_NOTIFY_OWNER, VRF_DEFAULT);
zclient_create_header(s, cmd, VRF_DEFAULT);
stream_put(s, &note, sizeof(note));
stream_putl(s, iptable->unique);
stream_putl(s, ipt.unique);
stream_putw_at(s, 0, stream_get_endp(s));
zserv_send_message(client, s);

View File

@ -89,7 +89,7 @@ extern void zsend_ipset_notify_owner(struct zebra_pbr_ipset *ipset,
extern void
zsend_ipset_entry_notify_owner(struct zebra_pbr_ipset_entry *ipset,
enum zapi_ipset_entry_notify_owner note);
extern void zsend_iptable_notify_owner(struct zebra_pbr_iptable *iptable,
extern void zsend_iptable_notify_owner(const struct zebra_dplane_ctx *ctx,
enum zapi_iptable_notify_owner note);
extern bool zserv_nexthop_num_warn(const char *caller, const struct prefix *p,
const unsigned int nexthop_num);

View File

@ -42,6 +42,7 @@
DEFINE_MTYPE_STATIC(ZEBRA, DP_CTX, "Zebra DPlane Ctx")
DEFINE_MTYPE_STATIC(ZEBRA, DP_INTF, "Zebra DPlane Intf")
DEFINE_MTYPE_STATIC(ZEBRA, DP_PROV, "Zebra DPlane Provider")
DEFINE_MTYPE_STATIC(ZEBRA, DP_IFACE, "Zebra DPlane IFACE")
#ifndef AOK
# define AOK 0
@ -307,6 +308,7 @@ struct zebra_dplane_ctx {
struct dplane_mac_info macinfo;
struct dplane_neigh_info neigh;
struct dplane_rule_info rule;
struct zebra_pbr_iptable iptable;
} u;
/* Namespace info, used especially for netlink kernel communication */
@ -438,6 +440,8 @@ static struct zebra_dplane_globals {
_Atomic uint32_t dg_update_yields;
_Atomic uint32_t dg_iptable_in;
_Atomic uint32_t dg_iptable_errors;
/* Dataplane pthread */
struct frr_pthread *dg_pthread;
@ -657,6 +661,23 @@ static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx)
case DPLANE_OP_BR_PORT_UPDATE:
case DPLANE_OP_NONE:
break;
case DPLANE_OP_IPTABLE_ADD:
case DPLANE_OP_IPTABLE_DELETE:
if (ctx->u.iptable.interface_name_list) {
struct listnode *node, *nnode;
char *ifname;
for (ALL_LIST_ELEMENTS(
ctx->u.iptable.interface_name_list, node,
nnode, ifname)) {
LISTNODE_DETACH(
ctx->u.iptable.interface_name_list,
node);
XFREE(MTYPE_DP_IFACE, ifname);
}
list_delete(&ctx->u.iptable.interface_name_list);
}
}
}
@ -895,6 +916,13 @@ const char *dplane_op2str(enum dplane_op_e op)
case DPLANE_OP_NEIGH_DISCOVER:
ret = "NEIGH_DISCOVER";
break;
case DPLANE_OP_IPTABLE_ADD:
ret = "IPTABLE_ADD";
break;
case DPLANE_OP_IPTABLE_DELETE:
ret = "IPTABLE_DELETE";
break;
}
return ret;
@ -1843,6 +1871,17 @@ dplane_ctx_get_br_port_backup_nhg_id(const struct zebra_dplane_ctx *ctx)
return ctx->u.br_port.backup_nhg_id;
}
/* Accessors for PBR iptable information */
bool
dplane_ctx_get_pbr_iptable(const struct zebra_dplane_ctx *ctx,
struct zebra_pbr_iptable *table)
{
DPLANE_CTX_VALID(ctx);
memcpy(table, &ctx->u.iptable, sizeof(struct zebra_pbr_iptable));
return true;
}
/*
* End of dplane context accessors
*/
@ -2398,6 +2437,52 @@ static int dplane_ctx_rule_init(struct zebra_dplane_ctx *ctx,
return AOK;
}
/**
* dplane_ctx_iptable_init() - Initialize a context block for a PBR iptable
* update.
*
* @ctx: Dataplane context to init
* @op: Operation being performed
* @new_rule: PBR iptable
*
* Return: Result status
*/
static int dplane_ctx_iptable_init(struct zebra_dplane_ctx *ctx,
enum dplane_op_e op,
struct zebra_pbr_iptable *iptable)
{
char *ifname;
struct listnode *node;
if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
zlog_debug(
"init dplane ctx %s: Unique %u Fwmark %u Family %s Action %s",
dplane_op2str(op), iptable->unique, iptable->fwmark,
family2str(iptable->family),
iptable->action == ZEBRA_IPTABLES_DROP ? "Drop"
: "Forward");
}
ctx->zd_op = op;
ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS;
dplane_ctx_ns_init(ctx, zebra_ns_lookup(NS_DEFAULT), false);
ctx->zd_is_update = false;
ctx->zd_vrf_id = iptable->vrf_id;
memcpy(&ctx->u.iptable, iptable, sizeof(struct zebra_pbr_iptable));
ctx->u.iptable.interface_name_list = NULL;
if (iptable->nb_interface > 0) {
ctx->u.iptable.interface_name_list = list_new();
for (ALL_LIST_ELEMENTS_RO(iptable->interface_name_list, node,
ifname)) {
listnode_add(ctx->u.iptable.interface_name_list,
XSTRDUP(MTYPE_DP_IFACE, ifname));
}
}
return AOK;
}
/*
* Enqueue a new update,
* and ensure an event is active for the dataplane pthread.
@ -3608,6 +3693,50 @@ enum zebra_dplane_result dplane_pbr_rule_update(struct zebra_pbr_rule *old_rule,
{
return rule_update_internal(DPLANE_OP_RULE_UPDATE, new_rule, old_rule);
}
/*
* Common helper api for iptable updates
*/
static enum zebra_dplane_result
iptable_update_internal(enum dplane_op_e op, struct zebra_pbr_iptable *iptable)
{
enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
struct zebra_dplane_ctx *ctx;
int ret;
ctx = dplane_ctx_alloc();
ret = dplane_ctx_iptable_init(ctx, op, iptable);
if (ret != AOK)
goto done;
ret = dplane_update_enqueue(ctx);
done:
atomic_fetch_add_explicit(&zdplane_info.dg_iptable_in, 1,
memory_order_relaxed);
if (ret == AOK)
result = ZEBRA_DPLANE_REQUEST_QUEUED;
else {
atomic_fetch_add_explicit(&zdplane_info.dg_iptable_errors, 1,
memory_order_relaxed);
dplane_ctx_free(&ctx);
}
return result;
}
enum zebra_dplane_result
dplane_pbr_iptable_add(struct zebra_pbr_iptable *iptable)
{
return iptable_update_internal(DPLANE_OP_IPTABLE_ADD, iptable);
}
enum zebra_dplane_result
dplane_pbr_iptable_delete(struct zebra_pbr_iptable *iptable)
{
return iptable_update_internal(DPLANE_OP_IPTABLE_DELETE, iptable);
}
/*
* Handler for 'show dplane'
@ -3693,6 +3822,12 @@ int dplane_show_helper(struct vty *vty, bool detailed)
vty_out(vty, "Bridge port updates: %" PRIu64 "\n", incoming);
vty_out(vty, "Bridge port errors: %" PRIu64 "\n", errs);
incoming = atomic_load_explicit(&zdplane_info.dg_iptable_in,
memory_order_relaxed);
errs = atomic_load_explicit(&zdplane_info.dg_iptable_errors,
memory_order_relaxed);
vty_out(vty, "IPtable updates: %" PRIu64 "\n", incoming);
vty_out(vty, "IPtable errors: %" PRIu64 "\n", errs);
return CMD_SUCCESS;
}
@ -4103,6 +4238,15 @@ static void kernel_dplane_log_detail(struct zebra_dplane_ctx *ctx)
case DPLANE_OP_NONE:
break;
case DPLANE_OP_IPTABLE_ADD:
case DPLANE_OP_IPTABLE_DELETE: {
struct zebra_pbr_iptable ipt;
if (dplane_ctx_get_pbr_iptable(ctx, &ipt))
zlog_debug("Dplane iptable update op %s, unique(%u), ctx %p",
dplane_op2str(dplane_ctx_get_op(ctx)), ipt.unique, ctx);
} break;
}
}
@ -4199,6 +4343,14 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx)
1, memory_order_relaxed);
break;
case DPLANE_OP_IPTABLE_ADD:
case DPLANE_OP_IPTABLE_DELETE:
if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
atomic_fetch_add_explicit(
&zdplane_info.dg_iptable_errors, 1,
memory_order_relaxed);
break;
/* Ignore 'notifications' - no-op */
case DPLANE_OP_SYS_ROUTE_ADD:
case DPLANE_OP_SYS_ROUTE_DELETE:
@ -4215,6 +4367,13 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx)
}
}
static void kernel_dplane_process_iptable(struct zebra_dplane_provider *prov,
struct zebra_dplane_ctx *ctx)
{
zebra_pbr_process_iptable(ctx);
dplane_provider_enqueue_out_ctx(prov, ctx);
}
/*
* Kernel provider callback
*/
@ -4236,11 +4395,14 @@ static int kernel_dplane_process_func(struct zebra_dplane_provider *prov)
ctx = dplane_provider_dequeue_in_ctx(prov);
if (ctx == NULL)
break;
if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
kernel_dplane_log_detail(ctx);
TAILQ_INSERT_TAIL(&work_list, ctx, zd_q_entries);
if ((dplane_ctx_get_op(ctx) == DPLANE_OP_IPTABLE_ADD ||
dplane_ctx_get_op(ctx) == DPLANE_OP_IPTABLE_DELETE))
kernel_dplane_process_iptable(prov, ctx);
else
TAILQ_INSERT_TAIL(&work_list, ctx, zd_q_entries);
}
kernel_update_multi(&work_list);

View File

@ -155,6 +155,10 @@ enum dplane_op_e {
/* bridge port update */
DPLANE_OP_BR_PORT_UPDATE,
/* Policy based routing rule update */
DPLANE_OP_IPTABLE_ADD,
DPLANE_OP_IPTABLE_DELETE,
};
/*
@ -475,7 +479,11 @@ const struct prefix *
dplane_ctx_rule_get_dst_ip(const struct zebra_dplane_ctx *ctx);
const struct prefix *
dplane_ctx_rule_get_old_dst_ip(const struct zebra_dplane_ctx *ctx);
/* Accessors for policy based routing iptable information */
struct zebra_pbr_iptable;
bool
dplane_ctx_get_pbr_iptable(const struct zebra_dplane_ctx *ctx,
struct zebra_pbr_iptable *table);
/* Accessors for bridge port information */
uint32_t dplane_ctx_get_br_port_flags(const struct zebra_dplane_ctx *ctx);
uint32_t
@ -648,6 +656,11 @@ enum zebra_dplane_result dplane_pbr_rule_delete(struct zebra_pbr_rule *rule);
enum zebra_dplane_result
dplane_pbr_rule_update(struct zebra_pbr_rule *old_rule,
struct zebra_pbr_rule *new_rule);
/* iptable */
enum zebra_dplane_result
dplane_pbr_iptable_add(struct zebra_pbr_iptable *iptable);
enum zebra_dplane_result
dplane_pbr_iptable_delete(struct zebra_pbr_iptable *iptable);
/* Encode route information into data plane context. */
int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,

View File

@ -2715,6 +2715,8 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx)
case DPLANE_OP_NEIGH_DISCOVER:
case DPLANE_OP_BR_PORT_UPDATE:
case DPLANE_OP_NONE:
case DPLANE_OP_IPTABLE_ADD:
case DPLANE_OP_IPTABLE_DELETE:
break;
}

View File

@ -542,6 +542,25 @@ void zebra_pbr_del_rule(struct zebra_pbr_rule *rule)
__func__);
}
void zebra_pbr_process_iptable(struct zebra_dplane_ctx *ctx)
{
int mode, ret = 0;
struct zebra_pbr_iptable ipt;
if (dplane_ctx_get_op(ctx) == DPLANE_OP_IPTABLE_ADD)
mode = 1;
else
mode = 0;
if (dplane_ctx_get_pbr_iptable(ctx, &ipt)) {
ret = hook_call(zebra_pbr_iptable_update, mode, &ipt);
if (ret)
dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS);
}
if (!ret)
dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_FAILURE);
}
static void zebra_pbr_cleanup_rules(struct hash_bucket *b, void *data)
{
struct zebra_pbr_rule *rule = b->data;
@ -761,13 +780,8 @@ static void *pbr_iptable_alloc_intern(void *arg)
void zebra_pbr_add_iptable(struct zebra_pbr_iptable *iptable)
{
int ret;
(void)hash_get(zrouter.iptable_hash, iptable, pbr_iptable_alloc_intern);
ret = hook_call(zebra_pbr_iptable_update, 1, iptable);
kernel_pbr_iptable_add_del_status(iptable,
ret ? ZEBRA_DPLANE_INSTALL_SUCCESS
: ZEBRA_DPLANE_INSTALL_FAILURE);
(void)dplane_pbr_iptable_add(iptable);
}
void zebra_pbr_del_iptable(struct zebra_pbr_iptable *iptable)
@ -775,7 +789,7 @@ void zebra_pbr_del_iptable(struct zebra_pbr_iptable *iptable)
struct zebra_pbr_iptable *lookup;
lookup = hash_lookup(zrouter.iptable_hash, iptable);
hook_call(zebra_pbr_iptable_update, 0, iptable);
(void)dplane_pbr_iptable_delete(iptable);
if (lookup) {
struct listnode *node, *nnode;
char *name;
@ -812,6 +826,16 @@ void zebra_pbr_dplane_result(struct zebra_dplane_ctx *ctx)
zsend_rule_notify_owner(ctx, res == ZEBRA_DPLANE_REQUEST_SUCCESS
? ZAPI_RULE_REMOVED
: ZAPI_RULE_FAIL_REMOVE);
else if (op == DPLANE_OP_IPTABLE_ADD)
zsend_iptable_notify_owner(ctx,
res == ZEBRA_DPLANE_REQUEST_SUCCESS
? ZAPI_IPTABLE_INSTALLED
: ZAPI_IPTABLE_FAIL_INSTALL);
else if (op == DPLANE_OP_IPTABLE_DELETE)
zsend_iptable_notify_owner(ctx,
res == ZEBRA_DPLANE_REQUEST_SUCCESS
? ZAPI_IPTABLE_REMOVED
: ZAPI_IPTABLE_FAIL_REMOVE);
else
flog_err(
EC_ZEBRA_PBR_RULE_UPDATE,
@ -875,32 +899,6 @@ void kernel_pbr_ipset_entry_add_del_status(
}
}
/*
* Handle success or failure of ipset (un)install in the kernel.
*/
void kernel_pbr_iptable_add_del_status(struct zebra_pbr_iptable *iptable,
enum zebra_dplane_status res)
{
switch (res) {
case ZEBRA_DPLANE_INSTALL_SUCCESS:
zsend_iptable_notify_owner(iptable, ZAPI_IPTABLE_INSTALLED);
break;
case ZEBRA_DPLANE_INSTALL_FAILURE:
zsend_iptable_notify_owner(iptable, ZAPI_IPTABLE_FAIL_INSTALL);
break;
case ZEBRA_DPLANE_DELETE_SUCCESS:
zsend_iptable_notify_owner(iptable,
ZAPI_IPTABLE_REMOVED);
break;
case ZEBRA_DPLANE_DELETE_FAILURE:
zsend_iptable_notify_owner(iptable,
ZAPI_IPTABLE_FAIL_REMOVE);
break;
case ZEBRA_DPLANE_STATUS_NONE:
break;
}
}
/*
* Handle rule delete notification from kernel.
*/

View File

@ -177,6 +177,7 @@ 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);
void zebra_pbr_process_iptable(struct zebra_dplane_ctx *ctx);
/*
* Get to know existing PBR rules in the kernel - typically called at startup.
@ -198,9 +199,6 @@ extern void kernel_pbr_ipset_entry_add_del_status(
struct zebra_pbr_ipset_entry *ipset,
enum zebra_dplane_status res);
extern void kernel_pbr_iptable_add_del_status(struct zebra_pbr_iptable *iptable,
enum zebra_dplane_status res);
/*
* Handle rule delete notification from kernel.
*/

View File

@ -3887,6 +3887,8 @@ static int rib_process_dplane_results(struct thread *thread)
case DPLANE_OP_RULE_ADD:
case DPLANE_OP_RULE_DELETE:
case DPLANE_OP_RULE_UPDATE:
case DPLANE_OP_IPTABLE_ADD:
case DPLANE_OP_IPTABLE_DELETE:
zebra_pbr_dplane_result(ctx);
break;