Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | /* Route map function. |
3 | | * Copyright (C) 1998, 1999 Kunihiro Ishiguro |
4 | | */ |
5 | | |
6 | | #include <zebra.h> |
7 | | |
8 | | #include "linklist.h" |
9 | | #include "memory.h" |
10 | | #include "command.h" |
11 | | #include "vector.h" |
12 | | #include "prefix.h" |
13 | | #include "vty.h" |
14 | | #include "routemap.h" |
15 | | #include "command.h" |
16 | | #include "log.h" |
17 | | #include "hash.h" |
18 | | #include "libfrr.h" |
19 | | #include "lib_errors.h" |
20 | | #include "table.h" |
21 | | #include "json.h" |
22 | | #include "jhash.h" |
23 | | |
24 | | #include "lib/routemap_clippy.c" |
25 | | |
26 | 8 | DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP, "Route map"); |
27 | 8 | DEFINE_MTYPE(LIB, ROUTE_MAP_NAME, "Route map name"); |
28 | 8 | DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP_INDEX, "Route map index"); |
29 | 8 | DEFINE_MTYPE(LIB, ROUTE_MAP_RULE, "Route map rule"); |
30 | 8 | DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP_RULE_STR, "Route map rule str"); |
31 | 8 | DEFINE_MTYPE(LIB, ROUTE_MAP_COMPILED, "Route map compiled"); |
32 | 8 | DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP_DEP, "Route map dependency"); |
33 | 8 | DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP_DEP_DATA, "Route map dependency data"); |
34 | 8 | |
35 | 8 | DEFINE_QOBJ_TYPE(route_map_index); |
36 | 8 | DEFINE_QOBJ_TYPE(route_map); |
37 | 8 | |
38 | 8 | static int rmap_cmd_name_cmp(const struct route_map_rule_cmd_proxy *a, |
39 | 8 | const struct route_map_rule_cmd_proxy *b) |
40 | 8 | { |
41 | 2 | return strcmp(a->cmd->str, b->cmd->str); |
42 | 2 | } |
43 | | |
44 | | static uint32_t rmap_cmd_name_hash(const struct route_map_rule_cmd_proxy *item) |
45 | 99 | { |
46 | 99 | return jhash(item->cmd->str, strlen(item->cmd->str), 0xbfd69320); |
47 | 99 | } |
48 | | |
49 | 99 | DECLARE_HASH(rmap_cmd_name, struct route_map_rule_cmd_proxy, itm, routemap.c:rmap_cmd_name_add Line | Count | Source | 49 | | DECLARE_HASH(rmap_cmd_name, struct route_map_rule_cmd_proxy, itm, |
Unexecuted instantiation: routemap.c:rmap_cmd_name_fini |
50 | | rmap_cmd_name_cmp, rmap_cmd_name_hash); |
51 | | |
52 | | static struct rmap_cmd_name_head rmap_match_cmds[1] = { |
53 | | INIT_HASH(rmap_match_cmds[0]), |
54 | | }; |
55 | | static struct rmap_cmd_name_head rmap_set_cmds[1] = { |
56 | | INIT_HASH(rmap_set_cmds[0]), |
57 | | }; |
58 | | |
59 | 0 | #define IPv4_PREFIX_LIST "ip address prefix-list" |
60 | 0 | #define IPv6_PREFIX_LIST "ipv6 address prefix-list" |
61 | | |
62 | | #define IS_RULE_IPv4_PREFIX_LIST(S) \ |
63 | 0 | (strncmp(S, IPv4_PREFIX_LIST, strlen(IPv4_PREFIX_LIST)) == 0) |
64 | | #define IS_RULE_IPv6_PREFIX_LIST(S) \ |
65 | 0 | (strncmp(S, IPv6_PREFIX_LIST, strlen(IPv6_PREFIX_LIST)) == 0) |
66 | | |
67 | | struct route_map_pentry_dep { |
68 | | struct prefix_list_entry *pentry; |
69 | | const char *plist_name; |
70 | | route_map_event_t event; |
71 | | }; |
72 | | |
73 | | static void route_map_pfx_tbl_update(route_map_event_t event, |
74 | | struct route_map_index *index, afi_t afi, |
75 | | const char *plist_name); |
76 | | static void route_map_pfx_table_add_default(afi_t afi, |
77 | | struct route_map_index *index); |
78 | | static void route_map_pfx_table_del_default(afi_t afi, |
79 | | struct route_map_index *index); |
80 | | static void route_map_add_plist_entries(afi_t afi, |
81 | | struct route_map_index *index, |
82 | | const char *plist_name, |
83 | | struct prefix_list_entry *entry); |
84 | | static void route_map_del_plist_entries(afi_t afi, |
85 | | struct route_map_index *index, |
86 | | const char *plist_name, |
87 | | struct prefix_list_entry *entry); |
88 | | |
89 | | static struct hash *route_map_get_dep_hash(route_map_event_t event); |
90 | | static void route_map_free_map(struct route_map *map); |
91 | | |
92 | | struct route_map_match_set_hooks rmap_match_set_hook; |
93 | | |
94 | | /* match interface */ |
95 | | void route_map_match_interface_hook(int (*func)( |
96 | | struct route_map_index *index, const char *command, |
97 | | const char *arg, route_map_event_t type, |
98 | | char *errmsg, size_t errmsg_len)) |
99 | 3 | { |
100 | 3 | rmap_match_set_hook.match_interface = func; |
101 | 3 | } |
102 | | |
103 | | /* no match interface */ |
104 | | void route_map_no_match_interface_hook(int (*func)( |
105 | | struct route_map_index *index, const char *command, |
106 | | const char *arg, route_map_event_t type, |
107 | | char *errmsg, size_t errmsg_len)) |
108 | 3 | { |
109 | 3 | rmap_match_set_hook.no_match_interface = func; |
110 | 3 | } |
111 | | |
112 | | /* match ip address */ |
113 | | void route_map_match_ip_address_hook(int (*func)( |
114 | | struct route_map_index *index, const char *command, |
115 | | const char *arg, route_map_event_t type, |
116 | | char *errmsg, size_t errmsg_len)) |
117 | 3 | { |
118 | 3 | rmap_match_set_hook.match_ip_address = func; |
119 | 3 | } |
120 | | |
121 | | /* no match ip address */ |
122 | | void route_map_no_match_ip_address_hook(int (*func)( |
123 | | struct route_map_index *index, const char *command, |
124 | | const char *arg, route_map_event_t type, |
125 | | char *errmsg, size_t errmsg_len)) |
126 | 3 | { |
127 | 3 | rmap_match_set_hook.no_match_ip_address = func; |
128 | 3 | } |
129 | | |
130 | | /* match ip address prefix list */ |
131 | | void route_map_match_ip_address_prefix_list_hook(int (*func)( |
132 | | struct route_map_index *index, const char *command, |
133 | | const char *arg, route_map_event_t type, |
134 | | char *errmsg, size_t errmsg_len)) |
135 | 3 | { |
136 | 3 | rmap_match_set_hook.match_ip_address_prefix_list = func; |
137 | 3 | } |
138 | | |
139 | | /* no match ip address prefix list */ |
140 | | void route_map_no_match_ip_address_prefix_list_hook(int (*func)( |
141 | | struct route_map_index *index, const char *command, |
142 | | const char *arg, route_map_event_t type, |
143 | | char *errmsg, size_t errmsg_len)) |
144 | 3 | { |
145 | 3 | rmap_match_set_hook.no_match_ip_address_prefix_list = func; |
146 | 3 | } |
147 | | |
148 | | /* match ip next hop */ |
149 | | void route_map_match_ip_next_hop_hook(int (*func)( |
150 | | struct route_map_index *index, const char *command, |
151 | | const char *arg, route_map_event_t type, |
152 | | char *errmsg, size_t errmsg_len)) |
153 | 3 | { |
154 | 3 | rmap_match_set_hook.match_ip_next_hop = func; |
155 | 3 | } |
156 | | |
157 | | /* no match ip next hop */ |
158 | | void route_map_no_match_ip_next_hop_hook(int (*func)( |
159 | | struct route_map_index *index, const char *command, |
160 | | const char *arg, route_map_event_t type, |
161 | | char *errmsg, size_t errmsg_len)) |
162 | 3 | { |
163 | 3 | rmap_match_set_hook.no_match_ip_next_hop = func; |
164 | 3 | } |
165 | | |
166 | | /* match ipv6 next-hop */ |
167 | | void route_map_match_ipv6_next_hop_hook(int (*func)( |
168 | | struct route_map_index *index, const char *command, const char *arg, |
169 | | route_map_event_t type, char *errmsg, size_t errmsg_len)) |
170 | 1 | { |
171 | 1 | rmap_match_set_hook.match_ipv6_next_hop = func; |
172 | 1 | } |
173 | | |
174 | | /* no match ipv6 next-hop */ |
175 | | void route_map_no_match_ipv6_next_hop_hook(int (*func)( |
176 | | struct route_map_index *index, const char *command, const char *arg, |
177 | | route_map_event_t type, char *errmsg, size_t errmsg_len)) |
178 | 1 | { |
179 | 1 | rmap_match_set_hook.no_match_ipv6_next_hop = func; |
180 | 1 | } |
181 | | |
182 | | /* match ip next hop prefix list */ |
183 | | void route_map_match_ip_next_hop_prefix_list_hook(int (*func)( |
184 | | struct route_map_index *index, const char *command, |
185 | | const char *arg, route_map_event_t type, |
186 | | char *errmsg, size_t errmsg_len)) |
187 | 3 | { |
188 | 3 | rmap_match_set_hook.match_ip_next_hop_prefix_list = func; |
189 | 3 | } |
190 | | |
191 | | /* no match ip next hop prefix list */ |
192 | | void route_map_no_match_ip_next_hop_prefix_list_hook(int (*func)( |
193 | | struct route_map_index *index, const char *command, |
194 | | const char *arg, route_map_event_t type, |
195 | | char *errmsg, size_t errmsg_len)) |
196 | 3 | { |
197 | 3 | rmap_match_set_hook.no_match_ip_next_hop_prefix_list = func; |
198 | 3 | } |
199 | | |
200 | | /* match ip next-hop type */ |
201 | | void route_map_match_ip_next_hop_type_hook(int (*func)( |
202 | | struct route_map_index *index, const char *command, |
203 | | const char *arg, route_map_event_t type, |
204 | | char *errmsg, size_t errmsg_len)) |
205 | 3 | { |
206 | 3 | rmap_match_set_hook.match_ip_next_hop_type = func; |
207 | 3 | } |
208 | | |
209 | | /* no match ip next-hop type */ |
210 | | void route_map_no_match_ip_next_hop_type_hook(int (*func)( |
211 | | struct route_map_index *index, const char *command, |
212 | | const char *arg, route_map_event_t type, |
213 | | char *errmsg, size_t errmsg_len)) |
214 | 3 | { |
215 | 3 | rmap_match_set_hook.no_match_ip_next_hop_type = func; |
216 | 3 | } |
217 | | |
218 | | /* match ipv6 address */ |
219 | | void route_map_match_ipv6_address_hook(int (*func)( |
220 | | struct route_map_index *index, const char *command, |
221 | | const char *arg, route_map_event_t type, |
222 | | char *errmsg, size_t errmsg_len)) |
223 | 2 | { |
224 | 2 | rmap_match_set_hook.match_ipv6_address = func; |
225 | 2 | } |
226 | | |
227 | | /* no match ipv6 address */ |
228 | | void route_map_no_match_ipv6_address_hook(int (*func)( |
229 | | struct route_map_index *index, const char *command, |
230 | | const char *arg, route_map_event_t type, |
231 | | char *errmsg, size_t errmsg_len)) |
232 | 2 | { |
233 | 2 | rmap_match_set_hook.no_match_ipv6_address = func; |
234 | 2 | } |
235 | | |
236 | | |
237 | | /* match ipv6 address prefix list */ |
238 | | void route_map_match_ipv6_address_prefix_list_hook(int (*func)( |
239 | | struct route_map_index *index, const char *command, |
240 | | const char *arg, route_map_event_t type, |
241 | | char *errmsg, size_t errmsg_len)) |
242 | 2 | { |
243 | 2 | rmap_match_set_hook.match_ipv6_address_prefix_list = func; |
244 | 2 | } |
245 | | |
246 | | /* no match ipv6 address prefix list */ |
247 | | void route_map_no_match_ipv6_address_prefix_list_hook(int (*func)( |
248 | | struct route_map_index *index, const char *command, |
249 | | const char *arg, route_map_event_t type, |
250 | | char *errmsg, size_t errmsg_len)) |
251 | 2 | { |
252 | 2 | rmap_match_set_hook.no_match_ipv6_address_prefix_list = func; |
253 | 2 | } |
254 | | |
255 | | /* match ipv6 next-hop type */ |
256 | | void route_map_match_ipv6_next_hop_type_hook(int (*func)( |
257 | | struct route_map_index *index, const char *command, |
258 | | const char *arg, route_map_event_t type, |
259 | | char *errmsg, size_t errmsg_len)) |
260 | 2 | { |
261 | 2 | rmap_match_set_hook.match_ipv6_next_hop_type = func; |
262 | 2 | } |
263 | | |
264 | | /* no match ipv6 next-hop type */ |
265 | | void route_map_no_match_ipv6_next_hop_type_hook(int (*func)( |
266 | | struct route_map_index *index, const char *command, |
267 | | const char *arg, route_map_event_t type, |
268 | | char *errmsg, size_t errmsg_len)) |
269 | 2 | { |
270 | 2 | rmap_match_set_hook.no_match_ipv6_next_hop_type = func; |
271 | 2 | } |
272 | | |
273 | | /* match ipv6 next-hop prefix-list */ |
274 | | void route_map_match_ipv6_next_hop_prefix_list_hook(int (*func)( |
275 | | struct route_map_index *index, const char *command, const char *arg, |
276 | | route_map_event_t type, char *errmsg, size_t errmsg_len)) |
277 | 1 | { |
278 | 1 | rmap_match_set_hook.match_ipv6_next_hop_prefix_list = func; |
279 | 1 | } |
280 | | |
281 | | /* no match ipv6 next-hop prefix-list */ |
282 | | void route_map_no_match_ipv6_next_hop_prefix_list_hook(int (*func)( |
283 | | struct route_map_index *index, const char *command, const char *arg, |
284 | | route_map_event_t type, char *errmsg, size_t errmsg_len)) |
285 | 1 | { |
286 | 1 | rmap_match_set_hook.no_match_ipv6_next_hop_prefix_list = func; |
287 | 1 | } |
288 | | |
289 | | /* match metric */ |
290 | | void route_map_match_metric_hook(int (*func)( |
291 | | struct route_map_index *index, const char *command, |
292 | | const char *arg, route_map_event_t type, |
293 | | char *errmsg, size_t errmsg_len)) |
294 | 1 | { |
295 | 1 | rmap_match_set_hook.match_metric = func; |
296 | 1 | } |
297 | | |
298 | | /* no match metric */ |
299 | | void route_map_no_match_metric_hook(int (*func)( |
300 | | struct route_map_index *index, const char *command, |
301 | | const char *arg, route_map_event_t type, |
302 | | char *errmsg, size_t errmsg_len)) |
303 | 1 | { |
304 | 1 | rmap_match_set_hook.no_match_metric = func; |
305 | 1 | } |
306 | | |
307 | | /* match tag */ |
308 | | void route_map_match_tag_hook(int (*func)(struct route_map_index *index, |
309 | | const char *command, const char *arg, |
310 | | route_map_event_t type, |
311 | | char *errmsg, size_t errmsg_len)) |
312 | 3 | { |
313 | 3 | rmap_match_set_hook.match_tag = func; |
314 | 3 | } |
315 | | |
316 | | /* no match tag */ |
317 | | void route_map_no_match_tag_hook(int (*func)( |
318 | | struct route_map_index *index, const char *command, |
319 | | const char *arg, route_map_event_t type, |
320 | | char *errmsg, size_t errmsg_len)) |
321 | 3 | { |
322 | 3 | rmap_match_set_hook.no_match_tag = func; |
323 | 3 | } |
324 | | |
325 | | /* set sr-te color */ |
326 | | void route_map_set_srte_color_hook(int (*func)(struct route_map_index *index, |
327 | | const char *command, |
328 | | const char *arg, |
329 | | char *errmsg, size_t errmsg_len)) |
330 | 1 | { |
331 | 1 | rmap_match_set_hook.set_srte_color = func; |
332 | 1 | } |
333 | | |
334 | | /* no set sr-te color */ |
335 | | void route_map_no_set_srte_color_hook(int (*func)(struct route_map_index *index, |
336 | | const char *command, |
337 | | const char *arg, |
338 | | char *errmsg, size_t errmsg_len)) |
339 | 1 | { |
340 | 1 | rmap_match_set_hook.no_set_srte_color = func; |
341 | 1 | } |
342 | | |
343 | | /* set ip nexthop */ |
344 | | void route_map_set_ip_nexthop_hook(int (*func)(struct route_map_index *index, |
345 | | const char *command, |
346 | | const char *arg, |
347 | | char *errmsg, size_t errmsg_len)) |
348 | 1 | { |
349 | 1 | rmap_match_set_hook.set_ip_nexthop = func; |
350 | 1 | } |
351 | | |
352 | | /* no set ip nexthop */ |
353 | | void route_map_no_set_ip_nexthop_hook(int (*func)(struct route_map_index *index, |
354 | | const char *command, |
355 | | const char *arg, |
356 | | char *errmsg, |
357 | | size_t errmsg_len)) |
358 | 1 | { |
359 | 1 | rmap_match_set_hook.no_set_ip_nexthop = func; |
360 | 1 | } |
361 | | |
362 | | /* set ipv6 nexthop local */ |
363 | | void route_map_set_ipv6_nexthop_local_hook( |
364 | | int (*func)(struct route_map_index *index, |
365 | | const char *command, const char *arg, |
366 | | char *errmsg, size_t errmsg_len)) |
367 | 1 | { |
368 | 1 | rmap_match_set_hook.set_ipv6_nexthop_local = func; |
369 | 1 | } |
370 | | |
371 | | /* no set ipv6 nexthop local */ |
372 | | void route_map_no_set_ipv6_nexthop_local_hook( |
373 | | int (*func)(struct route_map_index *index, |
374 | | const char *command, const char *arg, |
375 | | char *errmsg, size_t errmsg_len)) |
376 | 1 | { |
377 | 1 | rmap_match_set_hook.no_set_ipv6_nexthop_local = func; |
378 | 1 | } |
379 | | |
380 | | /* set metric */ |
381 | | void route_map_set_metric_hook(int (*func)(struct route_map_index *index, |
382 | | const char *command, |
383 | | const char *arg, |
384 | | char *errmsg, size_t errmsg_len)) |
385 | 2 | { |
386 | 2 | rmap_match_set_hook.set_metric = func; |
387 | 2 | } |
388 | | |
389 | | /* no set metric */ |
390 | | void route_map_no_set_metric_hook(int (*func)(struct route_map_index *index, |
391 | | const char *command, |
392 | | const char *arg, |
393 | | char *errmsg, size_t errmsg_len)) |
394 | 2 | { |
395 | 2 | rmap_match_set_hook.no_set_metric = func; |
396 | 2 | } |
397 | | /* set min-metric */ |
398 | | void route_map_set_min_metric_hook(int (*func)(struct route_map_index *index, |
399 | | const char *command, |
400 | | const char *arg, char *errmsg, |
401 | | size_t errmsg_len)) |
402 | 1 | { |
403 | 1 | rmap_match_set_hook.set_min_metric = func; |
404 | 1 | } |
405 | | |
406 | | /* no set min-metric */ |
407 | | void route_map_no_set_min_metric_hook(int (*func)(struct route_map_index *index, |
408 | | const char *command, |
409 | | const char *arg, char *errmsg, |
410 | | size_t errmsg_len)) |
411 | 1 | { |
412 | 1 | rmap_match_set_hook.no_set_min_metric = func; |
413 | 1 | } |
414 | | /* set max-metric */ |
415 | | void route_map_set_max_metric_hook(int (*func)(struct route_map_index *index, |
416 | | const char *command, |
417 | | const char *arg, char *errmsg, |
418 | | size_t errmsg_len)) |
419 | 1 | { |
420 | 1 | rmap_match_set_hook.set_max_metric = func; |
421 | 1 | } |
422 | | |
423 | | /* no set max-metric */ |
424 | | void route_map_no_set_max_metric_hook(int (*func)(struct route_map_index *index, |
425 | | const char *command, |
426 | | const char *arg, char *errmsg, |
427 | | size_t errmsg_len)) |
428 | 1 | { |
429 | 1 | rmap_match_set_hook.no_set_max_metric = func; |
430 | 1 | } |
431 | | |
432 | | /* set tag */ |
433 | | void route_map_set_tag_hook(int (*func)(struct route_map_index *index, |
434 | | const char *command, const char *arg, |
435 | | char *errmsg, size_t errmsg_len)) |
436 | 2 | { |
437 | 2 | rmap_match_set_hook.set_tag = func; |
438 | 2 | } |
439 | | |
440 | | /* no set tag */ |
441 | | void route_map_no_set_tag_hook(int (*func)(struct route_map_index *index, |
442 | | const char *command, |
443 | | const char *arg, |
444 | | char *errmsg, size_t errmsg_len)) |
445 | 2 | { |
446 | 2 | rmap_match_set_hook.no_set_tag = func; |
447 | 2 | } |
448 | | |
449 | | int generic_match_add(struct route_map_index *index, |
450 | | const char *command, const char *arg, |
451 | | route_map_event_t type, |
452 | | char *errmsg, size_t errmsg_len) |
453 | 0 | { |
454 | 0 | enum rmap_compile_rets ret; |
455 | |
|
456 | 0 | ret = route_map_add_match(index, command, arg, type); |
457 | 0 | switch (ret) { |
458 | 0 | case RMAP_RULE_MISSING: |
459 | 0 | snprintf(errmsg, errmsg_len, "%% [%s] Can't find rule.", |
460 | 0 | frr_protonameinst); |
461 | 0 | return CMD_WARNING_CONFIG_FAILED; |
462 | 0 | case RMAP_COMPILE_ERROR: |
463 | 0 | snprintf(errmsg, errmsg_len, |
464 | 0 | "%% [%s] Argument form is unsupported or malformed.", |
465 | 0 | frr_protonameinst); |
466 | 0 | return CMD_WARNING_CONFIG_FAILED; |
467 | 0 | case RMAP_COMPILE_SUCCESS: |
468 | | /* |
469 | | * Nothing to do here move along |
470 | | */ |
471 | 0 | break; |
472 | 0 | } |
473 | | |
474 | 0 | return CMD_SUCCESS; |
475 | 0 | } |
476 | | |
477 | | int generic_match_delete(struct route_map_index *index, |
478 | | const char *command, const char *arg, |
479 | | route_map_event_t type, |
480 | | char *errmsg, size_t errmsg_len) |
481 | 0 | { |
482 | 0 | enum rmap_compile_rets ret; |
483 | 0 | int retval = CMD_SUCCESS; |
484 | 0 | char *dep_name = NULL; |
485 | 0 | const char *tmpstr; |
486 | 0 | char *rmap_name = NULL; |
487 | |
|
488 | 0 | if (type != RMAP_EVENT_MATCH_DELETED) { |
489 | | /* ignore the mundane, the types without any dependency */ |
490 | 0 | if (arg == NULL) { |
491 | 0 | if ((tmpstr = route_map_get_match_arg(index, command)) |
492 | 0 | != NULL) |
493 | 0 | dep_name = |
494 | 0 | XSTRDUP(MTYPE_ROUTE_MAP_RULE, tmpstr); |
495 | 0 | } else { |
496 | 0 | dep_name = XSTRDUP(MTYPE_ROUTE_MAP_RULE, arg); |
497 | 0 | } |
498 | 0 | rmap_name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, index->map->name); |
499 | 0 | } |
500 | |
|
501 | 0 | ret = route_map_delete_match(index, command, dep_name, type); |
502 | 0 | switch (ret) { |
503 | 0 | case RMAP_RULE_MISSING: |
504 | 0 | snprintf(errmsg, errmsg_len, "%% [%s] Can't find rule.", |
505 | 0 | frr_protonameinst); |
506 | 0 | retval = CMD_WARNING_CONFIG_FAILED; |
507 | 0 | break; |
508 | 0 | case RMAP_COMPILE_ERROR: |
509 | 0 | snprintf(errmsg, errmsg_len, |
510 | 0 | "%% [%s] Argument form is unsupported or malformed.", |
511 | 0 | frr_protonameinst); |
512 | 0 | retval = CMD_WARNING_CONFIG_FAILED; |
513 | 0 | break; |
514 | 0 | case RMAP_COMPILE_SUCCESS: |
515 | | /* |
516 | | * Nothing to do here |
517 | | */ |
518 | 0 | break; |
519 | 0 | } |
520 | | |
521 | 0 | XFREE(MTYPE_ROUTE_MAP_RULE, dep_name); |
522 | 0 | XFREE(MTYPE_ROUTE_MAP_NAME, rmap_name); |
523 | |
|
524 | 0 | return retval; |
525 | 0 | } |
526 | | |
527 | | int generic_set_add(struct route_map_index *index, |
528 | | const char *command, const char *arg, |
529 | | char *errmsg, size_t errmsg_len) |
530 | 0 | { |
531 | 0 | enum rmap_compile_rets ret; |
532 | |
|
533 | 0 | ret = route_map_add_set(index, command, arg); |
534 | 0 | switch (ret) { |
535 | 0 | case RMAP_RULE_MISSING: |
536 | 0 | snprintf(errmsg, errmsg_len, |
537 | 0 | "%% [%s] Can't find rule.", frr_protonameinst); |
538 | 0 | return CMD_WARNING_CONFIG_FAILED; |
539 | 0 | case RMAP_COMPILE_ERROR: |
540 | 0 | snprintf(errmsg, errmsg_len, |
541 | 0 | "%% [%s] Argument form is unsupported or malformed.", |
542 | 0 | frr_protonameinst); |
543 | 0 | return CMD_WARNING_CONFIG_FAILED; |
544 | 0 | case RMAP_COMPILE_SUCCESS: |
545 | 0 | break; |
546 | 0 | } |
547 | | |
548 | 0 | return CMD_SUCCESS; |
549 | 0 | } |
550 | | |
551 | | int generic_set_delete(struct route_map_index *index, |
552 | | const char *command, const char *arg, |
553 | | char *errmsg, size_t errmsg_len) |
554 | 0 | { |
555 | 0 | enum rmap_compile_rets ret; |
556 | |
|
557 | 0 | ret = route_map_delete_set(index, command, arg); |
558 | 0 | switch (ret) { |
559 | 0 | case RMAP_RULE_MISSING: |
560 | 0 | snprintf(errmsg, errmsg_len, "%% [%s] Can't find rule.", |
561 | 0 | frr_protonameinst); |
562 | 0 | return CMD_WARNING_CONFIG_FAILED; |
563 | 0 | case RMAP_COMPILE_ERROR: |
564 | 0 | snprintf(errmsg, errmsg_len, |
565 | 0 | "%% [%s] Argument form is unsupported or malformed.", |
566 | 0 | frr_protonameinst); |
567 | 0 | return CMD_WARNING_CONFIG_FAILED; |
568 | 0 | case RMAP_COMPILE_SUCCESS: |
569 | 0 | break; |
570 | 0 | } |
571 | | |
572 | 0 | return CMD_SUCCESS; |
573 | 0 | } |
574 | | |
575 | | |
576 | | /* Master list of route map. */ |
577 | | struct route_map_list route_map_master = {NULL, NULL, NULL, NULL, NULL}; |
578 | | struct hash *route_map_master_hash = NULL; |
579 | | |
580 | | static unsigned int route_map_hash_key_make(const void *p) |
581 | 0 | { |
582 | 0 | const struct route_map *map = p; |
583 | 0 | return string_hash_make(map->name); |
584 | 0 | } |
585 | | |
586 | | static bool route_map_hash_cmp(const void *p1, const void *p2) |
587 | 0 | { |
588 | 0 | const struct route_map *map1 = p1; |
589 | 0 | const struct route_map *map2 = p2; |
590 | |
|
591 | 0 | if (!strcmp(map1->name, map2->name)) |
592 | 0 | return true; |
593 | | |
594 | 0 | return false; |
595 | 0 | } |
596 | | |
597 | | enum route_map_upd8_type { |
598 | | ROUTE_MAP_ADD = 1, |
599 | | ROUTE_MAP_DEL, |
600 | | }; |
601 | | |
602 | | /* all possible route-map dependency types */ |
603 | | enum route_map_dep_type { |
604 | | ROUTE_MAP_DEP_RMAP = 1, |
605 | | ROUTE_MAP_DEP_CLIST, |
606 | | ROUTE_MAP_DEP_ECLIST, |
607 | | ROUTE_MAP_DEP_LCLIST, |
608 | | ROUTE_MAP_DEP_PLIST, |
609 | | ROUTE_MAP_DEP_ASPATH, |
610 | | ROUTE_MAP_DEP_FILTER, |
611 | | ROUTE_MAP_DEP_MAX, |
612 | | }; |
613 | | |
614 | | struct route_map_dep { |
615 | | char *dep_name; |
616 | | struct hash *dep_rmap_hash; |
617 | | struct hash *this_hash; /* ptr to the hash structure this is part of */ |
618 | | }; |
619 | | |
620 | | struct route_map_dep_data { |
621 | | /* Route-map name. |
622 | | */ |
623 | | char *rname; |
624 | | /* Count of number of sequences of this |
625 | | * route-map that depend on the same entity. |
626 | | */ |
627 | | uint16_t refcnt; |
628 | | }; |
629 | | |
630 | | /* Hashes maintaining dependency between various sublists used by route maps */ |
631 | | static struct hash *route_map_dep_hash[ROUTE_MAP_DEP_MAX]; |
632 | | |
633 | | static unsigned int route_map_dep_hash_make_key(const void *p); |
634 | | static void route_map_clear_all_references(char *rmap_name); |
635 | | static void route_map_rule_delete(struct route_map_rule_list *, |
636 | | struct route_map_rule *); |
637 | | |
638 | | uint32_t rmap_debug; |
639 | | |
640 | | /* New route map allocation. Please note route map's name must be |
641 | | specified. */ |
642 | | static struct route_map *route_map_new(const char *name) |
643 | 0 | { |
644 | 0 | struct route_map *new; |
645 | |
|
646 | 0 | new = XCALLOC(MTYPE_ROUTE_MAP, sizeof(struct route_map)); |
647 | 0 | new->name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, name); |
648 | 0 | QOBJ_REG(new, route_map); |
649 | 0 | return new; |
650 | 0 | } |
651 | | |
652 | | /* Add new name to route_map. */ |
653 | | static struct route_map *route_map_add(const char *name) |
654 | 0 | { |
655 | 0 | struct route_map *map, *exist; |
656 | 0 | struct route_map_list *list; |
657 | |
|
658 | 0 | map = route_map_new(name); |
659 | 0 | list = &route_map_master; |
660 | | |
661 | | /* |
662 | | * Add map to the hash |
663 | | * |
664 | | * If the map already exists in the hash, then we know that |
665 | | * FRR is now in a sequence of delete/create. |
666 | | * All FRR needs to do here is set the to_be_processed |
667 | | * bit (to inherit from the old one |
668 | | */ |
669 | 0 | exist = hash_release(route_map_master_hash, map); |
670 | 0 | if (exist) { |
671 | 0 | map->to_be_processed = exist->to_be_processed; |
672 | 0 | route_map_free_map(exist); |
673 | 0 | } |
674 | 0 | hash_get(route_map_master_hash, map, hash_alloc_intern); |
675 | | |
676 | | /* Add new entry to the head of the list to match how it is added in the |
677 | | * hash table. This is to ensure that if the same route-map has been |
678 | | * created more than once and then marked for deletion (which can happen |
679 | | * if prior deletions haven't completed as BGP hasn't yet done the |
680 | | * route-map processing), the order of the entities is the same in both |
681 | | * the list and the hash table. Otherwise, since there is nothing to |
682 | | * distinguish between the two entries, the wrong entry could get freed. |
683 | | * TODO: This needs to be re-examined to handle it better - e.g., revive |
684 | | * a deleted entry if the route-map is created again. |
685 | | */ |
686 | 0 | map->prev = NULL; |
687 | 0 | map->next = list->head; |
688 | 0 | if (list->head) |
689 | 0 | list->head->prev = map; |
690 | 0 | list->head = map; |
691 | 0 | if (!list->tail) |
692 | 0 | list->tail = map; |
693 | | |
694 | | /* Execute hook. */ |
695 | 0 | if (route_map_master.add_hook) { |
696 | 0 | (*route_map_master.add_hook)(name); |
697 | 0 | route_map_notify_dependencies(name, RMAP_EVENT_CALL_ADDED); |
698 | 0 | } |
699 | |
|
700 | 0 | if (!map->ipv4_prefix_table) |
701 | 0 | map->ipv4_prefix_table = route_table_init(); |
702 | |
|
703 | 0 | if (!map->ipv6_prefix_table) |
704 | 0 | map->ipv6_prefix_table = route_table_init(); |
705 | |
|
706 | 0 | if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) |
707 | 0 | zlog_debug("Add route-map %s", name); |
708 | 0 | return map; |
709 | 0 | } |
710 | | |
711 | | /* this is supposed to be called post processing by |
712 | | * the delete hook function. Don't invoke delete_hook |
713 | | * again in this routine. |
714 | | */ |
715 | | static void route_map_free_map(struct route_map *map) |
716 | 0 | { |
717 | 0 | struct route_map_list *list; |
718 | 0 | struct route_map_index *index; |
719 | |
|
720 | 0 | if (map == NULL) |
721 | 0 | return; |
722 | | |
723 | 0 | while ((index = map->head) != NULL) |
724 | 0 | route_map_index_delete(index, 0); |
725 | |
|
726 | 0 | if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) |
727 | 0 | zlog_debug("Deleting route-map %s", map->name); |
728 | |
|
729 | 0 | list = &route_map_master; |
730 | |
|
731 | 0 | QOBJ_UNREG(map); |
732 | |
|
733 | 0 | if (map->next) |
734 | 0 | map->next->prev = map->prev; |
735 | 0 | else |
736 | 0 | list->tail = map->prev; |
737 | |
|
738 | 0 | if (map->prev) |
739 | 0 | map->prev->next = map->next; |
740 | 0 | else |
741 | 0 | list->head = map->next; |
742 | |
|
743 | 0 | route_table_finish(map->ipv4_prefix_table); |
744 | 0 | route_table_finish(map->ipv6_prefix_table); |
745 | |
|
746 | 0 | hash_release(route_map_master_hash, map); |
747 | 0 | XFREE(MTYPE_ROUTE_MAP_NAME, map->name); |
748 | 0 | XFREE(MTYPE_ROUTE_MAP, map); |
749 | 0 | } |
750 | | |
751 | | /* Route map delete from list. */ |
752 | | void route_map_delete(struct route_map *map) |
753 | 0 | { |
754 | 0 | struct route_map_index *index; |
755 | 0 | char *name; |
756 | |
|
757 | 0 | while ((index = map->head) != NULL) |
758 | 0 | route_map_index_delete(index, 0); |
759 | |
|
760 | 0 | name = map->name; |
761 | 0 | map->head = NULL; |
762 | | |
763 | | /* Clear all dependencies */ |
764 | 0 | route_map_clear_all_references(name); |
765 | 0 | map->deleted = true; |
766 | | /* Execute deletion hook. */ |
767 | 0 | if (route_map_master.delete_hook) { |
768 | 0 | (*route_map_master.delete_hook)(name); |
769 | 0 | route_map_notify_dependencies(name, RMAP_EVENT_CALL_DELETED); |
770 | 0 | } |
771 | |
|
772 | 0 | if (!map->to_be_processed) { |
773 | 0 | route_map_free_map(map); |
774 | 0 | } |
775 | 0 | } |
776 | | |
777 | | /* Lookup route map by route map name string. */ |
778 | | struct route_map *route_map_lookup_by_name(const char *name) |
779 | 0 | { |
780 | 0 | struct route_map *map; |
781 | 0 | struct route_map tmp_map; |
782 | |
|
783 | 0 | if (!name) |
784 | 0 | return NULL; |
785 | | |
786 | | // map.deleted is false via memset |
787 | 0 | memset(&tmp_map, 0, sizeof(tmp_map)); |
788 | 0 | tmp_map.name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, name); |
789 | 0 | map = hash_lookup(route_map_master_hash, &tmp_map); |
790 | 0 | XFREE(MTYPE_ROUTE_MAP_NAME, tmp_map.name); |
791 | |
|
792 | 0 | if (map && map->deleted) |
793 | 0 | return NULL; |
794 | | |
795 | 0 | return map; |
796 | 0 | } |
797 | | |
798 | | /* Simple helper to warn if route-map does not exist. */ |
799 | | struct route_map *route_map_lookup_warn_noexist(struct vty *vty, const char *name) |
800 | 0 | { |
801 | 0 | struct route_map *route_map = route_map_lookup_by_name(name); |
802 | |
|
803 | 0 | if (!route_map) |
804 | 0 | if (vty_shell_serv(vty)) |
805 | 0 | vty_out(vty, "The route-map '%s' does not exist.\n", name); |
806 | |
|
807 | 0 | return route_map; |
808 | 0 | } |
809 | | |
810 | | int route_map_mark_updated(const char *name) |
811 | 0 | { |
812 | 0 | struct route_map *map; |
813 | 0 | int ret = -1; |
814 | 0 | struct route_map tmp_map; |
815 | |
|
816 | 0 | if (!name) |
817 | 0 | return (ret); |
818 | | |
819 | 0 | map = route_map_lookup_by_name(name); |
820 | | |
821 | | /* If we did not find the routemap with deleted=false try again |
822 | | * with deleted=true |
823 | | */ |
824 | 0 | if (!map) { |
825 | 0 | memset(&tmp_map, 0, sizeof(tmp_map)); |
826 | 0 | tmp_map.name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, name); |
827 | 0 | tmp_map.deleted = true; |
828 | 0 | map = hash_lookup(route_map_master_hash, &tmp_map); |
829 | 0 | XFREE(MTYPE_ROUTE_MAP_NAME, tmp_map.name); |
830 | 0 | } |
831 | |
|
832 | 0 | if (map) { |
833 | 0 | map->to_be_processed = true; |
834 | 0 | ret = 0; |
835 | 0 | } |
836 | |
|
837 | 0 | return (ret); |
838 | 0 | } |
839 | | |
840 | | static void route_map_clear_updated(struct route_map *map) |
841 | 0 | { |
842 | 0 | if (map) { |
843 | 0 | map->to_be_processed = false; |
844 | 0 | if (map->deleted) |
845 | 0 | route_map_free_map(map); |
846 | 0 | } |
847 | 0 | } |
848 | | |
849 | | /* Lookup route map. If there isn't route map create one and return |
850 | | it. */ |
851 | | struct route_map *route_map_get(const char *name) |
852 | 0 | { |
853 | 0 | struct route_map *map; |
854 | |
|
855 | 0 | map = route_map_lookup_by_name(name); |
856 | 0 | if (map == NULL) |
857 | 0 | map = route_map_add(name); |
858 | |
|
859 | 0 | return map; |
860 | 0 | } |
861 | | |
862 | | void route_map_walk_update_list(void (*route_map_update_fn)(char *name)) |
863 | 0 | { |
864 | 0 | struct route_map *node; |
865 | 0 | struct route_map *nnode = NULL; |
866 | |
|
867 | 0 | for (node = route_map_master.head; node; node = nnode) { |
868 | 0 | if (node->to_be_processed) { |
869 | | /* DD: Should we add any thread yield code here */ |
870 | 0 | route_map_update_fn(node->name); |
871 | 0 | nnode = node->next; |
872 | 0 | route_map_clear_updated(node); |
873 | 0 | } else |
874 | 0 | nnode = node->next; |
875 | 0 | } |
876 | 0 | } |
877 | | |
878 | | /* Return route map's type string. */ |
879 | | static const char *route_map_type_str(enum route_map_type type) |
880 | 0 | { |
881 | 0 | switch (type) { |
882 | 0 | case RMAP_PERMIT: |
883 | 0 | return "permit"; |
884 | 0 | case RMAP_DENY: |
885 | 0 | return "deny"; |
886 | 0 | case RMAP_ANY: |
887 | 0 | return ""; |
888 | 0 | } |
889 | | |
890 | 0 | return ""; |
891 | 0 | } |
892 | | |
893 | | static const char *route_map_cmd_result_str(enum route_map_cmd_result_t res) |
894 | 0 | { |
895 | 0 | switch (res) { |
896 | 0 | case RMAP_MATCH: |
897 | 0 | return "match"; |
898 | 0 | case RMAP_NOMATCH: |
899 | 0 | return "no match"; |
900 | 0 | case RMAP_NOOP: |
901 | 0 | return "noop"; |
902 | 0 | case RMAP_ERROR: |
903 | 0 | return "error"; |
904 | 0 | case RMAP_OKAY: |
905 | 0 | return "okay"; |
906 | 0 | } |
907 | 0 |
|
908 | 0 | return "invalid"; |
909 | 0 | } |
910 | | |
911 | | static const char *route_map_result_str(route_map_result_t res) |
912 | 0 | { |
913 | 0 | switch (res) { |
914 | 0 | case RMAP_DENYMATCH: |
915 | 0 | return "deny"; |
916 | 0 | case RMAP_PERMITMATCH: |
917 | 0 | return "permit"; |
918 | 0 | } |
919 | 0 |
|
920 | 0 | return "invalid"; |
921 | 0 | } |
922 | | |
923 | | /* show route-map */ |
924 | | static void vty_show_route_map_entry(struct vty *vty, struct route_map *map, |
925 | | json_object *json) |
926 | 0 | { |
927 | 0 | struct route_map_index *index; |
928 | 0 | struct route_map_rule *rule; |
929 | 0 | json_object *json_rmap = NULL; |
930 | 0 | json_object *json_rules = NULL; |
931 | |
|
932 | 0 | if (json) { |
933 | 0 | json_rmap = json_object_new_object(); |
934 | 0 | json_object_object_add(json, map->name, json_rmap); |
935 | |
|
936 | 0 | json_rules = json_object_new_array(); |
937 | 0 | json_object_int_add(json_rmap, "invoked", |
938 | 0 | map->applied - map->applied_clear); |
939 | 0 | json_object_boolean_add(json_rmap, "disabledOptimization", |
940 | 0 | map->optimization_disabled); |
941 | 0 | json_object_boolean_add(json_rmap, "processedChange", |
942 | 0 | map->to_be_processed); |
943 | 0 | json_object_object_add(json_rmap, "rules", json_rules); |
944 | 0 | } else { |
945 | 0 | vty_out(vty, |
946 | 0 | "route-map: %s Invoked: %" PRIu64 |
947 | 0 | " Optimization: %s Processed Change: %s\n", |
948 | 0 | map->name, map->applied - map->applied_clear, |
949 | 0 | map->optimization_disabled ? "disabled" : "enabled", |
950 | 0 | map->to_be_processed ? "true" : "false"); |
951 | 0 | } |
952 | |
|
953 | 0 | for (index = map->head; index; index = index->next) { |
954 | 0 | if (json) { |
955 | 0 | json_object *json_rule; |
956 | 0 | json_object *json_matches; |
957 | 0 | json_object *json_sets; |
958 | 0 | char action[BUFSIZ] = {}; |
959 | |
|
960 | 0 | json_rule = json_object_new_object(); |
961 | 0 | json_object_array_add(json_rules, json_rule); |
962 | |
|
963 | 0 | json_object_int_add(json_rule, "sequenceNumber", |
964 | 0 | index->pref); |
965 | 0 | json_object_string_add(json_rule, "type", |
966 | 0 | route_map_type_str(index->type)); |
967 | 0 | json_object_int_add(json_rule, "invoked", |
968 | 0 | index->applied |
969 | 0 | - index->applied_clear); |
970 | | |
971 | | /* Description */ |
972 | 0 | if (index->description) |
973 | 0 | json_object_string_add(json_rule, "description", |
974 | 0 | index->description); |
975 | | |
976 | | /* Match clauses */ |
977 | 0 | json_matches = json_object_new_array(); |
978 | 0 | json_object_object_add(json_rule, "matchClauses", |
979 | 0 | json_matches); |
980 | 0 | for (rule = index->match_list.head; rule; |
981 | 0 | rule = rule->next) { |
982 | 0 | char buf[BUFSIZ]; |
983 | |
|
984 | 0 | snprintf(buf, sizeof(buf), "%s %s", |
985 | 0 | rule->cmd->str, rule->rule_str); |
986 | 0 | json_array_string_add(json_matches, buf); |
987 | 0 | } |
988 | | |
989 | | /* Set clauses */ |
990 | 0 | json_sets = json_object_new_array(); |
991 | 0 | json_object_object_add(json_rule, "setClauses", |
992 | 0 | json_sets); |
993 | 0 | for (rule = index->set_list.head; rule; |
994 | 0 | rule = rule->next) { |
995 | 0 | char buf[BUFSIZ]; |
996 | |
|
997 | 0 | snprintf(buf, sizeof(buf), "%s %s", |
998 | 0 | rule->cmd->str, rule->rule_str); |
999 | 0 | json_array_string_add(json_sets, buf); |
1000 | 0 | } |
1001 | | |
1002 | | /* Call clause */ |
1003 | 0 | if (index->nextrm) |
1004 | 0 | json_object_string_add(json_rule, "callClause", |
1005 | 0 | index->nextrm); |
1006 | | |
1007 | | /* Exit Policy */ |
1008 | 0 | if (index->exitpolicy == RMAP_GOTO) |
1009 | 0 | snprintf(action, sizeof(action), "Goto %d", |
1010 | 0 | index->nextpref); |
1011 | 0 | else if (index->exitpolicy == RMAP_NEXT) |
1012 | 0 | snprintf(action, sizeof(action), |
1013 | 0 | "Continue to next entry"); |
1014 | 0 | else if (index->exitpolicy == RMAP_EXIT) |
1015 | 0 | snprintf(action, sizeof(action), |
1016 | 0 | "Exit routemap"); |
1017 | 0 | if (action[0] != '\0') |
1018 | 0 | json_object_string_add(json_rule, "action", |
1019 | 0 | action); |
1020 | 0 | } else { |
1021 | 0 | vty_out(vty, " %s, sequence %d Invoked %" PRIu64 "\n", |
1022 | 0 | route_map_type_str(index->type), index->pref, |
1023 | 0 | index->applied - index->applied_clear); |
1024 | | |
1025 | | /* Description */ |
1026 | 0 | if (index->description) |
1027 | 0 | vty_out(vty, " Description:\n %s\n", |
1028 | 0 | index->description); |
1029 | | |
1030 | | /* Match clauses */ |
1031 | 0 | vty_out(vty, " Match clauses:\n"); |
1032 | 0 | for (rule = index->match_list.head; rule; |
1033 | 0 | rule = rule->next) |
1034 | 0 | vty_out(vty, " %s %s\n", rule->cmd->str, |
1035 | 0 | rule->rule_str); |
1036 | | |
1037 | | /* Set clauses */ |
1038 | 0 | vty_out(vty, " Set clauses:\n"); |
1039 | 0 | for (rule = index->set_list.head; rule; |
1040 | 0 | rule = rule->next) |
1041 | 0 | vty_out(vty, " %s %s\n", rule->cmd->str, |
1042 | 0 | rule->rule_str); |
1043 | | |
1044 | | /* Call clause */ |
1045 | 0 | vty_out(vty, " Call clause:\n"); |
1046 | 0 | if (index->nextrm) |
1047 | 0 | vty_out(vty, " Call %s\n", index->nextrm); |
1048 | | |
1049 | | /* Exit Policy */ |
1050 | 0 | vty_out(vty, " Action:\n"); |
1051 | 0 | if (index->exitpolicy == RMAP_GOTO) |
1052 | 0 | vty_out(vty, " Goto %d\n", index->nextpref); |
1053 | 0 | else if (index->exitpolicy == RMAP_NEXT) |
1054 | 0 | vty_out(vty, " Continue to next entry\n"); |
1055 | 0 | else if (index->exitpolicy == RMAP_EXIT) |
1056 | 0 | vty_out(vty, " Exit routemap\n"); |
1057 | 0 | } |
1058 | 0 | } |
1059 | 0 | } |
1060 | | |
1061 | | static int sort_route_map(const void **map1, const void **map2) |
1062 | 0 | { |
1063 | 0 | const struct route_map *m1 = *map1; |
1064 | 0 | const struct route_map *m2 = *map2; |
1065 | |
|
1066 | 0 | return strcmp(m1->name, m2->name); |
1067 | 0 | } |
1068 | | |
1069 | | static int vty_show_route_map(struct vty *vty, const char *name, bool use_json) |
1070 | 0 | { |
1071 | 0 | struct route_map *map; |
1072 | 0 | json_object *json = NULL; |
1073 | 0 | json_object *json_proto = NULL; |
1074 | |
|
1075 | 0 | if (use_json) { |
1076 | 0 | json = json_object_new_object(); |
1077 | 0 | json_proto = json_object_new_object(); |
1078 | 0 | json_object_object_add(json, frr_protonameinst, json_proto); |
1079 | 0 | } else |
1080 | 0 | vty_out(vty, "%s:\n", frr_protonameinst); |
1081 | |
|
1082 | 0 | if (name) { |
1083 | 0 | map = route_map_lookup_by_name(name); |
1084 | |
|
1085 | 0 | if (map) { |
1086 | 0 | vty_show_route_map_entry(vty, map, json_proto); |
1087 | 0 | } else if (!use_json) { |
1088 | 0 | vty_out(vty, "%s: 'route-map %s' not found\n", |
1089 | 0 | frr_protonameinst, name); |
1090 | 0 | } |
1091 | 0 | } else { |
1092 | |
|
1093 | 0 | struct list *maplist = list_new(); |
1094 | 0 | struct listnode *ln; |
1095 | |
|
1096 | 0 | for (map = route_map_master.head; map; map = map->next) |
1097 | 0 | listnode_add(maplist, map); |
1098 | |
|
1099 | 0 | list_sort(maplist, sort_route_map); |
1100 | |
|
1101 | 0 | for (ALL_LIST_ELEMENTS_RO(maplist, ln, map)) |
1102 | 0 | vty_show_route_map_entry(vty, map, json_proto); |
1103 | |
|
1104 | 0 | list_delete(&maplist); |
1105 | 0 | } |
1106 | |
|
1107 | 0 | return vty_json(vty, json); |
1108 | 0 | } |
1109 | | |
1110 | | /* Unused route map details */ |
1111 | | static int vty_show_unused_route_map(struct vty *vty) |
1112 | 0 | { |
1113 | 0 | struct list *maplist = list_new(); |
1114 | 0 | struct listnode *ln; |
1115 | 0 | struct route_map *map; |
1116 | |
|
1117 | 0 | for (map = route_map_master.head; map; map = map->next) { |
1118 | | /* If use_count is zero, No protocol is using this routemap. |
1119 | | * so adding to the list. |
1120 | | */ |
1121 | 0 | if (!map->use_count) |
1122 | 0 | listnode_add(maplist, map); |
1123 | 0 | } |
1124 | |
|
1125 | 0 | if (maplist->count > 0) { |
1126 | 0 | vty_out(vty, "\n%s:\n", frr_protonameinst); |
1127 | 0 | list_sort(maplist, sort_route_map); |
1128 | |
|
1129 | 0 | for (ALL_LIST_ELEMENTS_RO(maplist, ln, map)) |
1130 | 0 | vty_show_route_map_entry(vty, map, NULL); |
1131 | 0 | } else { |
1132 | 0 | vty_out(vty, "\n%s: None\n", frr_protonameinst); |
1133 | 0 | } |
1134 | |
|
1135 | 0 | list_delete(&maplist); |
1136 | 0 | return CMD_SUCCESS; |
1137 | 0 | } |
1138 | | |
1139 | | /* New route map allocation. Please note route map's name must be |
1140 | | specified. */ |
1141 | | static struct route_map_index *route_map_index_new(void) |
1142 | 0 | { |
1143 | 0 | struct route_map_index *new; |
1144 | |
|
1145 | 0 | new = XCALLOC(MTYPE_ROUTE_MAP_INDEX, sizeof(struct route_map_index)); |
1146 | 0 | new->exitpolicy = RMAP_EXIT; /* Default to Cisco-style */ |
1147 | 0 | TAILQ_INIT(&new->rhclist); |
1148 | 0 | QOBJ_REG(new, route_map_index); |
1149 | 0 | return new; |
1150 | 0 | } |
1151 | | |
1152 | | /* Free route map index. */ |
1153 | | void route_map_index_delete(struct route_map_index *index, int notify) |
1154 | 0 | { |
1155 | 0 | struct routemap_hook_context *rhc; |
1156 | 0 | struct route_map_rule *rule; |
1157 | |
|
1158 | 0 | QOBJ_UNREG(index); |
1159 | |
|
1160 | 0 | if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) |
1161 | 0 | zlog_debug("Deleting route-map %s sequence %d", |
1162 | 0 | index->map->name, index->pref); |
1163 | | |
1164 | | /* Free route map entry description. */ |
1165 | 0 | XFREE(MTYPE_TMP, index->description); |
1166 | | |
1167 | | /* Free route map northbound hook contexts. */ |
1168 | 0 | while ((rhc = TAILQ_FIRST(&index->rhclist)) != NULL) |
1169 | 0 | routemap_hook_context_free(rhc); |
1170 | | |
1171 | | /* Free route match. */ |
1172 | 0 | while ((rule = index->match_list.head) != NULL) { |
1173 | 0 | if (IS_RULE_IPv4_PREFIX_LIST(rule->cmd->str)) |
1174 | 0 | route_map_pfx_tbl_update(RMAP_EVENT_PLIST_DELETED, |
1175 | 0 | index, AFI_IP, rule->rule_str); |
1176 | 0 | else if (IS_RULE_IPv6_PREFIX_LIST(rule->cmd->str)) |
1177 | 0 | route_map_pfx_tbl_update(RMAP_EVENT_PLIST_DELETED, |
1178 | 0 | index, AFI_IP6, |
1179 | 0 | rule->rule_str); |
1180 | |
|
1181 | 0 | route_map_rule_delete(&index->match_list, rule); |
1182 | 0 | } |
1183 | | |
1184 | | /* Free route set. */ |
1185 | 0 | while ((rule = index->set_list.head) != NULL) |
1186 | 0 | route_map_rule_delete(&index->set_list, rule); |
1187 | | |
1188 | | /* Remove index from route map list. */ |
1189 | 0 | if (index->next) |
1190 | 0 | index->next->prev = index->prev; |
1191 | 0 | else |
1192 | 0 | index->map->tail = index->prev; |
1193 | |
|
1194 | 0 | if (index->prev) |
1195 | 0 | index->prev->next = index->next; |
1196 | 0 | else |
1197 | 0 | index->map->head = index->next; |
1198 | | |
1199 | | /* Free 'char *nextrm' if not NULL */ |
1200 | 0 | XFREE(MTYPE_ROUTE_MAP_NAME, index->nextrm); |
1201 | |
|
1202 | 0 | route_map_pfx_tbl_update(RMAP_EVENT_INDEX_DELETED, index, 0, NULL); |
1203 | | |
1204 | | /* Execute event hook. */ |
1205 | 0 | if (route_map_master.event_hook && notify) { |
1206 | 0 | (*route_map_master.event_hook)(index->map->name); |
1207 | 0 | route_map_notify_dependencies(index->map->name, |
1208 | 0 | RMAP_EVENT_CALL_ADDED); |
1209 | 0 | } |
1210 | 0 | XFREE(MTYPE_ROUTE_MAP_INDEX, index); |
1211 | 0 | } |
1212 | | |
1213 | | /* Lookup index from route map. */ |
1214 | | static struct route_map_index *route_map_index_lookup(struct route_map *map, |
1215 | | enum route_map_type type, |
1216 | | int pref) |
1217 | 0 | { |
1218 | 0 | struct route_map_index *index; |
1219 | |
|
1220 | 0 | for (index = map->head; index; index = index->next) |
1221 | 0 | if ((index->type == type || type == RMAP_ANY) |
1222 | 0 | && index->pref == pref) |
1223 | 0 | return index; |
1224 | 0 | return NULL; |
1225 | 0 | } |
1226 | | |
1227 | | /* Add new index to route map. */ |
1228 | | static struct route_map_index * |
1229 | | route_map_index_add(struct route_map *map, enum route_map_type type, int pref) |
1230 | 0 | { |
1231 | 0 | struct route_map_index *index; |
1232 | 0 | struct route_map_index *point; |
1233 | | |
1234 | | /* Allocate new route map inex. */ |
1235 | 0 | index = route_map_index_new(); |
1236 | 0 | index->map = map; |
1237 | 0 | index->type = type; |
1238 | 0 | index->pref = pref; |
1239 | | |
1240 | | /* Compare preference. */ |
1241 | 0 | for (point = map->head; point; point = point->next) |
1242 | 0 | if (point->pref >= pref) |
1243 | 0 | break; |
1244 | |
|
1245 | 0 | if (map->head == NULL) { |
1246 | 0 | map->head = map->tail = index; |
1247 | 0 | } else if (point == NULL) { |
1248 | 0 | index->prev = map->tail; |
1249 | 0 | map->tail->next = index; |
1250 | 0 | map->tail = index; |
1251 | 0 | } else if (point == map->head) { |
1252 | 0 | index->next = map->head; |
1253 | 0 | map->head->prev = index; |
1254 | 0 | map->head = index; |
1255 | 0 | } else { |
1256 | 0 | index->next = point; |
1257 | 0 | index->prev = point->prev; |
1258 | 0 | if (point->prev) |
1259 | 0 | point->prev->next = index; |
1260 | 0 | point->prev = index; |
1261 | 0 | } |
1262 | |
|
1263 | 0 | route_map_pfx_tbl_update(RMAP_EVENT_INDEX_ADDED, index, 0, NULL); |
1264 | | |
1265 | | /* Execute event hook. */ |
1266 | 0 | if (route_map_master.event_hook) { |
1267 | 0 | (*route_map_master.event_hook)(map->name); |
1268 | 0 | route_map_notify_dependencies(map->name, RMAP_EVENT_CALL_ADDED); |
1269 | 0 | } |
1270 | |
|
1271 | 0 | if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) |
1272 | 0 | zlog_debug("Route-map %s add sequence %d, type: %s", |
1273 | 0 | map->name, pref, route_map_type_str(type)); |
1274 | |
|
1275 | 0 | return index; |
1276 | 0 | } |
1277 | | |
1278 | | /* Get route map index. */ |
1279 | | struct route_map_index * |
1280 | | route_map_index_get(struct route_map *map, enum route_map_type type, int pref) |
1281 | 0 | { |
1282 | 0 | struct route_map_index *index; |
1283 | |
|
1284 | 0 | index = route_map_index_lookup(map, RMAP_ANY, pref); |
1285 | 0 | if (index && index->type != type) { |
1286 | | /* Delete index from route map. */ |
1287 | 0 | route_map_index_delete(index, 1); |
1288 | 0 | index = NULL; |
1289 | 0 | } |
1290 | 0 | if (index == NULL) |
1291 | 0 | index = route_map_index_add(map, type, pref); |
1292 | 0 | return index; |
1293 | 0 | } |
1294 | | |
1295 | | /* New route map rule */ |
1296 | | static struct route_map_rule *route_map_rule_new(void) |
1297 | 0 | { |
1298 | 0 | struct route_map_rule *new; |
1299 | |
|
1300 | 0 | new = XCALLOC(MTYPE_ROUTE_MAP_RULE, sizeof(struct route_map_rule)); |
1301 | 0 | return new; |
1302 | 0 | } |
1303 | | |
1304 | | /* Install rule command to the match list. */ |
1305 | | void _route_map_install_match(struct route_map_rule_cmd_proxy *proxy) |
1306 | 57 | { |
1307 | 57 | rmap_cmd_name_add(rmap_match_cmds, proxy); |
1308 | 57 | } |
1309 | | |
1310 | | /* Install rule command to the set list. */ |
1311 | | void _route_map_install_set(struct route_map_rule_cmd_proxy *proxy) |
1312 | 42 | { |
1313 | 42 | rmap_cmd_name_add(rmap_set_cmds, proxy); |
1314 | 42 | } |
1315 | | |
1316 | | /* Lookup rule command from match list. */ |
1317 | | static const struct route_map_rule_cmd *route_map_lookup_match(const char *name) |
1318 | 0 | { |
1319 | 0 | struct route_map_rule_cmd refcmd = {.str = name}; |
1320 | 0 | struct route_map_rule_cmd_proxy ref = {.cmd = &refcmd}; |
1321 | 0 | struct route_map_rule_cmd_proxy *res; |
1322 | |
|
1323 | 0 | res = rmap_cmd_name_find(rmap_match_cmds, &ref); |
1324 | 0 | if (res) |
1325 | 0 | return res->cmd; |
1326 | 0 | return NULL; |
1327 | 0 | } |
1328 | | |
1329 | | /* Lookup rule command from set list. */ |
1330 | | static const struct route_map_rule_cmd *route_map_lookup_set(const char *name) |
1331 | 0 | { |
1332 | 0 | struct route_map_rule_cmd refcmd = {.str = name}; |
1333 | 0 | struct route_map_rule_cmd_proxy ref = {.cmd = &refcmd}; |
1334 | 0 | struct route_map_rule_cmd_proxy *res; |
1335 | |
|
1336 | 0 | res = rmap_cmd_name_find(rmap_set_cmds, &ref); |
1337 | 0 | if (res) |
1338 | 0 | return res->cmd; |
1339 | 0 | return NULL; |
1340 | 0 | } |
1341 | | |
1342 | | /* Add match and set rule to rule list. */ |
1343 | | static void route_map_rule_add(struct route_map_rule_list *list, |
1344 | | struct route_map_rule *rule) |
1345 | 0 | { |
1346 | 0 | rule->next = NULL; |
1347 | 0 | rule->prev = list->tail; |
1348 | 0 | if (list->tail) |
1349 | 0 | list->tail->next = rule; |
1350 | 0 | else |
1351 | 0 | list->head = rule; |
1352 | 0 | list->tail = rule; |
1353 | 0 | } |
1354 | | |
1355 | | /* Delete rule from rule list. */ |
1356 | | static void route_map_rule_delete(struct route_map_rule_list *list, |
1357 | | struct route_map_rule *rule) |
1358 | 0 | { |
1359 | 0 | if (rule->cmd->func_free) |
1360 | 0 | (*rule->cmd->func_free)(rule->value); |
1361 | |
|
1362 | 0 | XFREE(MTYPE_ROUTE_MAP_RULE_STR, rule->rule_str); |
1363 | |
|
1364 | 0 | if (rule->next) |
1365 | 0 | rule->next->prev = rule->prev; |
1366 | 0 | else |
1367 | 0 | list->tail = rule->prev; |
1368 | 0 | if (rule->prev) |
1369 | 0 | rule->prev->next = rule->next; |
1370 | 0 | else |
1371 | 0 | list->head = rule->next; |
1372 | |
|
1373 | 0 | XFREE(MTYPE_ROUTE_MAP_RULE, rule); |
1374 | 0 | } |
1375 | | |
1376 | | /* strcmp wrapper function which don't crush even argument is NULL. */ |
1377 | | static int rulecmp(const char *dst, const char *src) |
1378 | 0 | { |
1379 | 0 | if (dst == NULL) { |
1380 | 0 | if (src == NULL) |
1381 | 0 | return 0; |
1382 | 0 | else |
1383 | 0 | return 1; |
1384 | 0 | } else { |
1385 | 0 | if (src == NULL) |
1386 | 0 | return 1; |
1387 | 0 | else |
1388 | 0 | return strcmp(dst, src); |
1389 | 0 | } |
1390 | 0 | return 1; |
1391 | 0 | } |
1392 | | |
1393 | | /* Use this to return the already specified argument for this match. This is |
1394 | | * useful to get the specified argument with a route map match rule when the |
1395 | | * rule is being deleted and the argument is not provided. |
1396 | | */ |
1397 | | const char *route_map_get_match_arg(struct route_map_index *index, |
1398 | | const char *match_name) |
1399 | 0 | { |
1400 | 0 | struct route_map_rule *rule; |
1401 | 0 | const struct route_map_rule_cmd *cmd; |
1402 | | |
1403 | | /* First lookup rule for add match statement. */ |
1404 | 0 | cmd = route_map_lookup_match(match_name); |
1405 | 0 | if (cmd == NULL) |
1406 | 0 | return NULL; |
1407 | | |
1408 | 0 | for (rule = index->match_list.head; rule; rule = rule->next) |
1409 | 0 | if (rule->cmd == cmd && rule->rule_str != NULL) |
1410 | 0 | return (rule->rule_str); |
1411 | | |
1412 | 0 | return NULL; |
1413 | 0 | } |
1414 | | |
1415 | | static route_map_event_t get_route_map_delete_event(route_map_event_t type) |
1416 | 0 | { |
1417 | 0 | switch (type) { |
1418 | 0 | case RMAP_EVENT_CALL_ADDED: |
1419 | 0 | return RMAP_EVENT_CALL_DELETED; |
1420 | 0 | case RMAP_EVENT_PLIST_ADDED: |
1421 | 0 | return RMAP_EVENT_PLIST_DELETED; |
1422 | 0 | case RMAP_EVENT_CLIST_ADDED: |
1423 | 0 | return RMAP_EVENT_CLIST_DELETED; |
1424 | 0 | case RMAP_EVENT_ECLIST_ADDED: |
1425 | 0 | return RMAP_EVENT_ECLIST_DELETED; |
1426 | 0 | case RMAP_EVENT_LLIST_ADDED: |
1427 | 0 | return RMAP_EVENT_LLIST_DELETED; |
1428 | 0 | case RMAP_EVENT_ASLIST_ADDED: |
1429 | 0 | return RMAP_EVENT_ASLIST_DELETED; |
1430 | 0 | case RMAP_EVENT_FILTER_ADDED: |
1431 | 0 | return RMAP_EVENT_FILTER_DELETED; |
1432 | 0 | case RMAP_EVENT_SET_ADDED: |
1433 | 0 | case RMAP_EVENT_SET_DELETED: |
1434 | 0 | case RMAP_EVENT_SET_REPLACED: |
1435 | 0 | case RMAP_EVENT_MATCH_ADDED: |
1436 | 0 | case RMAP_EVENT_MATCH_DELETED: |
1437 | 0 | case RMAP_EVENT_MATCH_REPLACED: |
1438 | 0 | case RMAP_EVENT_INDEX_ADDED: |
1439 | 0 | case RMAP_EVENT_INDEX_DELETED: |
1440 | 0 | case RMAP_EVENT_CALL_DELETED: |
1441 | 0 | case RMAP_EVENT_PLIST_DELETED: |
1442 | 0 | case RMAP_EVENT_CLIST_DELETED: |
1443 | 0 | case RMAP_EVENT_ECLIST_DELETED: |
1444 | 0 | case RMAP_EVENT_LLIST_DELETED: |
1445 | 0 | case RMAP_EVENT_ASLIST_DELETED: |
1446 | 0 | case RMAP_EVENT_FILTER_DELETED: |
1447 | | /* This function returns the appropriate 'deleted' event type |
1448 | | * for every 'added' event type passed to this function. |
1449 | | * This is done only for named entities used in the |
1450 | | * route-map match commands. |
1451 | | * This function is not to be invoked for any of the other event |
1452 | | * types. |
1453 | | */ |
1454 | 0 | assert(0); |
1455 | 0 | } |
1456 | | |
1457 | 0 | assert(0); |
1458 | | /* |
1459 | | * Return to make c happy but if we get here something has gone |
1460 | | * terribly terribly wrong, so yes this return makes no sense. |
1461 | | */ |
1462 | 0 | return RMAP_EVENT_CALL_ADDED; |
1463 | 0 | } |
1464 | | |
1465 | | /* Add match statement to route map. */ |
1466 | | enum rmap_compile_rets route_map_add_match(struct route_map_index *index, |
1467 | | const char *match_name, |
1468 | | const char *match_arg, |
1469 | | route_map_event_t type) |
1470 | 0 | { |
1471 | 0 | struct route_map_rule *rule; |
1472 | 0 | struct route_map_rule *next; |
1473 | 0 | const struct route_map_rule_cmd *cmd; |
1474 | 0 | void *compile; |
1475 | 0 | int8_t delete_rmap_event_type = 0; |
1476 | 0 | const char *rule_key; |
1477 | | |
1478 | | /* First lookup rule for add match statement. */ |
1479 | 0 | cmd = route_map_lookup_match(match_name); |
1480 | 0 | if (cmd == NULL) |
1481 | 0 | return RMAP_RULE_MISSING; |
1482 | | |
1483 | | /* Next call compile function for this match statement. */ |
1484 | 0 | if (cmd->func_compile) { |
1485 | 0 | compile = (*cmd->func_compile)(match_arg); |
1486 | 0 | if (compile == NULL) |
1487 | 0 | return RMAP_COMPILE_ERROR; |
1488 | 0 | } else |
1489 | 0 | compile = NULL; |
1490 | | /* use the compiled results if applicable */ |
1491 | 0 | if (compile && cmd->func_get_rmap_rule_key) |
1492 | 0 | rule_key = (*cmd->func_get_rmap_rule_key) |
1493 | 0 | (compile); |
1494 | 0 | else |
1495 | 0 | rule_key = match_arg; |
1496 | | |
1497 | | /* If argument is completely same ignore it. */ |
1498 | 0 | for (rule = index->match_list.head; rule; rule = next) { |
1499 | 0 | next = rule->next; |
1500 | 0 | if (rule->cmd == cmd) { |
1501 | | /* If the configured route-map match rule is exactly |
1502 | | * the same as the existing configuration then, |
1503 | | * ignore the duplicate configuration. |
1504 | | */ |
1505 | 0 | if (rulecmp(match_arg, rule->rule_str) == 0) { |
1506 | 0 | if (cmd->func_free) |
1507 | 0 | (*cmd->func_free)(compile); |
1508 | |
|
1509 | 0 | return RMAP_COMPILE_SUCCESS; |
1510 | 0 | } |
1511 | | |
1512 | | /* If IPv4 or IPv6 prefix-list match criteria |
1513 | | * has been delete to the route-map index, update |
1514 | | * the route-map's prefix table. |
1515 | | */ |
1516 | 0 | if (IS_RULE_IPv4_PREFIX_LIST(match_name)) |
1517 | 0 | route_map_pfx_tbl_update( |
1518 | 0 | RMAP_EVENT_PLIST_DELETED, index, AFI_IP, |
1519 | 0 | rule->rule_str); |
1520 | 0 | else if (IS_RULE_IPv6_PREFIX_LIST(match_name)) |
1521 | 0 | route_map_pfx_tbl_update( |
1522 | 0 | RMAP_EVENT_PLIST_DELETED, index, |
1523 | 0 | AFI_IP6, rule->rule_str); |
1524 | | |
1525 | | /* Remove the dependency of the route-map on the rule |
1526 | | * that is being replaced. |
1527 | | */ |
1528 | 0 | if (type >= RMAP_EVENT_CALL_ADDED) { |
1529 | 0 | delete_rmap_event_type = |
1530 | 0 | get_route_map_delete_event(type); |
1531 | 0 | route_map_upd8_dependency( |
1532 | 0 | delete_rmap_event_type, |
1533 | 0 | rule->rule_str, |
1534 | 0 | index->map->name); |
1535 | 0 | } |
1536 | |
|
1537 | 0 | route_map_rule_delete(&index->match_list, rule); |
1538 | 0 | } |
1539 | 0 | } |
1540 | | |
1541 | | /* Add new route map match rule. */ |
1542 | 0 | rule = route_map_rule_new(); |
1543 | 0 | rule->cmd = cmd; |
1544 | 0 | rule->value = compile; |
1545 | 0 | if (match_arg) |
1546 | 0 | rule->rule_str = XSTRDUP(MTYPE_ROUTE_MAP_RULE_STR, match_arg); |
1547 | 0 | else |
1548 | 0 | rule->rule_str = NULL; |
1549 | | |
1550 | | /* Add new route match rule to linked list. */ |
1551 | 0 | route_map_rule_add(&index->match_list, rule); |
1552 | | |
1553 | | /* If IPv4 or IPv6 prefix-list match criteria |
1554 | | * has been added to the route-map index, update |
1555 | | * the route-map's prefix table. |
1556 | | */ |
1557 | 0 | if (IS_RULE_IPv4_PREFIX_LIST(match_name)) { |
1558 | 0 | route_map_pfx_tbl_update(RMAP_EVENT_PLIST_ADDED, index, AFI_IP, |
1559 | 0 | match_arg); |
1560 | 0 | } else if (IS_RULE_IPv6_PREFIX_LIST(match_name)) { |
1561 | 0 | route_map_pfx_tbl_update(RMAP_EVENT_PLIST_ADDED, index, AFI_IP6, |
1562 | 0 | match_arg); |
1563 | 0 | } |
1564 | | |
1565 | | /* Execute event hook. */ |
1566 | 0 | if (route_map_master.event_hook) { |
1567 | 0 | (*route_map_master.event_hook)(index->map->name); |
1568 | 0 | route_map_notify_dependencies(index->map->name, |
1569 | 0 | RMAP_EVENT_CALL_ADDED); |
1570 | 0 | } |
1571 | 0 | if (type != RMAP_EVENT_MATCH_ADDED) |
1572 | 0 | route_map_upd8_dependency(type, rule_key, index->map->name); |
1573 | |
|
1574 | 0 | return RMAP_COMPILE_SUCCESS; |
1575 | 0 | } |
1576 | | |
1577 | | /* Delete specified route match rule. */ |
1578 | | enum rmap_compile_rets route_map_delete_match(struct route_map_index *index, |
1579 | | const char *match_name, |
1580 | | const char *match_arg, |
1581 | | route_map_event_t type) |
1582 | 0 | { |
1583 | 0 | struct route_map_rule *rule; |
1584 | 0 | const struct route_map_rule_cmd *cmd; |
1585 | 0 | const char *rule_key; |
1586 | |
|
1587 | 0 | cmd = route_map_lookup_match(match_name); |
1588 | 0 | if (cmd == NULL) |
1589 | 0 | return RMAP_RULE_MISSING; |
1590 | | |
1591 | 0 | for (rule = index->match_list.head; rule; rule = rule->next) |
1592 | 0 | if (rule->cmd == cmd && (rulecmp(rule->rule_str, match_arg) == 0 |
1593 | 0 | || match_arg == NULL)) { |
1594 | | /* Execute event hook. */ |
1595 | 0 | if (route_map_master.event_hook) { |
1596 | 0 | (*route_map_master.event_hook)(index->map->name); |
1597 | 0 | route_map_notify_dependencies( |
1598 | 0 | index->map->name, |
1599 | 0 | RMAP_EVENT_CALL_ADDED); |
1600 | 0 | } |
1601 | 0 | if (cmd->func_get_rmap_rule_key) |
1602 | 0 | rule_key = (*cmd->func_get_rmap_rule_key) |
1603 | 0 | (rule->value); |
1604 | 0 | else |
1605 | 0 | rule_key = match_arg; |
1606 | |
|
1607 | 0 | if (type != RMAP_EVENT_MATCH_DELETED && rule_key) |
1608 | 0 | route_map_upd8_dependency(type, rule_key, |
1609 | 0 | index->map->name); |
1610 | |
|
1611 | 0 | route_map_rule_delete(&index->match_list, rule); |
1612 | | |
1613 | | /* If IPv4 or IPv6 prefix-list match criteria |
1614 | | * has been delete from the route-map index, update |
1615 | | * the route-map's prefix table. |
1616 | | */ |
1617 | 0 | if (IS_RULE_IPv4_PREFIX_LIST(match_name)) { |
1618 | 0 | route_map_pfx_tbl_update( |
1619 | 0 | RMAP_EVENT_PLIST_DELETED, index, AFI_IP, |
1620 | 0 | match_arg); |
1621 | 0 | } else if (IS_RULE_IPv6_PREFIX_LIST(match_name)) { |
1622 | 0 | route_map_pfx_tbl_update( |
1623 | 0 | RMAP_EVENT_PLIST_DELETED, index, |
1624 | 0 | AFI_IP6, match_arg); |
1625 | 0 | } |
1626 | |
|
1627 | 0 | return RMAP_COMPILE_SUCCESS; |
1628 | 0 | } |
1629 | | /* Can't find matched rule. */ |
1630 | 0 | return RMAP_RULE_MISSING; |
1631 | 0 | } |
1632 | | |
1633 | | /* Add route-map set statement to the route map. */ |
1634 | | enum rmap_compile_rets route_map_add_set(struct route_map_index *index, |
1635 | | const char *set_name, |
1636 | | const char *set_arg) |
1637 | 0 | { |
1638 | 0 | struct route_map_rule *rule; |
1639 | 0 | struct route_map_rule *next; |
1640 | 0 | const struct route_map_rule_cmd *cmd; |
1641 | 0 | void *compile; |
1642 | |
|
1643 | 0 | cmd = route_map_lookup_set(set_name); |
1644 | 0 | if (cmd == NULL) |
1645 | 0 | return RMAP_RULE_MISSING; |
1646 | | |
1647 | | /* Next call compile function for this match statement. */ |
1648 | 0 | if (cmd->func_compile) { |
1649 | 0 | compile = (*cmd->func_compile)(set_arg); |
1650 | 0 | if (compile == NULL) |
1651 | 0 | return RMAP_COMPILE_ERROR; |
1652 | 0 | } else |
1653 | 0 | compile = NULL; |
1654 | | |
1655 | | /* Add by WJL. if old set command of same kind exist, delete it first |
1656 | | to ensure only one set command of same kind exist under a |
1657 | | route_map_index. */ |
1658 | 0 | for (rule = index->set_list.head; rule; rule = next) { |
1659 | 0 | next = rule->next; |
1660 | 0 | if (rule->cmd == cmd) |
1661 | 0 | route_map_rule_delete(&index->set_list, rule); |
1662 | 0 | } |
1663 | | |
1664 | | /* Add new route map match rule. */ |
1665 | 0 | rule = route_map_rule_new(); |
1666 | 0 | rule->cmd = cmd; |
1667 | 0 | rule->value = compile; |
1668 | 0 | if (set_arg) |
1669 | 0 | rule->rule_str = XSTRDUP(MTYPE_ROUTE_MAP_RULE_STR, set_arg); |
1670 | 0 | else |
1671 | 0 | rule->rule_str = NULL; |
1672 | | |
1673 | | /* Add new route match rule to linked list. */ |
1674 | 0 | route_map_rule_add(&index->set_list, rule); |
1675 | | |
1676 | | /* Execute event hook. */ |
1677 | 0 | if (route_map_master.event_hook) { |
1678 | 0 | (*route_map_master.event_hook)(index->map->name); |
1679 | 0 | route_map_notify_dependencies(index->map->name, |
1680 | 0 | RMAP_EVENT_CALL_ADDED); |
1681 | 0 | } |
1682 | 0 | return RMAP_COMPILE_SUCCESS; |
1683 | 0 | } |
1684 | | |
1685 | | /* Delete route map set rule. */ |
1686 | | enum rmap_compile_rets route_map_delete_set(struct route_map_index *index, |
1687 | | const char *set_name, |
1688 | | const char *set_arg) |
1689 | 0 | { |
1690 | 0 | struct route_map_rule *rule; |
1691 | 0 | const struct route_map_rule_cmd *cmd; |
1692 | |
|
1693 | 0 | cmd = route_map_lookup_set(set_name); |
1694 | 0 | if (cmd == NULL) |
1695 | 0 | return RMAP_RULE_MISSING; |
1696 | | |
1697 | 0 | for (rule = index->set_list.head; rule; rule = rule->next) |
1698 | 0 | if ((rule->cmd == cmd) && (rulecmp(rule->rule_str, set_arg) == 0 |
1699 | 0 | || set_arg == NULL)) { |
1700 | 0 | route_map_rule_delete(&index->set_list, rule); |
1701 | | /* Execute event hook. */ |
1702 | 0 | if (route_map_master.event_hook) { |
1703 | 0 | (*route_map_master.event_hook)(index->map->name); |
1704 | 0 | route_map_notify_dependencies( |
1705 | 0 | index->map->name, |
1706 | 0 | RMAP_EVENT_CALL_ADDED); |
1707 | 0 | } |
1708 | 0 | return RMAP_COMPILE_SUCCESS; |
1709 | 0 | } |
1710 | | /* Can't find matched rule. */ |
1711 | 0 | return RMAP_RULE_MISSING; |
1712 | 0 | } |
1713 | | |
1714 | | static enum route_map_cmd_result_t |
1715 | | route_map_apply_match(struct route_map_rule_list *match_list, |
1716 | | const struct prefix *prefix, void *object) |
1717 | 0 | { |
1718 | 0 | enum route_map_cmd_result_t ret = RMAP_NOMATCH; |
1719 | 0 | struct route_map_rule *match; |
1720 | 0 | bool is_matched = false; |
1721 | | |
1722 | | |
1723 | | /* Check all match rule and if there is no match rule, go to the |
1724 | | set statement. */ |
1725 | 0 | if (!match_list->head) |
1726 | 0 | ret = RMAP_MATCH; |
1727 | 0 | else { |
1728 | 0 | for (match = match_list->head; match; match = match->next) { |
1729 | | /* |
1730 | | * Try each match statement. If any match does not |
1731 | | * return RMAP_MATCH or RMAP_NOOP, return. |
1732 | | * Otherwise continue on to next match statement. |
1733 | | * All match statements must MATCH for |
1734 | | * end-result to be a match. |
1735 | | * (Exception:If match stmts result in a mix of |
1736 | | * MATCH/NOOP, then also end-result is a match) |
1737 | | * If all result in NOOP, end-result is NOOP. |
1738 | | */ |
1739 | 0 | ret = (*match->cmd->func_apply)(match->value, prefix, |
1740 | 0 | object); |
1741 | | |
1742 | | /* |
1743 | | * If the consolidated result of func_apply is: |
1744 | | * ----------------------------------------------- |
1745 | | * | MATCH | NOMATCH | NOOP | Final Result | |
1746 | | * ------------------------------------------------ |
1747 | | * | yes | yes | yes | NOMATCH | |
1748 | | * | no | no | yes | NOOP | |
1749 | | * | yes | no | yes | MATCH | |
1750 | | * | no | yes | yes | NOMATCH | |
1751 | | * |----------------------------------------------- |
1752 | | * |
1753 | | * Traditionally, all rules within route-map |
1754 | | * should match for it to MATCH. |
1755 | | * If there are noops within the route-map rules, |
1756 | | * it follows the above matrix. |
1757 | | * |
1758 | | * Eg: route-map rm1 permit 10 |
1759 | | * match rule1 |
1760 | | * match rule2 |
1761 | | * match rule3 |
1762 | | * .... |
1763 | | * route-map rm1 permit 20 |
1764 | | * match ruleX |
1765 | | * match ruleY |
1766 | | * ... |
1767 | | */ |
1768 | |
|
1769 | 0 | switch (ret) { |
1770 | 0 | case RMAP_MATCH: |
1771 | 0 | is_matched = true; |
1772 | 0 | break; |
1773 | | |
1774 | 0 | case RMAP_NOMATCH: |
1775 | 0 | return ret; |
1776 | | |
1777 | 0 | case RMAP_NOOP: |
1778 | 0 | if (is_matched) |
1779 | 0 | ret = RMAP_MATCH; |
1780 | 0 | break; |
1781 | | |
1782 | 0 | case RMAP_OKAY: |
1783 | 0 | case RMAP_ERROR: |
1784 | 0 | break; |
1785 | 0 | } |
1786 | |
|
1787 | 0 | } |
1788 | 0 | } |
1789 | 0 | return ret; |
1790 | 0 | } |
1791 | | |
1792 | | static struct list *route_map_get_index_list(struct route_node **rn, |
1793 | | const struct prefix *prefix, |
1794 | | struct route_table *table) |
1795 | 0 | { |
1796 | 0 | struct route_node *tmp_rn = NULL; |
1797 | |
|
1798 | 0 | if (!(*rn)) { |
1799 | 0 | *rn = route_node_match(table, prefix); |
1800 | |
|
1801 | 0 | if (!(*rn)) |
1802 | 0 | return NULL; |
1803 | | |
1804 | 0 | if ((*rn)->info) |
1805 | 0 | return (struct list *)((*rn)->info); |
1806 | | |
1807 | | /* If rn->info is NULL, get the parent. |
1808 | | * Store the rn in tmp_rn and unlock it later. |
1809 | | */ |
1810 | 0 | tmp_rn = *rn; |
1811 | 0 | } |
1812 | | |
1813 | 0 | do { |
1814 | 0 | *rn = (*rn)->parent; |
1815 | 0 | if (tmp_rn) |
1816 | 0 | route_unlock_node(tmp_rn); |
1817 | |
|
1818 | 0 | if (!(*rn)) |
1819 | 0 | break; |
1820 | | |
1821 | 0 | if ((*rn)->info) { |
1822 | 0 | route_lock_node(*rn); |
1823 | 0 | return (struct list *)((*rn)->info); |
1824 | 0 | } |
1825 | 0 | } while (!(*rn)->info); |
1826 | | |
1827 | 0 | return NULL; |
1828 | 0 | } |
1829 | | |
1830 | | /* |
1831 | | * This function returns the route-map index that best matches the prefix. |
1832 | | */ |
1833 | | static struct route_map_index * |
1834 | | route_map_get_index(struct route_map *map, const struct prefix *prefix, |
1835 | | void *object, enum route_map_cmd_result_t *match_ret) |
1836 | 0 | { |
1837 | 0 | enum route_map_cmd_result_t ret = RMAP_NOMATCH; |
1838 | 0 | struct list *candidate_rmap_list = NULL; |
1839 | 0 | struct route_node *rn = NULL; |
1840 | 0 | struct listnode *ln = NULL, *nn = NULL; |
1841 | 0 | struct route_map_index *index = NULL, *best_index = NULL; |
1842 | 0 | struct route_map_index *head_index = NULL; |
1843 | 0 | struct route_table *table = NULL; |
1844 | | |
1845 | | /* Route-map optimization relies on LPM lookups of the prefix to reduce |
1846 | | * the amount of route-map clauses a given prefix needs to be processed |
1847 | | * against. These LPM trees are IPv4/IPv6-specific and prefix->family |
1848 | | * must be AF_INET or AF_INET6 in order for the lookup to succeed. So if |
1849 | | * the AF doesn't line up with the LPM trees, skip the optimization. |
1850 | | */ |
1851 | 0 | if (map->optimization_disabled) { |
1852 | 0 | if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) |
1853 | 0 | zlog_debug( |
1854 | 0 | "Skipping route-map optimization for route-map: %s, pfx: %pFX, family: %d", |
1855 | 0 | map->name, prefix, prefix->family); |
1856 | 0 | return map->head; |
1857 | 0 | } |
1858 | | |
1859 | 0 | if (prefix->family == AF_INET) |
1860 | 0 | table = map->ipv4_prefix_table; |
1861 | 0 | else |
1862 | 0 | table = map->ipv6_prefix_table; |
1863 | |
|
1864 | 0 | do { |
1865 | 0 | candidate_rmap_list = |
1866 | 0 | route_map_get_index_list(&rn, prefix, table); |
1867 | 0 | if (!rn) |
1868 | 0 | break; |
1869 | | |
1870 | | /* If the index at the head of the list is of seq higher |
1871 | | * than that in best_index, ignore the list and get the |
1872 | | * parent node's list. |
1873 | | */ |
1874 | 0 | head_index = (struct route_map_index *)(listgetdata( |
1875 | 0 | listhead(candidate_rmap_list))); |
1876 | 0 | if (best_index && head_index |
1877 | 0 | && (best_index->pref < head_index->pref)) { |
1878 | 0 | route_unlock_node(rn); |
1879 | 0 | continue; |
1880 | 0 | } |
1881 | | |
1882 | 0 | for (ALL_LIST_ELEMENTS(candidate_rmap_list, ln, nn, index)) { |
1883 | | /* If the index is of seq higher than that in |
1884 | | * best_index, ignore the list and get the parent |
1885 | | * node's list. |
1886 | | */ |
1887 | 0 | if (best_index && (best_index->pref < index->pref)) |
1888 | 0 | break; |
1889 | | |
1890 | 0 | ret = route_map_apply_match(&index->match_list, prefix, |
1891 | 0 | object); |
1892 | |
|
1893 | 0 | if (ret == RMAP_MATCH) { |
1894 | 0 | *match_ret = ret; |
1895 | 0 | best_index = index; |
1896 | 0 | break; |
1897 | 0 | } else if (ret == RMAP_NOOP) { |
1898 | | /* |
1899 | | * If match_ret is denymatch, even if we see |
1900 | | * more noops, we retain this return value and |
1901 | | * return this eventually if there are no |
1902 | | * matches. |
1903 | | * If a best match route-map index already |
1904 | | * exists, do not reset the match_ret. |
1905 | | */ |
1906 | 0 | if (!best_index && (*match_ret != RMAP_NOMATCH)) |
1907 | 0 | *match_ret = ret; |
1908 | 0 | } else { |
1909 | | /* |
1910 | | * ret is RMAP_NOMATCH. |
1911 | | * If a best match route-map index already |
1912 | | * exists, do not reset the match_ret. |
1913 | | */ |
1914 | 0 | if (!best_index) |
1915 | 0 | *match_ret = ret; |
1916 | 0 | } |
1917 | 0 | } |
1918 | |
|
1919 | 0 | route_unlock_node(rn); |
1920 | |
|
1921 | 0 | } while (rn); |
1922 | | |
1923 | 0 | return best_index; |
1924 | 0 | } |
1925 | | |
1926 | | static int route_map_candidate_list_cmp(struct route_map_index *idx1, |
1927 | | struct route_map_index *idx2) |
1928 | 0 | { |
1929 | 0 | return idx1->pref - idx2->pref; |
1930 | 0 | } |
1931 | | |
1932 | | /* |
1933 | | * This function adds the route-map index into the default route's |
1934 | | * route-node in the route-map's IPv4/IPv6 prefix-table. |
1935 | | */ |
1936 | | static void route_map_pfx_table_add_default(afi_t afi, |
1937 | | struct route_map_index *index) |
1938 | 0 | { |
1939 | 0 | struct route_node *rn = NULL; |
1940 | 0 | struct list *rmap_candidate_list = NULL; |
1941 | 0 | struct prefix p; |
1942 | 0 | bool updated_rn = false; |
1943 | 0 | struct route_table *table = NULL; |
1944 | |
|
1945 | 0 | memset(&p, 0, sizeof(p)); |
1946 | 0 | p.family = afi2family(afi); |
1947 | 0 | p.prefixlen = 0; |
1948 | |
|
1949 | 0 | if (p.family == AF_INET) |
1950 | 0 | table = index->map->ipv4_prefix_table; |
1951 | 0 | else |
1952 | 0 | table = index->map->ipv6_prefix_table; |
1953 | | |
1954 | | /* Add default route to table */ |
1955 | 0 | rn = route_node_get(table, &p); |
1956 | |
|
1957 | 0 | if (!rn) |
1958 | 0 | return; |
1959 | | |
1960 | 0 | if (!rn->info) { |
1961 | 0 | rmap_candidate_list = list_new(); |
1962 | 0 | rmap_candidate_list->cmp = |
1963 | 0 | (int (*)(void *, void *))route_map_candidate_list_cmp; |
1964 | 0 | rn->info = rmap_candidate_list; |
1965 | 0 | } else { |
1966 | 0 | rmap_candidate_list = (struct list *)rn->info; |
1967 | 0 | updated_rn = true; |
1968 | 0 | } |
1969 | |
|
1970 | 0 | listnode_add_sort_nodup(rmap_candidate_list, index); |
1971 | 0 | if (updated_rn) |
1972 | 0 | route_unlock_node(rn); |
1973 | 0 | } |
1974 | | |
1975 | | /* |
1976 | | * This function removes the route-map index from the default route's |
1977 | | * route-node in the route-map's IPv4/IPv6 prefix-table. |
1978 | | */ |
1979 | | static void route_map_pfx_table_del_default(afi_t afi, |
1980 | | struct route_map_index *index) |
1981 | 0 | { |
1982 | 0 | struct route_node *rn = NULL; |
1983 | 0 | struct list *rmap_candidate_list = NULL; |
1984 | 0 | struct prefix p; |
1985 | 0 | struct route_table *table = NULL; |
1986 | |
|
1987 | 0 | memset(&p, 0, sizeof(p)); |
1988 | 0 | p.family = afi2family(afi); |
1989 | 0 | p.prefixlen = 0; |
1990 | |
|
1991 | 0 | if (p.family == AF_INET) |
1992 | 0 | table = index->map->ipv4_prefix_table; |
1993 | 0 | else |
1994 | 0 | table = index->map->ipv6_prefix_table; |
1995 | | |
1996 | | /* Remove RMAP index from default route in table */ |
1997 | 0 | rn = route_node_lookup(table, &p); |
1998 | 0 | if (!rn || !rn->info) |
1999 | 0 | return; |
2000 | | |
2001 | 0 | rmap_candidate_list = (struct list *)rn->info; |
2002 | |
|
2003 | 0 | listnode_delete(rmap_candidate_list, index); |
2004 | |
|
2005 | 0 | if (listcount(rmap_candidate_list) == 0) { |
2006 | 0 | list_delete(&rmap_candidate_list); |
2007 | 0 | rn->info = NULL; |
2008 | 0 | route_unlock_node(rn); |
2009 | 0 | } |
2010 | 0 | route_unlock_node(rn); |
2011 | 0 | } |
2012 | | |
2013 | | /* |
2014 | | * This function adds the route-map index to the route-node for |
2015 | | * the prefix-entry in the route-map's IPv4/IPv6 prefix-table. |
2016 | | */ |
2017 | | static void route_map_pfx_table_add(struct route_table *table, |
2018 | | struct route_map_index *index, |
2019 | | struct prefix_list_entry *pentry) |
2020 | 0 | { |
2021 | 0 | struct route_node *rn = NULL; |
2022 | 0 | struct list *rmap_candidate_list = NULL; |
2023 | 0 | bool updated_rn = false; |
2024 | |
|
2025 | 0 | rn = route_node_get(table, &pentry->prefix); |
2026 | 0 | if (!rn) |
2027 | 0 | return; |
2028 | | |
2029 | 0 | if (!rn->info) { |
2030 | 0 | rmap_candidate_list = list_new(); |
2031 | 0 | rmap_candidate_list->cmp = |
2032 | 0 | (int (*)(void *, void *))route_map_candidate_list_cmp; |
2033 | 0 | rn->info = rmap_candidate_list; |
2034 | 0 | } else { |
2035 | 0 | rmap_candidate_list = (struct list *)rn->info; |
2036 | 0 | updated_rn = true; |
2037 | 0 | } |
2038 | |
|
2039 | 0 | listnode_add_sort_nodup(rmap_candidate_list, index); |
2040 | 0 | if (updated_rn) |
2041 | 0 | route_unlock_node(rn); |
2042 | 0 | } |
2043 | | |
2044 | | /* |
2045 | | * This function removes the route-map index from the route-node for |
2046 | | * the prefix-entry in the route-map's IPv4/IPv6 prefix-table. |
2047 | | */ |
2048 | | static void route_map_pfx_table_del(struct route_table *table, |
2049 | | struct route_map_index *index, |
2050 | | struct prefix_list_entry *pentry) |
2051 | 0 | { |
2052 | 0 | struct route_node *rn = NULL; |
2053 | 0 | struct list *rmap_candidate_list = NULL; |
2054 | |
|
2055 | 0 | rn = route_node_lookup(table, &pentry->prefix); |
2056 | 0 | if (!rn || !rn->info) |
2057 | 0 | return; |
2058 | | |
2059 | 0 | rmap_candidate_list = (struct list *)rn->info; |
2060 | |
|
2061 | 0 | listnode_delete(rmap_candidate_list, index); |
2062 | |
|
2063 | 0 | if (listcount(rmap_candidate_list) == 0) { |
2064 | 0 | list_delete(&rmap_candidate_list); |
2065 | 0 | rn->info = NULL; |
2066 | 0 | route_unlock_node(rn); |
2067 | 0 | } |
2068 | 0 | route_unlock_node(rn); |
2069 | 0 | } |
2070 | | |
2071 | | /* This function checks for the presence of an IPv4 prefix-list |
2072 | | * match rule in the given route-map index. |
2073 | | */ |
2074 | | static bool route_map_is_ip_pfx_list_rule_present(struct route_map_index *index) |
2075 | 0 | { |
2076 | 0 | struct route_map_rule_list *match_list = NULL; |
2077 | 0 | struct route_map_rule *rule = NULL; |
2078 | |
|
2079 | 0 | match_list = &index->match_list; |
2080 | 0 | for (rule = match_list->head; rule; rule = rule->next) |
2081 | 0 | if (IS_RULE_IPv4_PREFIX_LIST(rule->cmd->str)) |
2082 | 0 | return true; |
2083 | | |
2084 | 0 | return false; |
2085 | 0 | } |
2086 | | |
2087 | | /* This function checks for the presence of an IPv6 prefix-list |
2088 | | * match rule in the given route-map index. |
2089 | | */ |
2090 | | static bool |
2091 | | route_map_is_ipv6_pfx_list_rule_present(struct route_map_index *index) |
2092 | 0 | { |
2093 | 0 | struct route_map_rule_list *match_list = NULL; |
2094 | 0 | struct route_map_rule *rule = NULL; |
2095 | |
|
2096 | 0 | match_list = &index->match_list; |
2097 | 0 | for (rule = match_list->head; rule; rule = rule->next) |
2098 | 0 | if (IS_RULE_IPv6_PREFIX_LIST(rule->cmd->str)) |
2099 | 0 | return true; |
2100 | | |
2101 | 0 | return false; |
2102 | 0 | } |
2103 | | |
2104 | | /* This function does the following: |
2105 | | * 1) If plist_name is not present, search for a IPv4 or IPv6 prefix-list |
2106 | | * match clause (based on the afi passed to this foo) and get the |
2107 | | * prefix-list name. |
2108 | | * 2) Look up the prefix-list using the name. |
2109 | | * 3) If the prefix-list is not found then, add the index to the IPv4/IPv6 |
2110 | | * default-route's node in the trie (based on the afi passed to this foo). |
2111 | | * 4) If the prefix-list is found then, remove the index from the IPv4/IPv6 |
2112 | | * default-route's node in the trie (based on the afi passed to this foo). |
2113 | | * 5) If a prefix-entry is passed then, create a route-node for this entry and |
2114 | | * add this index to the route-node. |
2115 | | * 6) If prefix-entry is not passed then, for every prefix-entry in the |
2116 | | * prefix-list, create a route-node for this entry and |
2117 | | * add this index to the route-node. |
2118 | | */ |
2119 | | static void route_map_add_plist_entries(afi_t afi, |
2120 | | struct route_map_index *index, |
2121 | | const char *plist_name, |
2122 | | struct prefix_list_entry *entry) |
2123 | 0 | { |
2124 | 0 | struct route_map_rule_list *match_list = NULL; |
2125 | 0 | struct route_map_rule *match = NULL; |
2126 | 0 | struct prefix_list *plist = NULL; |
2127 | 0 | struct prefix_list_entry *pentry = NULL; |
2128 | 0 | bool plist_rule_is_present = false; |
2129 | |
|
2130 | 0 | if (!plist_name) { |
2131 | 0 | match_list = &index->match_list; |
2132 | |
|
2133 | 0 | for (match = match_list->head; match; match = match->next) { |
2134 | 0 | if (afi == AFI_IP) { |
2135 | 0 | if (IS_RULE_IPv4_PREFIX_LIST(match->cmd->str)) { |
2136 | 0 | plist_rule_is_present = true; |
2137 | 0 | break; |
2138 | 0 | } |
2139 | 0 | } else { |
2140 | 0 | if (IS_RULE_IPv6_PREFIX_LIST(match->cmd->str)) { |
2141 | 0 | plist_rule_is_present = true; |
2142 | 0 | break; |
2143 | 0 | } |
2144 | 0 | } |
2145 | 0 | } |
2146 | |
|
2147 | 0 | if (plist_rule_is_present) |
2148 | 0 | plist = prefix_list_lookup(afi, match->rule_str); |
2149 | 0 | } else { |
2150 | 0 | plist = prefix_list_lookup(afi, plist_name); |
2151 | 0 | } |
2152 | |
|
2153 | 0 | if (!plist) { |
2154 | 0 | route_map_pfx_table_add_default(afi, index); |
2155 | 0 | return; |
2156 | 0 | } |
2157 | | |
2158 | | /* Default entry should be deleted only if the first entry of the |
2159 | | * prefix-list is created. |
2160 | | */ |
2161 | 0 | if (entry) { |
2162 | 0 | if (plist->count == 1) |
2163 | 0 | route_map_pfx_table_del_default(afi, index); |
2164 | 0 | } else { |
2165 | 0 | route_map_pfx_table_del_default(afi, index); |
2166 | 0 | } |
2167 | |
|
2168 | 0 | if (entry) { |
2169 | 0 | if (afi == AFI_IP) { |
2170 | 0 | route_map_pfx_table_add(index->map->ipv4_prefix_table, |
2171 | 0 | index, entry); |
2172 | 0 | } else { |
2173 | 0 | route_map_pfx_table_add(index->map->ipv6_prefix_table, |
2174 | 0 | index, entry); |
2175 | 0 | } |
2176 | 0 | } else { |
2177 | 0 | for (pentry = plist->head; pentry; pentry = pentry->next) { |
2178 | 0 | if (afi == AFI_IP) { |
2179 | 0 | route_map_pfx_table_add( |
2180 | 0 | index->map->ipv4_prefix_table, index, |
2181 | 0 | pentry); |
2182 | 0 | } else { |
2183 | 0 | route_map_pfx_table_add( |
2184 | 0 | index->map->ipv6_prefix_table, index, |
2185 | 0 | pentry); |
2186 | 0 | } |
2187 | 0 | } |
2188 | 0 | } |
2189 | 0 | } |
2190 | | |
2191 | | /* This function does the following: |
2192 | | * 1) If plist_name is not present, search for a IPv4 or IPv6 prefix-list |
2193 | | * match clause (based on the afi passed to this foo) and get the |
2194 | | * prefix-list name. |
2195 | | * 2) Look up the prefix-list using the name. |
2196 | | * 3) If the prefix-list is not found then, delete the index from the IPv4/IPv6 |
2197 | | * default-route's node in the trie (based on the afi passed to this foo). |
2198 | | * 4) If a prefix-entry is passed then, remove this index from the route-node |
2199 | | * for the prefix in this prefix-entry. |
2200 | | * 5) If prefix-entry is not passed then, for every prefix-entry in the |
2201 | | * prefix-list, remove this index from the route-node |
2202 | | * for the prefix in this prefix-entry. |
2203 | | */ |
2204 | | static void route_map_del_plist_entries(afi_t afi, |
2205 | | struct route_map_index *index, |
2206 | | const char *plist_name, |
2207 | | struct prefix_list_entry *entry) |
2208 | 0 | { |
2209 | 0 | struct route_map_rule_list *match_list = NULL; |
2210 | 0 | struct route_map_rule *match = NULL; |
2211 | 0 | struct prefix_list *plist = NULL; |
2212 | 0 | struct prefix_list_entry *pentry = NULL; |
2213 | 0 | bool plist_rule_is_present = false; |
2214 | |
|
2215 | 0 | if (!plist_name) { |
2216 | 0 | match_list = &index->match_list; |
2217 | |
|
2218 | 0 | for (match = match_list->head; match; match = match->next) { |
2219 | 0 | if (afi == AFI_IP) { |
2220 | 0 | if (IS_RULE_IPv4_PREFIX_LIST(match->cmd->str)) { |
2221 | 0 | plist_rule_is_present = true; |
2222 | 0 | break; |
2223 | 0 | } |
2224 | 0 | } else { |
2225 | 0 | if (IS_RULE_IPv6_PREFIX_LIST(match->cmd->str)) { |
2226 | 0 | plist_rule_is_present = true; |
2227 | 0 | break; |
2228 | 0 | } |
2229 | 0 | } |
2230 | 0 | } |
2231 | |
|
2232 | 0 | if (plist_rule_is_present) |
2233 | 0 | plist = prefix_list_lookup(afi, match->rule_str); |
2234 | 0 | } else { |
2235 | 0 | plist = prefix_list_lookup(afi, plist_name); |
2236 | 0 | } |
2237 | |
|
2238 | 0 | if (!plist) { |
2239 | 0 | route_map_pfx_table_del_default(afi, index); |
2240 | 0 | return; |
2241 | 0 | } |
2242 | | |
2243 | 0 | if (entry) { |
2244 | 0 | if (afi == AFI_IP) { |
2245 | 0 | route_map_pfx_table_del(index->map->ipv4_prefix_table, |
2246 | 0 | index, entry); |
2247 | 0 | } else { |
2248 | 0 | route_map_pfx_table_del(index->map->ipv6_prefix_table, |
2249 | 0 | index, entry); |
2250 | 0 | } |
2251 | 0 | } else { |
2252 | 0 | for (pentry = plist->head; pentry; pentry = pentry->next) { |
2253 | 0 | if (afi == AFI_IP) { |
2254 | 0 | route_map_pfx_table_del( |
2255 | 0 | index->map->ipv4_prefix_table, index, |
2256 | 0 | pentry); |
2257 | 0 | } else { |
2258 | 0 | route_map_pfx_table_del( |
2259 | 0 | index->map->ipv6_prefix_table, index, |
2260 | 0 | pentry); |
2261 | 0 | } |
2262 | 0 | } |
2263 | 0 | } |
2264 | 0 | } |
2265 | | |
2266 | | /* |
2267 | | * This function handles the cases where a prefix-list is added/removed |
2268 | | * as a match command from a particular route-map index. |
2269 | | * It updates the prefix-table of the route-map accordingly. |
2270 | | */ |
2271 | | static void route_map_trie_update(afi_t afi, route_map_event_t event, |
2272 | | struct route_map_index *index, |
2273 | | const char *plist_name) |
2274 | 0 | { |
2275 | 0 | if (event == RMAP_EVENT_PLIST_ADDED) { |
2276 | 0 | if (afi == AFI_IP) { |
2277 | 0 | if (!route_map_is_ipv6_pfx_list_rule_present(index)) { |
2278 | 0 | route_map_pfx_table_del_default(AFI_IP6, index); |
2279 | 0 | route_map_add_plist_entries(afi, index, |
2280 | 0 | plist_name, NULL); |
2281 | 0 | } else { |
2282 | 0 | route_map_del_plist_entries(AFI_IP6, index, |
2283 | 0 | NULL, NULL); |
2284 | 0 | } |
2285 | 0 | } else { |
2286 | 0 | if (!route_map_is_ip_pfx_list_rule_present(index)) { |
2287 | 0 | route_map_pfx_table_del_default(AFI_IP, index); |
2288 | 0 | route_map_add_plist_entries(afi, index, |
2289 | 0 | plist_name, NULL); |
2290 | 0 | } else { |
2291 | 0 | route_map_del_plist_entries(AFI_IP, index, NULL, |
2292 | 0 | NULL); |
2293 | 0 | } |
2294 | 0 | } |
2295 | 0 | } else if (event == RMAP_EVENT_PLIST_DELETED) { |
2296 | 0 | if (afi == AFI_IP) { |
2297 | 0 | route_map_del_plist_entries(afi, index, plist_name, |
2298 | 0 | NULL); |
2299 | | |
2300 | | /* If IPv6 prefix-list match rule is not present, |
2301 | | * add this index to the IPv4 default route's trie |
2302 | | * node. |
2303 | | * Also, add this index to the trie nodes created |
2304 | | * for each of the prefix-entries within the IPv6 |
2305 | | * prefix-list, if the IPv6 prefix-list match rule |
2306 | | * is present. Else, add this index to the IPv6 |
2307 | | * default route's trie node. |
2308 | | */ |
2309 | 0 | if (!route_map_is_ipv6_pfx_list_rule_present(index)) |
2310 | 0 | route_map_pfx_table_add_default(afi, index); |
2311 | |
|
2312 | 0 | route_map_add_plist_entries(AFI_IP6, index, NULL, NULL); |
2313 | 0 | } else { |
2314 | 0 | route_map_del_plist_entries(afi, index, plist_name, |
2315 | 0 | NULL); |
2316 | | |
2317 | | /* If IPv4 prefix-list match rule is not present, |
2318 | | * add this index to the IPv6 default route's trie |
2319 | | * node. |
2320 | | * Also, add this index to the trie nodes created |
2321 | | * for each of the prefix-entries within the IPv4 |
2322 | | * prefix-list, if the IPv4 prefix-list match rule |
2323 | | * is present. Else, add this index to the IPv4 |
2324 | | * default route's trie node. |
2325 | | */ |
2326 | 0 | if (!route_map_is_ip_pfx_list_rule_present(index)) |
2327 | 0 | route_map_pfx_table_add_default(afi, index); |
2328 | |
|
2329 | 0 | route_map_add_plist_entries(AFI_IP, index, NULL, NULL); |
2330 | 0 | } |
2331 | 0 | } |
2332 | 0 | } |
2333 | | |
2334 | | /* |
2335 | | * This function handles the cases where a route-map index and |
2336 | | * prefix-list is added/removed. |
2337 | | * It updates the prefix-table of the route-map accordingly. |
2338 | | */ |
2339 | | static void route_map_pfx_tbl_update(route_map_event_t event, |
2340 | | struct route_map_index *index, afi_t afi, |
2341 | | const char *plist_name) |
2342 | 0 | { |
2343 | 0 | if (!index) |
2344 | 0 | return; |
2345 | | |
2346 | 0 | if (event == RMAP_EVENT_INDEX_ADDED) { |
2347 | 0 | route_map_pfx_table_add_default(AFI_IP, index); |
2348 | 0 | route_map_pfx_table_add_default(AFI_IP6, index); |
2349 | 0 | return; |
2350 | 0 | } |
2351 | | |
2352 | 0 | if (event == RMAP_EVENT_INDEX_DELETED) { |
2353 | 0 | route_map_pfx_table_del_default(AFI_IP, index); |
2354 | 0 | route_map_pfx_table_del_default(AFI_IP6, index); |
2355 | |
|
2356 | 0 | return; |
2357 | 0 | } |
2358 | | |
2359 | | /* Handle prefix-list match rule addition/deletion. |
2360 | | */ |
2361 | 0 | route_map_trie_update(afi, event, index, plist_name); |
2362 | 0 | } |
2363 | | |
2364 | | /* |
2365 | | * This function handles the cases where a new prefix-entry is added to |
2366 | | * a prefix-list or, an existing prefix-entry is removed from the prefix-list. |
2367 | | * It updates the prefix-table of the route-map accordingly. |
2368 | | */ |
2369 | | static void route_map_pentry_update(route_map_event_t event, |
2370 | | const char *plist_name, |
2371 | | struct route_map_index *index, |
2372 | | struct prefix_list_entry *pentry) |
2373 | 0 | { |
2374 | 0 | struct prefix_list *plist = NULL; |
2375 | 0 | afi_t afi; |
2376 | 0 | unsigned char family = pentry->prefix.family; |
2377 | |
|
2378 | 0 | if (family == AF_INET) { |
2379 | 0 | afi = AFI_IP; |
2380 | 0 | plist = prefix_list_lookup(AFI_IP, plist_name); |
2381 | 0 | } else { |
2382 | 0 | afi = AFI_IP6; |
2383 | 0 | plist = prefix_list_lookup(AFI_IP6, plist_name); |
2384 | 0 | } |
2385 | |
|
2386 | 0 | if (event == RMAP_EVENT_PLIST_ADDED) { |
2387 | 0 | if (afi == AFI_IP) { |
2388 | 0 | if (!route_map_is_ipv6_pfx_list_rule_present(index)) |
2389 | 0 | route_map_add_plist_entries(afi, index, |
2390 | 0 | plist_name, pentry); |
2391 | 0 | } else { |
2392 | 0 | if (!route_map_is_ip_pfx_list_rule_present(index)) |
2393 | 0 | route_map_add_plist_entries(afi, index, |
2394 | 0 | plist_name, pentry); |
2395 | 0 | } |
2396 | 0 | } else if (event == RMAP_EVENT_PLIST_DELETED) { |
2397 | 0 | route_map_del_plist_entries(afi, index, plist_name, pentry); |
2398 | |
|
2399 | 0 | if (plist->count == 1) { |
2400 | 0 | if (afi == AFI_IP) { |
2401 | 0 | if (!route_map_is_ipv6_pfx_list_rule_present( |
2402 | 0 | index)) |
2403 | 0 | route_map_pfx_table_add_default(afi, |
2404 | 0 | index); |
2405 | 0 | } else { |
2406 | 0 | if (!route_map_is_ip_pfx_list_rule_present( |
2407 | 0 | index)) |
2408 | 0 | route_map_pfx_table_add_default(afi, |
2409 | 0 | index); |
2410 | 0 | } |
2411 | 0 | } |
2412 | 0 | } |
2413 | 0 | } |
2414 | | |
2415 | | static void route_map_pentry_process_dependency(struct hash_bucket *bucket, |
2416 | | void *data) |
2417 | 0 | { |
2418 | 0 | char *rmap_name = NULL; |
2419 | 0 | struct route_map *rmap = NULL; |
2420 | 0 | struct route_map_index *index = NULL; |
2421 | 0 | struct route_map_rule_list *match_list = NULL; |
2422 | 0 | struct route_map_rule *match = NULL; |
2423 | 0 | struct route_map_dep_data *dep_data = NULL; |
2424 | 0 | struct route_map_pentry_dep *pentry_dep = |
2425 | 0 | (struct route_map_pentry_dep *)data; |
2426 | 0 | unsigned char family = pentry_dep->pentry->prefix.family; |
2427 | |
|
2428 | 0 | dep_data = (struct route_map_dep_data *)bucket->data; |
2429 | 0 | if (!dep_data) |
2430 | 0 | return; |
2431 | | |
2432 | 0 | rmap_name = dep_data->rname; |
2433 | 0 | rmap = route_map_lookup_by_name(rmap_name); |
2434 | 0 | if (!rmap || !rmap->head) |
2435 | 0 | return; |
2436 | | |
2437 | 0 | for (index = rmap->head; index; index = index->next) { |
2438 | 0 | match_list = &index->match_list; |
2439 | |
|
2440 | 0 | if (!match_list) |
2441 | 0 | continue; |
2442 | | |
2443 | 0 | for (match = match_list->head; match; match = match->next) { |
2444 | 0 | if (strcmp(match->rule_str, pentry_dep->plist_name) |
2445 | 0 | == 0) { |
2446 | 0 | if (IS_RULE_IPv4_PREFIX_LIST(match->cmd->str) |
2447 | 0 | && family == AF_INET) { |
2448 | 0 | route_map_pentry_update( |
2449 | 0 | pentry_dep->event, |
2450 | 0 | pentry_dep->plist_name, index, |
2451 | 0 | pentry_dep->pentry); |
2452 | 0 | } else if (IS_RULE_IPv6_PREFIX_LIST( |
2453 | 0 | match->cmd->str) |
2454 | 0 | && family == AF_INET6) { |
2455 | 0 | route_map_pentry_update( |
2456 | 0 | pentry_dep->event, |
2457 | 0 | pentry_dep->plist_name, index, |
2458 | 0 | pentry_dep->pentry); |
2459 | 0 | } |
2460 | 0 | } |
2461 | 0 | } |
2462 | 0 | } |
2463 | 0 | } |
2464 | | |
2465 | | void route_map_notify_pentry_dependencies(const char *affected_name, |
2466 | | struct prefix_list_entry *pentry, |
2467 | | route_map_event_t event) |
2468 | 4.21k | { |
2469 | 4.21k | struct route_map_dep *dep = NULL; |
2470 | 4.21k | struct hash *upd8_hash = NULL; |
2471 | 4.21k | struct route_map_pentry_dep pentry_dep; |
2472 | | |
2473 | 4.21k | if (!affected_name || !pentry) |
2474 | 0 | return; |
2475 | | |
2476 | 4.21k | upd8_hash = route_map_get_dep_hash(event); |
2477 | 4.21k | if (!upd8_hash) |
2478 | 0 | return; |
2479 | | |
2480 | 4.21k | dep = (struct route_map_dep *)hash_get(upd8_hash, (void *)affected_name, |
2481 | 4.21k | NULL); |
2482 | 4.21k | if (dep) { |
2483 | 0 | if (!dep->this_hash) |
2484 | 0 | dep->this_hash = upd8_hash; |
2485 | |
|
2486 | 0 | memset(&pentry_dep, 0, sizeof(pentry_dep)); |
2487 | 0 | pentry_dep.pentry = pentry; |
2488 | 0 | pentry_dep.plist_name = affected_name; |
2489 | 0 | pentry_dep.event = event; |
2490 | |
|
2491 | 0 | hash_iterate(dep->dep_rmap_hash, |
2492 | 0 | route_map_pentry_process_dependency, |
2493 | 0 | (void *)&pentry_dep); |
2494 | 0 | } |
2495 | 4.21k | } |
2496 | | |
2497 | | /* Apply route map's each index to the object. |
2498 | | |
2499 | | The matrix for a route-map looks like this: |
2500 | | (note, this includes the description for the "NEXT" |
2501 | | and "GOTO" frobs now |
2502 | | |
2503 | | | Match | No Match | No op |
2504 | | |-----------|--------------|------- |
2505 | | permit | action | cont | cont. |
2506 | | | | default:deny | default:permit |
2507 | | -------------------+----------------------- |
2508 | | | deny | cont | cont. |
2509 | | deny | | default:deny | default:permit |
2510 | | |-----------|--------------|-------- |
2511 | | |
2512 | | action) |
2513 | | -Apply Set statements, accept route |
2514 | | -If Call statement is present jump to the specified route-map, if it |
2515 | | denies the route we finish. |
2516 | | -If NEXT is specified, goto NEXT statement |
2517 | | -If GOTO is specified, goto the first clause where pref > nextpref |
2518 | | -If nothing is specified, do as Cisco and finish |
2519 | | deny) |
2520 | | -Route is denied by route-map. |
2521 | | cont) |
2522 | | -Goto Next index |
2523 | | |
2524 | | If we get no matches after we've processed all updates, then the route |
2525 | | is dropped too. |
2526 | | |
2527 | | Some notes on the new "CALL", "NEXT" and "GOTO" |
2528 | | call WORD - If this clause is matched, then the set statements |
2529 | | are executed and then we jump to route-map 'WORD'. If |
2530 | | this route-map denies the route, we finish, in other |
2531 | | case we |
2532 | | do whatever the exit policy (EXIT, NEXT or GOTO) tells. |
2533 | | on-match next - If this clause is matched, then the set statements |
2534 | | are executed and then we drop through to the next clause |
2535 | | on-match goto n - If this clause is matched, then the set statements |
2536 | | are executed and then we goto the nth clause, or the |
2537 | | first clause greater than this. In order to ensure |
2538 | | route-maps *always* exit, you cannot jump backwards. |
2539 | | Sorry ;) |
2540 | | |
2541 | | We need to make sure our route-map processing matches the above |
2542 | | */ |
2543 | | route_map_result_t route_map_apply_ext(struct route_map *map, |
2544 | | const struct prefix *prefix, |
2545 | | void *match_object, void *set_object, |
2546 | | int *pref) |
2547 | 0 | { |
2548 | 0 | static int recursion = 0; |
2549 | 0 | enum route_map_cmd_result_t match_ret = RMAP_NOMATCH; |
2550 | 0 | route_map_result_t ret = RMAP_PERMITMATCH; |
2551 | 0 | struct route_map_index *index = NULL; |
2552 | 0 | struct route_map_rule *set = NULL; |
2553 | 0 | bool skip_match_clause = false; |
2554 | 0 | struct prefix conv; |
2555 | |
|
2556 | 0 | if (recursion > RMAP_RECURSION_LIMIT) { |
2557 | 0 | if (map) |
2558 | 0 | map->applied++; |
2559 | |
|
2560 | 0 | flog_warn( |
2561 | 0 | EC_LIB_RMAP_RECURSION_LIMIT, |
2562 | 0 | "route-map recursion limit (%d) reached, discarding route", |
2563 | 0 | RMAP_RECURSION_LIMIT); |
2564 | 0 | recursion = 0; |
2565 | 0 | return RMAP_DENYMATCH; |
2566 | 0 | } |
2567 | | |
2568 | 0 | if (map == NULL || map->head == NULL) { |
2569 | 0 | if (map) |
2570 | 0 | map->applied++; |
2571 | 0 | ret = RMAP_DENYMATCH; |
2572 | 0 | goto route_map_apply_end; |
2573 | 0 | } |
2574 | | |
2575 | 0 | map->applied++; |
2576 | | |
2577 | | /* |
2578 | | * Handling for matching evpn_routes in the prefix table. |
2579 | | * |
2580 | | * We convert type2/5 prefix to ipv4/6 prefix to do longest |
2581 | | * prefix matching on. |
2582 | | */ |
2583 | 0 | if (prefix->family == AF_EVPN) { |
2584 | 0 | if (evpn_prefix2prefix(prefix, &conv) != 0) { |
2585 | 0 | if (unlikely(CHECK_FLAG(rmap_debug, |
2586 | 0 | DEBUG_ROUTEMAP_DETAIL))) |
2587 | 0 | zlog_debug( |
2588 | 0 | "Unable to convert EVPN prefix %pFX into IPv4/IPv6 prefix. Falling back to non-optimized route-map lookup", |
2589 | 0 | prefix); |
2590 | 0 | } else { |
2591 | 0 | if (unlikely(CHECK_FLAG(rmap_debug, |
2592 | 0 | DEBUG_ROUTEMAP_DETAIL))) |
2593 | 0 | zlog_debug( |
2594 | 0 | "Converted EVPN prefix %pFX into %pFX for optimized route-map lookup", |
2595 | 0 | prefix, &conv); |
2596 | |
|
2597 | 0 | prefix = &conv; |
2598 | 0 | } |
2599 | 0 | } |
2600 | |
|
2601 | 0 | index = route_map_get_index(map, prefix, match_object, &match_ret); |
2602 | 0 | if (index) { |
2603 | 0 | index->applied++; |
2604 | 0 | if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) |
2605 | 0 | zlog_debug( |
2606 | 0 | "Best match route-map: %s, sequence: %d for pfx: %pFX, result: %s", |
2607 | 0 | map->name, index->pref, prefix, |
2608 | 0 | route_map_cmd_result_str(match_ret)); |
2609 | 0 | } else { |
2610 | 0 | if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) |
2611 | 0 | zlog_debug( |
2612 | 0 | "No best match sequence for pfx: %pFX in route-map: %s, result: %s", |
2613 | 0 | prefix, map->name, |
2614 | 0 | route_map_cmd_result_str(match_ret)); |
2615 | | /* |
2616 | | * No index matches this prefix. Return deny unless, |
2617 | | * match_ret = RMAP_NOOP. |
2618 | | */ |
2619 | 0 | if (match_ret == RMAP_NOOP) |
2620 | 0 | ret = RMAP_PERMITMATCH; |
2621 | 0 | else |
2622 | 0 | ret = RMAP_DENYMATCH; |
2623 | 0 | goto route_map_apply_end; |
2624 | 0 | } |
2625 | 0 | skip_match_clause = true; |
2626 | |
|
2627 | 0 | for (; index; index = index->next) { |
2628 | 0 | if (!skip_match_clause) { |
2629 | 0 | index->applied++; |
2630 | | /* Apply this index. */ |
2631 | 0 | match_ret = route_map_apply_match(&index->match_list, |
2632 | 0 | prefix, match_object); |
2633 | 0 | if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) { |
2634 | 0 | zlog_debug( |
2635 | 0 | "Route-map: %s, sequence: %d, prefix: %pFX, result: %s", |
2636 | 0 | map->name, index->pref, prefix, |
2637 | 0 | route_map_cmd_result_str(match_ret)); |
2638 | 0 | } |
2639 | 0 | } else |
2640 | 0 | skip_match_clause = false; |
2641 | | |
2642 | | |
2643 | | /* Now we apply the matrix from above */ |
2644 | 0 | if (match_ret == RMAP_NOOP) |
2645 | | /* |
2646 | | * Do not change the return value. Retain the previous |
2647 | | * return value. Previous values can be: |
2648 | | * 1)permitmatch (if a nomatch was never |
2649 | | * seen before in this route-map.) |
2650 | | * 2)denymatch (if a nomatch was seen earlier in one |
2651 | | * of the previous sequences) |
2652 | | */ |
2653 | | |
2654 | | /* |
2655 | | * 'cont' from matrix - continue to next route-map |
2656 | | * sequence |
2657 | | */ |
2658 | 0 | continue; |
2659 | 0 | else if (match_ret == RMAP_NOMATCH) { |
2660 | | |
2661 | | /* |
2662 | | * The return value is now changed to denymatch. |
2663 | | * So from here on out, even if we see more noops, |
2664 | | * we retain this return value and return this |
2665 | | * eventually if there are no matches. |
2666 | | */ |
2667 | 0 | ret = RMAP_DENYMATCH; |
2668 | | |
2669 | | /* |
2670 | | * 'cont' from matrix - continue to next route-map |
2671 | | * sequence |
2672 | | */ |
2673 | 0 | continue; |
2674 | 0 | } else if (match_ret == RMAP_MATCH) { |
2675 | 0 | if (index->type == RMAP_PERMIT) |
2676 | | /* 'action' */ |
2677 | 0 | { |
2678 | | /* Match succeeded, rmap is of type permit */ |
2679 | 0 | ret = RMAP_PERMITMATCH; |
2680 | | |
2681 | | /* permit+match must execute sets */ |
2682 | 0 | for (set = index->set_list.head; set; |
2683 | 0 | set = set->next) |
2684 | | /* |
2685 | | * set cmds return RMAP_OKAY or |
2686 | | * RMAP_ERROR. We do not care if |
2687 | | * set succeeded or not. So, ignore |
2688 | | * return code. |
2689 | | */ |
2690 | 0 | (void)(*set->cmd->func_apply)( |
2691 | 0 | set->value, prefix, set_object); |
2692 | | |
2693 | | /* Call another route-map if available */ |
2694 | 0 | if (index->nextrm) { |
2695 | 0 | struct route_map *nextrm = |
2696 | 0 | route_map_lookup_by_name( |
2697 | 0 | index->nextrm); |
2698 | |
|
2699 | 0 | if (nextrm) /* Target route-map found, |
2700 | | jump to it */ |
2701 | 0 | { |
2702 | 0 | recursion++; |
2703 | 0 | ret = route_map_apply_ext( |
2704 | 0 | nextrm, prefix, |
2705 | 0 | match_object, |
2706 | 0 | set_object, NULL); |
2707 | 0 | recursion--; |
2708 | 0 | } |
2709 | | |
2710 | | /* If nextrm returned 'deny', finish. */ |
2711 | 0 | if (ret == RMAP_DENYMATCH) |
2712 | 0 | goto route_map_apply_end; |
2713 | 0 | } |
2714 | | |
2715 | 0 | switch (index->exitpolicy) { |
2716 | 0 | case RMAP_EXIT: |
2717 | 0 | goto route_map_apply_end; |
2718 | 0 | case RMAP_NEXT: |
2719 | 0 | continue; |
2720 | 0 | case RMAP_GOTO: { |
2721 | | /* Find the next clause to jump to */ |
2722 | 0 | struct route_map_index *next = |
2723 | 0 | index->next; |
2724 | 0 | int nextpref = index->nextpref; |
2725 | |
|
2726 | 0 | while (next && next->pref < nextpref) { |
2727 | 0 | index = next; |
2728 | 0 | next = next->next; |
2729 | 0 | } |
2730 | 0 | if (next == NULL) { |
2731 | | /* No clauses match! */ |
2732 | 0 | goto route_map_apply_end; |
2733 | 0 | } |
2734 | 0 | } |
2735 | 0 | } |
2736 | 0 | } else if (index->type == RMAP_DENY) |
2737 | | /* 'deny' */ |
2738 | 0 | { |
2739 | 0 | ret = RMAP_DENYMATCH; |
2740 | 0 | goto route_map_apply_end; |
2741 | 0 | } |
2742 | 0 | } |
2743 | 0 | } |
2744 | | |
2745 | 0 | route_map_apply_end: |
2746 | 0 | if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) |
2747 | 0 | zlog_debug("Route-map: %s, prefix: %pFX, result: %s", |
2748 | 0 | (map ? map->name : "null"), prefix, |
2749 | 0 | route_map_result_str(ret)); |
2750 | |
|
2751 | 0 | if (pref) { |
2752 | 0 | if (index != NULL && ret == RMAP_PERMITMATCH) |
2753 | 0 | *pref = index->pref; |
2754 | 0 | else |
2755 | 0 | *pref = 65536; |
2756 | 0 | } |
2757 | |
|
2758 | 0 | return (ret); |
2759 | 0 | } |
2760 | | |
2761 | | void route_map_add_hook(void (*func)(const char *)) |
2762 | 4 | { |
2763 | 4 | route_map_master.add_hook = func; |
2764 | 4 | } |
2765 | | |
2766 | | void route_map_delete_hook(void (*func)(const char *)) |
2767 | 4 | { |
2768 | 4 | route_map_master.delete_hook = func; |
2769 | 4 | } |
2770 | | |
2771 | | void route_map_event_hook(void (*func)(const char *name)) |
2772 | 4 | { |
2773 | 4 | route_map_master.event_hook = func; |
2774 | 4 | } |
2775 | | |
2776 | | /* Routines for route map dependency lists and dependency processing */ |
2777 | | static bool route_map_rmap_hash_cmp(const void *p1, const void *p2) |
2778 | 0 | { |
2779 | 0 | return strcmp(((const struct route_map_dep_data *)p1)->rname, |
2780 | 0 | ((const struct route_map_dep_data *)p2)->rname) |
2781 | 0 | == 0; |
2782 | 0 | } |
2783 | | |
2784 | | static bool route_map_dep_hash_cmp(const void *p1, const void *p2) |
2785 | 0 | { |
2786 | |
|
2787 | 0 | return (strcmp(((const struct route_map_dep *)p1)->dep_name, |
2788 | 0 | (const char *)p2) |
2789 | 0 | == 0); |
2790 | 0 | } |
2791 | | |
2792 | | static void route_map_clear_reference(struct hash_bucket *bucket, void *arg) |
2793 | 0 | { |
2794 | 0 | struct route_map_dep *dep = bucket->data; |
2795 | 0 | struct route_map_dep_data *dep_data = NULL, tmp_dep_data; |
2796 | |
|
2797 | 0 | memset(&tmp_dep_data, 0, sizeof(tmp_dep_data)); |
2798 | 0 | tmp_dep_data.rname = arg; |
2799 | 0 | dep_data = hash_release(dep->dep_rmap_hash, &tmp_dep_data); |
2800 | 0 | if (dep_data) { |
2801 | 0 | if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) |
2802 | 0 | zlog_debug("Clearing reference for %s to %s count: %d", |
2803 | 0 | dep->dep_name, tmp_dep_data.rname, |
2804 | 0 | dep_data->refcnt); |
2805 | |
|
2806 | 0 | XFREE(MTYPE_ROUTE_MAP_NAME, dep_data->rname); |
2807 | 0 | XFREE(MTYPE_ROUTE_MAP_DEP_DATA, dep_data); |
2808 | 0 | } |
2809 | 0 | if (!dep->dep_rmap_hash->count) { |
2810 | 0 | dep = hash_release(dep->this_hash, (void *)dep->dep_name); |
2811 | 0 | hash_free(dep->dep_rmap_hash); |
2812 | 0 | XFREE(MTYPE_ROUTE_MAP_NAME, dep->dep_name); |
2813 | 0 | XFREE(MTYPE_ROUTE_MAP_DEP, dep); |
2814 | 0 | } |
2815 | 0 | } |
2816 | | |
2817 | | static void route_map_clear_all_references(char *rmap_name) |
2818 | 0 | { |
2819 | 0 | int i; |
2820 | |
|
2821 | 0 | if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) |
2822 | 0 | zlog_debug("Clearing references for %s", rmap_name); |
2823 | |
|
2824 | 0 | for (i = 1; i < ROUTE_MAP_DEP_MAX; i++) { |
2825 | 0 | hash_iterate(route_map_dep_hash[i], route_map_clear_reference, |
2826 | 0 | (void *)rmap_name); |
2827 | 0 | } |
2828 | 0 | } |
2829 | | |
2830 | | static unsigned int route_map_dep_data_hash_make_key(const void *p) |
2831 | 0 | { |
2832 | 0 | const struct route_map_dep_data *dep_data = p; |
2833 | |
|
2834 | 0 | return string_hash_make(dep_data->rname); |
2835 | 0 | } |
2836 | | |
2837 | | static void *route_map_dep_hash_alloc(void *p) |
2838 | 0 | { |
2839 | 0 | char *dep_name = (char *)p; |
2840 | 0 | struct route_map_dep *dep_entry; |
2841 | |
|
2842 | 0 | dep_entry = XCALLOC(MTYPE_ROUTE_MAP_DEP, sizeof(struct route_map_dep)); |
2843 | 0 | dep_entry->dep_name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, dep_name); |
2844 | 0 | dep_entry->dep_rmap_hash = |
2845 | 0 | hash_create_size(8, route_map_dep_data_hash_make_key, |
2846 | 0 | route_map_rmap_hash_cmp, "Route Map Dep Hash"); |
2847 | 0 | dep_entry->this_hash = NULL; |
2848 | |
|
2849 | 0 | return dep_entry; |
2850 | 0 | } |
2851 | | |
2852 | | static void *route_map_name_hash_alloc(void *p) |
2853 | 0 | { |
2854 | 0 | struct route_map_dep_data *dep_data = NULL, *tmp_dep_data = NULL; |
2855 | |
|
2856 | 0 | dep_data = XCALLOC(MTYPE_ROUTE_MAP_DEP_DATA, |
2857 | 0 | sizeof(struct route_map_dep_data)); |
2858 | 0 | tmp_dep_data = p; |
2859 | 0 | dep_data->rname = XSTRDUP(MTYPE_ROUTE_MAP_NAME, tmp_dep_data->rname); |
2860 | 0 | return dep_data; |
2861 | 0 | } |
2862 | | |
2863 | | static unsigned int route_map_dep_hash_make_key(const void *p) |
2864 | 0 | { |
2865 | 0 | return (string_hash_make((char *)p)); |
2866 | 0 | } |
2867 | | |
2868 | | static void route_map_print_dependency(struct hash_bucket *bucket, void *data) |
2869 | 0 | { |
2870 | 0 | struct route_map_dep_data *dep_data = bucket->data; |
2871 | 0 | char *rmap_name = dep_data->rname; |
2872 | 0 | char *dep_name = data; |
2873 | |
|
2874 | 0 | zlog_debug("%s: Dependency for %s: %s", __func__, dep_name, rmap_name); |
2875 | 0 | } |
2876 | | |
2877 | | static int route_map_dep_update(struct hash *dephash, const char *dep_name, |
2878 | | const char *rmap_name, route_map_event_t type) |
2879 | 0 | { |
2880 | 0 | struct route_map_dep *dep = NULL; |
2881 | 0 | char *dname, *rname; |
2882 | 0 | int ret = 0; |
2883 | 0 | struct route_map_dep_data *dep_data = NULL, *ret_dep_data = NULL; |
2884 | 0 | struct route_map_dep_data tmp_dep_data; |
2885 | |
|
2886 | 0 | dname = XSTRDUP(MTYPE_ROUTE_MAP_NAME, dep_name); |
2887 | 0 | rname = XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap_name); |
2888 | |
|
2889 | 0 | switch (type) { |
2890 | 0 | case RMAP_EVENT_PLIST_ADDED: |
2891 | 0 | case RMAP_EVENT_CLIST_ADDED: |
2892 | 0 | case RMAP_EVENT_ECLIST_ADDED: |
2893 | 0 | case RMAP_EVENT_ASLIST_ADDED: |
2894 | 0 | case RMAP_EVENT_LLIST_ADDED: |
2895 | 0 | case RMAP_EVENT_CALL_ADDED: |
2896 | 0 | case RMAP_EVENT_FILTER_ADDED: |
2897 | 0 | if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) |
2898 | 0 | zlog_debug("Adding dependency for filter %s in route-map %s", |
2899 | 0 | dep_name, rmap_name); |
2900 | 0 | dep = (struct route_map_dep *)hash_get( |
2901 | 0 | dephash, dname, route_map_dep_hash_alloc); |
2902 | 0 | if (!dep) { |
2903 | 0 | ret = -1; |
2904 | 0 | goto out; |
2905 | 0 | } |
2906 | | |
2907 | 0 | if (!dep->this_hash) |
2908 | 0 | dep->this_hash = dephash; |
2909 | |
|
2910 | 0 | memset(&tmp_dep_data, 0, sizeof(tmp_dep_data)); |
2911 | 0 | tmp_dep_data.rname = rname; |
2912 | 0 | dep_data = hash_lookup(dep->dep_rmap_hash, &tmp_dep_data); |
2913 | 0 | if (!dep_data) |
2914 | 0 | dep_data = hash_get(dep->dep_rmap_hash, &tmp_dep_data, |
2915 | 0 | route_map_name_hash_alloc); |
2916 | |
|
2917 | 0 | dep_data->refcnt++; |
2918 | 0 | break; |
2919 | 0 | case RMAP_EVENT_PLIST_DELETED: |
2920 | 0 | case RMAP_EVENT_CLIST_DELETED: |
2921 | 0 | case RMAP_EVENT_ECLIST_DELETED: |
2922 | 0 | case RMAP_EVENT_ASLIST_DELETED: |
2923 | 0 | case RMAP_EVENT_LLIST_DELETED: |
2924 | 0 | case RMAP_EVENT_CALL_DELETED: |
2925 | 0 | case RMAP_EVENT_FILTER_DELETED: |
2926 | 0 | if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) |
2927 | 0 | zlog_debug("Deleting dependency for filter %s in route-map %s", |
2928 | 0 | dep_name, rmap_name); |
2929 | 0 | dep = (struct route_map_dep *)hash_get(dephash, dname, NULL); |
2930 | 0 | if (!dep) { |
2931 | 0 | goto out; |
2932 | 0 | } |
2933 | | |
2934 | 0 | memset(&tmp_dep_data, 0, sizeof(tmp_dep_data)); |
2935 | 0 | tmp_dep_data.rname = rname; |
2936 | 0 | dep_data = hash_lookup(dep->dep_rmap_hash, &tmp_dep_data); |
2937 | | /* |
2938 | | * If dep_data is NULL then something has gone seriously |
2939 | | * wrong in route-map handling. Note it and prevent |
2940 | | * the crash. |
2941 | | */ |
2942 | 0 | if (!dep_data) { |
2943 | 0 | zlog_warn( |
2944 | 0 | "route-map dependency for route-map %s: %s is not correct", |
2945 | 0 | rmap_name, dep_name); |
2946 | 0 | goto out; |
2947 | 0 | } |
2948 | | |
2949 | 0 | dep_data->refcnt--; |
2950 | |
|
2951 | 0 | if (!dep_data->refcnt) { |
2952 | 0 | ret_dep_data = hash_release(dep->dep_rmap_hash, |
2953 | 0 | &tmp_dep_data); |
2954 | 0 | if (ret_dep_data) { |
2955 | 0 | XFREE(MTYPE_ROUTE_MAP_NAME, |
2956 | 0 | ret_dep_data->rname); |
2957 | 0 | XFREE(MTYPE_ROUTE_MAP_DEP_DATA, ret_dep_data); |
2958 | 0 | } |
2959 | 0 | } |
2960 | |
|
2961 | 0 | if (!dep->dep_rmap_hash->count) { |
2962 | 0 | dep = hash_release(dephash, dname); |
2963 | 0 | hash_free(dep->dep_rmap_hash); |
2964 | 0 | XFREE(MTYPE_ROUTE_MAP_NAME, dep->dep_name); |
2965 | 0 | XFREE(MTYPE_ROUTE_MAP_DEP, dep); |
2966 | 0 | } |
2967 | 0 | break; |
2968 | 0 | case RMAP_EVENT_SET_ADDED: |
2969 | 0 | case RMAP_EVENT_SET_DELETED: |
2970 | 0 | case RMAP_EVENT_SET_REPLACED: |
2971 | 0 | case RMAP_EVENT_MATCH_ADDED: |
2972 | 0 | case RMAP_EVENT_MATCH_DELETED: |
2973 | 0 | case RMAP_EVENT_MATCH_REPLACED: |
2974 | 0 | case RMAP_EVENT_INDEX_ADDED: |
2975 | 0 | case RMAP_EVENT_INDEX_DELETED: |
2976 | 0 | break; |
2977 | 0 | } |
2978 | | |
2979 | 0 | if (dep) { |
2980 | 0 | if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) |
2981 | 0 | hash_iterate(dep->dep_rmap_hash, |
2982 | 0 | route_map_print_dependency, dname); |
2983 | 0 | } |
2984 | |
|
2985 | 0 | out: |
2986 | 0 | XFREE(MTYPE_ROUTE_MAP_NAME, rname); |
2987 | 0 | XFREE(MTYPE_ROUTE_MAP_NAME, dname); |
2988 | 0 | return ret; |
2989 | 0 | } |
2990 | | |
2991 | | static struct hash *route_map_get_dep_hash(route_map_event_t event) |
2992 | 7.53k | { |
2993 | 7.53k | struct hash *upd8_hash = NULL; |
2994 | | |
2995 | 7.53k | switch (event) { |
2996 | 5.12k | case RMAP_EVENT_PLIST_ADDED: |
2997 | 7.53k | case RMAP_EVENT_PLIST_DELETED: |
2998 | 7.53k | upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_PLIST]; |
2999 | 7.53k | break; |
3000 | 0 | case RMAP_EVENT_CLIST_ADDED: |
3001 | 0 | case RMAP_EVENT_CLIST_DELETED: |
3002 | 0 | upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_CLIST]; |
3003 | 0 | break; |
3004 | 0 | case RMAP_EVENT_ECLIST_ADDED: |
3005 | 0 | case RMAP_EVENT_ECLIST_DELETED: |
3006 | 0 | upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_ECLIST]; |
3007 | 0 | break; |
3008 | 0 | case RMAP_EVENT_ASLIST_ADDED: |
3009 | 0 | case RMAP_EVENT_ASLIST_DELETED: |
3010 | 0 | upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_ASPATH]; |
3011 | 0 | break; |
3012 | 0 | case RMAP_EVENT_LLIST_ADDED: |
3013 | 0 | case RMAP_EVENT_LLIST_DELETED: |
3014 | 0 | upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_LCLIST]; |
3015 | 0 | break; |
3016 | 0 | case RMAP_EVENT_CALL_ADDED: |
3017 | 0 | case RMAP_EVENT_CALL_DELETED: |
3018 | 0 | case RMAP_EVENT_MATCH_ADDED: |
3019 | 0 | case RMAP_EVENT_MATCH_DELETED: |
3020 | 0 | upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_RMAP]; |
3021 | 0 | break; |
3022 | 0 | case RMAP_EVENT_FILTER_ADDED: |
3023 | 0 | case RMAP_EVENT_FILTER_DELETED: |
3024 | 0 | upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_FILTER]; |
3025 | 0 | break; |
3026 | | /* |
3027 | | * Should we actually be ignoring these? |
3028 | | * I am not sure but at this point in time, let |
3029 | | * us get them into this switch and we can peel |
3030 | | * them into the appropriate place in the future |
3031 | | */ |
3032 | 0 | case RMAP_EVENT_SET_ADDED: |
3033 | 0 | case RMAP_EVENT_SET_DELETED: |
3034 | 0 | case RMAP_EVENT_SET_REPLACED: |
3035 | 0 | case RMAP_EVENT_MATCH_REPLACED: |
3036 | 0 | case RMAP_EVENT_INDEX_ADDED: |
3037 | 0 | case RMAP_EVENT_INDEX_DELETED: |
3038 | 0 | upd8_hash = NULL; |
3039 | 0 | break; |
3040 | 7.53k | } |
3041 | 7.53k | return (upd8_hash); |
3042 | 7.53k | } |
3043 | | |
3044 | | static void route_map_process_dependency(struct hash_bucket *bucket, void *data) |
3045 | 0 | { |
3046 | 0 | struct route_map_dep_data *dep_data = NULL; |
3047 | 0 | char *rmap_name = NULL; |
3048 | |
|
3049 | 0 | dep_data = bucket->data; |
3050 | 0 | rmap_name = dep_data->rname; |
3051 | |
|
3052 | 0 | if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) |
3053 | 0 | zlog_debug("Notifying %s of dependency", rmap_name); |
3054 | 0 | if (route_map_master.event_hook) |
3055 | 0 | (*route_map_master.event_hook)(rmap_name); |
3056 | 0 | } |
3057 | | |
3058 | | void route_map_upd8_dependency(route_map_event_t type, const char *arg, |
3059 | | const char *rmap_name) |
3060 | 0 | { |
3061 | 0 | struct hash *upd8_hash = NULL; |
3062 | |
|
3063 | 0 | if ((upd8_hash = route_map_get_dep_hash(type))) { |
3064 | 0 | route_map_dep_update(upd8_hash, arg, rmap_name, type); |
3065 | |
|
3066 | 0 | if (type == RMAP_EVENT_CALL_ADDED) { |
3067 | | /* Execute hook. */ |
3068 | 0 | if (route_map_master.add_hook) |
3069 | 0 | (*route_map_master.add_hook)(rmap_name); |
3070 | 0 | } else if (type == RMAP_EVENT_CALL_DELETED) { |
3071 | | /* Execute hook. */ |
3072 | 0 | if (route_map_master.delete_hook) |
3073 | 0 | (*route_map_master.delete_hook)(rmap_name); |
3074 | 0 | } |
3075 | 0 | } |
3076 | 0 | } |
3077 | | |
3078 | | void route_map_notify_dependencies(const char *affected_name, |
3079 | | route_map_event_t event) |
3080 | 3.31k | { |
3081 | 3.31k | struct route_map_dep *dep; |
3082 | 3.31k | struct hash *upd8_hash; |
3083 | 3.31k | char *name; |
3084 | | |
3085 | 3.31k | if (!affected_name) |
3086 | 0 | return; |
3087 | | |
3088 | 3.31k | name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, affected_name); |
3089 | | |
3090 | 3.31k | if ((upd8_hash = route_map_get_dep_hash(event)) == NULL) { |
3091 | 0 | XFREE(MTYPE_ROUTE_MAP_NAME, name); |
3092 | 0 | return; |
3093 | 0 | } |
3094 | | |
3095 | 3.31k | dep = (struct route_map_dep *)hash_get(upd8_hash, name, NULL); |
3096 | 3.31k | if (dep) { |
3097 | 0 | if (!dep->this_hash) |
3098 | 0 | dep->this_hash = upd8_hash; |
3099 | |
|
3100 | 0 | if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) |
3101 | 0 | zlog_debug("Filter %s updated", dep->dep_name); |
3102 | 0 | hash_iterate(dep->dep_rmap_hash, route_map_process_dependency, |
3103 | 0 | (void *)event); |
3104 | 0 | } |
3105 | | |
3106 | 3.31k | XFREE(MTYPE_ROUTE_MAP_NAME, name); |
3107 | 3.31k | } |
3108 | | |
3109 | | /* VTY related functions. */ |
3110 | | static void clear_route_map_helper(struct route_map *map) |
3111 | 0 | { |
3112 | 0 | struct route_map_index *index; |
3113 | |
|
3114 | 0 | map->applied_clear = map->applied; |
3115 | 0 | for (index = map->head; index; index = index->next) |
3116 | 0 | index->applied_clear = index->applied; |
3117 | 0 | } |
3118 | | |
3119 | | DEFPY (rmap_clear_counters, |
3120 | | rmap_clear_counters_cmd, |
3121 | | "clear route-map counters [RMAP_NAME$rmapname]", |
3122 | | CLEAR_STR |
3123 | | "route-map information\n" |
3124 | | "counters associated with the specified route-map\n" |
3125 | | "route-map name\n") |
3126 | 0 | { |
3127 | 0 | struct route_map *map; |
3128 | |
|
3129 | 0 | if (rmapname) { |
3130 | 0 | map = route_map_lookup_by_name(rmapname); |
3131 | |
|
3132 | 0 | if (map) |
3133 | 0 | clear_route_map_helper(map); |
3134 | 0 | else { |
3135 | 0 | vty_out(vty, "%s: 'route-map %s' not found\n", |
3136 | 0 | frr_protonameinst, rmapname); |
3137 | 0 | return CMD_SUCCESS; |
3138 | 0 | } |
3139 | 0 | } else { |
3140 | 0 | for (map = route_map_master.head; map; map = map->next) |
3141 | 0 | clear_route_map_helper(map); |
3142 | 0 | } |
3143 | | |
3144 | 0 | return CMD_SUCCESS; |
3145 | |
|
3146 | 0 | } |
3147 | | |
3148 | | DEFUN (rmap_show_name, |
3149 | | rmap_show_name_cmd, |
3150 | | "show route-map [WORD] [json]", |
3151 | | SHOW_STR |
3152 | | "route-map information\n" |
3153 | | "route-map name\n" |
3154 | | JSON_STR) |
3155 | 0 | { |
3156 | 0 | bool uj = use_json(argc, argv); |
3157 | 0 | int idx = 0; |
3158 | 0 | const char *name = NULL; |
3159 | |
|
3160 | 0 | if (argv_find(argv, argc, "WORD", &idx)) |
3161 | 0 | name = argv[idx]->arg; |
3162 | |
|
3163 | 0 | return vty_show_route_map(vty, name, uj); |
3164 | 0 | } |
3165 | | |
3166 | | DEFUN (rmap_show_unused, |
3167 | | rmap_show_unused_cmd, |
3168 | | "show route-map-unused", |
3169 | | SHOW_STR |
3170 | | "unused route-map information\n") |
3171 | 0 | { |
3172 | 0 | return vty_show_unused_route_map(vty); |
3173 | 0 | } |
3174 | | |
3175 | | DEFPY (debug_rmap, |
3176 | | debug_rmap_cmd, |
3177 | | "debug route-map [detail]$detail", |
3178 | | DEBUG_STR |
3179 | | "Debug option set for route-maps\n" |
3180 | | "Detailed output\n") |
3181 | 0 | { |
3182 | 0 | if (!detail) |
3183 | 0 | SET_FLAG(rmap_debug, DEBUG_ROUTEMAP); |
3184 | 0 | else |
3185 | 0 | SET_FLAG(rmap_debug, DEBUG_ROUTEMAP | DEBUG_ROUTEMAP_DETAIL); |
3186 | |
|
3187 | 0 | return CMD_SUCCESS; |
3188 | 0 | } |
3189 | | |
3190 | | DEFPY (no_debug_rmap, |
3191 | | no_debug_rmap_cmd, |
3192 | | "no debug route-map [detail]$detail", |
3193 | | NO_STR |
3194 | | DEBUG_STR |
3195 | | "Debug option set for route-maps\n" |
3196 | | "Detailed output\n") |
3197 | 0 | { |
3198 | 0 | if (!detail) |
3199 | 0 | UNSET_FLAG(rmap_debug, DEBUG_ROUTEMAP); |
3200 | 0 | else |
3201 | 0 | UNSET_FLAG(rmap_debug, DEBUG_ROUTEMAP | DEBUG_ROUTEMAP_DETAIL); |
3202 | |
|
3203 | 0 | return CMD_SUCCESS; |
3204 | 0 | } |
3205 | | |
3206 | | /* Debug node. */ |
3207 | | static int rmap_config_write_debug(struct vty *vty); |
3208 | | static struct cmd_node rmap_debug_node = { |
3209 | | .name = "route-map debug", |
3210 | | .node = RMAP_DEBUG_NODE, |
3211 | | .prompt = "", |
3212 | | .config_write = rmap_config_write_debug, |
3213 | | }; |
3214 | | |
3215 | | void route_map_show_debug(struct vty *vty) |
3216 | 0 | { |
3217 | 0 | if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) |
3218 | 0 | vty_out(vty, "debug route-map\n"); |
3219 | 0 | } |
3220 | | |
3221 | | /* Configuration write function. */ |
3222 | | static int rmap_config_write_debug(struct vty *vty) |
3223 | 0 | { |
3224 | 0 | int write = 0; |
3225 | |
|
3226 | 0 | if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) { |
3227 | 0 | vty_out(vty, "debug route-map\n"); |
3228 | 0 | write++; |
3229 | 0 | } |
3230 | |
|
3231 | 0 | return write; |
3232 | 0 | } |
3233 | | |
3234 | | /* Common route map rules */ |
3235 | | |
3236 | | void *route_map_rule_tag_compile(const char *arg) |
3237 | 0 | { |
3238 | 0 | unsigned long int tmp; |
3239 | 0 | char *endptr; |
3240 | 0 | route_tag_t *tag; |
3241 | |
|
3242 | 0 | errno = 0; |
3243 | 0 | tmp = strtoul(arg, &endptr, 0); |
3244 | 0 | if (arg[0] == '\0' || *endptr != '\0' || errno || tmp > ROUTE_TAG_MAX) |
3245 | 0 | return NULL; |
3246 | | |
3247 | 0 | tag = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(*tag)); |
3248 | 0 | *tag = tmp; |
3249 | |
|
3250 | 0 | return tag; |
3251 | 0 | } |
3252 | | |
3253 | | void route_map_rule_tag_free(void *rule) |
3254 | 0 | { |
3255 | 0 | XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); |
3256 | 0 | } |
3257 | | |
3258 | | void route_map_finish(void) |
3259 | 0 | { |
3260 | 0 | int i; |
3261 | 0 | struct route_map_rule_cmd_proxy *proxy; |
3262 | | |
3263 | | /* these 2 hash tables have INIT_HASH initializers, so the "default" |
3264 | | * state is "initialized & empty" => fini() followed by init() to |
3265 | | * return to that same state |
3266 | | */ |
3267 | 0 | while ((proxy = rmap_cmd_name_pop(rmap_match_cmds))) |
3268 | 0 | (void)proxy; |
3269 | 0 | rmap_cmd_name_fini(rmap_match_cmds); |
3270 | 0 | rmap_cmd_name_init(rmap_match_cmds); |
3271 | |
|
3272 | 0 | while ((proxy = rmap_cmd_name_pop(rmap_set_cmds))) |
3273 | 0 | (void)proxy; |
3274 | 0 | rmap_cmd_name_fini(rmap_set_cmds); |
3275 | 0 | rmap_cmd_name_init(rmap_set_cmds); |
3276 | | |
3277 | | /* |
3278 | | * All protocols are setting these to NULL |
3279 | | * by default on shutdown( route_map_finish ) |
3280 | | * Why are we making them do this work? |
3281 | | */ |
3282 | 0 | route_map_master.add_hook = NULL; |
3283 | 0 | route_map_master.delete_hook = NULL; |
3284 | 0 | route_map_master.event_hook = NULL; |
3285 | | |
3286 | | /* cleanup route_map */ |
3287 | 0 | while (route_map_master.head) { |
3288 | 0 | struct route_map *map = route_map_master.head; |
3289 | 0 | map->to_be_processed = false; |
3290 | 0 | route_map_delete(map); |
3291 | 0 | } |
3292 | |
|
3293 | 0 | for (i = 1; i < ROUTE_MAP_DEP_MAX; i++) { |
3294 | 0 | hash_free(route_map_dep_hash[i]); |
3295 | 0 | route_map_dep_hash[i] = NULL; |
3296 | 0 | } |
3297 | |
|
3298 | 0 | hash_free(route_map_master_hash); |
3299 | 0 | route_map_master_hash = NULL; |
3300 | 0 | } |
3301 | | |
3302 | | /* Increment the use_count counter while attaching the route map */ |
3303 | | void route_map_counter_increment(struct route_map *map) |
3304 | 0 | { |
3305 | 0 | if (map) |
3306 | 0 | map->use_count++; |
3307 | 0 | } |
3308 | | |
3309 | | /* Decrement the use_count counter while detaching the route map. */ |
3310 | | void route_map_counter_decrement(struct route_map *map) |
3311 | 0 | { |
3312 | 0 | if (map) { |
3313 | 0 | if (map->use_count <= 0) |
3314 | 0 | return; |
3315 | 0 | map->use_count--; |
3316 | 0 | } |
3317 | 0 | } |
3318 | | |
3319 | | DEFUN_HIDDEN(show_route_map_pfx_tbl, show_route_map_pfx_tbl_cmd, |
3320 | | "show route-map RMAP_NAME prefix-table", |
3321 | | SHOW_STR |
3322 | | "route-map\n" |
3323 | | "route-map name\n" |
3324 | | "internal prefix-table\n") |
3325 | 0 | { |
3326 | 0 | const char *rmap_name = argv[2]->arg; |
3327 | 0 | struct route_map *rmap = NULL; |
3328 | 0 | struct route_table *rm_pfx_tbl4 = NULL; |
3329 | 0 | struct route_table *rm_pfx_tbl6 = NULL; |
3330 | 0 | struct route_node *rn = NULL, *prn = NULL; |
3331 | 0 | struct list *rmap_index_list = NULL; |
3332 | 0 | struct listnode *ln = NULL, *nln = NULL; |
3333 | 0 | struct route_map_index *index = NULL; |
3334 | 0 | uint8_t len = 54; |
3335 | |
|
3336 | 0 | vty_out(vty, "%s:\n", frr_protonameinst); |
3337 | 0 | rmap = route_map_lookup_by_name(rmap_name); |
3338 | 0 | if (rmap) { |
3339 | 0 | rm_pfx_tbl4 = rmap->ipv4_prefix_table; |
3340 | 0 | if (rm_pfx_tbl4) { |
3341 | 0 | vty_out(vty, "\n%s%43s%s\n", "IPv4 Prefix", "", |
3342 | 0 | "Route-map Index List"); |
3343 | 0 | vty_out(vty, "%s%39s%s\n", "_______________", "", |
3344 | 0 | "____________________"); |
3345 | 0 | for (rn = route_top(rm_pfx_tbl4); rn; |
3346 | 0 | rn = route_next(rn)) { |
3347 | 0 | vty_out(vty, " %pRN (%d)\n", rn, |
3348 | 0 | route_node_get_lock_count(rn)); |
3349 | |
|
3350 | 0 | vty_out(vty, "(P) "); |
3351 | 0 | prn = rn->parent; |
3352 | 0 | if (prn) { |
3353 | 0 | vty_out(vty, "%pRN\n", prn); |
3354 | 0 | } |
3355 | |
|
3356 | 0 | vty_out(vty, "\n"); |
3357 | 0 | rmap_index_list = (struct list *)rn->info; |
3358 | 0 | if (!rmap_index_list |
3359 | 0 | || !listcount(rmap_index_list)) |
3360 | 0 | vty_out(vty, "%*s%s\n", len, "", "-"); |
3361 | 0 | else |
3362 | 0 | for (ALL_LIST_ELEMENTS(rmap_index_list, |
3363 | 0 | ln, nln, |
3364 | 0 | index)) { |
3365 | 0 | vty_out(vty, "%*s%s seq %d\n", |
3366 | 0 | len, "", |
3367 | 0 | index->map->name, |
3368 | 0 | index->pref); |
3369 | 0 | } |
3370 | 0 | vty_out(vty, "\n"); |
3371 | 0 | } |
3372 | 0 | } |
3373 | |
|
3374 | 0 | rm_pfx_tbl6 = rmap->ipv6_prefix_table; |
3375 | 0 | if (rm_pfx_tbl6) { |
3376 | 0 | vty_out(vty, "\n%s%43s%s\n", "IPv6 Prefix", "", |
3377 | 0 | "Route-map Index List"); |
3378 | 0 | vty_out(vty, "%s%39s%s\n", "_______________", "", |
3379 | 0 | "____________________"); |
3380 | 0 | for (rn = route_top(rm_pfx_tbl6); rn; |
3381 | 0 | rn = route_next(rn)) { |
3382 | 0 | vty_out(vty, " %pRN (%d)\n", rn, |
3383 | 0 | route_node_get_lock_count(rn)); |
3384 | |
|
3385 | 0 | vty_out(vty, "(P) "); |
3386 | 0 | prn = rn->parent; |
3387 | 0 | if (prn) { |
3388 | 0 | vty_out(vty, "%pRN\n", prn); |
3389 | 0 | } |
3390 | |
|
3391 | 0 | vty_out(vty, "\n"); |
3392 | 0 | rmap_index_list = (struct list *)rn->info; |
3393 | 0 | if (!rmap_index_list |
3394 | 0 | || !listcount(rmap_index_list)) |
3395 | 0 | vty_out(vty, "%*s%s\n", len, "", "-"); |
3396 | 0 | else |
3397 | 0 | for (ALL_LIST_ELEMENTS(rmap_index_list, |
3398 | 0 | ln, nln, |
3399 | 0 | index)) { |
3400 | 0 | vty_out(vty, "%*s%s seq %d\n", |
3401 | 0 | len, "", |
3402 | 0 | index->map->name, |
3403 | 0 | index->pref); |
3404 | 0 | } |
3405 | 0 | vty_out(vty, "\n"); |
3406 | 0 | } |
3407 | 0 | } |
3408 | 0 | } |
3409 | |
|
3410 | 0 | vty_out(vty, "\n"); |
3411 | 0 | return CMD_SUCCESS; |
3412 | 0 | } |
3413 | | |
3414 | | /* Initialization of route map vector. */ |
3415 | | void route_map_init(void) |
3416 | 4 | { |
3417 | 4 | int i; |
3418 | | |
3419 | 4 | route_map_master_hash = |
3420 | 4 | hash_create_size(8, route_map_hash_key_make, route_map_hash_cmp, |
3421 | 4 | "Route Map Master Hash"); |
3422 | | |
3423 | 32 | for (i = 1; i < ROUTE_MAP_DEP_MAX; i++) |
3424 | 28 | route_map_dep_hash[i] = hash_create_size( |
3425 | 28 | 8, route_map_dep_hash_make_key, route_map_dep_hash_cmp, |
3426 | 28 | "Route Map Dep Hash"); |
3427 | | |
3428 | 4 | UNSET_FLAG(rmap_debug, DEBUG_ROUTEMAP); |
3429 | | |
3430 | 4 | route_map_cli_init(); |
3431 | | |
3432 | | /* Install route map top node. */ |
3433 | 4 | install_node(&rmap_debug_node); |
3434 | | |
3435 | | /* Install route map commands. */ |
3436 | 4 | install_element(CONFIG_NODE, &debug_rmap_cmd); |
3437 | 4 | install_element(CONFIG_NODE, &no_debug_rmap_cmd); |
3438 | | |
3439 | | /* Install show command */ |
3440 | 4 | install_element(ENABLE_NODE, &rmap_clear_counters_cmd); |
3441 | | |
3442 | 4 | install_element(ENABLE_NODE, &rmap_show_name_cmd); |
3443 | 4 | install_element(ENABLE_NODE, &rmap_show_unused_cmd); |
3444 | | |
3445 | 4 | install_element(ENABLE_NODE, &debug_rmap_cmd); |
3446 | 4 | install_element(ENABLE_NODE, &no_debug_rmap_cmd); |
3447 | | |
3448 | 4 | install_element(ENABLE_NODE, &show_route_map_pfx_tbl_cmd); |
3449 | 4 | } |