diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index c77a357e9f..8dd0c14d5d 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -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; } diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 46171df848..92a11cc4bf 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -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, ¬e, 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, ¬e, 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); diff --git a/zebra/zapi_msg.h b/zebra/zapi_msg.h index c03278669a..2401db4d18 100644 --- a/zebra/zapi_msg.h +++ b/zebra/zapi_msg.h @@ -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); diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index b9b6b4af71..b9e6df2a7a 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -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); diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index f3ab1058a5..3416c62b84 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -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, diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 2864b96c83..3fafa19a70 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -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; } diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index 87ab900092..51c3bd9fdd 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -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. */ diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h index e7504a3547..1e025bab64 100644 --- a/zebra/zebra_pbr.h +++ b/zebra/zebra_pbr.h @@ -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. */ diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index c5d977017e..b6303485fa 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -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;