diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/buffer.c | 24 | ||||
| -rw-r--r-- | lib/command.c | 33 | ||||
| -rw-r--r-- | lib/command.h | 2 | ||||
| -rw-r--r-- | lib/command_match.c | 4 | ||||
| -rw-r--r-- | lib/compiler.h | 23 | ||||
| -rw-r--r-- | lib/csv.c | 3 | ||||
| -rw-r--r-- | lib/filter.c | 267 | ||||
| -rw-r--r-- | lib/frr_zmq.c | 24 | ||||
| -rw-r--r-- | lib/frrlua.c | 118 | ||||
| -rw-r--r-- | lib/frrlua.h | 25 | ||||
| -rw-r--r-- | lib/frrscript.c | 215 | ||||
| -rw-r--r-- | lib/frrscript.h | 182 | ||||
| -rw-r--r-- | lib/if.c | 12 | ||||
| -rw-r--r-- | lib/if.h | 3 | ||||
| -rw-r--r-- | lib/libfrr.c | 6 | ||||
| -rw-r--r-- | lib/link_state.c | 12 | ||||
| -rw-r--r-- | lib/network.c | 3 | ||||
| -rw-r--r-- | lib/ntop.c | 14 | ||||
| -rw-r--r-- | lib/pbr.h | 6 | ||||
| -rw-r--r-- | lib/plist.c | 300 | ||||
| -rw-r--r-- | lib/plist.h | 15 | ||||
| -rw-r--r-- | lib/prefix.c | 20 | ||||
| -rw-r--r-- | lib/prefix.h | 42 | ||||
| -rw-r--r-- | lib/route_opaque.h | 8 | ||||
| -rw-r--r-- | lib/route_types.txt | 4 | ||||
| -rw-r--r-- | lib/routemap.c | 214 | ||||
| -rw-r--r-- | lib/routemap.h | 1 | ||||
| -rw-r--r-- | lib/routemap_cli.c | 5 | ||||
| -rw-r--r-- | lib/sockopt.c | 16 | ||||
| -rw-r--r-- | lib/sockunion.c | 3 | ||||
| -rw-r--r-- | lib/sockunion.h | 1 | ||||
| -rw-r--r-- | lib/stream.c | 8 | ||||
| -rw-r--r-- | lib/subdir.am | 17 | ||||
| -rw-r--r-- | lib/systemd.c | 173 | ||||
| -rw-r--r-- | lib/systemd.h | 10 | ||||
| -rw-r--r-- | lib/table.c | 4 | ||||
| -rw-r--r-- | lib/vrf.c | 1 | ||||
| -rw-r--r-- | lib/zclient.c | 14 |
38 files changed, 1342 insertions, 490 deletions
diff --git a/lib/buffer.c b/lib/buffer.c index 7929b3709d..41b1adc9fc 100644 --- a/lib/buffer.c +++ b/lib/buffer.c @@ -357,7 +357,8 @@ buffer_status_t buffer_flush_window(struct buffer *b, int fd, int width, iov_size = ((iov_index > IOV_MAX) ? IOV_MAX : iov_index); - if ((nbytes = writev(fd, c_iov, iov_size)) < 0) { + nbytes = writev(fd, c_iov, iov_size); + if (nbytes < 0) { flog_err(EC_LIB_SOCKET, "%s: writev to fd %d failed: %s", __func__, fd, safe_strerror(errno)); @@ -370,7 +371,8 @@ buffer_status_t buffer_flush_window(struct buffer *b, int fd, int width, } } #else /* IOV_MAX */ - if ((nbytes = writev(fd, iov, iov_index)) < 0) + nbytes = writev(fd, iov, iov_index); + if (nbytes < 0) flog_err(EC_LIB_SOCKET, "%s: writev to fd %d failed: %s", __func__, fd, safe_strerror(errno)); #endif /* IOV_MAX */ @@ -472,13 +474,17 @@ buffer_status_t buffer_write(struct buffer *b, int fd, const void *p, /* Buffer is not empty, so do not attempt to write the new data. */ nbytes = 0; - else if ((nbytes = write(fd, p, size)) < 0) { - if (ERRNO_IO_RETRY(errno)) - nbytes = 0; - else { - flog_err(EC_LIB_SOCKET, "%s: write error on fd %d: %s", - __func__, fd, safe_strerror(errno)); - return BUFFER_ERROR; + else { + nbytes = write(fd, p, size); + if (nbytes < 0) { + if (ERRNO_IO_RETRY(errno)) + nbytes = 0; + else { + flog_err(EC_LIB_SOCKET, + "%s: write error on fd %d: %s", + __func__, fd, safe_strerror(errno)); + return BUFFER_ERROR; + } } } /* Add any remaining data to the buffer. */ diff --git a/lib/command.c b/lib/command.c index 560d4a09f8..422544b70b 100644 --- a/lib/command.c +++ b/lib/command.c @@ -160,6 +160,9 @@ static bool vty_check_node_for_xpath_decrement(enum node_type target_node, || node == BGP_FLOWSPECV6_NODE)) return false; + if (target_node == INTERFACE_NODE && node == LINK_PARAMS_NODE) + return false; + return true; } @@ -2419,26 +2422,30 @@ DEFUN(find, } #if defined(DEV_BUILD) && defined(HAVE_SCRIPTING) -DEFUN(script, - script_cmd, - "script SCRIPT", - "Test command - execute a script\n" - "Script name (same as filename in /etc/frr/scripts/\n") +DEFUN(script, script_cmd, "script SCRIPT FUNCTION", + "Test command - execute a function in a script\n" + "Script name (same as filename in /etc/frr/scripts/)\n" + "Function name (in the script)\n") { struct prefix p; (void)str2prefix("1.2.3.4/24", &p); + struct frrscript *fs = frrscript_new(argv[1]->arg); - struct frrscript *fs = frrscript_load(argv[1]->arg, NULL); - - if (fs == NULL) { - vty_out(vty, "Script '/etc/frr/scripts/%s.lua' not found\n", - argv[1]->arg); - } else { - int ret = frrscript_call(fs, NULL); - vty_out(vty, "Script result: %d\n", ret); + if (frrscript_load(fs, argv[2]->arg, NULL)) { + vty_out(vty, + "/etc/frr/scripts/%s.lua or function '%s' not found\n", + argv[1]->arg, argv[2]->arg); } + int ret = frrscript_call(fs, argv[2]->arg, ("p", &p)); + char buf[40]; + prefix2str(&p, buf, sizeof(buf)); + vty_out(vty, "p: %s\n", buf); + vty_out(vty, "Script result: %d\n", ret); + + frrscript_delete(fs); + return CMD_SUCCESS; } #endif diff --git a/lib/command.h b/lib/command.h index 13bd61e9fd..2b50bc2374 100644 --- a/lib/command.h +++ b/lib/command.h @@ -438,6 +438,8 @@ struct cmd_node { #define BFD_PROFILE_STR "BFD profile.\n" #define BFD_PROFILE_NAME_STR "BFD profile name.\n" #define SHARP_STR "Sharp Routing Protocol\n" +#define OSPF_GR_STR \ + "OSPF non-stop forwarding (NSF) also known as OSPF Graceful Restart\n" #define CMD_VNI_RANGE "(1-16777215)" #define CONF_BACKUP_EXT ".sav" diff --git a/lib/command_match.c b/lib/command_match.c index e9e8466ffd..5703510148 100644 --- a/lib/command_match.c +++ b/lib/command_match.c @@ -813,7 +813,7 @@ static enum match_type match_ipv4_prefix(const char *str) str++; } - if (atoi(sp) > 32) + if (atoi(sp) > IPV4_MAX_BITLEN) return no_match; return exact_match; @@ -948,7 +948,7 @@ static enum match_type match_ipv6_prefix(const char *str, bool prefix) if (*endptr != '\0') return no_match; - if (mask < 0 || mask > 128) + if (mask < 0 || mask > IPV6_MAX_BITLEN) return no_match; return exact_match; diff --git a/lib/compiler.h b/lib/compiler.h index 970ed297fc..bf443906eb 100644 --- a/lib/compiler.h +++ b/lib/compiler.h @@ -173,6 +173,29 @@ extern "C" { #define MACRO_REPEAT(NAME, ...) \ MACRO_VARIANT(_MACRO_REPEAT, ##__VA_ARGS__)(NAME, ##__VA_ARGS__) +/* per-arglist repeat macro, use like this: + * #define foo(...) MAP_LISTS(F, ##__VA_ARGS__) + * where F is a n-ary function where n is the number of args in each arglist. + * e.g.: MAP_LISTS(f, (a, b), (c, d)) + * expands to: f(a, b); f(c, d) + */ + +#define ESC(...) __VA_ARGS__ +#define MAP_LISTS(M, ...) \ + _CONCAT(_MAP_LISTS_, PP_NARG(__VA_ARGS__))(M, ##__VA_ARGS__) +#define _MAP_LISTS_0(M) +#define _MAP_LISTS_1(M, _1) ESC(M _1) +#define _MAP_LISTS_2(M, _1, _2) ESC(M _1; M _2) +#define _MAP_LISTS_3(M, _1, _2, _3) ESC(M _1; M _2; M _3) +#define _MAP_LISTS_4(M, _1, _2, _3, _4) ESC(M _1; M _2; M _3; M _4) +#define _MAP_LISTS_5(M, _1, _2, _3, _4, _5) ESC(M _1; M _2; M _3; M _4; M _5) +#define _MAP_LISTS_6(M, _1, _2, _3, _4, _5, _6) \ + ESC(M _1; M _2; M _3; M _4; M _5; M _6) +#define _MAP_LISTS_7(M, _1, _2, _3, _4, _5, _6, _7) \ + ESC(M _1; M _2; M _3; M _4; M _5; M _6; M _7) +#define _MAP_LISTS_8(M, _1, _2, _3, _4, _5, _6, _7, _8) \ + ESC(M _1; M _2; M _3; M _4; M _5; M _6; M _7; M _8) + /* * for warnings on macros, put in the macro content like this: * #define MACRO BLA CPP_WARN("MACRO has been deprecated") @@ -641,7 +641,8 @@ static int get_memory_usage(pid_t pid) char *vm; snprintf(status_child, sizeof(status_child), "/proc/%d/status", pid); - if ((fd = open(status_child, O_RDONLY)) < 0) + fd = open(status_child, O_RDONLY); + if (fd < 0) return -1; read(fd, buf, 4095); diff --git a/lib/filter.c b/lib/filter.c index b7a935d076..744ea9c480 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -30,6 +30,7 @@ #include "routemap.h" #include "libfrr.h" #include "northbound_cli.h" +#include "json.h" DEFINE_MTYPE_STATIC(LIB, ACCESS_LIST, "Access List"); DEFINE_MTYPE_STATIC(LIB, ACCESS_LIST_STR, "Access List Str"); @@ -108,10 +109,14 @@ static int filter_match_cisco(struct filter *mfilter, const struct prefix *p) masklen2ip(p->prefixlen, &mask); check_mask = mask.s_addr & ~filter->mask_mask.s_addr; - if (memcmp(&check_addr, &filter->addr.s_addr, 4) == 0 - && memcmp(&check_mask, &filter->mask.s_addr, 4) == 0) + if (memcmp(&check_addr, &filter->addr.s_addr, IPV4_MAX_BYTELEN) + == 0 + && memcmp(&check_mask, &filter->mask.s_addr, + IPV4_MAX_BYTELEN) + == 0) return 1; - } else if (memcmp(&check_addr, &filter->addr.s_addr, 4) == 0) + } else if (memcmp(&check_addr, &filter->addr.s_addr, IPV4_MAX_BYTELEN) + == 0) return 1; return 0; @@ -439,71 +444,158 @@ void access_list_filter_add(struct access_list *access, host A single host address */ -static void config_write_access_zebra(struct vty *, struct filter *); -static void config_write_access_cisco(struct vty *, struct filter *); +static void config_write_access_zebra(struct vty *, struct filter *, + json_object *); +static void config_write_access_cisco(struct vty *, struct filter *, + json_object *); + +static const char *filter_type2str(struct filter *filter) +{ + if (filter->cisco) { + if (filter->u.cfilter.extended) + return "Extended"; + else + return "Standard"; + } else + return "Zebra"; +} /* show access-list command. */ -static int filter_show(struct vty *vty, const char *name, afi_t afi) +static int filter_show(struct vty *vty, const char *name, afi_t afi, + bool use_json) { struct access_list *access; struct access_master *master; struct filter *mfilter; struct filter_cisco *filter; - int write = 0; + bool first; + json_object *json = NULL; + json_object *json_proto = NULL; master = access_master_get(afi); - if (master == NULL) + if (master == NULL) { + if (use_json) + vty_out(vty, "{}\n"); return 0; + } + + if (use_json) + json = json_object_new_object(); /* Print the name of the protocol */ - vty_out(vty, "%s:\n", frr_protoname); + if (json) { + json_proto = json_object_new_object(); + json_object_object_add(json, frr_protoname, json_proto); + } else + vty_out(vty, "%s:\n", frr_protoname); for (access = master->str.head; access; access = access->next) { + json_object *json_acl = NULL; + json_object *json_rules = NULL; + if (name && strcmp(access->name, name) != 0) continue; - write = 1; + first = true; for (mfilter = access->head; mfilter; mfilter = mfilter->next) { + json_object *json_rule = NULL; + filter = &mfilter->u.cfilter; - if (write) { - vty_out(vty, "%s %s access list %s\n", - mfilter->cisco ? (filter->extended - ? "Extended" - : "Standard") - : "Zebra", - (afi == AFI_IP) - ? ("IP") - : ((afi == AFI_IP6) ? ("IPv6 ") - : ("MAC ")), - access->name); - write = 0; + if (first) { + const char *type = filter_type2str(mfilter); + + if (json) { + json_acl = json_object_new_object(); + json_object_object_add(json_proto, + access->name, + json_acl); + + json_object_string_add(json_acl, "type", + type); + json_object_string_add(json_acl, + "addressFamily", + afi2str(afi)); + json_rules = json_object_new_array(); + json_object_object_add( + json_acl, "rules", json_rules); + } else { + vty_out(vty, "%s %s access list %s\n", + type, + (afi == AFI_IP) + ? ("IP") + : ((afi == AFI_IP6) + ? ("IPv6 ") + : ("MAC ")), + access->name); + } + + first = false; } - vty_out(vty, " seq %" PRId64, mfilter->seq); - vty_out(vty, " %s%s", filter_type_str(mfilter), - mfilter->type == FILTER_DENY ? " " : ""); + if (json) { + json_rule = json_object_new_object(); + json_object_array_add(json_rules, json_rule); + + json_object_int_add(json_rule, "sequenceNumber", + mfilter->seq); + json_object_string_add( + json_rule, "filterType", + filter_type_str(mfilter)); + } else { + vty_out(vty, " seq %" PRId64, mfilter->seq); + vty_out(vty, " %s%s", filter_type_str(mfilter), + mfilter->type == FILTER_DENY ? " " + : ""); + } if (!mfilter->cisco) - config_write_access_zebra(vty, mfilter); + config_write_access_zebra(vty, mfilter, + json_rule); else if (filter->extended) - config_write_access_cisco(vty, mfilter); + config_write_access_cisco(vty, mfilter, + json_rule); else { - if (filter->addr_mask.s_addr == 0xffffffff) - vty_out(vty, " any\n"); - else { - vty_out(vty, " %pI4", &filter->addr); + if (json) { + char buf[BUFSIZ]; + + json_object_string_add( + json_rule, "address", + inet_ntop(AF_INET, + &filter->addr, buf, + sizeof(buf))); + json_object_string_add( + json_rule, "mask", + inet_ntop(AF_INET, + &filter->addr_mask, + buf, sizeof(buf))); + } else { if (filter->addr_mask.s_addr - != INADDR_ANY) - vty_out(vty, - ", wildcard bits %pI4", - &filter->addr_mask); - vty_out(vty, "\n"); + == 0xffffffff) + vty_out(vty, " any\n"); + else { + vty_out(vty, " %pI4", + &filter->addr); + if (filter->addr_mask.s_addr + != INADDR_ANY) + vty_out(vty, + ", wildcard bits %pI4", + &filter->addr_mask); + vty_out(vty, "\n"); + } } } } } + + if (json) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + return CMD_SUCCESS; } @@ -515,7 +607,7 @@ DEFUN (show_mac_access_list, "mac access lists\n" "List mac access lists\n") { - return filter_show(vty, NULL, AFI_L2VPN); + return filter_show(vty, NULL, AFI_L2VPN, false); } DEFUN (show_mac_access_list_name, @@ -526,22 +618,24 @@ DEFUN (show_mac_access_list_name, "List mac access lists\n" "mac address\n") { - return filter_show(vty, argv[3]->arg, AFI_L2VPN); + return filter_show(vty, argv[3]->arg, AFI_L2VPN, false); } DEFUN (show_ip_access_list, show_ip_access_list_cmd, - "show ip access-list", + "show ip access-list [json]", SHOW_STR IP_STR - "List IP access lists\n") + "List IP access lists\n" + JSON_STR) { - return filter_show(vty, NULL, AFI_IP); + bool uj = use_json(argc, argv); + return filter_show(vty, NULL, AFI_IP, uj); } DEFUN (show_ip_access_list_name, show_ip_access_list_name_cmd, - "show ip access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)|WORD>", + "show ip access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)|WORD> [json]", SHOW_STR IP_STR "List IP access lists\n" @@ -549,41 +643,64 @@ DEFUN (show_ip_access_list_name, "IP extended access list\n" "IP standard access list (expanded range)\n" "IP extended access list (expanded range)\n" - "IP zebra access-list\n") + "IP zebra access-list\n" + JSON_STR) { + bool uj = use_json(argc, argv); int idx_acl = 3; - return filter_show(vty, argv[idx_acl]->arg, AFI_IP); + return filter_show(vty, argv[idx_acl]->arg, AFI_IP, uj); } DEFUN (show_ipv6_access_list, show_ipv6_access_list_cmd, - "show ipv6 access-list", + "show ipv6 access-list [json]", SHOW_STR IPV6_STR - "List IPv6 access lists\n") + "List IPv6 access lists\n" + JSON_STR) { - return filter_show(vty, NULL, AFI_IP6); + bool uj = use_json(argc, argv); + return filter_show(vty, NULL, AFI_IP6, uj); } DEFUN (show_ipv6_access_list_name, show_ipv6_access_list_name_cmd, - "show ipv6 access-list WORD", + "show ipv6 access-list WORD [json]", SHOW_STR IPV6_STR "List IPv6 access lists\n" - "IPv6 zebra access-list\n") + "IPv6 zebra access-list\n" + JSON_STR) { + bool uj = use_json(argc, argv); int idx_word = 3; - return filter_show(vty, argv[idx_word]->arg, AFI_IP6); + return filter_show(vty, argv[idx_word]->arg, AFI_IP6, uj); } -static void config_write_access_cisco(struct vty *vty, struct filter *mfilter) +static void config_write_access_cisco(struct vty *vty, struct filter *mfilter, + json_object *json) { struct filter_cisco *filter; filter = &mfilter->u.cfilter; - if (filter->extended) { + if (json) { + char buf[BUFSIZ]; + + json_object_boolean_add(json, "extended", !!filter->extended); + json_object_string_add( + json, "sourceAddress", + inet_ntop(AF_INET, &filter->addr, buf, sizeof(buf))); + json_object_string_add(json, "sourceMask", + inet_ntop(AF_INET, &filter->addr_mask, + buf, sizeof(buf))); + json_object_string_add( + json, "destinationAddress", + inet_ntop(AF_INET, &filter->mask, buf, sizeof(buf))); + json_object_string_add(json, "destinationMask", + inet_ntop(AF_INET, &filter->mask_mask, + buf, sizeof(buf))); + } else { vty_out(vty, " ip"); if (filter->addr_mask.s_addr == 0xffffffff) vty_out(vty, " any"); @@ -603,19 +720,11 @@ static void config_write_access_cisco(struct vty *vty, struct filter *mfilter) vty_out(vty, " %pI4", &filter->mask_mask); } vty_out(vty, "\n"); - } else { - if (filter->addr_mask.s_addr == 0xffffffff) - vty_out(vty, " any\n"); - else { - vty_out(vty, " %pI4", &filter->addr); - if (filter->addr_mask.s_addr != INADDR_ANY) - vty_out(vty, " %pI4", &filter->addr_mask); - vty_out(vty, "\n"); - } } } -static void config_write_access_zebra(struct vty *vty, struct filter *mfilter) +static void config_write_access_zebra(struct vty *vty, struct filter *mfilter, + json_object *json) { struct filter_zebra *filter; struct prefix *p; @@ -624,21 +733,29 @@ static void config_write_access_zebra(struct vty *vty, struct filter *mfilter) filter = &mfilter->u.zfilter; p = &filter->prefix; - if (p->prefixlen == 0 && !filter->exact) - vty_out(vty, " any"); - else if (p->family == AF_INET6 || p->family == AF_INET) - vty_out(vty, " %s/%d%s", - inet_ntop(p->family, &p->u.prefix, buf, BUFSIZ), - p->prefixlen, filter->exact ? " exact-match" : ""); - else if (p->family == AF_ETHERNET) { - if (p->prefixlen == 0) + if (json) { + json_object_string_add(json, "prefix", + prefix2str(p, buf, sizeof(buf))); + json_object_boolean_add(json, "exact-match", !!filter->exact); + } else { + if (p->prefixlen == 0 && !filter->exact) vty_out(vty, " any"); - else - vty_out(vty, " %s", prefix_mac2str(&(p->u.prefix_eth), - buf, sizeof(buf))); - } + else if (p->family == AF_INET6 || p->family == AF_INET) + vty_out(vty, " %s/%d%s", + inet_ntop(p->family, &p->u.prefix, buf, BUFSIZ), + p->prefixlen, + filter->exact ? " exact-match" : ""); + else if (p->family == AF_ETHERNET) { + if (p->prefixlen == 0) + vty_out(vty, " any"); + else + vty_out(vty, " %s", + prefix_mac2str(&(p->u.prefix_eth), buf, + sizeof(buf))); + } - vty_out(vty, "\n"); + vty_out(vty, "\n"); + } } static struct cmd_node access_mac_node = { diff --git a/lib/frr_zmq.c b/lib/frr_zmq.c index ce52848a25..ea9c828f7c 100644 --- a/lib/frr_zmq.c +++ b/lib/frr_zmq.c @@ -17,6 +17,14 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/* + * IF YOU MODIFY THIS FILE PLEASE RUN `make check` and ensure that + * the test_zmq.c unit test is still working. There are dependancies + * between the two that are extremely fragile. My understanding + * is that there is specialized ownership of the cb pointer based + * upon what is happening. Those assumptions are supposed to be + * tested in the test_zmq.c + */ #include <zebra.h> #include <zmq.h> @@ -309,8 +317,22 @@ void frrzmq_thread_cancel(struct frrzmq_cb **cb, struct cb_core *core) core->cancelled = true; thread_cancel(&core->thread); + /* + * Looking at this code one would assume that FRR + * would want a `!(*cb)->write.thread. This was + * attempted in e08165def1c62beee0e87385 but this + * change caused `make check` to stop working + * which was not noticed because our CI system + * does not build with zeromq. Put this back + * to the code as written in 2017. e08165de.. + * was introduced in 2021. So someone was ok + * with frrzmq_thread_cancel for 4 years. This will + * allow those people doing `make check` to continue + * working. In the meantime if the people using + * this code see an issue they can fix it + */ if ((*cb)->read.cancelled && !(*cb)->read.thread - && (*cb)->write.cancelled && !(*cb)->write.thread) + && (*cb)->write.cancelled && (*cb)->write.thread) XFREE(MTYPE_ZEROMQ_CB, *cb); } diff --git a/lib/frrlua.c b/lib/frrlua.c index d8aaa3aa3c..96d7269440 100644 --- a/lib/frrlua.c +++ b/lib/frrlua.c @@ -29,6 +29,8 @@ #include "log.h" #include "buffer.h" +DEFINE_MTYPE(LIB, SCRIPT_RES, "Scripting results"); + /* Lua stuff */ /* @@ -52,10 +54,9 @@ int frrlua_table_get_integer(lua_State *L, const char *key) } /* - * Encoders. - * * This section has functions that convert internal FRR datatypes into Lua - * datatypes. + * datatypes: one encoder function and two decoder functions for each type. + * */ void lua_pushprefix(lua_State *L, const struct prefix *prefix) @@ -71,14 +72,19 @@ void lua_pushprefix(lua_State *L, const struct prefix *prefix) lua_setfield(L, -2, "family"); } -void *lua_toprefix(lua_State *L, int idx) +void lua_decode_prefix(lua_State *L, int idx, struct prefix *prefix) { - struct prefix *p = XCALLOC(MTYPE_TMP, sizeof(struct prefix)); - lua_getfield(L, idx, "network"); - (void)str2prefix(lua_tostring(L, -1), p); + (void)str2prefix(lua_tostring(L, -1), prefix); lua_pop(L, 1); + /* pop the table */ + lua_pop(L, 1); +} +void *lua_toprefix(lua_State *L, int idx) +{ + struct prefix *p = XCALLOC(MTYPE_SCRIPT_RES, sizeof(struct prefix)); + lua_decode_prefix(L, idx, p); return p; } @@ -109,10 +115,8 @@ void lua_pushinterface(lua_State *L, const struct interface *ifp) lua_setfield(L, -2, "linklayer_type"); } -void *lua_tointerface(lua_State *L, int idx) +void lua_decode_interface(lua_State *L, int idx, struct interface *ifp) { - struct interface *ifp = XCALLOC(MTYPE_TMP, sizeof(struct interface)); - lua_getfield(L, idx, "name"); strlcpy(ifp->name, lua_tostring(L, -1), sizeof(ifp->name)); lua_pop(L, 1); @@ -146,13 +150,22 @@ void *lua_tointerface(lua_State *L, int idx) lua_getfield(L, idx, "linklayer_type"); ifp->ll_type = lua_tointeger(L, -1); lua_pop(L, 1); + /* pop the table */ + lua_pop(L, 1); +} +void *lua_tointerface(lua_State *L, int idx) +{ + struct interface *ifp = + XCALLOC(MTYPE_SCRIPT_RES, sizeof(struct interface)); + lua_decode_interface(L, idx, ifp); return ifp; } void lua_pushinaddr(lua_State *L, const struct in_addr *addr) { char buf[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, addr, buf, sizeof(buf)); lua_newtable(L); @@ -162,14 +175,20 @@ void lua_pushinaddr(lua_State *L, const struct in_addr *addr) lua_setfield(L, -2, "string"); } -void *lua_toinaddr(lua_State *L, int idx) +void lua_decode_inaddr(lua_State *L, int idx, struct in_addr *inaddr) { - struct in_addr *inaddr = XCALLOC(MTYPE_TMP, sizeof(struct in_addr)); - lua_getfield(L, idx, "value"); inaddr->s_addr = lua_tointeger(L, -1); lua_pop(L, 1); + /* pop the table */ + lua_pop(L, 1); +} +void *lua_toinaddr(lua_State *L, int idx) +{ + struct in_addr *inaddr = + XCALLOC(MTYPE_SCRIPT_RES, sizeof(struct in_addr)); + lua_decode_inaddr(L, idx, inaddr); return inaddr; } @@ -177,6 +196,7 @@ void *lua_toinaddr(lua_State *L, int idx) void lua_pushin6addr(lua_State *L, const struct in6_addr *addr) { char buf[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, addr, buf, sizeof(buf)); lua_newtable(L); @@ -186,20 +206,27 @@ void lua_pushin6addr(lua_State *L, const struct in6_addr *addr) lua_setfield(L, -2, "string"); } -void *lua_toin6addr(lua_State *L, int idx) +void lua_decode_in6addr(lua_State *L, int idx, struct in6_addr *in6addr) { - struct in6_addr *in6addr = XCALLOC(MTYPE_TMP, sizeof(struct in6_addr)); - lua_getfield(L, idx, "string"); inet_pton(AF_INET6, lua_tostring(L, -1), in6addr); lua_pop(L, 1); + /* pop the table */ + lua_pop(L, 1); +} +void *lua_toin6addr(lua_State *L, int idx) +{ + struct in6_addr *in6addr = + XCALLOC(MTYPE_SCRIPT_RES, sizeof(struct in6_addr)); + lua_decode_in6addr(L, idx, in6addr); return in6addr; } void lua_pushsockunion(lua_State *L, const union sockunion *su) { char buf[SU_ADDRSTRLEN]; + sockunion2str(su, buf, sizeof(buf)); lua_newtable(L); @@ -210,13 +237,21 @@ void lua_pushsockunion(lua_State *L, const union sockunion *su) lua_setfield(L, -2, "string"); } -void *lua_tosockunion(lua_State *L, int idx) +void lua_decode_sockunion(lua_State *L, int idx, union sockunion *su) { - union sockunion *su = XCALLOC(MTYPE_TMP, sizeof(union sockunion)); - lua_getfield(L, idx, "string"); str2sockunion(lua_tostring(L, -1), su); + lua_pop(L, 1); + /* pop the table */ + lua_pop(L, 1); +} +void *lua_tosockunion(lua_State *L, int idx) +{ + union sockunion *su = + XCALLOC(MTYPE_SCRIPT_RES, sizeof(union sockunion)); + + lua_decode_sockunion(L, idx, su); return su; } @@ -225,12 +260,17 @@ void lua_pushtimet(lua_State *L, const time_t *time) lua_pushinteger(L, *time); } -void *lua_totimet(lua_State *L, int idx) +void lua_decode_timet(lua_State *L, int idx, time_t *t) { - time_t *t = XCALLOC(MTYPE_TMP, sizeof(time_t)); - *t = lua_tointeger(L, idx); + lua_pop(L, 1); +} +void *lua_totimet(lua_State *L, int idx) +{ + time_t *t = XCALLOC(MTYPE_SCRIPT_RES, sizeof(time_t)); + + lua_decode_timet(L, idx, t); return t; } @@ -239,25 +279,51 @@ void lua_pushintegerp(lua_State *L, const long long *num) lua_pushinteger(L, *num); } -void *lua_tointegerp(lua_State *L, int idx) +void lua_decode_integerp(lua_State *L, int idx, long long *num) { int isnum; - long long *num = XCALLOC(MTYPE_TMP, sizeof(long long)); - *num = lua_tonumberx(L, idx, &isnum); + lua_pop(L, 1); assert(isnum); +} +void *lua_tointegerp(lua_State *L, int idx) +{ + long long *num = XCALLOC(MTYPE_SCRIPT_RES, sizeof(long long)); + + lua_decode_integerp(L, idx, num); return num; } +void lua_decode_stringp(lua_State *L, int idx, char *str) +{ + strlcpy(str, lua_tostring(L, idx), strlen(str) + 1); + lua_pop(L, 1); +} + void *lua_tostringp(lua_State *L, int idx) { - char *string = XSTRDUP(MTYPE_TMP, lua_tostring(L, idx)); + char *string = XSTRDUP(MTYPE_SCRIPT_RES, lua_tostring(L, idx)); return string; } /* + * Decoder for const values, since we cannot modify them. + */ +void lua_decode_noop(lua_State *L, int idx, const void *ptr) +{ +} + + +/* + * Noop decoder for int. + */ +void lua_decode_integer_noop(lua_State *L, int idx, int i) +{ +} + +/* * Logging. * * Lua-compatible wrappers for FRR logging functions. diff --git a/lib/frrlua.h b/lib/frrlua.h index 6fb30938b0..3e16c82e22 100644 --- a/lib/frrlua.h +++ b/lib/frrlua.h @@ -34,6 +34,8 @@ extern "C" { #endif +DECLARE_MTYPE(SCRIPT_RES); + /* * gcc-10 is complaining about the wrapper function * not being compatible with lua_pushstring returning @@ -50,6 +52,8 @@ static inline void lua_pushstring_wrapper(lua_State *L, const char *str) */ void lua_pushprefix(lua_State *L, const struct prefix *prefix); +void lua_decode_prefix(lua_State *L, int idx, struct prefix *prefix); + /* * Converts the Lua value at idx to a prefix. * @@ -63,6 +67,8 @@ void *lua_toprefix(lua_State *L, int idx); */ void lua_pushinterface(lua_State *L, const struct interface *ifp); +void lua_decode_interface(lua_State *L, int idx, struct interface *ifp); + /* * Converts the Lua value at idx to an interface. * @@ -77,6 +83,8 @@ void *lua_tointerface(lua_State *L, int idx); */ void lua_pushinaddr(lua_State *L, const struct in_addr *addr); +void lua_decode_inaddr(lua_State *L, int idx, struct in_addr *addr); + /* * Converts the Lua value at idx to an in_addr. * @@ -90,6 +98,8 @@ void *lua_toinaddr(lua_State *L, int idx); */ void lua_pushin6addr(lua_State *L, const struct in6_addr *addr); +void lua_decode_in6addr(lua_State *L, int idx, struct in6_addr *addr); + /* * Converts the Lua value at idx to an in6_addr. * @@ -103,6 +113,8 @@ void *lua_toin6addr(lua_State *L, int idx); */ void lua_pushtimet(lua_State *L, const time_t *time); +void lua_decode_timet(lua_State *L, int idx, time_t *time); + /* * Converts the Lua value at idx to a time_t. * @@ -116,6 +128,8 @@ void *lua_totimet(lua_State *L, int idx); */ void lua_pushsockunion(lua_State *L, const union sockunion *su); +void lua_decode_sockunion(lua_State *L, int idx, union sockunion *su); + /* * Converts the Lua value at idx to a sockunion. * @@ -129,6 +143,8 @@ void *lua_tosockunion(lua_State *L, int idx); */ void lua_pushintegerp(lua_State *L, const long long *num); +void lua_decode_integerp(lua_State *L, int idx, long long *num); + /* * Converts the Lua value at idx to an int. * @@ -137,6 +153,8 @@ void lua_pushintegerp(lua_State *L, const long long *num); */ void *lua_tointegerp(lua_State *L, int idx); +void lua_decode_stringp(lua_State *L, int idx, char *str); + /* * Pop string. * @@ -146,6 +164,13 @@ void *lua_tointegerp(lua_State *L, int idx); void *lua_tostringp(lua_State *L, int idx); /* + * No-op decoders + */ +void lua_decode_noop(lua_State *L, int idx, const void *ptr); + +void lua_decode_integer_noop(lua_State *L, int idx, int i); + +/* * Retrieve an integer from table on the top of the stack. * * key diff --git a/lib/frrscript.c b/lib/frrscript.c index 10d400886d..d00b84ccbb 100644 --- a/lib/frrscript.c +++ b/lib/frrscript.c @@ -102,83 +102,136 @@ static void codec_free(struct codec *c) } #endif -/* Generic script APIs */ +/* Lua function hash utils */ -int frrscript_call(struct frrscript *fs, struct frrscript_env *env) +unsigned int lua_function_hash_key(const void *data) { - struct frrscript_codec c = {}; - const void *arg; - const char *bindname; + const struct lua_function_state *lfs = data; - /* Encode script arguments */ - for (int i = 0; env && env[i].val != NULL; i++) { - bindname = env[i].name; - c.typename = env[i].typename; - arg = env[i].val; + return string_hash_make(lfs->name); +} - struct frrscript_codec *codec = hash_lookup(codec_hash, &c); - assert(codec && "No encoder for type"); - codec->encoder(fs->L, arg); +bool lua_function_hash_cmp(const void *d1, const void *d2) +{ + const struct lua_function_state *lfs1 = d1; + const struct lua_function_state *lfs2 = d2; - lua_setglobal(fs->L, bindname); - } + return strmatch(lfs1->name, lfs2->name); +} + +void *lua_function_alloc(void *arg) +{ + struct lua_function_state *tmp = arg; + + struct lua_function_state *lfs = + XCALLOC(MTYPE_SCRIPT, sizeof(struct lua_function_state)); + lfs->name = tmp->name; + lfs->L = tmp->L; + return lfs; +} + +static void lua_function_free(struct hash_bucket *b, void *data) +{ + struct lua_function_state *lfs = (struct lua_function_state *)b->data; + lua_close(lfs->L); + XFREE(MTYPE_SCRIPT, lfs); +} - int ret = lua_pcall(fs->L, 0, 0, 0); +/* internal frrscript APIs */ + +int _frrscript_call_lua(struct lua_function_state *lfs, int nargs) +{ + + int ret; + ret = lua_pcall(lfs->L, nargs, 1, 0); switch (ret) { case LUA_OK: break; case LUA_ERRRUN: - zlog_err("Script '%s' runtime error: %s", fs->name, - lua_tostring(fs->L, -1)); + zlog_err("Lua hook call '%s' : runtime error: %s", lfs->name, + lua_tostring(lfs->L, -1)); break; case LUA_ERRMEM: - zlog_err("Script '%s' memory error: %s", fs->name, - lua_tostring(fs->L, -1)); + zlog_err("Lua hook call '%s' : memory error: %s", lfs->name, + lua_tostring(lfs->L, -1)); break; case LUA_ERRERR: - zlog_err("Script '%s' error handler error: %s", fs->name, - lua_tostring(fs->L, -1)); + zlog_err("Lua hook call '%s' : error handler error: %s", + lfs->name, lua_tostring(lfs->L, -1)); break; case LUA_ERRGCMM: - zlog_err("Script '%s' garbage collector error: %s", fs->name, - lua_tostring(fs->L, -1)); + zlog_err("Lua hook call '%s' : garbage collector error: %s", + lfs->name, lua_tostring(lfs->L, -1)); break; default: - zlog_err("Script '%s' unknown error: %s", fs->name, - lua_tostring(fs->L, -1)); + zlog_err("Lua hook call '%s' : unknown error: %s", lfs->name, + lua_tostring(lfs->L, -1)); break; } if (ret != LUA_OK) { - lua_pop(fs->L, 1); + lua_pop(lfs->L, 1); goto done; } + if (lua_gettop(lfs->L) != 1) { + zlog_err( + "Lua hook call '%s': Lua function should return only 1 result", + lfs->name); + ret = 1; + goto done; + } + + if (lua_istable(lfs->L, 1) != 1) { + zlog_err( + "Lua hook call '%s': Lua function should return a Lua table", + lfs->name); + ret = 1; + } + done: /* LUA_OK is 0, so we can just return lua_pcall's result directly */ return ret; } -void *frrscript_get_result(struct frrscript *fs, - const struct frrscript_env *result) +void *frrscript_get_result(struct frrscript *fs, const char *function_name, + const char *name, + void *(*lua_to)(lua_State *L, int idx)) { - void *r; - struct frrscript_codec c = {.typename = result->typename}; + void *p; + struct lua_function_state *lfs; + struct lua_function_state lookup = {.name = function_name}; - struct frrscript_codec *codec = hash_lookup(codec_hash, &c); - assert(codec && "No encoder for type"); + lfs = hash_lookup(fs->lua_function_hash, &lookup); - if (!codec->decoder) { - zlog_err("No script decoder for type '%s'", result->typename); + if (lfs == NULL) + return NULL; + + /* At this point, the Lua state should have only the returned table. + * We will then search the table for the key/value we're interested in. + * Then if the value is present (i.e. non-nil), call the lua_to* + * decoder. + */ + assert(lua_gettop(lfs->L) == 1); + assert(lua_istable(lfs->L, -1) == 1); + lua_getfield(lfs->L, -1, name); + if (lua_isnil(lfs->L, -1)) { + lua_pop(lfs->L, 1); + zlog_warn( + "frrscript: '%s.lua': '%s': tried to decode '%s' as result but failed", + fs->name, function_name, name); return NULL; } + p = lua_to(lfs->L, 2); - lua_getglobal(fs->L, result->name); - r = codec->decoder(fs->L, -1); - lua_pop(fs->L, 1); + /* At the end, the Lua state should be same as it was at the start + * i.e. containing soley the returned table. + */ + assert(lua_gettop(lfs->L) == 1); + assert(lua_istable(lfs->L, -1) == 1); - return r; + return p; } void frrscript_register_type_codec(struct frrscript_codec *codec) @@ -199,61 +252,99 @@ void frrscript_register_type_codecs(struct frrscript_codec *codecs) frrscript_register_type_codec(&codecs[i]); } -struct frrscript *frrscript_load(const char *name, - int (*load_cb)(struct frrscript *)) +struct frrscript *frrscript_new(const char *name) { struct frrscript *fs = XCALLOC(MTYPE_SCRIPT, sizeof(struct frrscript)); fs->name = XSTRDUP(MTYPE_SCRIPT, name); - fs->L = luaL_newstate(); - frrlua_export_logging(fs->L); + fs->lua_function_hash = + hash_create(lua_function_hash_key, lua_function_hash_cmp, + "Lua function state hash"); + return fs; +} + +int frrscript_load(struct frrscript *fs, const char *function_name, + int (*load_cb)(struct frrscript *)) +{ + + /* Set up the Lua script */ + lua_State *L = luaL_newstate(); - char fname[MAXPATHLEN * 2]; - snprintf(fname, sizeof(fname), "%s/%s.lua", scriptdir, fs->name); + frrlua_export_logging(L); - int ret = luaL_loadfile(fs->L, fname); + char script_name[MAXPATHLEN]; + + if (snprintf(script_name, sizeof(script_name), "%s/%s.lua", scriptdir, + fs->name) + >= (int)sizeof(script_name)) { + zlog_err("frrscript: path to script %s/%s.lua is too long", + scriptdir, fs->name); + goto fail; + } + int ret = luaL_dofile(L, script_name); switch (ret) { case LUA_OK: break; case LUA_ERRSYNTAX: - zlog_err("Failed loading script '%s': syntax error: %s", fname, - lua_tostring(fs->L, -1)); + zlog_err( + "frrscript: failed loading script '%s.lua': syntax error: %s", + script_name, lua_tostring(L, -1)); break; case LUA_ERRMEM: - zlog_err("Failed loading script '%s': out-of-memory error: %s", - fname, lua_tostring(fs->L, -1)); + zlog_err( + "frrscript: failed loading script '%s.lua': out-of-memory error: %s", + script_name, lua_tostring(L, -1)); break; case LUA_ERRGCMM: zlog_err( - "Failed loading script '%s': garbage collector error: %s", - fname, lua_tostring(fs->L, -1)); + "frrscript: failed loading script '%s.lua': garbage collector error: %s", + script_name, lua_tostring(L, -1)); break; case LUA_ERRFILE: - zlog_err("Failed loading script '%s': file read error: %s", - fname, lua_tostring(fs->L, -1)); + zlog_err( + "frrscript: failed loading script '%s.lua': file read error: %s", + script_name, lua_tostring(L, -1)); break; default: - zlog_err("Failed loading script '%s': unknown error: %s", fname, - lua_tostring(fs->L, -1)); + zlog_err( + "frrscript: failed loading script '%s.lua': unknown error: %s", + script_name, lua_tostring(L, -1)); break; } if (ret != LUA_OK) goto fail; - if (load_cb && (*load_cb)(fs) != 0) + /* Push the Lua function we want */ + lua_getglobal(L, function_name); + if (lua_isfunction(L, lua_gettop(L)) == 0) { + zlog_err("frrscript: loaded script '%s.lua' but %s not found", + script_name, function_name); goto fail; + } - return fs; + if (load_cb && (*load_cb)(fs) != 0) { + zlog_err( + "frrscript: '%s.lua': %s: loaded but callback returned non-zero exit code", + script_name, function_name); + goto fail; + } + + /* Add the Lua function state to frrscript */ + struct lua_function_state key = {.name = function_name, .L = L}; + + hash_get(fs->lua_function_hash, &key, lua_function_alloc); + + return 0; fail: - frrscript_unload(fs); - return NULL; + lua_close(L); + return 1; } -void frrscript_unload(struct frrscript *fs) +void frrscript_delete(struct frrscript *fs) { - lua_close(fs->L); + hash_iterate(fs->lua_function_hash, lua_function_free, NULL); XFREE(MTYPE_SCRIPT, fs->name); XFREE(MTYPE_SCRIPT, fs); } diff --git a/lib/frrscript.h b/lib/frrscript.h index f4057f531b..540676c099 100644 --- a/lib/frrscript.h +++ b/lib/frrscript.h @@ -25,6 +25,7 @@ #include <lua.h> #include "frrlua.h" +#include "bgpd/bgp_script.h" // for peer and attr encoders/decoders #ifdef __cplusplus extern "C" { @@ -39,14 +40,30 @@ struct frrscript_codec { decoder_func decoder; }; +struct lua_function_state { + const char *name; + lua_State *L; +}; + struct frrscript { /* Script name */ char *name; - /* Lua state */ - struct lua_State *L; + /* Hash of Lua function name to Lua function state */ + struct hash *lua_function_hash; }; + +/* + * Hash related functions for lua_function_hash + */ + +void *lua_function_alloc(void *arg); + +unsigned int lua_function_hash_key(const void *data); + +bool lua_function_hash_cmp(const void *d1, const void *d2); + struct frrscript_env { /* Value type */ const char *typename; @@ -59,15 +76,24 @@ struct frrscript_env { }; /* - * Create new FRR script. + * Create new struct frrscript for a Lua script. + * This will hold the states for the Lua functions in this script. + * + * scriptname + * Name of the Lua script file, without the .lua + */ +struct frrscript *frrscript_new(const char *scriptname); + +/* + * Load a function into frrscript, run callback if any */ -struct frrscript *frrscript_load(const char *name, - int (*load_cb)(struct frrscript *)); +int frrscript_load(struct frrscript *fs, const char *function_name, + int (*load_cb)(struct frrscript *)); /* - * Destroy FRR script. + * Delete Lua function states and frrscript */ -void frrscript_unload(struct frrscript *fs); +void frrscript_delete(struct frrscript *fs); /* * Register a Lua codec for a type. @@ -96,38 +122,150 @@ void frrscript_register_type_codecs(struct frrscript_codec *codecs); */ void frrscript_init(const char *scriptdir); +/* + * This macro is mapped to every (name, value) in frrscript_call, + * so this in turn maps them onto their encoders + */ +#define ENCODE_ARGS(name, value) ENCODE_ARGS_WITH_STATE(lfs->L, (value)) /* - * Call script. + * This macro is also mapped to every (name, value) in frrscript_call, but + * not every value can be mapped to its decoder - only those that appear + * in the returned table will. To find out if they appear in the returned + * table, first pop the value and check if its nil. Only call the decoder + * if non-nil. * - * fs - * The script to call; this is obtained from frrscript_load(). + * At the end, the only thing left on the stack should be the + * returned table. + */ +#define DECODE_ARGS(name, value) \ + do { \ + lua_getfield(lfs->L, 1, (name)); \ + if (lua_isnil(lfs->L, 2)) { \ + lua_pop(lfs->L, 1); \ + } else { \ + DECODE_ARGS_WITH_STATE(lfs->L, (value)); \ + } \ + assert(lua_gettop(lfs->L) == 1); \ + } while (0) + +/* + * Maps the type of value to its encoder/decoder. + * Add new mappings here. + * + * L + * Lua state + * scriptdir + * Directory in which to look for scripts + */ +#define ENCODE_ARGS_WITH_STATE(L, value) \ + _Generic((value), \ +int : lua_pushinteger, \ +long long * : lua_pushintegerp, \ +struct prefix * : lua_pushprefix, \ +struct interface * : lua_pushinterface, \ +struct in_addr * : lua_pushinaddr, \ +struct in6_addr * : lua_pushin6addr, \ +union sockunion * : lua_pushsockunion, \ +time_t * : lua_pushtimet, \ +char * : lua_pushstring_wrapper, \ +struct attr * : lua_pushattr, \ +struct peer * : lua_pushpeer, \ +const struct prefix * : lua_pushprefix \ +)((L), (value)) + +#define DECODE_ARGS_WITH_STATE(L, value) \ + _Generic((value), \ +int : lua_decode_integer_noop, \ +long long * : lua_decode_integerp, \ +struct prefix * : lua_decode_prefix, \ +struct interface * : lua_decode_interface, \ +struct in_addr * : lua_decode_inaddr, \ +struct in6_addr * : lua_decode_in6addr, \ +union sockunion * : lua_decode_sockunion, \ +time_t * : lua_decode_timet, \ +char * : lua_decode_stringp, \ +struct attr * : lua_decode_attr, \ +struct peer * : lua_decode_noop, \ +const struct prefix * : lua_decode_noop \ +)((L), -1, (value)) + +/* + * Call Lua function state (abstraction for a single Lua function) * - * env - * The script's environment. Specify this as an array of frrscript_env. + * lfs + * The Lua function to call; this should have been loaded in by + * frrscript_load(). nargs Number of arguments the function accepts * * Returns: * 0 if the script ran successfully, nonzero otherwise. */ -int frrscript_call(struct frrscript *fs, struct frrscript_env *env); +int _frrscript_call_lua(struct lua_function_state *lfs, int nargs); +/* + * Wrapper for calling Lua function state. Maps values passed in to their + * encoder and decoder types. + * + * fs + * The struct frrscript in which the Lua fuction was loaded into + * f + * Name of the Lua function. + * + * Returns: + * 0 if the script ran successfully, nonzero otherwise. + */ +#define frrscript_call(fs, f, ...) \ + ({ \ + struct lua_function_state lookup = {.name = (f)}; \ + struct lua_function_state *lfs; \ + lfs = hash_lookup((fs)->lua_function_hash, &lookup); \ + lfs == NULL ? ({ \ + zlog_err( \ + "frrscript: '%s.lua': '%s': tried to call this function but it was not loaded", \ + (fs)->name, (f)); \ + 1; \ + }) \ + : ({ \ + MAP_LISTS(ENCODE_ARGS, ##__VA_ARGS__); \ + _frrscript_call_lua( \ + lfs, PP_NARG(__VA_ARGS__)); \ + }) != 0 \ + ? ({ \ + zlog_err( \ + "frrscript: '%s.lua': '%s': this function called but returned non-zero exit code. No variables modified.", \ + (fs)->name, (f)); \ + 1; \ + }) \ + : ({ \ + MAP_LISTS(DECODE_ARGS, \ + ##__VA_ARGS__); \ + 0; \ + }); \ + }) /* - * Get result from finished script. + * Get result from finished function * * fs * The script. This script must have been run already. - * - * result - * The result to extract from the script. - * This reuses the frrscript_env type, but only the typename and name fields - * need to be set. The value is returned directly. + * function_name + * Name of the Lua function. + * name + * Name of the result. + * This will be used as a string key to retrieve from the table that the + * Lua function returns. + * The name here should *not* appear in frrscript_call. + * lua_to + * Function pointer to a lua_to decoder function. + * This function should allocate and decode a value from the Lua state. * * Returns: - * The script result of the specified name and type, or NULL. + * A pointer to the decoded value from the Lua state, or NULL if no such + * value. */ -void *frrscript_get_result(struct frrscript *fs, - const struct frrscript_env *result); +void *frrscript_get_result(struct frrscript *fs, const char *function_name, + const char *name, + void *(*lua_to)(lua_State *L, int idx)); #ifdef __cplusplus } @@ -1349,10 +1349,20 @@ static const struct cmd_variable_handler if_var_handlers[] = { {.tokenname = "INTERFACE", .completions = if_autocomplete}, {.completions = NULL}}; -void if_cmd_init(void) +static struct cmd_node interface_node = { + .name = "interface", + .node = INTERFACE_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-if)# ", +}; + +void if_cmd_init(int (*config_write)(struct vty *)) { cmd_variable_handler_register(if_var_handlers); + interface_node.config_write = config_write; + install_node(&interface_node); + install_element(CONFIG_NODE, &interface_cmd); install_element(CONFIG_NODE, &no_interface_cmd); @@ -597,7 +597,8 @@ struct if_link_params *if_link_params_get(struct interface *); void if_link_params_free(struct interface *); /* Northbound. */ -extern void if_cmd_init(void); +struct vty; +extern void if_cmd_init(int (*config_write)(struct vty *)); extern void if_zapi_callbacks(int (*create)(struct interface *ifp), int (*up)(struct interface *ifp), int (*down)(struct interface *ifp), diff --git a/lib/libfrr.c b/lib/libfrr.c index 0817182f7a..97dab74d9b 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -44,6 +44,7 @@ #include "frr_pthread.h" #include "defaults.h" #include "frrscript.h" +#include "systemd.h" DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm)); DEFINE_HOOK(frr_config_pre, (struct thread_master * tm), (tm)); @@ -363,6 +364,11 @@ void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv) startup_fds |= UINT64_C(0x1) << (uint64_t)i; } + + /* note this doesn't do anything, it just grabs state, so doing it + * early in _preinit is perfect. + */ + systemd_init_env(); } bool frr_is_startup_fd(int fd) diff --git a/lib/link_state.c b/lib/link_state.c index e8a6b89f89..062384aac7 100644 --- a/lib/link_state.c +++ b/lib/link_state.c @@ -538,8 +538,6 @@ struct ls_edge *ls_edge_add(struct ls_ted *ted, /* Create Edge and add it to the TED */ new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_edge)); - if (!new) - return NULL; new->attributes = attributes; new->key = key; @@ -804,8 +802,6 @@ struct ls_ted *ls_ted_new(const uint32_t key, const char *name, struct ls_ted *new; new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_ted)); - if (new == NULL) - return new; /* Set basic information for this ted */ new->key = key; @@ -1005,8 +1001,6 @@ static struct ls_node *ls_parse_node(struct stream *s) size_t len; node = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_node)); - if (node == NULL) - return NULL; STREAM_GET(&node->adv, s, sizeof(struct ls_node_id)); STREAM_GETW(s, node->flags); @@ -1051,8 +1045,6 @@ static struct ls_attributes *ls_parse_attributes(struct stream *s) size_t len; attr = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_attributes)); - if (attr == NULL) - return NULL; attr->srlgs = NULL; STREAM_GET(&attr->adv, s, sizeof(struct ls_node_id)); @@ -1157,8 +1149,6 @@ static struct ls_prefix *ls_parse_prefix(struct stream *s) size_t len; ls_pref = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_prefix)); - if (ls_pref == NULL) - return NULL; STREAM_GET(&ls_pref->adv, s, sizeof(struct ls_node_id)); STREAM_GETW(s, ls_pref->flags); @@ -1193,8 +1183,6 @@ struct ls_message *ls_parse_msg(struct stream *s) struct ls_message *msg; msg = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_message)); - if (msg == NULL) - return NULL; /* Read LS Message header */ STREAM_GETC(s, msg->event); diff --git a/lib/network.c b/lib/network.c index 411661a5e1..b60ad9a57c 100644 --- a/lib/network.c +++ b/lib/network.c @@ -78,7 +78,8 @@ int set_nonblocking(int fd) /* According to the Single UNIX Spec, the return value for F_GETFL should never be negative. */ - if ((flags = fcntl(fd, F_GETFL)) < 0) { + flags = fcntl(fd, F_GETFL); + if (flags < 0) { flog_err(EC_LIB_SYSTEM_CALL, "fcntl(F_GETFL) failed for fd %d: %s", fd, safe_strerror(errno)); diff --git a/lib/ntop.c b/lib/ntop.c index ccbf8793d3..1b2dd7a6d1 100644 --- a/lib/ntop.c +++ b/lib/ntop.c @@ -40,14 +40,18 @@ static inline void putbyte(uint8_t bytex, char **posx) bool zero = false; int byte = bytex, tmp, a, b; - if ((tmp = byte - 200) >= 0) { + tmp = byte - 200; + if (tmp >= 0) { *pos++ = '2'; zero = true; byte = tmp; - } else if ((tmp = byte - 100) >= 0) { - *pos++ = '1'; - zero = true; - byte = tmp; + } else { + tmp = byte - 100; + if (tmp >= 0) { + *pos++ = '1'; + zero = true; + byte = tmp; + } } /* make sure the compiler knows the value range of "byte" */ @@ -49,7 +49,8 @@ struct pbr_filter { #define PBR_FILTER_PROTO (1 << 5) #define PBR_FILTER_SRC_PORT_RANGE (1 << 6) #define PBR_FILTER_DST_PORT_RANGE (1 << 7) -#define PBR_FILTER_DSFIELD (1 << 8) +#define PBR_FILTER_DSFIELD (1 << 8) +#define PBR_FILTER_IP_PROTOCOL (1 << 9) #define PBR_DSFIELD_DSCP (0xfc) /* Upper 6 bits of DS field: DSCP */ #define PBR_DSFIELD_ECN (0x03) /* Lower 2 bits of DS field: BCN */ @@ -67,6 +68,9 @@ struct pbr_filter { /* Filter with fwmark */ uint32_t fwmark; + + /* Filter with the ip protocol */ + uint8_t ip_proto; }; /* diff --git a/lib/plist.c b/lib/plist.c index 0ee02f8a0b..2f9f06f43b 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -750,7 +750,7 @@ static const char *prefix_list_type_str(struct prefix_list_entry *pentry) } static int prefix_list_entry_match(struct prefix_list_entry *pentry, - const struct prefix *p) + const struct prefix *p, bool address_mode) { int ret; @@ -761,6 +761,9 @@ static int prefix_list_entry_match(struct prefix_list_entry *pentry, if (!ret) return 0; + if (address_mode) + return 1; + /* In case of le nor ge is specified, exact match is performed. */ if (!pentry->le && !pentry->ge) { if (pentry->prefix.prefixlen != p->prefixlen) @@ -777,14 +780,15 @@ static int prefix_list_entry_match(struct prefix_list_entry *pentry, return 1; } -enum prefix_list_type prefix_list_apply_which_prefix( +enum prefix_list_type prefix_list_apply_ext( struct prefix_list *plist, - const struct prefix **which, - const void *object) + const struct prefix_list_entry **which, + union prefixconstptr object, + bool address_mode) { struct prefix_list_entry *pentry, *pbest = NULL; - const struct prefix *p = (const struct prefix *)object; + const struct prefix *p = object.p; const uint8_t *byte = p->u.val; size_t depth; size_t validbits = p->prefixlen; @@ -809,7 +813,7 @@ enum prefix_list_type prefix_list_apply_which_prefix( pentry = pentry->next_best) { if (pbest && pbest->seq < pentry->seq) continue; - if (prefix_list_entry_match(pentry, p)) + if (prefix_list_entry_match(pentry, p, address_mode)) pbest = pentry; } @@ -830,7 +834,7 @@ enum prefix_list_type prefix_list_apply_which_prefix( pentry = pentry->next_best) { if (pbest && pbest->seq < pentry->seq) continue; - if (prefix_list_entry_match(pentry, p)) + if (prefix_list_entry_match(pentry, p, address_mode)) pbest = pentry; } break; @@ -838,7 +842,7 @@ enum prefix_list_type prefix_list_apply_which_prefix( if (which) { if (pbest) - *which = &pbest->prefix; + *which = pbest; else *which = NULL; } @@ -878,7 +882,7 @@ static void __attribute__((unused)) prefix_list_print(struct prefix_list *plist) } } -/* Retrun 1 when plist already include pentry policy. */ +/* Return 1 when plist already include pentry policy. */ static struct prefix_list_entry * prefix_entry_dup_check(struct prefix_list *plist, struct prefix_list_entry *new) { @@ -928,102 +932,206 @@ enum display_type { first_match_display }; -static void vty_show_prefix_entry(struct vty *vty, afi_t afi, +static void vty_show_prefix_entry(struct vty *vty, json_object *json, afi_t afi, struct prefix_list *plist, struct prefix_master *master, enum display_type dtype, int seqnum) { struct prefix_list_entry *pentry; + json_object *json_pl = NULL; /* Print the name of the protocol */ - vty_out(vty, "%s: ", frr_protoname); + if (json) { + json_pl = json_object_new_object(); + json_object_object_add(json, plist->name, json_pl); + } else + vty_out(vty, "%s: ", frr_protoname); if (dtype == normal_display) { - vty_out(vty, "ip%s prefix-list %s: %d entries\n", - afi == AFI_IP ? "" : "v6", plist->name, plist->count); - if (plist->desc) - vty_out(vty, " Description: %s\n", plist->desc); + if (json) { + json_object_string_add(json_pl, "addressFamily", + afi2str(afi)); + json_object_int_add(json_pl, "entries", plist->count); + if (plist->desc) + json_object_string_add(json_pl, "description", + plist->desc); + } else { + vty_out(vty, "ip%s prefix-list %s: %d entries\n", + afi == AFI_IP ? "" : "v6", plist->name, + plist->count); + if (plist->desc) + vty_out(vty, " Description: %s\n", + plist->desc); + } } else if (dtype == summary_display || dtype == detail_display) { - vty_out(vty, "ip%s prefix-list %s:\n", - afi == AFI_IP ? "" : "v6", plist->name); - - if (plist->desc) - vty_out(vty, " Description: %s\n", plist->desc); - - vty_out(vty, - " count: %d, range entries: %d, sequences: %" PRId64 " - %" PRId64 "\n", - plist->count, plist->rangecount, - plist->head ? plist->head->seq : 0, - plist->tail ? plist->tail->seq : 0); + if (json) { + json_object_string_add(json_pl, "addressFamily", + afi2str(afi)); + if (plist->desc) + json_object_string_add(json_pl, "description", + plist->desc); + json_object_int_add(json_pl, "count", plist->count); + json_object_int_add(json_pl, "rangeEntries", + plist->rangecount); + json_object_int_add(json_pl, "sequenceStart", + plist->head ? plist->head->seq : 0); + json_object_int_add(json_pl, "sequenceEnd", + plist->tail ? plist->tail->seq : 0); + } else { + vty_out(vty, "ip%s prefix-list %s:\n", + afi == AFI_IP ? "" : "v6", plist->name); + + if (plist->desc) + vty_out(vty, " Description: %s\n", + plist->desc); + + vty_out(vty, + " count: %d, range entries: %d, sequences: %" PRId64 + " - %" PRId64 "\n", + plist->count, plist->rangecount, + plist->head ? plist->head->seq : 0, + plist->tail ? plist->tail->seq : 0); + } } if (dtype != summary_display) { + json_object *json_entries = NULL; + + if (json) { + json_entries = json_object_new_array(); + json_object_object_add(json_pl, "entries", + json_entries); + } + for (pentry = plist->head; pentry; pentry = pentry->next) { if (dtype == sequential_display && pentry->seq != seqnum) continue; - vty_out(vty, " "); + if (json) { + json_object *json_entry; + char buf[BUFSIZ]; - vty_out(vty, "seq %" PRId64 " ", pentry->seq); + json_entry = json_object_new_object(); + json_object_array_add(json_entries, json_entry); - vty_out(vty, "%s ", prefix_list_type_str(pentry)); - - if (pentry->any) - vty_out(vty, "any"); - else { - struct prefix *p = &pentry->prefix; - - vty_out(vty, "%pFX", p); + json_object_int_add(json_entry, + "sequenceNumber", + pentry->seq); + json_object_string_add( + json_entry, "type", + prefix_list_type_str(pentry)); + json_object_string_add( + json_entry, "prefix", + prefix2str(&pentry->prefix, buf, + sizeof(buf))); if (pentry->ge) - vty_out(vty, " ge %d", pentry->ge); + json_object_int_add( + json_entry, + "minimumPrefixLength", + pentry->ge); if (pentry->le) - vty_out(vty, " le %d", pentry->le); + json_object_int_add( + json_entry, + "maximumPrefixLength", + pentry->le); + + if (dtype == detail_display + || dtype == sequential_display) { + json_object_int_add(json_entry, + "hitCount", + pentry->hitcnt); + json_object_int_add(json_entry, + "referenceCount", + pentry->refcnt); + } + } else { + vty_out(vty, " "); + + vty_out(vty, "seq %" PRId64 " ", pentry->seq); + + vty_out(vty, "%s ", + prefix_list_type_str(pentry)); + + if (pentry->any) + vty_out(vty, "any"); + else { + struct prefix *p = &pentry->prefix; + + vty_out(vty, "%pFX", p); + + if (pentry->ge) + vty_out(vty, " ge %d", + pentry->ge); + if (pentry->le) + vty_out(vty, " le %d", + pentry->le); + } + + if (dtype == detail_display + || dtype == sequential_display) + vty_out(vty, + " (hit count: %ld, refcount: %ld)", + pentry->hitcnt, pentry->refcnt); + + vty_out(vty, "\n"); } - - if (dtype == detail_display - || dtype == sequential_display) - vty_out(vty, " (hit count: %ld, refcount: %ld)", - pentry->hitcnt, pentry->refcnt); - - vty_out(vty, "\n"); } } } static int vty_show_prefix_list(struct vty *vty, afi_t afi, const char *name, - const char *seq, enum display_type dtype) + const char *seq, enum display_type dtype, + bool uj) { struct prefix_list *plist; struct prefix_master *master; int64_t seqnum = 0; + json_object *json = NULL; + json_object *json_proto = NULL; master = prefix_master_get(afi, 0); if (master == NULL) return CMD_WARNING; + if (uj) { + json = json_object_new_object(); + json_proto = json_object_new_object(); + json_object_object_add(json, frr_protoname, json_proto); + } + if (seq) seqnum = (int64_t)atol(seq); if (name) { plist = prefix_list_lookup(afi, name); if (!plist) { - vty_out(vty, "%% Can't find specified prefix-list\n"); + if (!uj) + vty_out(vty, + "%% Can't find specified prefix-list\n"); return CMD_WARNING; } - vty_show_prefix_entry(vty, afi, plist, master, dtype, seqnum); + vty_show_prefix_entry(vty, json_proto, afi, plist, master, + dtype, seqnum); } else { if (dtype == detail_display || dtype == summary_display) { - if (master->recent) + if (master->recent && !uj) vty_out(vty, "Prefix-list with the last deletion/insertion: %s\n", master->recent->name); } for (plist = master->str.head; plist; plist = plist->next) - vty_show_prefix_entry(vty, afi, plist, master, dtype, - seqnum); + vty_show_prefix_entry(vty, json_proto, afi, plist, + master, dtype, seqnum); + } + + if (uj) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); } return CMD_SUCCESS; @@ -1146,19 +1254,21 @@ static int vty_clear_prefix_list(struct vty *vty, afi_t afi, const char *name, DEFPY (show_ip_prefix_list, show_ip_prefix_list_cmd, - "show ip prefix-list [WORD [seq$dseq (1-4294967295)$arg]]", + "show ip prefix-list [WORD [seq$dseq (1-4294967295)$arg]] [json$uj]", SHOW_STR IP_STR PREFIX_LIST_STR "Name of a prefix list\n" "sequence number of an entry\n" - "Sequence number\n") + "Sequence number\n" + JSON_STR) { enum display_type dtype = normal_display; if (dseq) dtype = sequential_display; - return vty_show_prefix_list(vty, AFI_IP, prefix_list, arg_str, dtype); + return vty_show_prefix_list(vty, AFI_IP, prefix_list, arg_str, dtype, + !!uj); } DEFPY (show_ip_prefix_list_prefix, @@ -1184,28 +1294,30 @@ DEFPY (show_ip_prefix_list_prefix, DEFPY (show_ip_prefix_list_summary, show_ip_prefix_list_summary_cmd, - "show ip prefix-list summary [WORD$prefix_list]", + "show ip prefix-list summary [WORD$prefix_list] [json$uj]", SHOW_STR IP_STR PREFIX_LIST_STR "Summary of prefix lists\n" - "Name of a prefix list\n") + "Name of a prefix list\n" + JSON_STR) { return vty_show_prefix_list(vty, AFI_IP, prefix_list, NULL, - summary_display); + summary_display, !!uj); } DEFPY (show_ip_prefix_list_detail, show_ip_prefix_list_detail_cmd, - "show ip prefix-list detail [WORD$prefix_list]", + "show ip prefix-list detail [WORD$prefix_list] [json$uj]", SHOW_STR IP_STR PREFIX_LIST_STR "Detail of prefix lists\n" - "Name of a prefix list\n") + "Name of a prefix list\n" + JSON_STR) { return vty_show_prefix_list(vty, AFI_IP, prefix_list, NULL, - detail_display); + detail_display, !!uj); } DEFPY (clear_ip_prefix_list, @@ -1222,19 +1334,21 @@ DEFPY (clear_ip_prefix_list, DEFPY (show_ipv6_prefix_list, show_ipv6_prefix_list_cmd, - "show ipv6 prefix-list [WORD [seq$dseq (1-4294967295)$arg]]", + "show ipv6 prefix-list [WORD [seq$dseq (1-4294967295)$arg]] [json$uj]", SHOW_STR IPV6_STR PREFIX_LIST_STR "Name of a prefix list\n" "sequence number of an entry\n" - "Sequence number\n") + "Sequence number\n" + JSON_STR) { enum display_type dtype = normal_display; if (dseq) dtype = sequential_display; - return vty_show_prefix_list(vty, AFI_IP6, prefix_list, arg_str, dtype); + return vty_show_prefix_list(vty, AFI_IP6, prefix_list, arg_str, dtype, + !!uj); } DEFPY (show_ipv6_prefix_list_prefix, @@ -1260,28 +1374,30 @@ DEFPY (show_ipv6_prefix_list_prefix, DEFPY (show_ipv6_prefix_list_summary, show_ipv6_prefix_list_summary_cmd, - "show ipv6 prefix-list summary [WORD$prefix-list]", + "show ipv6 prefix-list summary [WORD$prefix-list] [json$uj]", SHOW_STR IPV6_STR PREFIX_LIST_STR "Summary of prefix lists\n" - "Name of a prefix list\n") + "Name of a prefix list\n" + JSON_STR) { return vty_show_prefix_list(vty, AFI_IP6, prefix_list, NULL, - summary_display); + summary_display, !!uj); } DEFPY (show_ipv6_prefix_list_detail, show_ipv6_prefix_list_detail_cmd, - "show ipv6 prefix-list detail [WORD$prefix-list]", + "show ipv6 prefix-list detail [WORD$prefix-list] [json$uj]", SHOW_STR IPV6_STR PREFIX_LIST_STR "Detail of prefix lists\n" - "Name of a prefix list\n") + "Name of a prefix list\n" + JSON_STR) { return vty_show_prefix_list(vty, AFI_IP6, prefix_list, NULL, - detail_display); + detail_display, !!uj); } DEFPY (clear_ipv6_prefix_list, @@ -1296,6 +1412,51 @@ DEFPY (clear_ipv6_prefix_list, return vty_clear_prefix_list(vty, AFI_IP6, prefix_list, prefix_str); } +DEFPY (debug_prefix_list_match, + debug_prefix_list_match_cmd, + "debug prefix-list WORD$prefix-list match <A.B.C.D/M|X:X::X:X/M>" + " [address-mode$addr_mode]", + DEBUG_STR + "Prefix-list test access\n" + "Name of a prefix list\n" + "Test prefix for prefix list result\n" + "Prefix to test in ip prefix-list\n" + "Prefix to test in ipv6 prefix-list\n" + "Use address matching mode (PIM RP)\n") +{ + struct prefix_list *plist; + const struct prefix_list_entry *entry = NULL; + enum prefix_list_type ret; + + plist = prefix_list_lookup(family2afi(match->family), prefix_list); + if (!plist) { + vty_out(vty, "%% no prefix list named %s for AFI %s\n", + prefix_list, afi2str(family2afi(match->family))); + return CMD_WARNING; + } + + ret = prefix_list_apply_ext(plist, &entry, match, !!addr_mode); + + vty_out(vty, "%s prefix list %s yields %s for %pFX, ", + afi2str(family2afi(match->family)), prefix_list, + ret == PREFIX_DENY ? "DENY" : "PERMIT", match); + + if (!entry) + vty_out(vty, "no match found\n"); + else { + vty_out(vty, "matching entry #%"PRId64": %pFX", entry->seq, + &entry->prefix); + if (entry->ge) + vty_out(vty, " ge %d", entry->ge); + if (entry->le) + vty_out(vty, " le %d", entry->le); + vty_out(vty, "\n"); + } + + /* allow using this in scripts for quick prefix-list member tests */ + return (ret == PREFIX_PERMIT) ? CMD_SUCCESS : CMD_WARNING; +} + struct stream *prefix_bgp_orf_entry(struct stream *s, struct prefix_list *plist, uint8_t init_flag, uint8_t permit_flag, uint8_t deny_flag) @@ -1537,6 +1698,7 @@ static void prefix_list_init_ipv6(void) install_element(VIEW_NODE, &show_ipv6_prefix_list_prefix_cmd); install_element(VIEW_NODE, &show_ipv6_prefix_list_summary_cmd); install_element(VIEW_NODE, &show_ipv6_prefix_list_detail_cmd); + install_element(VIEW_NODE, &debug_prefix_list_match_cmd); install_element(ENABLE_NODE, &clear_ipv6_prefix_list_cmd); } diff --git a/lib/plist.h b/lib/plist.h index 57eb763a68..c9507df57c 100644 --- a/lib/plist.h +++ b/lib/plist.h @@ -37,6 +37,7 @@ enum prefix_list_type { }; struct prefix_list; +struct prefix_list_entry; struct orf_prefix { uint32_t seq; @@ -63,12 +64,18 @@ extern struct prefix_list *prefix_list_lookup(afi_t, const char *); * * If no pointer is sent in, do not return anything. * If it is a empty plist return a NULL pointer. + * + * address_mode = the "prefix" being passed in is really an address, match + * regardless of prefix length (i.e. ge/le are ignored.) prefix->prefixlen + * must be /32. */ extern enum prefix_list_type -prefix_list_apply_which_prefix(struct prefix_list *plist, - const struct prefix **which, - const void *object); -#define prefix_list_apply(A, B) prefix_list_apply_which_prefix((A), NULL, (B)) +prefix_list_apply_ext(struct prefix_list *plist, + const struct prefix_list_entry **matches, + union prefixconstptr prefix, + bool address_mode); +#define prefix_list_apply(A, B) \ + prefix_list_apply_ext((A), NULL, (B), false) extern struct prefix_list *prefix_bgp_orf_lookup(afi_t, const char *); extern struct stream *prefix_bgp_orf_entry(struct stream *, diff --git a/lib/prefix.c b/lib/prefix.c index 7dbb5f07f0..ef7d2e59da 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -577,7 +577,7 @@ int str2prefix_ipv4(const char *str, struct prefix_ipv4 *p) /* Get prefix length. */ plen = (uint8_t)atoi(++pnt); - if (plen > IPV4_MAX_PREFIXLEN) + if (plen > IPV4_MAX_BITLEN) return 0; p->family = AF_INET; @@ -1034,7 +1034,8 @@ const char *prefix2str(union prefixconstptr pu, char *str, int size) l = strlen(buf); buf[l++] = '/'; byte = p->prefixlen; - if ((tmp = p->prefixlen - 100) >= 0) { + tmp = p->prefixlen - 100; + if (tmp >= 0) { buf[l++] = '1'; z = true; byte = tmp; @@ -1128,7 +1129,7 @@ void apply_classful_mask_ipv4(struct prefix_ipv4 *p) destination = ntohl(p->prefix.s_addr); - if (p->prefixlen == IPV4_MAX_PREFIXLEN) + if (p->prefixlen == IPV4_MAX_BITLEN) ; /* do nothing for host routes */ else if (IN_CLASSC(destination)) { @@ -1148,12 +1149,13 @@ in_addr_t ipv4_broadcast_addr(in_addr_t hostaddr, int masklen) struct in_addr mask; masklen2ip(masklen, &mask); - return (masklen != IPV4_MAX_PREFIXLEN - 1) ? - /* normal case */ - (hostaddr | ~mask.s_addr) - : - /* For prefix 31 return 255.255.255.255 (RFC3021) */ - htonl(0xFFFFFFFF); + return (masklen != IPV4_MAX_BITLEN - 1) + ? + /* normal case */ + (hostaddr | ~mask.s_addr) + : + /* For prefix 31 return 255.255.255.255 (RFC3021) */ + htonl(0xFFFFFFFF); } /* Utility function to convert ipv4 netmask to prefixes diff --git a/lib/prefix.h b/lib/prefix.h index 217a23d561..944c94f57f 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -372,7 +372,6 @@ union prefixconstptr { /* Max bit/byte length of IPv4 address. */ #define IPV4_MAX_BYTELEN 4 #define IPV4_MAX_BITLEN 32 -#define IPV4_MAX_PREFIXLEN 32 #define IPV4_ADDR_CMP(D,S) memcmp ((D), (S), IPV4_MAX_BYTELEN) static inline bool ipv4_addr_same(const struct in_addr *a, @@ -398,7 +397,6 @@ static inline void ipv4_addr_copy(struct in_addr *dst, /* Max bit/byte length of IPv6 address. */ #define IPV6_MAX_BYTELEN 16 #define IPV6_MAX_BITLEN 128 -#define IPV6_MAX_PREFIXLEN 128 #define IPV6_ADDR_CMP(D,S) memcmp ((D), (S), IPV6_MAX_BYTELEN) #define IPV6_ADDR_SAME(D,S) (memcmp ((D), (S), IPV6_MAX_BYTELEN) == 0) #define IPV6_ADDR_COPY(D,S) memcpy ((D), (S), IPV6_MAX_BYTELEN) @@ -481,11 +479,6 @@ extern void prefix_ipv4_free(struct prefix_ipv4 **p); extern int str2prefix_ipv4(const char *, struct prefix_ipv4 *); extern void apply_mask_ipv4(struct prefix_ipv4 *); -#define PREFIX_COPY(DST, SRC) \ - *((struct prefix *)(DST)) = *((const struct prefix *)(SRC)) -#define PREFIX_COPY_IPV4(DST, SRC) \ - *((struct prefix_ipv4 *)(DST)) = *((const struct prefix_ipv4 *)(SRC)) - extern int prefix_ipv4_any(const struct prefix_ipv4 *); extern void apply_classful_mask_ipv4(struct prefix_ipv4 *); @@ -503,9 +496,6 @@ extern void prefix_ipv6_free(struct prefix_ipv6 **p); extern int str2prefix_ipv6(const char *, struct prefix_ipv6 *); extern void apply_mask_ipv6(struct prefix_ipv6 *); -#define PREFIX_COPY_IPV6(DST, SRC) \ - *((struct prefix_ipv6 *)(DST)) = *((const struct prefix_ipv6 *)(SRC)) - extern int ip6_masklen(struct in6_addr); extern void masklen2ip6(const int, struct in6_addr *); @@ -547,20 +537,32 @@ static inline int ipv4_martian(struct in_addr *addr) return 0; } -static inline int is_default_prefix(const struct prefix *p) +static inline bool is_default_prefix4(const struct prefix_ipv4 *p) { - if (!p) - return 0; + return p && p->family == AF_INET && p->prefixlen == 0 + && p->prefix.s_addr == INADDR_ANY; +} - if ((p->family == AF_INET) && (p->u.prefix4.s_addr == INADDR_ANY) - && (p->prefixlen == 0)) - return 1; +static inline bool is_default_prefix6(const struct prefix_ipv6 *p) +{ + return p && p->family == AF_INET6 && p->prefixlen == 0 + && memcmp(&p->prefix, &in6addr_any, sizeof(struct in6_addr)) + == 0; +} - if ((p->family == AF_INET6) && (p->prefixlen == 0) - && (!memcmp(&p->u.prefix6, &in6addr_any, sizeof(struct in6_addr)))) - return 1; +static inline bool is_default_prefix(const struct prefix *p) +{ + if (p == NULL) + return false; + + switch (p->family) { + case AF_INET: + return is_default_prefix4((const struct prefix_ipv4 *)p); + case AF_INET6: + return is_default_prefix6((const struct prefix_ipv6 *)p); + } - return 0; + return false; } static inline int is_host_route(const struct prefix *p) diff --git a/lib/route_opaque.h b/lib/route_opaque.h index 599a0363eb..7c4e9a16e1 100644 --- a/lib/route_opaque.h +++ b/lib/route_opaque.h @@ -21,12 +21,15 @@ #ifndef FRR_ROUTE_OPAQUE_H #define FRR_ROUTE_OPAQUE_H +#include "assert.h" +#include "zclient.h" + #include "bgpd/bgp_aspath.h" #include "bgpd/bgp_community.h" #include "bgpd/bgp_lcommunity.h" struct bgp_zebra_opaque { - char aspath[ASPATH_STR_DEFAULT_LEN]; + char aspath[256]; /* Show at least 10 communities AA:BB */ char community[COMMUNITY_SIZE * 20]; @@ -35,4 +38,7 @@ struct bgp_zebra_opaque { char lcommunity[LCOMMUNITY_SIZE * 30]; }; +static_assert(sizeof(struct bgp_zebra_opaque) <= ZAPI_MESSAGE_OPAQUE_LENGTH, + "BGP opaque data shouldn't be larger than zebra's buffer"); + #endif /* FRR_ROUTE_OPAQUE_H */ diff --git a/lib/route_types.txt b/lib/route_types.txt index c48391545d..77639070c9 100644 --- a/lib/route_types.txt +++ b/lib/route_types.txt @@ -1,4 +1,4 @@ -# Canonical Zserv route types information registry for Quagga. +# Canonical Zserv route types information registry for FRR. # # Used to construct route_types.c and route_types.h # @@ -60,7 +60,7 @@ ZEBRA_ROUTE_PIM, pim, pimd, 'P', 0, 0, 0, "PIM", pimd ZEBRA_ROUTE_EIGRP, eigrp, eigrpd, 'E', 1, 0, 1, "EIGRP", eigrpd ZEBRA_ROUTE_NHRP, nhrp, nhrpd, 'N', 1, 1, 1, "NHRP", nhrpd # HSLS and OLSR both are AFI independent (so: 1, 1), however -# we want to disable for them for general Quagga distribution. +# we want to disable for them for general FRR distribution. # This at least makes it trivial for users of these protocols # to 'switch on' redist support (direct numeric entry remaining # possible). diff --git a/lib/routemap.c b/lib/routemap.c index 9dc1c7c82d..5d45dc1047 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -22,6 +22,7 @@ #include "linklist.h" #include "memory.h" +#include "command.h" #include "vector.h" #include "prefix.h" #include "vty.h" @@ -32,6 +33,7 @@ #include "libfrr.h" #include "lib_errors.h" #include "table.h" +#include "json.h" DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP, "Route map"); DEFINE_MTYPE(LIB, ROUTE_MAP_NAME, "Route map name"); @@ -840,50 +842,140 @@ static const char *route_map_result_str(route_map_result_t res) } /* show route-map */ -static void vty_show_route_map_entry(struct vty *vty, struct route_map *map) +static void vty_show_route_map_entry(struct vty *vty, struct route_map *map, + json_object *json) { struct route_map_index *index; struct route_map_rule *rule; - - vty_out(vty, "route-map: %s Invoked: %" PRIu64 " Optimization: %s Processed Change: %s\n", - map->name, map->applied - map->applied_clear, - map->optimization_disabled ? "disabled" : "enabled", - map->to_be_processed ? "true" : "false"); + json_object *json_rmap = NULL; + json_object *json_rules = NULL; + + if (json) { + json_rmap = json_object_new_object(); + json_object_object_add(json, map->name, json_rmap); + + json_rules = json_object_new_array(); + json_object_int_add(json_rmap, "invoked", + map->applied - map->applied_clear); + json_object_boolean_add(json_rmap, "disabledOptimization", + map->optimization_disabled); + json_object_boolean_add(json_rmap, "processedChange", + map->to_be_processed); + json_object_object_add(json_rmap, "rules", json_rules); + } else { + vty_out(vty, + "route-map: %s Invoked: %" PRIu64 + " Optimization: %s Processed Change: %s\n", + map->name, map->applied - map->applied_clear, + map->optimization_disabled ? "disabled" : "enabled", + map->to_be_processed ? "true" : "false"); + } for (index = map->head; index; index = index->next) { - vty_out(vty, " %s, sequence %d Invoked %" PRIu64 "\n", - route_map_type_str(index->type), index->pref, - index->applied - index->applied_clear); - - /* Description */ - if (index->description) - vty_out(vty, " Description:\n %s\n", - index->description); - - /* Match clauses */ - vty_out(vty, " Match clauses:\n"); - for (rule = index->match_list.head; rule; rule = rule->next) - vty_out(vty, " %s %s\n", rule->cmd->str, - rule->rule_str); - - vty_out(vty, " Set clauses:\n"); - for (rule = index->set_list.head; rule; rule = rule->next) - vty_out(vty, " %s %s\n", rule->cmd->str, - rule->rule_str); - - /* Call clause */ - vty_out(vty, " Call clause:\n"); - if (index->nextrm) - vty_out(vty, " Call %s\n", index->nextrm); - - /* Exit Policy */ - vty_out(vty, " Action:\n"); - if (index->exitpolicy == RMAP_GOTO) - vty_out(vty, " Goto %d\n", index->nextpref); - else if (index->exitpolicy == RMAP_NEXT) - vty_out(vty, " Continue to next entry\n"); - else if (index->exitpolicy == RMAP_EXIT) - vty_out(vty, " Exit routemap\n"); + if (json) { + json_object *json_rule; + json_object *json_matches; + json_object *json_sets; + char action[BUFSIZ] = {}; + + json_rule = json_object_new_object(); + json_object_array_add(json_rules, json_rule); + + json_object_int_add(json_rule, "sequenceNumber", + index->pref); + json_object_string_add(json_rule, "type", + route_map_type_str(index->type)); + json_object_int_add(json_rule, "invoked", + index->applied + - index->applied_clear); + + /* Description */ + if (index->description) + json_object_string_add(json_rule, "description", + index->description); + + /* Match clauses */ + json_matches = json_object_new_array(); + json_object_object_add(json_rule, "matchClauses", + json_matches); + for (rule = index->match_list.head; rule; + rule = rule->next) { + char buf[BUFSIZ]; + + snprintf(buf, sizeof(buf), "%s %s", + rule->cmd->str, rule->rule_str); + json_array_string_add(json_matches, buf); + } + + /* Set clauses */ + json_sets = json_object_new_array(); + json_object_object_add(json_rule, "setClauses", + json_sets); + for (rule = index->set_list.head; rule; + rule = rule->next) { + char buf[BUFSIZ]; + + snprintf(buf, sizeof(buf), "%s %s", + rule->cmd->str, rule->rule_str); + json_array_string_add(json_sets, buf); + } + + /* Call clause */ + if (index->nextrm) + json_object_string_add(json_rule, "callClause", + index->nextrm); + + /* Exit Policy */ + if (index->exitpolicy == RMAP_GOTO) + snprintf(action, sizeof(action), "Goto %d", + index->nextpref); + else if (index->exitpolicy == RMAP_NEXT) + snprintf(action, sizeof(action), + "Continue to next entry"); + else if (index->exitpolicy == RMAP_EXIT) + snprintf(action, sizeof(action), + "Exit routemap"); + if (action[0] != '\0') + json_object_string_add(json_rule, "action", + action); + } else { + vty_out(vty, " %s, sequence %d Invoked %" PRIu64 "\n", + route_map_type_str(index->type), index->pref, + index->applied - index->applied_clear); + + /* Description */ + if (index->description) + vty_out(vty, " Description:\n %s\n", + index->description); + + /* Match clauses */ + vty_out(vty, " Match clauses:\n"); + for (rule = index->match_list.head; rule; + rule = rule->next) + vty_out(vty, " %s %s\n", rule->cmd->str, + rule->rule_str); + + /* Set clauses */ + vty_out(vty, " Set clauses:\n"); + for (rule = index->set_list.head; rule; + rule = rule->next) + vty_out(vty, " %s %s\n", rule->cmd->str, + rule->rule_str); + + /* Call clause */ + vty_out(vty, " Call clause:\n"); + if (index->nextrm) + vty_out(vty, " Call %s\n", index->nextrm); + + /* Exit Policy */ + vty_out(vty, " Action:\n"); + if (index->exitpolicy == RMAP_GOTO) + vty_out(vty, " Goto %d\n", index->nextpref); + else if (index->exitpolicy == RMAP_NEXT) + vty_out(vty, " Continue to next entry\n"); + else if (index->exitpolicy == RMAP_EXIT) + vty_out(vty, " Exit routemap\n"); + } } } @@ -895,22 +987,28 @@ static int sort_route_map(const void **map1, const void **map2) return strcmp(m1->name, m2->name); } -static int vty_show_route_map(struct vty *vty, const char *name) +static int vty_show_route_map(struct vty *vty, const char *name, bool use_json) { struct route_map *map; + json_object *json = NULL; + json_object *json_proto = NULL; - vty_out(vty, "%s:\n", frr_protonameinst); + if (use_json) { + json = json_object_new_object(); + json_proto = json_object_new_object(); + json_object_object_add(json, frr_protonameinst, json_proto); + } else + vty_out(vty, "%s:\n", frr_protonameinst); if (name) { map = route_map_lookup_by_name(name); if (map) { - vty_show_route_map_entry(vty, map); + vty_show_route_map_entry(vty, map, json_proto); return CMD_SUCCESS; - } else { + } else if (!use_json) { vty_out(vty, "%s: 'route-map %s' not found\n", frr_protonameinst, name); - return CMD_SUCCESS; } } else { @@ -923,10 +1021,18 @@ static int vty_show_route_map(struct vty *vty, const char *name) list_sort(maplist, sort_route_map); for (ALL_LIST_ELEMENTS_RO(maplist, ln, map)) - vty_show_route_map_entry(vty, map); + vty_show_route_map_entry(vty, map, json_proto); list_delete(&maplist); } + + if (use_json) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + return CMD_SUCCESS; } @@ -950,7 +1056,7 @@ static int vty_show_unused_route_map(struct vty *vty) list_sort(maplist, sort_route_map); for (ALL_LIST_ELEMENTS_RO(maplist, ln, map)) - vty_show_route_map_entry(vty, map); + vty_show_route_map_entry(vty, map, NULL); } else { vty_out(vty, "\n%s: None\n", frr_protonameinst); } @@ -2957,14 +3063,20 @@ DEFUN (rmap_clear_counters, DEFUN (rmap_show_name, rmap_show_name_cmd, - "show route-map [WORD]", + "show route-map [WORD] [json]", SHOW_STR "route-map information\n" - "route-map name\n") + "route-map name\n" + JSON_STR) { - int idx_word = 2; - const char *name = (argc == 3) ? argv[idx_word]->arg : NULL; - return vty_show_route_map(vty, name); + bool uj = use_json(argc, argv); + int idx = 0; + const char *name = NULL; + + if (argv_find(argv, argc, "WORD", &idx)) + name = argv[idx]->arg; + + return vty_show_route_map(vty, name, uj); } DEFUN (rmap_show_unused, diff --git a/lib/routemap.h b/lib/routemap.h index 4a40ec08b9..8af3b2c3c0 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -270,6 +270,7 @@ DECLARE_QOBJ_TYPE(route_map); /* BGP route-map match conditions */ #define IS_MATCH_LOCAL_PREF(C) \ (strmatch(C, "frr-bgp-route-map:match-local-preference")) +#define IS_MATCH_ALIAS(C) (strmatch(C, "frr-bgp-route-map:match-alias")) #define IS_MATCH_ORIGIN(C) \ (strmatch(C, "frr-bgp-route-map:match-origin")) #define IS_MATCH_RPKI(C) (strmatch(C, "frr-bgp-route-map:rpki")) diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c index bf982cfa2b..ec9033b3aa 100644 --- a/lib/routemap_cli.c +++ b/lib/routemap_cli.c @@ -634,6 +634,11 @@ void route_map_condition_show(struct vty *vty, struct lyd_node *dnode, yang_dnode_get_string( dnode, "./rmap-match-condition/frr-bgp-route-map:local-preference")); + } else if (IS_MATCH_ALIAS(condition)) { + vty_out(vty, " match alias %s\n", + yang_dnode_get_string( + dnode, + "./rmap-match-condition/frr-bgp-route-map:alias")); } else if (IS_MATCH_ORIGIN(condition)) { vty_out(vty, " match origin %s\n", yang_dnode_get_string( diff --git a/lib/sockopt.c b/lib/sockopt.c index 98bfda5079..150736e00c 100644 --- a/lib/sockopt.c +++ b/lib/sockopt.c @@ -379,14 +379,14 @@ static int setsockopt_ipv4_ifindex(int sock, ifindex_t val) int ret; #if defined(IP_PKTINFO) - if ((ret = setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &val, sizeof(val))) - < 0) + ret = setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &val, sizeof(val)); + if (ret < 0) flog_err(EC_LIB_SOCKET, "Can't set IP_PKTINFO option for fd %d to %d: %s", sock, val, safe_strerror(errno)); #elif defined(IP_RECVIF) - if ((ret = setsockopt(sock, IPPROTO_IP, IP_RECVIF, &val, sizeof(val))) - < 0) + ret = setsockopt(sock, IPPROTO_IP, IP_RECVIF, &val, sizeof(val)); + if (ret < 0) flog_err(EC_LIB_SOCKET, "Can't set IP_RECVIF option for fd %d to %d: %s", sock, val, safe_strerror(errno)); @@ -639,12 +639,8 @@ int sockopt_tcp_signature_ext(int sock, union sockunion *su, uint16_t prefixlen, #endif /* GNU_LINUX */ - if ((ret = setsockopt(sock, IPPROTO_TCP, optname, &md5sig, - sizeof(md5sig))) - < 0) { - /* ENOENT is harmless. It is returned when we clear a password - for which - one was not previously set. */ + ret = setsockopt(sock, IPPROTO_TCP, optname, &md5sig, sizeof(md5sig)); + if (ret < 0) { if (ENOENT == errno) ret = 0; else diff --git a/lib/sockunion.c b/lib/sockunion.c index e6340a1743..c7af458e9e 100644 --- a/lib/sockunion.c +++ b/lib/sockunion.c @@ -606,8 +606,7 @@ static void __attribute__((unused)) sockunion_print(const union sockunion *su) } } -static int in6addr_cmp(const struct in6_addr *addr1, - const struct in6_addr *addr2) +int in6addr_cmp(const struct in6_addr *addr1, const struct in6_addr *addr2) { unsigned int i; const uint8_t *p1, *p2; diff --git a/lib/sockunion.h b/lib/sockunion.h index 2cc80bb70f..9e6719ccf9 100644 --- a/lib/sockunion.h +++ b/lib/sockunion.h @@ -73,6 +73,7 @@ enum connect_result { connect_error, connect_success, connect_in_progress }; /* Prototypes. */ extern int str2sockunion(const char *, union sockunion *); extern const char *sockunion2str(const union sockunion *, char *, size_t); +int in6addr_cmp(const struct in6_addr *addr1, const struct in6_addr *addr2); extern int sockunion_cmp(const union sockunion *, const union sockunion *); extern int sockunion_same(const union sockunion *, const union sockunion *); extern unsigned int sockunion_hash(const union sockunion *); diff --git a/lib/stream.c b/lib/stream.c index 904ee73b10..1557500c60 100644 --- a/lib/stream.c +++ b/lib/stream.c @@ -1097,7 +1097,8 @@ ssize_t stream_read_try(struct stream *s, int fd, size_t size) return -1; } - if ((nbytes = read(fd, s->data + s->endp, size)) >= 0) { + nbytes = read(fd, s->data + s->endp, size); + if (nbytes >= 0) { s->endp += nbytes; return nbytes; } @@ -1126,9 +1127,8 @@ ssize_t stream_recvfrom(struct stream *s, int fd, size_t size, int flags, return -1; } - if ((nbytes = recvfrom(fd, s->data + s->endp, size, flags, from, - fromlen)) - >= 0) { + nbytes = recvfrom(fd, s->data + s->endp, size, flags, from, fromlen); + if (nbytes >= 0) { s->endp += nbytes; return nbytes; } diff --git a/lib/subdir.am b/lib/subdir.am index 90301d800a..714af43238 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -2,7 +2,7 @@ # libfrr # lib_LTLIBRARIES += lib/libfrr.la -lib_libfrr_la_LDFLAGS = -version-info 0:0:0 -Xlinker -e_libfrr_version +lib_libfrr_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0 -Xlinker -e_libfrr_version lib_libfrr_la_LIBADD = $(LIBCAP) $(UNWIND_LIBS) $(LIBYANG_LIBS) $(LUA_LIB) $(UST_LIBS) $(LIBM) lib_libfrr_la_SOURCES = \ @@ -322,7 +322,7 @@ lib_LTLIBRARIES += lib/libfrrsnmp.la endif lib_libfrrsnmp_la_CFLAGS = $(AM_CFLAGS) $(SNMP_CFLAGS) -std=gnu11 -lib_libfrrsnmp_la_LDFLAGS = -version-info 0:0:0 +lib_libfrrsnmp_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0 lib_libfrrsnmp_la_LIBADD = $(SNMP_LIBS) lib_libfrrsnmp_la_SOURCES = \ lib/agentx.c \ @@ -338,7 +338,7 @@ pkginclude_HEADERS += lib/resolver.h endif lib_libfrrcares_la_CFLAGS = $(AM_CFLAGS) $(CARES_CFLAGS) -lib_libfrrcares_la_LDFLAGS = -version-info 0:0:0 +lib_libfrrcares_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0 lib_libfrrcares_la_LIBADD = $(CARES_LIBS) lib_libfrrcares_la_SOURCES = \ lib/resolver.c \ @@ -353,7 +353,7 @@ pkginclude_HEADERS += lib/frr_zmq.h endif lib_libfrrzmq_la_CFLAGS = $(AM_CFLAGS) $(ZEROMQ_CFLAGS) -lib_libfrrzmq_la_LDFLAGS = -version-info 0:0:0 +lib_libfrrzmq_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0 lib_libfrrzmq_la_LIBADD = $(ZEROMQ_LIBS) lib_libfrrzmq_la_SOURCES = \ lib/frr_zmq.c \ @@ -367,7 +367,7 @@ module_LTLIBRARIES += lib/confd.la endif lib_confd_la_CFLAGS = $(AM_CFLAGS) $(CONFD_CFLAGS) -lib_confd_la_LDFLAGS = -avoid-version -module -shared -export-dynamic +lib_confd_la_LDFLAGS = $(MODULE_LDFLAGS) lib_confd_la_LIBADD = lib/libfrr.la $(CONFD_LIBS) lib_confd_la_SOURCES = lib/northbound_confd.c @@ -379,7 +379,7 @@ module_LTLIBRARIES += lib/sysrepo.la endif lib_sysrepo_la_CFLAGS = $(AM_CFLAGS) $(SYSREPO_CFLAGS) -lib_sysrepo_la_LDFLAGS = -avoid-version -module -shared -export-dynamic +lib_sysrepo_la_LDFLAGS = $(MODULE_LDFLAGS) lib_sysrepo_la_LIBADD = lib/libfrr.la $(SYSREPO_LIBS) lib_sysrepo_la_SOURCES = lib/northbound_sysrepo.c @@ -391,7 +391,7 @@ module_LTLIBRARIES += lib/grpc.la endif lib_grpc_la_CXXFLAGS = $(WERROR) $(GRPC_CFLAGS) -lib_grpc_la_LDFLAGS = -avoid-version -module -shared -export-dynamic +lib_grpc_la_LDFLAGS = $(MODULE_LDFLAGS) lib_grpc_la_LIBADD = lib/libfrr.la grpc/libfrrgrpc_pb.la $(GRPC_LIBS) lib_grpc_la_SOURCES = lib/northbound_grpc.cpp @@ -419,7 +419,8 @@ lib_grammar_sandbox_LDADD = \ lib_clippy_CPPFLAGS = $(CPPFLAGS_BASE) -D_GNU_SOURCE -DBUILDING_CLIPPY lib_clippy_CFLAGS = $(AC_CFLAGS) $(PYTHON_CFLAGS) lib_clippy_LDADD = $(PYTHON_LIBS) $(UST_LIBS) -lelf -lib_clippy_LDFLAGS = -export-dynamic +# no $(SAN_FLAGS) here +lib_clippy_LDFLAGS = -export-dynamic $(AC_LDFLAGS) $(AC_LDFLAGS_EXEC) lib_clippy_SOURCES = \ lib/jhash.c \ lib/clippy.c \ diff --git a/lib/systemd.c b/lib/systemd.c index c5cc3aa447..2238dc9f3d 100644 --- a/lib/systemd.c +++ b/lib/systemd.c @@ -20,68 +20,56 @@ */ #include <zebra.h> +#include <sys/un.h> #include "thread.h" #include "systemd.h" +#include "lib_errors.h" -#if defined HAVE_SYSTEMD -#include <systemd/sd-daemon.h> -#endif +/* these are cleared from env so they don't "leak" into things we fork(), + * particularly for watchfrr starting individual daemons + * + * watchdog_pid is currently not used since watchfrr starts forking. + * (TODO: handle that better, somehow?) + */ +static pid_t watchdog_pid = -1; +static intmax_t watchdog_msec; + +/* not used yet, but can trigger auto-switch to journald logging */ +bool sd_stdout_is_journal; +bool sd_stderr_is_journal; + +static char *notify_socket; -/* - * Wrapper this silliness if we - * don't have systemd +/* talk to whatever entity claims to be systemd ;) + * + * refer to sd_notify docs for messages systemd accepts over this socket. + * This function should be functionally equivalent to sd_notify(). */ static void systemd_send_information(const char *info) { -#if defined HAVE_SYSTEMD - sd_notify(0, info); -#else - return; -#endif -} + int sock; + struct sockaddr_un sun; -/* - * A return of 0 means that we are not watchdoged - */ -static int systemd_get_watchdog_time(int the_process) -{ -#if defined HAVE_SYSTEMD - uint64_t usec; - char *watchdog = NULL; - int ret; - - ret = sd_watchdog_enabled(0, &usec); - - /* - * If return is 0 -> we don't want watchdog - * if return is < 0, some sort of failure occurred - */ - if (ret < 0) - return 0; - - /* - * systemd can return that this process - * is not the expected sender of the watchdog timer - * If we set the_process = 0 then we expect to - * be able to send the watchdog to systemd - * irrelevant of the pid of this process. - */ - if (ret == 0 && the_process) - return 0; - - if (ret == 0 && !the_process) { - watchdog = getenv("WATCHDOG_USEC"); - if (!watchdog) - return 0; - - usec = atol(watchdog); - } + if (!notify_socket) + return; + + sock = socket(AF_UNIX, SOCK_DGRAM, 0); + if (sock < 0) + return; + + sun.sun_family = AF_UNIX; + strlcpy(sun.sun_path, notify_socket, sizeof(sun.sun_path)); - return (usec / 1000000) / 3; -#else - return 0; -#endif + /* linux abstract unix socket namespace */ + if (sun.sun_path[0] == '@') + sun.sun_path[0] = '\0'; + + /* nothing we can do if this errors out... */ + (void)sendto(sock, info, strlen(info), 0, (struct sockaddr *)&sun, + sizeof(sun)); + + close(sock); } void systemd_send_stopping(void) @@ -90,34 +78,27 @@ void systemd_send_stopping(void) systemd_send_information("STOPPING=1"); } -/* - * How many seconds should we wait between watchdog sends - */ -static int wsecs = 0; static struct thread_master *systemd_master = NULL; static int systemd_send_watchdog(struct thread *t) { systemd_send_information("WATCHDOG=1"); - thread_add_timer(systemd_master, systemd_send_watchdog, NULL, wsecs, - NULL); - + assert(watchdog_msec > 0); + thread_add_timer_msec(systemd_master, systemd_send_watchdog, NULL, + watchdog_msec, NULL); return 1; } -void systemd_send_started(struct thread_master *m, int the_process) +void systemd_send_started(struct thread_master *m) { assert(m != NULL); - wsecs = systemd_get_watchdog_time(the_process); systemd_master = m; systemd_send_information("READY=1"); - if (wsecs != 0) { - systemd_send_information("WATCHDOG=1"); - thread_add_timer(m, systemd_send_watchdog, m, wsecs, NULL); - } + if (watchdog_msec > 0) + systemd_send_watchdog(NULL); } void systemd_send_status(const char *status) @@ -127,3 +108,65 @@ void systemd_send_status(const char *status) snprintf(buffer, sizeof(buffer), "STATUS=%s", status); systemd_send_information(buffer); } + +static intmax_t getenv_int(const char *varname, intmax_t dflt) +{ + char *val, *err; + intmax_t intval; + + val = getenv(varname); + if (!val) + return dflt; + + intval = strtoimax(val, &err, 0); + if (*err || !*val) + return dflt; + return intval; +} + +void systemd_init_env(void) +{ + char *tmp; + uintmax_t dev, ino; + int len; + struct stat st; + + notify_socket = getenv("NOTIFY_SOCKET"); + + /* no point in setting up watchdog w/o notify socket */ + if (notify_socket) { + intmax_t watchdog_usec; + + watchdog_pid = getenv_int("WATCHDOG_PID", -1); + if (watchdog_pid <= 0) + watchdog_pid = -1; + + /* note this is the deadline, hence the divide by 3 */ + watchdog_usec = getenv_int("WATCHDOG_USEC", 0); + if (watchdog_usec >= 3000) + watchdog_msec = watchdog_usec / 3000; + else { + if (watchdog_usec != 0) + flog_err( + EC_LIB_UNAVAILABLE, + "systemd expects a %jd microsecond watchdog timer, but FRR only supports millisecond resolution!", + watchdog_usec); + watchdog_msec = 0; + } + } + + tmp = getenv("JOURNAL_STREAM"); + if (tmp && sscanf(tmp, "%ju:%ju%n", &dev, &ino, &len) == 2 + && (size_t)len == strlen(tmp)) { + if (fstat(1, &st) == 0 && st.st_dev == (dev_t)dev + && st.st_ino == (ino_t)ino) + sd_stdout_is_journal = true; + if (fstat(2, &st) == 0 && st.st_dev == (dev_t)dev + && st.st_ino == (ino_t)ino) + sd_stderr_is_journal = true; + } + + /* these should *not* be passed to any other process we start */ + unsetenv("WATCHDOG_PID"); + unsetenv("WATCHDOG_USEC"); +} diff --git a/lib/systemd.h b/lib/systemd.h index d9885c5d9c..1933f4f688 100644 --- a/lib/systemd.h +++ b/lib/systemd.h @@ -28,9 +28,6 @@ extern "C" { * * Design point is that if systemd is not being used on this system * then these functions becomes a no-op. - * - * To turn on systemd compilation, use --enable-systemd on - * configure run. */ void systemd_send_stopping(void); @@ -39,13 +36,18 @@ void systemd_send_stopping(void); * the_process - Should we send watchdog if we are not the requested * process? */ -void systemd_send_started(struct thread_master *master, int the_process); +void systemd_send_started(struct thread_master *master); /* * status - A status string to send to systemd */ void systemd_send_status(const char *status); +/* + * grab startup state from env vars + */ +void systemd_init_env(void); + #ifdef __cplusplus } #endif diff --git a/lib/table.c b/lib/table.c index dfd92c6189..e6030ca4ca 100644 --- a/lib/table.c +++ b/lib/table.c @@ -230,7 +230,7 @@ struct route_node *route_node_match_ipv4(struct route_table *table, memset(&p, 0, sizeof(struct prefix_ipv4)); p.family = AF_INET; - p.prefixlen = IPV4_MAX_PREFIXLEN; + p.prefixlen = IPV4_MAX_BITLEN; p.prefix = *addr; return route_node_match(table, (struct prefix *)&p); @@ -243,7 +243,7 @@ struct route_node *route_node_match_ipv6(struct route_table *table, memset(&p, 0, sizeof(struct prefix_ipv6)); p.family = AF_INET6; - p.prefixlen = IPV6_MAX_PREFIXLEN; + p.prefixlen = IPV6_MAX_BITLEN; p.prefix = *addr; return route_node_match(table, &p); @@ -287,6 +287,7 @@ void vrf_delete(struct vrf *vrf) RB_REMOVE(vrf_id_head, &vrfs_by_id, vrf); vrf->vrf_id = VRF_UNKNOWN; } + vrf->ns_ctxt = NULL; return; } diff --git a/lib/zclient.c b/lib/zclient.c index ffae1332af..0815e77d4e 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -446,7 +446,7 @@ enum zclient_send_status zclient_send_localsid(struct zclient *zclient, struct nexthop nh = {}; p.family = AF_INET6; - p.prefixlen = 128; + p.prefixlen = IPV6_MAX_BITLEN; p.prefix = *sid; api.vrf_id = VRF_DEFAULT; @@ -1432,7 +1432,7 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) STREAM_GETC(s, api->prefix.prefixlen); switch (api->prefix.family) { case AF_INET: - if (api->prefix.prefixlen > IPV4_MAX_PREFIXLEN) { + if (api->prefix.prefixlen > IPV4_MAX_BITLEN) { flog_err( EC_LIB_ZAPI_ENCODE, "%s: V4 prefixlen is %d which should not be more than 32", @@ -1441,7 +1441,7 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) } break; case AF_INET6: - if (api->prefix.prefixlen > IPV6_MAX_PREFIXLEN) { + if (api->prefix.prefixlen > IPV6_MAX_BITLEN) { flog_err( EC_LIB_ZAPI_ENCODE, "%s: v6 prefixlen is %d which should not be more than 128", @@ -1460,7 +1460,7 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) if (CHECK_FLAG(api->message, ZAPI_MESSAGE_SRCPFX)) { api->src_prefix.family = AF_INET6; STREAM_GETC(s, api->src_prefix.prefixlen); - if (api->src_prefix.prefixlen > IPV6_MAX_PREFIXLEN) { + if (api->src_prefix.prefixlen > IPV6_MAX_BITLEN) { flog_err( EC_LIB_ZAPI_ENCODE, "%s: SRC Prefix prefixlen received: %d is too large", @@ -1537,7 +1537,7 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) if (CHECK_FLAG(api->message, ZAPI_MESSAGE_OPAQUE)) { STREAM_GETW(s, api->opaque.length); - assert(api->opaque.length < ZAPI_MESSAGE_OPAQUE_LENGTH); + assert(api->opaque.length <= ZAPI_MESSAGE_OPAQUE_LENGTH); STREAM_GET(api->opaque.data, s, api->opaque.length); } @@ -3812,7 +3812,8 @@ static int zclient_read(struct thread *thread) zclient->t_read = NULL; /* Read zebra header (if we don't have it already). */ - if ((already = stream_get_endp(zclient->ibuf)) < ZEBRA_HEADER_SIZE) { + already = stream_get_endp(zclient->ibuf); + if (already < ZEBRA_HEADER_SIZE) { ssize_t nbyte; if (((nbyte = stream_read_try(zclient->ibuf, zclient->sock, ZEBRA_HEADER_SIZE - already)) @@ -3825,7 +3826,6 @@ static int zclient_read(struct thread *thread) return zclient_failed(zclient); } if (nbyte != (ssize_t)(ZEBRA_HEADER_SIZE - already)) { - /* Try again later. */ zclient_event(ZCLIENT_READ, zclient); return 0; } |
