diff --git a/lib/if_rmap.c b/lib/if_rmap.c index ae9c107187..5895924a5e 100644 --- a/lib/if_rmap.c +++ b/lib/if_rmap.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* route-map for interface. * Copyright (C) 1999 Kunihiro Ishiguro + * Copyright (C) 2023 LabN Consulting, L.L.C. */ #include @@ -10,7 +11,9 @@ #include "memory.h" #include "if.h" #include "if_rmap.h" -#include "ripd/ripd.h" +#include "northbound_cli.h" + +#include "lib/if_rmap_clippy.c" DEFINE_MTYPE_STATIC(LIB, IF_RMAP_CTX, "Interface route map container"); DEFINE_MTYPE_STATIC(LIB, IF_RMAP_CTX_NAME, @@ -18,8 +21,6 @@ DEFINE_MTYPE_STATIC(LIB, IF_RMAP_CTX_NAME, DEFINE_MTYPE_STATIC(LIB, IF_RMAP, "Interface route map"); DEFINE_MTYPE_STATIC(LIB, IF_RMAP_NAME, "I.f. route map name"); -static struct list *if_rmap_ctx_list; - static struct if_rmap *if_rmap_new(void) { struct if_rmap *new; @@ -31,7 +32,9 @@ static struct if_rmap *if_rmap_new(void) static void if_rmap_free(struct if_rmap *if_rmap) { - XFREE(MTYPE_IF_RMAP_NAME, if_rmap->ifname); + char *no_const_ifname = (char *)if_rmap->ifname; + + XFREE(MTYPE_IF_RMAP_NAME, no_const_ifname); XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_IN]); XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_OUT]); @@ -41,16 +44,11 @@ static void if_rmap_free(struct if_rmap *if_rmap) struct if_rmap *if_rmap_lookup(struct if_rmap_ctx *ctx, const char *ifname) { - struct if_rmap key; + struct if_rmap key = {.ifname = ifname}; struct if_rmap *if_rmap; - /* temporary copy */ - key.ifname = (ifname) ? XSTRDUP(MTYPE_IF_RMAP_NAME, ifname) : NULL; - if_rmap = hash_lookup(ctx->ifrmaphash, &key); - XFREE(MTYPE_IF_RMAP_NAME, key.ifname); - return if_rmap; } @@ -80,16 +78,11 @@ static void *if_rmap_hash_alloc(void *arg) static struct if_rmap *if_rmap_get(struct if_rmap_ctx *ctx, const char *ifname) { - struct if_rmap key; + struct if_rmap key = {.ifname = ifname}; struct if_rmap *ret; - /* temporary copy */ - key.ifname = (ifname) ? XSTRDUP(MTYPE_IF_RMAP_NAME, ifname) : NULL; - ret = hash_get(ctx->ifrmaphash, &key, if_rmap_hash_alloc); - XFREE(MTYPE_IF_RMAP_NAME, key.ifname); - return ret; } @@ -108,58 +101,32 @@ static bool if_rmap_hash_cmp(const void *arg1, const void *arg2) return strcmp(if_rmap1->ifname, if_rmap2->ifname) == 0; } -static struct if_rmap *if_rmap_set(struct if_rmap_ctx *ctx, - const char *ifname, enum if_rmap_type type, - const char *routemap_name) +static void if_rmap_set(struct if_rmap_ctx *ctx, const char *ifname, + enum if_rmap_type type, const char *routemap_name) { - struct if_rmap *if_rmap; + struct if_rmap *if_rmap = if_rmap_get(ctx, ifname); - if_rmap = if_rmap_get(ctx, ifname); - - if (type == IF_RMAP_IN) { - XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_IN]); - if_rmap->routemap[IF_RMAP_IN] = - XSTRDUP(MTYPE_IF_RMAP_NAME, routemap_name); - } - if (type == IF_RMAP_OUT) { - XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_OUT]); - if_rmap->routemap[IF_RMAP_OUT] = - XSTRDUP(MTYPE_IF_RMAP_NAME, routemap_name); - } + assert(type == IF_RMAP_IN || type == IF_RMAP_OUT); + XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[type]); + if_rmap->routemap[type] = XSTRDUP(MTYPE_IF_RMAP_NAME, routemap_name); if (ctx->if_rmap_add_hook) (ctx->if_rmap_add_hook)(ctx, if_rmap); - - return if_rmap; } -static int if_rmap_unset(struct if_rmap_ctx *ctx, - const char *ifname, enum if_rmap_type type, - const char *routemap_name) +static void if_rmap_unset(struct if_rmap_ctx *ctx, const char *ifname, + enum if_rmap_type type) { - struct if_rmap *if_rmap; + struct if_rmap *if_rmap = if_rmap_lookup(ctx, ifname); - if_rmap = if_rmap_lookup(ctx, ifname); if (!if_rmap) - return 0; + return; - if (type == IF_RMAP_IN) { - if (!if_rmap->routemap[IF_RMAP_IN]) - return 0; - if (strcmp(if_rmap->routemap[IF_RMAP_IN], routemap_name) != 0) - return 0; + assert(type == IF_RMAP_IN || type == IF_RMAP_OUT); + if (!if_rmap->routemap[type]) + return; - XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_IN]); - } - - if (type == IF_RMAP_OUT) { - if (!if_rmap->routemap[IF_RMAP_OUT]) - return 0; - if (strcmp(if_rmap->routemap[IF_RMAP_OUT], routemap_name) != 0) - return 0; - - XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_OUT]); - } + XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[type]); if (ctx->if_rmap_delete_hook) ctx->if_rmap_delete_hook(ctx, if_rmap); @@ -169,80 +136,131 @@ static int if_rmap_unset(struct if_rmap_ctx *ctx, hash_release(ctx->ifrmaphash, if_rmap); if_rmap_free(if_rmap); } - - return 1; } -DEFUN (if_rmap, - if_rmap_cmd, - "route-map RMAP_NAME IFNAME", - "Route map set\n" - "Route map name\n" - "Route map set for input filtering\n" - "Route map set for output filtering\n" - "Route map interface name\n") +static int if_route_map_handler(struct vty *vty, bool no, const char *dir, + const char *other_dir, const char *ifname, + const char *route_map) { - int idx_rmap_name = 1; - int idx_in_out = 2; - int idx_ifname = 3; - enum if_rmap_type type; - struct if_rmap_ctx *ctx; + enum nb_operation op = no ? NB_OP_DESTROY : NB_OP_MODIFY; const struct lyd_node *dnode; - struct rip *rip; + char xpath[XPATH_MAXLEN]; - dnode = yang_dnode_get(running_config->dnode, VTY_CURR_XPATH); - rip = nb_running_get_entry(dnode, NULL, true); - ctx = rip->if_rmap_ctx; - - if (strncmp(argv[idx_in_out]->text, "in", 1) == 0) - type = IF_RMAP_IN; - else if (strncmp(argv[idx_in_out]->text, "out", 1) == 0) - type = IF_RMAP_OUT; - else { - vty_out(vty, "route-map direction must be [in|out]\n"); - return CMD_WARNING_CONFIG_FAILED; + if (!no) { + snprintf( + xpath, sizeof(xpath), + "./if-route-maps/if-route-map[interface='%s']/%s-route-map", + ifname, dir); + } else { + /* + * If we are deleting the last policy for this interface, + * (i.e., no `in` or `out` policy). delete the interface list + * node instead. + */ + dnode = yang_dnode_get(vty->candidate_config->dnode, + VTY_CURR_XPATH); + if (yang_dnode_existsf( + dnode, + "./if-route-maps/if-route-map[interface='%s']/%s-route-map", + ifname, other_dir)) { + snprintf( + xpath, sizeof(xpath), + "./if-route-maps/if-route-map[interface='%s']/%s-route-map", + ifname, dir); + } else { + /* both dir will be empty so delete the list node */ + snprintf(xpath, sizeof(xpath), + "./if-route-maps/if-route-map[interface='%s']", + ifname); + } } + nb_cli_enqueue_change(vty, xpath, op, route_map); - if_rmap_set(ctx, argv[idx_ifname]->arg, - type, argv[idx_rmap_name]->arg); - - return CMD_SUCCESS; + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_if_rmap, - no_if_rmap_cmd, - "no route-map ROUTEMAP_NAME IFNAME", - NO_STR - "Route map unset\n" - "Route map name\n" - "Route map for input filtering\n" - "Route map for output filtering\n" - "Route map interface name\n") +DEFPY_YANG(if_ipv4_route_map, if_ipv4_route_map_cmd, + "route-map ROUTE-MAP IFNAME", + "Route map set\n" + "Route map name\n" + "Route map set for input filtering\n" + "Route map set for output filtering\n" INTERFACE_STR) { - int idx_routemap_name = 2; - int idx_in_out = 3; - int idx_ifname = 4; - int ret; - enum if_rmap_type type; - struct if_rmap_ctx *ctx = - (struct if_rmap_ctx *)listnode_head(if_rmap_ctx_list); + const char *dir = in ? "in" : "out"; + const char *other_dir = in ? "out" : "in"; - if (strncmp(argv[idx_in_out]->arg, "i", 1) == 0) - type = IF_RMAP_IN; - else if (strncmp(argv[idx_in_out]->arg, "o", 1) == 0) - type = IF_RMAP_OUT; - else { - vty_out(vty, "route-map direction must be [in|out]\n"); - return CMD_WARNING_CONFIG_FAILED; - } + return if_route_map_handler(vty, false, dir, other_dir, ifname, + route_map); +} - ret = if_rmap_unset(ctx, argv[idx_ifname]->arg, type, - argv[idx_routemap_name]->arg); - if (!ret) { - vty_out(vty, "route-map doesn't exist\n"); - return CMD_WARNING_CONFIG_FAILED; - } - return CMD_SUCCESS; +DEFPY_YANG(no_if_ipv4_route_map, no_if_ipv4_route_map_cmd, + "no route-map [ROUTE-MAP] IFNAME", + NO_STR + "Route map set\n" + "Route map name\n" + "Route map set for input filtering\n" + "Route map set for output filtering\n" INTERFACE_STR) +{ + const char *dir = in ? "in" : "out"; + const char *other_dir = in ? "out" : "in"; + + return if_route_map_handler(vty, true, dir, other_dir, ifname, + route_map); +} + +/* + * CLI infra requires new handlers for ripngd + */ +DEFPY_YANG(if_ipv6_route_map, if_ipv6_route_map_cmd, + "route-map ROUTE-MAP IFNAME", + "Route map set\n" + "Route map name\n" + "Route map set for input filtering\n" + "Route map set for output filtering\n" INTERFACE_STR) +{ + const char *dir = in ? "in" : "out"; + const char *other_dir = in ? "out" : "in"; + + return if_route_map_handler(vty, false, dir, other_dir, ifname, + route_map); +} + +DEFPY_YANG(no_if_ipv6_route_map, no_if_ipv6_route_map_cmd, + "no route-map [ROUTE-MAP] IFNAME", + NO_STR + "Route map set\n" + "Route map name\n" + "Route map set for input filtering\n" + "Route map set for output filtering\n" INTERFACE_STR) +{ + const char *dir = in ? "in" : "out"; + const char *other_dir = in ? "out" : "in"; + + return if_route_map_handler(vty, true, dir, other_dir, ifname, + route_map); +} + + +void if_rmap_yang_modify_cb(struct if_rmap_ctx *ctx, + const struct lyd_node *dnode, + enum if_rmap_type type, bool del) +{ + + const char *mapname = yang_dnode_get_string(dnode, NULL); + const char *ifname = yang_dnode_get_string(dnode, "../interface"); + + if (del) + if_rmap_unset(ctx, ifname, type); + else + if_rmap_set(ctx, ifname, type, mapname); +} + +void if_rmap_yang_destroy_cb(struct if_rmap_ctx *ctx, + const struct lyd_node *dnode) +{ + const char *ifname = yang_dnode_get_string(dnode, "interface"); + if_rmap_unset(ctx, ifname, IF_RMAP_IN); + if_rmap_unset(ctx, ifname, IF_RMAP_OUT); } @@ -279,10 +297,8 @@ int config_write_if_rmap(struct vty *vty, struct if_rmap_ctx *ctx) void if_rmap_ctx_delete(struct if_rmap_ctx *ctx) { - listnode_delete(if_rmap_ctx_list, ctx); hash_clean_and_free(&ctx->ifrmaphash, (void (*)(void *))if_rmap_free); - if (ctx->name) - XFREE(MTYPE_IF_RMAP_CTX_NAME, ctx->name); + XFREE(MTYPE_IF_RMAP_CTX_NAME, ctx->name); XFREE(MTYPE_IF_RMAP_CTX, ctx); } @@ -297,25 +313,20 @@ struct if_rmap_ctx *if_rmap_ctx_create(const char *name) ctx->ifrmaphash = hash_create_size(4, if_rmap_hash_make, if_rmap_hash_cmp, "Interface Route-Map Hash"); - if (!if_rmap_ctx_list) - if_rmap_ctx_list = list_new(); - listnode_add(if_rmap_ctx_list, ctx); return ctx; } void if_rmap_init(int node) { - if (node == RIPNG_NODE) { - } else if (node == RIP_NODE) { - install_element(RIP_NODE, &if_rmap_cmd); - install_element(RIP_NODE, &no_if_rmap_cmd); + if (node == RIP_NODE) { + install_element(RIP_NODE, &if_ipv4_route_map_cmd); + install_element(RIP_NODE, &no_if_ipv4_route_map_cmd); + } else if (node == RIPNG_NODE) { + install_element(RIPNG_NODE, &if_ipv6_route_map_cmd); + install_element(RIPNG_NODE, &no_if_ipv6_route_map_cmd); } - if_rmap_ctx_list = list_new(); } void if_rmap_terminate(void) { - if (!if_rmap_ctx_list) - return; - list_delete(&if_rmap_ctx_list); } diff --git a/lib/if_rmap.h b/lib/if_rmap.h index 3bdbc2a3b2..6f5fb578f0 100644 --- a/lib/if_rmap.h +++ b/lib/if_rmap.h @@ -6,15 +6,20 @@ #ifndef _ZEBRA_IF_RMAP_H #define _ZEBRA_IF_RMAP_H +#include "typesafe.h" + #ifdef __cplusplus extern "C" { #endif +struct lyd_node; +struct vty; + enum if_rmap_type { IF_RMAP_IN, IF_RMAP_OUT, IF_RMAP_MAX }; struct if_rmap { /* Name of the interface. */ - char *ifname; + const char *ifname; char *routemap[IF_RMAP_MAX]; }; @@ -45,6 +50,11 @@ void if_rmap_hook_delete(struct if_rmap_ctx *ctx, struct if_rmap *)); extern struct if_rmap *if_rmap_lookup(struct if_rmap_ctx *ctx, const char *ifname); +extern void if_rmap_yang_modify_cb(struct if_rmap_ctx *ctx, + const struct lyd_node *dnode, + enum if_rmap_type type, bool del); +extern void if_rmap_yang_destroy_cb(struct if_rmap_ctx *ctx, + const struct lyd_node *dnode); extern int config_write_if_rmap(struct vty *, struct if_rmap_ctx *ctx); #ifdef __cplusplus diff --git a/lib/subdir.am b/lib/subdir.am index 83eecbee57..dcc58b31ae 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -174,6 +174,7 @@ clippy_scan += \ lib/affinitymap_cli.c \ lib/if.c \ lib/filter_cli.c \ + lib/if_rmap.c \ lib/log_vty.c \ lib/nexthop_group.c \ lib/northbound_cli.c \ diff --git a/yang/frr-if-rmap.yang b/yang/frr-if-rmap.yang new file mode 100644 index 0000000000..0fa2c5eddf --- /dev/null +++ b/yang/frr-if-rmap.yang @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: BSD-2-Clause +module frr-if-rmap { + yang-version 1.1; + namespace "http://frrouting.org/yang/frr-if-rmap"; + prefix frr-if-map; + + import frr-interface { + prefix frr-interface; + } + + import frr-route-map { + prefix frr-route-map; + } + + organization + "FRRouting"; + contact + "FRR Users List: + FRR Development List: "; + description + "This module defines route map settings + + Copyright 2023 LabN Consulting L.L.C + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2023-04-09 { + description + "Initial revision"; + reference "FRRouting"; + } + + grouping if-route-maps-group { + description "Grouping for interface route maps"; + + container if-route-maps { + description "Collection of interface route-maps"; + + list if-route-map { + must "in-route-map or out-route-map"; + key "interface"; + description "Collection of route-maps for an interface"; + + leaf "interface" { + type frr-interface:interface-ref; + description "The interface the route maps are associated with"; + } + leaf "in-route-map" { + type frr-route-map:route-map-name; + description "Name of the ingress route map"; + } + leaf "out-route-map" { + type frr-route-map:route-map-name; + description "Name of the egress route map"; + } + } + } + } +} diff --git a/yang/subdir.am b/yang/subdir.am index 82a6a01474..eb17c38dbc 100644 --- a/yang/subdir.am +++ b/yang/subdir.am @@ -24,6 +24,7 @@ dist_yangmodels_DATA += yang/frr-filter.yang dist_yangmodels_DATA += yang/frr-module-translator.yang dist_yangmodels_DATA += yang/frr-nexthop.yang dist_yangmodels_DATA += yang/frr-test-module.yang +dist_yangmodels_DATA += yang/frr-if-rmap.yang dist_yangmodels_DATA += yang/frr-interface.yang dist_yangmodels_DATA += yang/frr-route-map.yang dist_yangmodels_DATA += yang/frr-zebra-route-map.yang