diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | bgpd/bgp_bmp.c | 2 | ||||
| -rw-r--r-- | lib/atomlist.h | 2 | ||||
| -rw-r--r-- | lib/filter.c | 18 | ||||
| -rw-r--r-- | lib/typerb.h | 2 | ||||
| -rw-r--r-- | lib/typesafe.h | 2 | ||||
| -rw-r--r-- | python/tsexpand.py | 131 | ||||
| -rw-r--r-- | tests/topotests/nb_config/test_nb_config.py | 4 | ||||
| -rw-r--r-- | vtysh/vtysh.c | 102 | ||||
| -rw-r--r-- | vtysh/vtysh.h | 4 |
10 files changed, 253 insertions, 15 deletions
diff --git a/.gitignore b/.gitignore index 07cdb11a21..60b4189952 100644 --- a/.gitignore +++ b/.gitignore @@ -62,6 +62,7 @@ *.cg.dot *.cg.svg *.xref +*_tsexpand.h ### gcov outputs diff --git a/bgpd/bgp_bmp.c b/bgpd/bgp_bmp.c index 2f3be0bc9d..bf08e30509 100644 --- a/bgpd/bgp_bmp.c +++ b/bgpd/bgp_bmp.c @@ -2496,7 +2496,7 @@ DEFPY(bmp_stats_send_experimental, { VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt); - bt->stats_send_experimental = !!no; + bt->stats_send_experimental = !no; return CMD_SUCCESS; } diff --git a/lib/atomlist.h b/lib/atomlist.h index 2b6a3a176f..faf1d7324e 100644 --- a/lib/atomlist.h +++ b/lib/atomlist.h @@ -6,8 +6,10 @@ #ifndef _FRR_ATOMLIST_H #define _FRR_ATOMLIST_H +#ifndef _TYPESAFE_EXPAND_MACROS #include "typesafe.h" #include "frratomic.h" +#endif /* _TYPESAFE_EXPAND_MACROS */ #ifdef __cplusplus extern "C" { diff --git a/lib/filter.c b/lib/filter.c index 5a0790f8bf..0722bed1cb 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -458,7 +458,6 @@ static int filter_show(struct vty *vty, const char *name, afi_t afi, struct filter_cisco *filter; bool first; json_object *json = NULL; - json_object *json_proto = NULL; master = access_master_get(afi); if (master == NULL) { @@ -469,12 +468,7 @@ static int filter_show(struct vty *vty, const char *name, afi_t afi, if (use_json) json = json_object_new_object(); - - /* Print the name of the protocol */ - if (json) { - json_proto = json_object_new_object(); - json_object_object_add(json, frr_protoname, json_proto); - } else + else vty_out(vty, "%s:\n", frr_protoname); for (access = master->str.head; access; access = access->next) { @@ -496,7 +490,7 @@ static int filter_show(struct vty *vty, const char *name, afi_t afi, if (json) { json_acl = json_object_new_object(); - json_object_object_add(json_proto, + json_object_object_add(json, access->name, json_acl); @@ -596,7 +590,7 @@ DEFUN (show_mac_access_list_name, return filter_show(vty, argv[3]->arg, AFI_L2VPN, false); } -DEFUN (show_ip_access_list, +DEFUN_NOSH (show_ip_access_list, show_ip_access_list_cmd, "show ip access-list [json]", SHOW_STR @@ -608,7 +602,7 @@ DEFUN (show_ip_access_list, return filter_show(vty, NULL, AFI_IP, uj); } -DEFUN (show_ip_access_list_name, +DEFUN_NOSH (show_ip_access_list_name, show_ip_access_list_name_cmd, "show ip access-list ACCESSLIST4_NAME [json]", SHOW_STR @@ -622,7 +616,7 @@ DEFUN (show_ip_access_list_name, return filter_show(vty, argv[idx_acl]->arg, AFI_IP, uj); } -DEFUN (show_ipv6_access_list, +DEFUN_NOSH (show_ipv6_access_list, show_ipv6_access_list_cmd, "show ipv6 access-list [json]", SHOW_STR @@ -634,7 +628,7 @@ DEFUN (show_ipv6_access_list, return filter_show(vty, NULL, AFI_IP6, uj); } -DEFUN (show_ipv6_access_list_name, +DEFUN_NOSH (show_ipv6_access_list_name, show_ipv6_access_list_name_cmd, "show ipv6 access-list ACCESSLIST6_NAME [json]", SHOW_STR diff --git a/lib/typerb.h b/lib/typerb.h index b020a665f6..93370e1012 100644 --- a/lib/typerb.h +++ b/lib/typerb.h @@ -9,8 +9,10 @@ #ifndef _FRR_TYPERB_H #define _FRR_TYPERB_H +#ifndef _TYPESAFE_EXPAND_MACROS #include <string.h> #include "typesafe.h" +#endif /* _TYPESAFE_EXPAND_MACROS */ #ifdef __cplusplus extern "C" { diff --git a/lib/typesafe.h b/lib/typesafe.h index 93258c5954..fc028049a4 100644 --- a/lib/typesafe.h +++ b/lib/typesafe.h @@ -6,10 +6,12 @@ #ifndef _FRR_TYPESAFE_H #define _FRR_TYPESAFE_H +#ifndef _TYPESAFE_EXPAND_MACROS #include <stddef.h> #include <stdint.h> #include <stdbool.h> #include "compiler.h" +#endif /* _TYPESAFE_EXPAND_MACROS */ #ifdef __cplusplus extern "C" { diff --git a/python/tsexpand.py b/python/tsexpand.py new file mode 100644 index 0000000000..5d6099750f --- /dev/null +++ b/python/tsexpand.py @@ -0,0 +1,131 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: MIT +# +# 2024 by David Lamparter +# +# this tool edits an FRR source .c file to expand the typesafe DECLARE_DLIST +# et al. definitions. This can be helpful to get better warnings/errors from +# GCC when something re. a typesafe container is involved. You can also use +# it on .h files. +# The actual expansions created by this tool are written to separate files +# called something like "lib/cspf__visited_tsexpand.h" (for a container named +# "visited") +# +# THIS TOOL EDITS THE FILE IN PLACE. MAKE A BACKUP IF YOU HAVE UNSAVED WORK +# IN PROGRESS (which is likely because you're debugging a typesafe container +# problem!) +# +# The PREDECL_XYZ is irrelevant for this tool, it needs to be run on the file +# that has the DECLARE_XYZ (can be .c or .h) +# +# the lines added by this tool all have /* $ts_expand: remove$ */ at the end +# you can undo the effects of this tool by calling sed: +# +# sed -e '/\$ts_expand: remove\$/ d' -i.orig filename.c + +import os +import sys +import re +import subprocess +import shlex + +decl_re = re.compile( + r"""(?<=\n)[ \t]*DECLARE_(LIST|ATOMLIST|DLIST|HEAP|HASH|(SORTLIST|SKIPLIST|RBTREE|ATOMSORT)_(NON)?UNIQ)\(\s*(?P<name>[^, \t\n]+)\s*,[^)]+\)\s*;[ \t]*\n""" +) +kill_re = re.compile(r"""(?<=\n)[^\n]*/\* \$ts_expand: remove\$ \*/\n""") + +src_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +# some files may be compiled with different CPPFLAGS, that's not supported +# here... +cpp = subprocess.check_output( + ["make", "var-CPP", "var-AM_CPPFLAGS", "var-DEFS"], cwd=src_root +) +cpp = shlex.split(cpp.decode("UTF-8")) + + +def process_file(filename): + with open(filename, "r") as ifd: + data = ifd.read() + + data = kill_re.sub("", data) + + before = 0 + + dirname = os.path.dirname(filename) + basename = os.path.basename(filename).removesuffix(".c").removesuffix(".h") + + xname = filename + ".exp" + with open(filename + ".exp", "w") as ofd: + for m in decl_re.finditer(data): + s = m.start() + e = m.end() + ofd.write(data[before:s]) + + # start gcc/clang with some "magic" options to make it expand the + # typesafe macros, but nothing else. + # -P removes the "#line" markers (which are useless because + # everything ends up on one line anyway) + # -D_TYPESAFE_EXPAND_MACROS prevents the system header files + # (stddef.h, stdint.h, etc.) from being included and expanded + # -imacros loads the macro definitions from typesafe.h, but + # doesn't include any of the "plain text" (i.e. prototypes + # and outside-macro struct definitions) from it + # atomlist.h is sufficient because it includes typesafe.h which + # includes typerb.h, that's all of them + p_expand = subprocess.Popen( + cpp + + [ + "-P", + "-D_TYPESAFE_EXPAND_MACROS", + "-imacros", + "lib/atomlist.h", + "-", + ], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + cwd=src_root, + ) + # the output will look like shit, all on one line. format it. + p_format = subprocess.Popen( + ["clang-format", "-"], + stdin=p_expand.stdout, + stdout=subprocess.PIPE, + cwd=src_root, + ) + # pipe between cpp & clang-format needs to be closed + p_expand.stdout.close() + + # ... and finally, write the DECLARE_XYZ statement, and ONLY that + # statements. No headers, no other definitions. + p_expand.stdin.write(data[s:e].encode("UTF-8")) + p_expand.stdin.close() + + odata = b"" + while rd := p_format.stdout.read(): + odata = odata + rd + + p_expand.wait() + p_format.wait() + + # and now that we have the expanded text, write it out, put an + # #include in the .c file, and put "#if 0" around the original + # DECLARE_XYZ statement (otherwise it'll be duplicate...) + newname = os.path.join(dirname, f"{basename}__{m.group('name')}_tsexpand.h") + with open(newname, "wb") as nfd: + nfd.write(odata) + + ofd.write(f'#include "{newname}" /* $ts_expand: remove$ */\n') + ofd.write("#if 0 /* $ts_expand: remove$ */\n") + ofd.write(data[s:e]) + ofd.write("#endif /* $ts_expand: remove$ */\n") + before = e + + ofd.write(data[before:]) + + os.rename(xname, filename) + + +if __name__ == "__main__": + for filename in sys.argv[1:]: + process_file(filename) diff --git a/tests/topotests/nb_config/test_nb_config.py b/tests/topotests/nb_config/test_nb_config.py index 8def19ffd5..9099ef10b8 100644 --- a/tests/topotests/nb_config/test_nb_config.py +++ b/tests/topotests/nb_config/test_nb_config.py @@ -50,7 +50,7 @@ def test_access_list_config_ordering(tgen): output = r1.vtysh_cmd("show ip access-list test json") got = json.loads(output) expected = json.loads( - '{"ZEBRA":{"test":{"type":"Standard", "addressFamily":"IPv4", "rules":[{"sequenceNumber":1, "filterType":"permit", "address":"10.0.0.1", "mask":"0.0.0.0"}]}}}' + '{"zebra":{"test":{"type":"Standard", "addressFamily":"IPv4", "rules":[{"sequenceNumber":1, "filterType":"permit", "address":"10.0.0.1", "mask":"0.0.0.0"}]}}}' ) result = json_cmp(got, expected) assert result is None @@ -63,7 +63,7 @@ def test_access_list_config_ordering(tgen): output = r1.vtysh_cmd("show ip access-list test json") got = json.loads(output) expected = json.loads( - '{"ZEBRA":{"test":{"type":"Zebra", "addressFamily":"IPv4", "rules":[{"sequenceNumber":1, "filterType":"permit", "prefix":"10.0.0.0/8", "exact-match":false}]}}}' + '{"zebra":{"test":{"type":"Zebra", "addressFamily":"IPv4", "rules":[{"sequenceNumber":1, "filterType":"permit", "prefix":"10.0.0.0/8", "exact-match":false}]}}}' ) result = json_cmp(got, expected) assert result is None diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 1a358017dc..b1c957d043 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -3628,6 +3628,104 @@ DEFPY (show_ipv6_prefix_list_detail, return CMD_SUCCESS; } +static void show_access_list_send(afi_t afi, const char *access_list, bool json) +{ + unsigned int i; + bool first = true; + char command_line[128]; + + if (afi == AFI_IP) + snprintf(command_line, sizeof(command_line), + "do show ip access-list "); + else if (afi == AFI_IP6) + snprintf(command_line, sizeof(command_line), + "do show ipv6 access-list "); + if (access_list) + strlcat(command_line, access_list, sizeof(command_line)); + if (json) { + strlcat(command_line, " json", sizeof(command_line)); + vty_out(vty, "{"); + } + + for (i = 0; i < array_size(vtysh_client); i++) { + const struct vtysh_client *client = &vtysh_client[i]; + bool is_connected = true; + + if (!CHECK_FLAG(client->flag, VTYSH_ACCESS_LIST_SHOW)) + continue; + + for (; client; client = client->next) + if (client->fd < 0) + is_connected = false; + + if (!is_connected) + continue; + + if (json && !first) + vty_out(vty, ","); + else + first = false; + + if (json) + vty_out(vty, "\"%s\":", vtysh_client[i].name); + + vtysh_client_execute_name(vtysh_client[i].name, command_line); + } + + if (json) + vty_out(vty, "}\n"); +} + +DEFPY (show_ip_access_list, + show_ip_access_list_cmd, + "show ip access-list [json$uj]", + SHOW_STR + IP_STR + "List IP access lists\n" + JSON_STR) +{ + show_access_list_send(AFI_IP, NULL, !!uj); + return CMD_SUCCESS; +} + +DEFPY (show_ip_access_list_name, + show_ip_access_list_name_cmd, + "show ip access-list ACCESSLIST4_NAME$name [json$uj]", + SHOW_STR + IP_STR + "List IP access lists\n" + "IP access-list name\n" + JSON_STR) +{ + show_access_list_send(AFI_IP, name, !!uj); + return CMD_SUCCESS; +} + +DEFPY (show_ipv6_access_list, + show_ipv6_access_list_cmd, + "show ipv6 access-list [json$uj]", + SHOW_STR + IPV6_STR + "List IPv6 access lists\n" + JSON_STR) +{ + show_access_list_send(AFI_IP6, NULL, !!uj); + return CMD_SUCCESS; +} + +DEFPY (show_ipv6_access_list_name, + show_ipv6_access_list_name_cmd, + "show ipv6 access-list ACCESSLIST6_NAME$name [json$uj]", + SHOW_STR + IPV6_STR + "List IPv6 access lists\n" + "IPv6 access-list name\n" + JSON_STR) +{ + show_access_list_send(AFI_IP6, name, !!uj); + return CMD_SUCCESS; +} + DEFUN (vtysh_integrated_config, vtysh_integrated_config_cmd, "service integrated-vtysh-config", @@ -5234,6 +5332,10 @@ void vtysh_init_vty(void) install_element(ENABLE_NODE, &show_ipv6_prefix_list_cmd); install_element(ENABLE_NODE, &show_ipv6_prefix_list_summary_cmd); install_element(ENABLE_NODE, &show_ipv6_prefix_list_detail_cmd); + install_element(ENABLE_NODE, &show_ip_access_list_cmd); + install_element(ENABLE_NODE, &show_ip_access_list_name_cmd); + install_element(ENABLE_NODE, &show_ipv6_access_list_cmd); + install_element(ENABLE_NODE, &show_ipv6_access_list_name_cmd); /* "write terminal" command. */ install_element(ENABLE_NODE, &vtysh_write_terminal_cmd); diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index 131fbef8ba..b1d57aa3c2 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -68,6 +68,10 @@ extern struct event_loop *master; VTYSH_ZEBRA | VTYSH_RIPD | VTYSH_RIPNGD | VTYSH_OSPFD | VTYSH_OSPF6D | \ VTYSH_BGPD | VTYSH_ISISD | VTYSH_PIMD | VTYSH_EIGRPD | \ VTYSH_FABRICD +#define VTYSH_ACCESS_LIST_SHOW \ + VTYSH_ZEBRA | VTYSH_RIPD | VTYSH_RIPNGD | VTYSH_OSPFD | VTYSH_OSPF6D | \ + VTYSH_BGPD | VTYSH_ISISD | VTYSH_PIMD | VTYSH_EIGRPD | \ + VTYSH_FABRICD #define VTYSH_PREFIX_LIST_SHOW \ VTYSH_ZEBRA | VTYSH_RIPD | VTYSH_RIPNGD | VTYSH_OSPFD | VTYSH_OSPF6D | \ VTYSH_BGPD | VTYSH_ISISD | VTYSH_PIMD | VTYSH_EIGRPD | \ |
