Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | /* route-map for interface. |
3 | | * Copyright (C) 1999 Kunihiro Ishiguro |
4 | | * Copyright (C) 2023 LabN Consulting, L.L.C. |
5 | | */ |
6 | | |
7 | | #include <zebra.h> |
8 | | |
9 | | #include "hash.h" |
10 | | #include "command.h" |
11 | | #include "memory.h" |
12 | | #include "if.h" |
13 | | #include "if_rmap.h" |
14 | | #include "northbound_cli.h" |
15 | | |
16 | | #include "lib/if_rmap_clippy.c" |
17 | | |
18 | 8 | DEFINE_MTYPE_STATIC(LIB, IF_RMAP_CTX, "Interface route map container"); |
19 | 8 | DEFINE_MTYPE_STATIC(LIB, IF_RMAP_CTX_NAME, |
20 | 8 | "Interface route map container name"); |
21 | 8 | DEFINE_MTYPE_STATIC(LIB, IF_RMAP, "Interface route map"); |
22 | 8 | DEFINE_MTYPE_STATIC(LIB, IF_RMAP_NAME, "I.f. route map name"); |
23 | 8 | |
24 | 8 | static struct if_rmap *if_rmap_new(void) |
25 | 8 | { |
26 | 0 | struct if_rmap *new; |
27 | |
|
28 | 0 | new = XCALLOC(MTYPE_IF_RMAP, sizeof(struct if_rmap)); |
29 | |
|
30 | 0 | return new; |
31 | 0 | } |
32 | | |
33 | | static void if_rmap_free(struct if_rmap *if_rmap) |
34 | 0 | { |
35 | 0 | char *no_const_ifname = (char *)if_rmap->ifname; |
36 | |
|
37 | 0 | XFREE(MTYPE_IF_RMAP_NAME, no_const_ifname); |
38 | |
|
39 | 0 | XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_IN]); |
40 | 0 | XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_OUT]); |
41 | |
|
42 | 0 | XFREE(MTYPE_IF_RMAP, if_rmap); |
43 | 0 | } |
44 | | |
45 | | struct if_rmap *if_rmap_lookup(struct if_rmap_ctx *ctx, const char *ifname) |
46 | 0 | { |
47 | 0 | struct if_rmap key = {.ifname = ifname}; |
48 | 0 | struct if_rmap *if_rmap; |
49 | |
|
50 | 0 | if_rmap = hash_lookup(ctx->ifrmaphash, &key); |
51 | |
|
52 | 0 | return if_rmap; |
53 | 0 | } |
54 | | |
55 | | void if_rmap_hook_add(struct if_rmap_ctx *ctx, |
56 | | void (*func)(struct if_rmap_ctx *ctx, struct if_rmap *)) |
57 | 0 | { |
58 | 0 | ctx->if_rmap_add_hook = func; |
59 | 0 | } |
60 | | |
61 | | void if_rmap_hook_delete(struct if_rmap_ctx *ctx, |
62 | | void (*func)(struct if_rmap_ctx *ctx, |
63 | | struct if_rmap *)) |
64 | 0 | { |
65 | 0 | ctx->if_rmap_delete_hook = func; |
66 | 0 | } |
67 | | |
68 | | static void *if_rmap_hash_alloc(void *arg) |
69 | 0 | { |
70 | 0 | struct if_rmap *ifarg = (struct if_rmap *)arg; |
71 | 0 | struct if_rmap *if_rmap; |
72 | |
|
73 | 0 | if_rmap = if_rmap_new(); |
74 | 0 | if_rmap->ifname = XSTRDUP(MTYPE_IF_RMAP_NAME, ifarg->ifname); |
75 | |
|
76 | 0 | return if_rmap; |
77 | 0 | } |
78 | | |
79 | | static struct if_rmap *if_rmap_get(struct if_rmap_ctx *ctx, const char *ifname) |
80 | 0 | { |
81 | 0 | struct if_rmap key = {.ifname = ifname}; |
82 | 0 | struct if_rmap *ret; |
83 | |
|
84 | 0 | ret = hash_get(ctx->ifrmaphash, &key, if_rmap_hash_alloc); |
85 | |
|
86 | 0 | return ret; |
87 | 0 | } |
88 | | |
89 | | static unsigned int if_rmap_hash_make(const void *data) |
90 | 0 | { |
91 | 0 | const struct if_rmap *if_rmap = data; |
92 | |
|
93 | 0 | return string_hash_make(if_rmap->ifname); |
94 | 0 | } |
95 | | |
96 | | static bool if_rmap_hash_cmp(const void *arg1, const void *arg2) |
97 | 0 | { |
98 | 0 | const struct if_rmap *if_rmap1 = arg1; |
99 | 0 | const struct if_rmap *if_rmap2 = arg2; |
100 | |
|
101 | 0 | return strcmp(if_rmap1->ifname, if_rmap2->ifname) == 0; |
102 | 0 | } |
103 | | |
104 | | static void if_rmap_set(struct if_rmap_ctx *ctx, const char *ifname, |
105 | | enum if_rmap_type type, const char *routemap_name) |
106 | 0 | { |
107 | 0 | struct if_rmap *if_rmap = if_rmap_get(ctx, ifname); |
108 | |
|
109 | 0 | assert(type == IF_RMAP_IN || type == IF_RMAP_OUT); |
110 | 0 | XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[type]); |
111 | 0 | if_rmap->routemap[type] = XSTRDUP(MTYPE_IF_RMAP_NAME, routemap_name); |
112 | |
|
113 | 0 | if (ctx->if_rmap_add_hook) |
114 | 0 | (ctx->if_rmap_add_hook)(ctx, if_rmap); |
115 | 0 | } |
116 | | |
117 | | static void if_rmap_unset(struct if_rmap_ctx *ctx, const char *ifname, |
118 | | enum if_rmap_type type) |
119 | 0 | { |
120 | 0 | struct if_rmap *if_rmap = if_rmap_lookup(ctx, ifname); |
121 | |
|
122 | 0 | if (!if_rmap) |
123 | 0 | return; |
124 | | |
125 | 0 | assert(type == IF_RMAP_IN || type == IF_RMAP_OUT); |
126 | 0 | if (!if_rmap->routemap[type]) |
127 | 0 | return; |
128 | | |
129 | 0 | XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[type]); |
130 | |
|
131 | 0 | if (ctx->if_rmap_delete_hook) |
132 | 0 | ctx->if_rmap_delete_hook(ctx, if_rmap); |
133 | |
|
134 | 0 | if (if_rmap->routemap[IF_RMAP_IN] == NULL && |
135 | 0 | if_rmap->routemap[IF_RMAP_OUT] == NULL) { |
136 | 0 | hash_release(ctx->ifrmaphash, if_rmap); |
137 | 0 | if_rmap_free(if_rmap); |
138 | 0 | } |
139 | 0 | } |
140 | | |
141 | | static int if_route_map_handler(struct vty *vty, bool no, const char *dir, |
142 | | const char *other_dir, const char *ifname, |
143 | | const char *route_map) |
144 | 0 | { |
145 | 0 | enum nb_operation op = no ? NB_OP_DESTROY : NB_OP_MODIFY; |
146 | 0 | const struct lyd_node *dnode; |
147 | 0 | char xpath[XPATH_MAXLEN]; |
148 | |
|
149 | 0 | if (!no) { |
150 | 0 | snprintf( |
151 | 0 | xpath, sizeof(xpath), |
152 | 0 | "./if-route-maps/if-route-map[interface='%s']/%s-route-map", |
153 | 0 | ifname, dir); |
154 | 0 | } else { |
155 | | /* |
156 | | * If we are deleting the last policy for this interface, |
157 | | * (i.e., no `in` or `out` policy). delete the interface list |
158 | | * node instead. |
159 | | */ |
160 | 0 | dnode = yang_dnode_get(vty->candidate_config->dnode, |
161 | 0 | VTY_CURR_XPATH); |
162 | 0 | if (yang_dnode_existsf( |
163 | 0 | dnode, |
164 | 0 | "./if-route-maps/if-route-map[interface='%s']/%s-route-map", |
165 | 0 | ifname, other_dir)) { |
166 | 0 | snprintf( |
167 | 0 | xpath, sizeof(xpath), |
168 | 0 | "./if-route-maps/if-route-map[interface='%s']/%s-route-map", |
169 | 0 | ifname, dir); |
170 | 0 | } else { |
171 | | /* both dir will be empty so delete the list node */ |
172 | 0 | snprintf(xpath, sizeof(xpath), |
173 | 0 | "./if-route-maps/if-route-map[interface='%s']", |
174 | 0 | ifname); |
175 | 0 | } |
176 | 0 | } |
177 | 0 | nb_cli_enqueue_change(vty, xpath, op, route_map); |
178 | |
|
179 | 0 | return nb_cli_apply_changes(vty, NULL); |
180 | 0 | } |
181 | | |
182 | | DEFPY_YANG(if_ipv4_route_map, if_ipv4_route_map_cmd, |
183 | | "route-map ROUTE-MAP <in$in|out> IFNAME", |
184 | | "Route map set\n" |
185 | | "Route map name\n" |
186 | | "Route map set for input filtering\n" |
187 | | "Route map set for output filtering\n" INTERFACE_STR) |
188 | 0 | { |
189 | 0 | const char *dir = in ? "in" : "out"; |
190 | 0 | const char *other_dir = in ? "out" : "in"; |
191 | |
|
192 | 0 | return if_route_map_handler(vty, false, dir, other_dir, ifname, |
193 | 0 | route_map); |
194 | 0 | } |
195 | | |
196 | | DEFPY_YANG(no_if_ipv4_route_map, no_if_ipv4_route_map_cmd, |
197 | | "no route-map [ROUTE-MAP] <in$in|out> IFNAME", |
198 | | NO_STR |
199 | | "Route map set\n" |
200 | | "Route map name\n" |
201 | | "Route map set for input filtering\n" |
202 | | "Route map set for output filtering\n" INTERFACE_STR) |
203 | 0 | { |
204 | 0 | const char *dir = in ? "in" : "out"; |
205 | 0 | const char *other_dir = in ? "out" : "in"; |
206 | |
|
207 | 0 | return if_route_map_handler(vty, true, dir, other_dir, ifname, |
208 | 0 | route_map); |
209 | 0 | } |
210 | | |
211 | | /* |
212 | | * CLI infra requires new handlers for ripngd |
213 | | */ |
214 | | DEFPY_YANG(if_ipv6_route_map, if_ipv6_route_map_cmd, |
215 | | "route-map ROUTE-MAP <in$in|out> IFNAME", |
216 | | "Route map set\n" |
217 | | "Route map name\n" |
218 | | "Route map set for input filtering\n" |
219 | | "Route map set for output filtering\n" INTERFACE_STR) |
220 | 0 | { |
221 | 0 | const char *dir = in ? "in" : "out"; |
222 | 0 | const char *other_dir = in ? "out" : "in"; |
223 | |
|
224 | 0 | return if_route_map_handler(vty, false, dir, other_dir, ifname, |
225 | 0 | route_map); |
226 | 0 | } |
227 | | |
228 | | DEFPY_YANG(no_if_ipv6_route_map, no_if_ipv6_route_map_cmd, |
229 | | "no route-map [ROUTE-MAP] <in$in|out> IFNAME", |
230 | | NO_STR |
231 | | "Route map set\n" |
232 | | "Route map name\n" |
233 | | "Route map set for input filtering\n" |
234 | | "Route map set for output filtering\n" INTERFACE_STR) |
235 | 0 | { |
236 | 0 | const char *dir = in ? "in" : "out"; |
237 | 0 | const char *other_dir = in ? "out" : "in"; |
238 | |
|
239 | 0 | return if_route_map_handler(vty, true, dir, other_dir, ifname, |
240 | 0 | route_map); |
241 | 0 | } |
242 | | |
243 | | void cli_show_if_route_map(struct vty *vty, const struct lyd_node *dnode, |
244 | | bool show_defaults) |
245 | 0 | { |
246 | 0 | if (yang_dnode_exists(dnode, "./in-route-map")) |
247 | 0 | vty_out(vty, " route-map %s in %s\n", |
248 | 0 | yang_dnode_get_string(dnode, "./in-route-map"), |
249 | 0 | yang_dnode_get_string(dnode, "./interface")); |
250 | 0 | if (yang_dnode_exists(dnode, "./out-route-map")) |
251 | 0 | vty_out(vty, " route-map %s out %s\n", |
252 | 0 | yang_dnode_get_string(dnode, "./out-route-map"), |
253 | 0 | yang_dnode_get_string(dnode, "./interface")); |
254 | 0 | } |
255 | | |
256 | | void if_rmap_yang_modify_cb(struct if_rmap_ctx *ctx, |
257 | | const struct lyd_node *dnode, |
258 | | enum if_rmap_type type, bool del) |
259 | 0 | { |
260 | |
|
261 | 0 | const char *mapname = yang_dnode_get_string(dnode, NULL); |
262 | 0 | const char *ifname = yang_dnode_get_string(dnode, "../interface"); |
263 | |
|
264 | 0 | if (del) |
265 | 0 | if_rmap_unset(ctx, ifname, type); |
266 | 0 | else |
267 | 0 | if_rmap_set(ctx, ifname, type, mapname); |
268 | 0 | } |
269 | | |
270 | | void if_rmap_yang_destroy_cb(struct if_rmap_ctx *ctx, |
271 | | const struct lyd_node *dnode) |
272 | 0 | { |
273 | 0 | const char *ifname = yang_dnode_get_string(dnode, "interface"); |
274 | 0 | if_rmap_unset(ctx, ifname, IF_RMAP_IN); |
275 | 0 | if_rmap_unset(ctx, ifname, IF_RMAP_OUT); |
276 | 0 | } |
277 | | |
278 | | void if_rmap_ctx_delete(struct if_rmap_ctx *ctx) |
279 | 0 | { |
280 | 0 | hash_clean_and_free(&ctx->ifrmaphash, (void (*)(void *))if_rmap_free); |
281 | 0 | XFREE(MTYPE_IF_RMAP_CTX_NAME, ctx->name); |
282 | 0 | XFREE(MTYPE_IF_RMAP_CTX, ctx); |
283 | 0 | } |
284 | | |
285 | | /* name is optional: either vrf name, or other */ |
286 | | struct if_rmap_ctx *if_rmap_ctx_create(const char *name) |
287 | 0 | { |
288 | 0 | struct if_rmap_ctx *ctx; |
289 | |
|
290 | 0 | ctx = XCALLOC(MTYPE_IF_RMAP_CTX, sizeof(struct if_rmap_ctx)); |
291 | |
|
292 | 0 | ctx->name = XSTRDUP(MTYPE_IF_RMAP_CTX_NAME, name); |
293 | 0 | ctx->ifrmaphash = |
294 | 0 | hash_create_size(4, if_rmap_hash_make, if_rmap_hash_cmp, |
295 | 0 | "Interface Route-Map Hash"); |
296 | 0 | return ctx; |
297 | 0 | } |
298 | | |
299 | | void if_rmap_init(int node) |
300 | 0 | { |
301 | 0 | if (node == RIP_NODE) { |
302 | 0 | install_element(RIP_NODE, &if_ipv4_route_map_cmd); |
303 | 0 | install_element(RIP_NODE, &no_if_ipv4_route_map_cmd); |
304 | 0 | } else if (node == RIPNG_NODE) { |
305 | 0 | install_element(RIPNG_NODE, &if_ipv6_route_map_cmd); |
306 | 0 | install_element(RIPNG_NODE, &no_if_ipv6_route_map_cmd); |
307 | 0 | } |
308 | 0 | } |
309 | | |
310 | | void if_rmap_terminate(void) |
311 | 0 | { |
312 | 0 | } |