diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/agentx.c | 44 | ||||
| -rw-r--r-- | lib/bfd.c | 22 | ||||
| -rw-r--r-- | lib/buffer.c | 10 | ||||
| -rw-r--r-- | lib/clippy.c | 3 | ||||
| -rw-r--r-- | lib/command.c | 79 | ||||
| -rw-r--r-- | lib/command.h | 42 | ||||
| -rw-r--r-- | lib/command_graph.h | 2 | ||||
| -rw-r--r-- | lib/command_parse.y | 2 | ||||
| -rw-r--r-- | lib/compiler.h | 23 | ||||
| -rw-r--r-- | lib/ferr.h | 2 | ||||
| -rw-r--r-- | lib/filter_cli.c | 24 | ||||
| -rw-r--r-- | lib/frr_zmq.c | 58 | ||||
| -rw-r--r-- | lib/frr_zmq.h | 42 | ||||
| -rw-r--r-- | lib/frrlua.c | 387 | ||||
| -rw-r--r-- | lib/frrlua.h | 216 | ||||
| -rw-r--r-- | lib/frrscript.c | 272 | ||||
| -rw-r--r-- | lib/frrscript.h | 138 | ||||
| -rw-r--r-- | lib/hash.c | 8 | ||||
| -rw-r--r-- | lib/hash.h | 2 | ||||
| -rw-r--r-- | lib/if.c | 176 | ||||
| -rw-r--r-- | lib/if.h | 2 | ||||
| -rw-r--r-- | lib/lib_vty.c | 6 | ||||
| -rw-r--r-- | lib/libfrr.c | 39 | ||||
| -rw-r--r-- | lib/libfrr.h | 5 | ||||
| -rw-r--r-- | lib/link_state.c | 1282 | ||||
| -rw-r--r-- | lib/link_state.h | 780 | ||||
| -rw-r--r-- | lib/log.c | 13 | ||||
| -rw-r--r-- | lib/log.h | 10 | ||||
| -rw-r--r-- | lib/module.c | 2 | ||||
| -rw-r--r-- | lib/module.h | 8 | ||||
| -rw-r--r-- | lib/network.c | 18 | ||||
| -rw-r--r-- | lib/network.h | 18 | ||||
| -rw-r--r-- | lib/northbound.h | 5 | ||||
| -rw-r--r-- | lib/northbound_cli.c | 26 | ||||
| -rw-r--r-- | lib/printf/printf-pos.c | 4 | ||||
| -rw-r--r-- | lib/printf/vfprintf.c | 6 | ||||
| -rw-r--r-- | lib/privs.c | 6 | ||||
| -rw-r--r-- | lib/privs.h | 2 | ||||
| -rw-r--r-- | lib/resolver.c | 3 | ||||
| -rwxr-xr-x | lib/route_types.pl | 2 | ||||
| -rw-r--r-- | lib/routemap.c | 58 | ||||
| -rw-r--r-- | lib/sigevent.c | 2 | ||||
| -rw-r--r-- | lib/smux.h | 53 | ||||
| -rw-r--r-- | lib/snmp.c | 54 | ||||
| -rw-r--r-- | lib/sockunion.c | 17 | ||||
| -rw-r--r-- | lib/sockunion.h | 1 | ||||
| -rw-r--r-- | lib/stream.c | 4 | ||||
| -rw-r--r-- | lib/subdir.am | 7 | ||||
| -rw-r--r-- | lib/thread.c | 137 | ||||
| -rw-r--r-- | lib/thread.h | 116 | ||||
| -rw-r--r-- | lib/vrf.c | 54 | ||||
| -rw-r--r-- | lib/vrf.h | 15 | ||||
| -rw-r--r-- | lib/vty.c | 125 | ||||
| -rw-r--r-- | lib/wheel.c | 3 | ||||
| -rw-r--r-- | lib/workqueue.c | 5 | ||||
| -rw-r--r-- | lib/xref.c | 130 | ||||
| -rw-r--r-- | lib/xref.h | 272 | ||||
| -rw-r--r-- | lib/yang.c | 1 | ||||
| -rw-r--r-- | lib/zclient.c | 71 | ||||
| -rw-r--r-- | lib/zclient.h | 64 | ||||
| -rw-r--r-- | lib/zlog.c | 22 | ||||
| -rw-r--r-- | lib/zlog.h | 79 |
62 files changed, 4417 insertions, 662 deletions
diff --git a/lib/agentx.c b/lib/agentx.c index 603d8d6172..5351f8bda2 100644 --- a/lib/agentx.c +++ b/lib/agentx.c @@ -32,6 +32,9 @@ #include "linklist.h" #include "version.h" #include "lib_errors.h" +#include "xref.h" + +XREF_SETUP() static int agentx_enabled = 0; @@ -262,11 +265,28 @@ void smux_register_mib(const char *descr, struct variable *var, size_t width, register_mib(descr, var, width, num, name, namelen); } -int smux_trap(struct variable *vp, size_t vp_len, const oid *ename, - size_t enamelen, const oid *name, size_t namelen, - const oid *iname, size_t inamelen, - const struct trap_object *trapobj, size_t trapobjlen, - uint8_t sptrap) +void smux_trap(struct variable *vp, size_t vp_len, const oid *ename, + size_t enamelen, const oid *name, size_t namelen, + const oid *iname, size_t inamelen, + const struct trap_object *trapobj, size_t trapobjlen, + uint8_t sptrap) +{ + struct index_oid trap_index[1]; + + /* copy the single index into the multi-index format */ + oid_copy(trap_index[0].indexname, iname, inamelen); + trap_index[0].indexlen = inamelen; + + smux_trap_multi_index(vp, vp_len, ename, enamelen, name, namelen, + trap_index, array_size(trap_index), trapobj, + trapobjlen, sptrap); +} + +int smux_trap_multi_index(struct variable *vp, size_t vp_len, const oid *ename, + size_t enamelen, const oid *name, size_t namelen, + struct index_oid *iname, size_t index_len, + const struct trap_object *trapobj, size_t trapobjlen, + uint8_t sptrap) { oid objid_snmptrap[] = {1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0}; size_t objid_snmptrap_len = sizeof(objid_snmptrap) / sizeof(oid); @@ -296,6 +316,13 @@ int smux_trap(struct variable *vp, size_t vp_len, const oid *ename, size_t val_len; WriteMethod *wm = NULL; struct variable cvp; + unsigned int iindex; + /* + * this allows the behaviour of smux_trap with a singe index + * for all objects to be maintained whilst allowing traps which + * have different indices per object to be supported + */ + iindex = (index_len == 1) ? 0 : i; /* Make OID. */ if (trapobj[i].namelen > 0) { @@ -303,8 +330,10 @@ int smux_trap(struct variable *vp, size_t vp_len, const oid *ename, onamelen = trapobj[i].namelen; oid_copy(oid, name, namelen); oid_copy(oid + namelen, trapobj[i].name, onamelen); - oid_copy(oid + namelen + onamelen, iname, inamelen); - oid_len = namelen + onamelen + inamelen; + oid_copy(oid + namelen + onamelen, + iname[iindex].indexname, + iname[iindex].indexlen); + oid_len = namelen + onamelen + iname[iindex].indexlen; } else { /* Scalar object */ onamelen = trapobj[i].namelen * (-1); @@ -330,6 +359,7 @@ int smux_trap(struct variable *vp, size_t vp_len, const oid *ename, cvp.magic = vp[j].magic; cvp.acl = vp[j].acl; cvp.findVar = vp[j].findVar; + /* Grab the result. */ val = cvp.findVar(&cvp, oid, &oid_len, 1, &val_len, &wm); @@ -224,6 +224,17 @@ struct interface *bfd_get_peer_info(struct stream *s, struct prefix *dp, int plen; int local_remote_cbit; + /* + * If the ifindex lookup fails the + * rest of the data in the stream is + * not read. All examples of this function + * call immediately use the dp->family which + * is not good. Ensure we are not using + * random data + */ + memset(dp, 0, sizeof(*dp)); + memset(sp, 0, sizeof(*sp)); + /* Get interface index. */ ifindex = stream_getl(s); @@ -249,13 +260,12 @@ struct interface *bfd_get_peer_info(struct stream *s, struct prefix *dp, /* Get BFD status. */ *status = stream_getl(s); - if (sp) { - sp->family = stream_getc(s); + sp->family = stream_getc(s); + + plen = prefix_blen(sp); + stream_get(&sp->u.prefix, s, plen); + sp->prefixlen = stream_getc(s); - plen = prefix_blen(sp); - stream_get(&sp->u.prefix, s, plen); - sp->prefixlen = stream_getc(s); - } local_remote_cbit = stream_getc(s); if (remote_cbit) *remote_cbit = local_remote_cbit; diff --git a/lib/buffer.c b/lib/buffer.c index 459d98e75d..42796faae8 100644 --- a/lib/buffer.c +++ b/lib/buffer.c @@ -468,16 +468,6 @@ buffer_status_t buffer_write(struct buffer *b, int fd, const void *p, { ssize_t nbytes; -#if 0 - /* - * Should we attempt to drain any previously buffered data? - * This could help reduce latency in pushing out the data if - * we are stuck in a long-running thread that is preventing - * the main select loop from calling the flush thread... - */ - if (b->head && (buffer_flush_available(b, fd) == BUFFER_ERROR)) - return BUFFER_ERROR; -#endif if (b->head) /* Buffer is not empty, so do not attempt to write the new data. */ diff --git a/lib/clippy.c b/lib/clippy.c index 2e09c24c66..15cd9d7a4b 100644 --- a/lib/clippy.c +++ b/lib/clippy.c @@ -107,7 +107,8 @@ int main(int argc, char **argv) #include "log.h" #include "zassert.h" -void vzlog(int prio, const char *format, va_list args) +void vzlogx(const struct xref_logmsg *xref, int prio, + const char *format, va_list args) { vfprintf(stderr, format, args); fputs("\n", stderr); diff --git a/lib/command.c b/lib/command.c index 87110157f6..6a4d504b2f 100644 --- a/lib/command.c +++ b/lib/command.c @@ -49,6 +49,8 @@ #include "northbound_cli.h" #include "network.h" +#include "frrscript.h" + DEFINE_MTYPE_STATIC(LIB, HOST, "Host config") DEFINE_MTYPE(LIB, COMPLETION, "Completion item") @@ -275,7 +277,7 @@ const char *cmd_prompt(enum node_type node) } /* Install a command into a node. */ -void install_element(enum node_type ntype, const struct cmd_element *cmd) +void _install_element(enum node_type ntype, const struct cmd_element *cmd) { struct cmd_node *cnode; @@ -321,7 +323,7 @@ void install_element(enum node_type ntype, const struct cmd_element *cmd) vector_set(cnode->cmd_vector, (void *)cmd); if (ntype == VIEW_NODE) - install_element(ENABLE_NODE, cmd); + _install_element(ENABLE_NODE, cmd); } void uninstall_element(enum node_type ntype, const struct cmd_element *cmd) @@ -863,6 +865,30 @@ enum node_type node_parent(enum node_type node) case BFD_PROFILE_NODE: ret = BFD_NODE; break; + case SR_TRAFFIC_ENG_NODE: + ret = SEGMENT_ROUTING_NODE; + break; + case SR_SEGMENT_LIST_NODE: + ret = SR_TRAFFIC_ENG_NODE; + break; + case SR_POLICY_NODE: + ret = SR_TRAFFIC_ENG_NODE; + break; + case SR_CANDIDATE_DYN_NODE: + ret = SR_POLICY_NODE; + break; + case PCEP_NODE: + ret = SR_TRAFFIC_ENG_NODE; + break; + case PCEP_PCE_CONFIG_NODE: + ret = PCEP_NODE; + break; + case PCEP_PCE_NODE: + ret = PCEP_NODE; + break; + case PCEP_PCC_NODE: + ret = PCEP_NODE; + break; default: ret = CONFIG_NODE; break; @@ -2279,6 +2305,31 @@ done: return CMD_SUCCESS; } +#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") +{ + struct prefix p; + + (void)str2prefix("1.2.3.4/24", &p); + + 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); + } + + return CMD_SUCCESS; +} +#endif + /* Set config filename. Called from vty.c */ void host_config_set(const char *filename) { @@ -2293,18 +2344,18 @@ const char *host_config_get(void) void install_default(enum node_type node) { - install_element(node, &config_exit_cmd); - install_element(node, &config_quit_cmd); - install_element(node, &config_end_cmd); - install_element(node, &config_help_cmd); - install_element(node, &config_list_cmd); - install_element(node, &show_cli_graph_cmd); - install_element(node, &find_cmd); + _install_element(node, &config_exit_cmd); + _install_element(node, &config_quit_cmd); + _install_element(node, &config_end_cmd); + _install_element(node, &config_help_cmd); + _install_element(node, &config_list_cmd); + _install_element(node, &show_cli_graph_cmd); + _install_element(node, &find_cmd); - install_element(node, &config_write_cmd); - install_element(node, &show_running_config_cmd); + _install_element(node, &config_write_cmd); + _install_element(node, &show_running_config_cmd); - install_element(node, &autocomplete_cmd); + _install_element(node, &autocomplete_cmd); nb_cli_install_default(node); } @@ -2373,6 +2424,10 @@ void cmd_init(int terminal) install_element(VIEW_NODE, &echo_cmd); install_element(VIEW_NODE, &autocomplete_cmd); install_element(VIEW_NODE, &find_cmd); +#if defined(DEV_BUILD) && defined(HAVE_SCRIPTING) + install_element(VIEW_NODE, &script_cmd); +#endif + install_element(ENABLE_NODE, &config_end_cmd); install_element(ENABLE_NODE, &config_disable_cmd); diff --git a/lib/command.h b/lib/command.h index 1b0504101c..71abb20b05 100644 --- a/lib/command.h +++ b/lib/command.h @@ -145,6 +145,15 @@ enum node_type { PROTOCOL_NODE, /* protocol filtering node */ MPLS_NODE, /* MPLS config node */ PW_NODE, /* Pseudowire config node */ + SEGMENT_ROUTING_NODE, /* Segment routing root node */ + SR_TRAFFIC_ENG_NODE, /* SR Traffic Engineering node */ + SR_SEGMENT_LIST_NODE, /* SR segment list config node */ + SR_POLICY_NODE, /* SR policy config node */ + SR_CANDIDATE_DYN_NODE, /* SR dynamic candidate path config node */ + PCEP_NODE, /* PCEP node */ + PCEP_PCE_CONFIG_NODE, /* PCE shared configuration node */ + PCEP_PCE_NODE, /* PCE configuration node */ + PCEP_PCC_NODE, /* PCC configuration node */ VTY_NODE, /* Vty node. */ FPM_NODE, /* Dataplane FPM node. */ LINK_PARAMS_NODE, /* Link-parameters node */ @@ -230,7 +239,11 @@ struct cmd_node { .attr = attrs, \ .daemon = dnum, \ .name = #cmdname, \ - }; + .xref = XREF_INIT(XREFT_DEFUN, NULL, #funcname), \ + }; \ + XREF_LINK(cmdname.xref); \ + /* end */ + #define DEFUN_CMD_FUNC_DECL(funcname) \ static int funcname(const struct cmd_element *, struct vty *, int, \ @@ -405,7 +418,8 @@ struct cmd_node { "<neighbor|interface|area|lsa|zebra|config|dbex|spf|route|lsdb|redistribute|hook|asbr|prefix|abr>" #define AREA_TAG_STR "[area tag]\n" #define COMMUNITY_AANN_STR "Community number where AA and NN are (0-65535)\n" -#define COMMUNITY_VAL_STR "Community number in AA:NN format (where AA and NN are (0-65535)) or local-AS|no-advertise|no-export|internet or additive\n" +#define COMMUNITY_VAL_STR \ + "Community number in AA:NN format (where AA and NN are (0-65535)) or local-AS|no-advertise|no-export|internet|graceful-shutdown|accept-own-nexthop|accept-own|route-filter-translated-v4|route-filter-v4|route-filter-translated-v6|route-filter-v6|llgr-stale|no-llgr|blackhole|no-peer or additive\n" #define MPLS_TE_STR "MPLS-TE specific commands\n" #define LINK_PARAMS_STR "Configure interface link parameters\n" #define OSPF_RI_STR "OSPF Router Information specific commands\n" @@ -474,7 +488,29 @@ struct cmd_node { /* Prototypes. */ extern void install_node(struct cmd_node *node); extern void install_default(enum node_type); -extern void install_element(enum node_type, const struct cmd_element *); + +struct xref_install_element { + struct xref xref; + + const struct cmd_element *cmd_element; + enum node_type node_type; +}; + +#ifndef VTYSH_EXTRACT_PL +#define install_element(node_type_, cmd_element_) do { \ + static const struct xref_install_element _xref \ + __attribute__((used)) = { \ + .xref = XREF_INIT(XREFT_INSTALL_ELEMENT, NULL, \ + __func__), \ + .cmd_element = cmd_element_, \ + .node_type = node_type_, \ + }; \ + XREF_LINK(_xref.xref); \ + _install_element(node_type_, cmd_element_); \ + } while (0) +#endif + +extern void _install_element(enum node_type, const struct cmd_element *); /* known issue with uninstall_element: changes to cmd_token->attr (i.e. * deprecated/hidden) are not reversed. */ diff --git a/lib/command_graph.h b/lib/command_graph.h index 179e104a57..09824460e6 100644 --- a/lib/command_graph.h +++ b/lib/command_graph.h @@ -31,6 +31,7 @@ #include "memory.h" #include "vector.h" #include "graph.h" +#include "xref.h" #ifdef __cplusplus extern "C" { @@ -105,6 +106,7 @@ struct cmd_element { struct cmd_token *[]); const char *name; /* symbol name for debugging */ + struct xref xref; }; /* text for <cr> command */ diff --git a/lib/command_parse.y b/lib/command_parse.y index ba5225b702..8135d02b4b 100644 --- a/lib/command_parse.y +++ b/lib/command_parse.y @@ -496,7 +496,7 @@ terminate_graph (CMD_YYLTYPE *locp, struct parser_ctx *ctx, zlog_err ("----------"); while (ctx->docstr && ctx->docstr[1] != '\0') zlog_err ("%s", strsep(&ctx->docstr, "\n")); - zlog_err ("----------\n"); + zlog_err ("----------"); } graph_add_edge (finalnode, end_token_node); diff --git a/lib/compiler.h b/lib/compiler.h index 217a60d888..70ef8e9bc8 100644 --- a/lib/compiler.h +++ b/lib/compiler.h @@ -279,6 +279,29 @@ extern "C" { #define array_size(ar) (sizeof(ar) / sizeof(ar[0])) +/* Some insane macros to count number of varargs to a functionlike macro */ +#define PP_ARG_N( \ + _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \ + _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \ + _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \ + _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \ + _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, \ + _61, _62, _63, N, ...) N + +#define PP_RSEQ_N() \ + 62, 61, 60, \ + 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, \ + 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, \ + 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, \ + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, \ + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, \ + 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 + +#define PP_NARG_(...) PP_ARG_N(__VA_ARGS__) +#define PP_NARG(...) PP_NARG_(_, ##__VA_ARGS__, PP_RSEQ_N()) + + /* sigh. this is so ugly, it overflows and wraps to being nice again. * * printfrr() supports "%Ld" for <int64_t>, whatever that is typedef'd to. diff --git a/lib/ferr.h b/lib/ferr.h index a89b595e87..4e95431cea 100644 --- a/lib/ferr.h +++ b/lib/ferr.h @@ -132,6 +132,8 @@ struct ferr { #define VTYSH_FRR_END 0x0FFFFFFF #define WATCHFRR_FERR_START 0x10000001 #define WATCHFRR_FERR_END 0x10FFFFFF +#define PATH_FERR_START 0x11000001 +#define PATH_FERR_END 0x11FFFFFF #define ZEBRA_FERR_START 0xF1000001 #define ZEBRA_FERR_END 0xF1FFFFFF #define END_FERR 0xFFFFFFFF diff --git a/lib/filter_cli.c b/lib/filter_cli.c index 54b6cda9a5..5d66a9fc73 100644 --- a/lib/filter_cli.c +++ b/lib/filter_cli.c @@ -259,7 +259,7 @@ DEFPY_YANG( /* Access-list must exist before entries. */ if (yang_dnode_exists(running_config->dnode, xpath) == false) - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; /* Use access-list data structure to fetch sequence. */ dnode = yang_dnode_get(running_config->dnode, xpath); @@ -268,7 +268,7 @@ DEFPY_YANG( mask_str ? mask_str : CISCO_HOST_WILDCARD_MASK, NULL, NULL); if (sseq == -1) - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; snprintfrr(xpath_entry, sizeof(xpath_entry), "%s/entry[sequence='%" PRId64 "']", xpath, sseq); @@ -436,7 +436,7 @@ DEFPY_YANG( /* Access-list must exist before entries. */ if (yang_dnode_exists(running_config->dnode, xpath) == false) - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; /* Use access-list data structure to fetch sequence. */ dnode = yang_dnode_get(running_config->dnode, xpath); @@ -469,7 +469,7 @@ DEFPY_YANG( "0.0.0.0", CISCO_ANY_WILDCARD_MASK); } if (sseq == -1) - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; snprintfrr(xpath_entry, sizeof(xpath_entry), "%s/entry[sequence='%" PRId64 "']", xpath, sseq); @@ -588,7 +588,7 @@ DEFPY_YANG( /* Access-list must exist before entries. */ if (yang_dnode_exists(running_config->dnode, xpath) == false) - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; /* Use access-list data structure to fetch sequence. */ dnode = yang_dnode_get(running_config->dnode, xpath); @@ -601,7 +601,7 @@ DEFPY_YANG( sseq = acl_zebra_get_seq(acl, action, (struct prefix *)prefix, exact); if (sseq == -1) - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; snprintfrr(xpath_entry, sizeof(xpath_entry), "%s/entry[sequence='%" PRId64 "']", xpath, sseq); @@ -786,7 +786,7 @@ DEFPY_YANG( /* Access-list must exist before entries. */ if (yang_dnode_exists(running_config->dnode, xpath) == false) - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; /* Use access-list data structure to fetch sequence. */ dnode = yang_dnode_get(running_config->dnode, xpath); @@ -799,7 +799,7 @@ DEFPY_YANG( sseq = acl_zebra_get_seq(acl, action, (struct prefix *)prefix, exact); if (sseq == -1) - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; snprintfrr(xpath_entry, sizeof(xpath_entry), "%s/entry[sequence='%" PRId64 "']", xpath, sseq); @@ -979,7 +979,7 @@ DEFPY_YANG( /* Access-list must exist before entries. */ if (yang_dnode_exists(running_config->dnode, xpath) == false) - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; /* Use access-list data structure to fetch sequence. */ dnode = yang_dnode_get(running_config->dnode, xpath); @@ -992,7 +992,7 @@ DEFPY_YANG( sseq = acl_zebra_get_seq(acl, action, (struct prefix *)prefix, false); if (sseq == -1) - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; snprintfrr(xpath_entry, sizeof(xpath_entry), "%s/entry[sequence='%" PRId64 "']", xpath, sseq); @@ -1277,7 +1277,7 @@ static int plist_remove(struct vty *vty, const char *iptype, const char *name, /* Access-list must exist before entries. */ if (yang_dnode_exists(running_config->dnode, xpath) == false) - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; /* Use access-list data structure to fetch sequence. */ assert(action != NULL); @@ -1290,7 +1290,7 @@ static int plist_remove(struct vty *vty, const char *iptype, const char *name, pl = nb_running_get_entry(dnode, NULL, true); pentry = prefix_list_entry_lookup(pl, p, plt, -1, le, ge); if (pentry == NULL) - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; snprintfrr(xpath_entry, sizeof(xpath_entry), "%s/entry[sequence='%" PRId64 "']", xpath, pentry->seq); diff --git a/lib/frr_zmq.c b/lib/frr_zmq.c index cc11d76700..33adcd7b80 100644 --- a/lib/frr_zmq.c +++ b/lib/frr_zmq.c @@ -135,9 +135,8 @@ static int frrzmq_read_msg(struct thread *t) if (read) frrzmq_check_events(cbp, &cb->write, ZMQ_POLLOUT); - funcname_thread_add_read_write( - THREAD_READ, t->master, frrzmq_read_msg, cbp, cb->fd, - &cb->read.thread, t->funcname, t->schedfrom, t->schedfrom_line); + _thread_add_read_write(t->xref, t->master, frrzmq_read_msg, cbp, + cb->fd, &cb->read.thread); return 0; out_err: @@ -148,14 +147,14 @@ out_err: return 1; } -int funcname_frrzmq_thread_add_read(struct thread_master *master, - void (*msgfunc)(void *arg, void *zmqsock), - void (*partfunc)(void *arg, void *zmqsock, - zmq_msg_t *msg, - unsigned partnum), - void (*errfunc)(void *arg, void *zmqsock), - void *arg, void *zmqsock, - struct frrzmq_cb **cbp, debugargdef) +int _frrzmq_thread_add_read(const struct xref_threadsched *xref, + struct thread_master *master, + void (*msgfunc)(void *arg, void *zmqsock), + void (*partfunc)(void *arg, void *zmqsock, + zmq_msg_t *msg, unsigned partnum), + void (*errfunc)(void *arg, void *zmqsock), + void *arg, void *zmqsock, + struct frrzmq_cb **cbp) { int fd, events; size_t len; @@ -192,13 +191,11 @@ int funcname_frrzmq_thread_add_read(struct thread_master *master, if (events & ZMQ_POLLIN) { thread_cancel(&cb->read.thread); - funcname_thread_add_event(master, frrzmq_read_msg, cbp, fd, - &cb->read.thread, funcname, schedfrom, - fromln); + _thread_add_event(xref, master, frrzmq_read_msg, cbp, fd, + &cb->read.thread); } else - funcname_thread_add_read_write( - THREAD_READ, master, frrzmq_read_msg, cbp, fd, - &cb->read.thread, funcname, schedfrom, fromln); + _thread_add_read_write(xref, master, frrzmq_read_msg, cbp, fd, + &cb->read.thread); return 0; } @@ -244,10 +241,8 @@ static int frrzmq_write_msg(struct thread *t) if (written) frrzmq_check_events(cbp, &cb->read, ZMQ_POLLIN); - funcname_thread_add_read_write(THREAD_WRITE, t->master, - frrzmq_write_msg, cbp, cb->fd, - &cb->write.thread, t->funcname, - t->schedfrom, t->schedfrom_line); + _thread_add_read_write(t->xref, t->master, frrzmq_write_msg, cbp, + cb->fd, &cb->write.thread); return 0; out_err: @@ -257,11 +252,12 @@ out_err: cb->write.cb_error(cb->write.arg, cb->zmqsock); return 1; } -int funcname_frrzmq_thread_add_write(struct thread_master *master, - void (*msgfunc)(void *arg, void *zmqsock), - void (*errfunc)(void *arg, void *zmqsock), - void *arg, void *zmqsock, - struct frrzmq_cb **cbp, debugargdef) + +int _frrzmq_thread_add_write(const struct xref_threadsched *xref, + struct thread_master *master, + void (*msgfunc)(void *arg, void *zmqsock), + void (*errfunc)(void *arg, void *zmqsock), + void *arg, void *zmqsock, struct frrzmq_cb **cbp) { int fd, events; size_t len; @@ -298,13 +294,11 @@ int funcname_frrzmq_thread_add_write(struct thread_master *master, if (events & ZMQ_POLLOUT) { thread_cancel(&cb->write.thread); - funcname_thread_add_event(master, frrzmq_write_msg, cbp, fd, - &cb->write.thread, funcname, - schedfrom, fromln); + _thread_add_event(xref, master, frrzmq_write_msg, cbp, fd, + &cb->write.thread); } else - funcname_thread_add_read_write( - THREAD_WRITE, master, frrzmq_write_msg, cbp, fd, - &cb->write.thread, funcname, schedfrom, fromln); + _thread_add_read_write(xref, master, frrzmq_write_msg, cbp, fd, + &cb->write.thread); return 0; } diff --git a/lib/frr_zmq.h b/lib/frr_zmq.h index 4303df9ccd..d30cf8a841 100644 --- a/lib/frr_zmq.h +++ b/lib/frr_zmq.h @@ -67,18 +67,32 @@ extern void *frrzmq_context; extern void frrzmq_init(void); extern void frrzmq_finish(void); -#define debugargdef const char *funcname, const char *schedfrom, int fromln +#define _xref_zmq_a(type, f, d, call) \ + ({ \ + static const struct xref_threadsched _xref \ + __attribute__((used)) = { \ + .xref = XREF_INIT(XREFT_THREADSCHED, NULL, __func__), \ + .funcname = #f, \ + .dest = #d, \ + .thread_type = THREAD_ ## type, \ + }; \ + XREF_LINK(_xref.xref); \ + call; \ + }) \ + /* end */ /* core event registration, one of these 2 macros should be used */ #define frrzmq_thread_add_read_msg(m, f, e, a, z, d) \ - funcname_frrzmq_thread_add_read(m, f, NULL, e, a, z, d, #f, __FILE__, \ - __LINE__) + _xref_zmq_a(READ, f, d, \ + _frrzmq_thread_add_read(&_xref, m, f, NULL, e, a, z, d)) + #define frrzmq_thread_add_read_part(m, f, e, a, z, d) \ - funcname_frrzmq_thread_add_read(m, NULL, f, e, a, z, d, #f, __FILE__, \ - __LINE__) + _xref_zmq_a(READ, f, d, \ + _frrzmq_thread_add_read(&_xref, m, NULL, f, e, a, z, d)) + #define frrzmq_thread_add_write_msg(m, f, e, a, z, d) \ - funcname_frrzmq_thread_add_write(m, f, e, a, z, d, #f, __FILE__, \ - __LINE__) + _xref_zmq_a(WRITE, f, d, \ + _frrzmq_thread_add_write(&_xref, m, f, e, a, z, d)) struct cb_core; struct frrzmq_cb; @@ -104,16 +118,18 @@ struct frrzmq_cb; * may schedule the event to run as soon as libfrr is back in its main * loop. */ -extern int funcname_frrzmq_thread_add_read( - struct thread_master *master, void (*msgfunc)(void *arg, void *zmqsock), +extern int _frrzmq_thread_add_read( + const struct xref_threadsched *xref, struct thread_master *master, + void (*msgfunc)(void *arg, void *zmqsock), void (*partfunc)(void *arg, void *zmqsock, zmq_msg_t *msg, unsigned partnum), void (*errfunc)(void *arg, void *zmqsock), void *arg, void *zmqsock, - struct frrzmq_cb **cb, debugargdef); -extern int funcname_frrzmq_thread_add_write( - struct thread_master *master, void (*msgfunc)(void *arg, void *zmqsock), + struct frrzmq_cb **cb); +extern int _frrzmq_thread_add_write( + const struct xref_threadsched *xref, struct thread_master *master, + void (*msgfunc)(void *arg, void *zmqsock), void (*errfunc)(void *arg, void *zmqsock), void *arg, void *zmqsock, - struct frrzmq_cb **cb, debugargdef); + struct frrzmq_cb **cb); extern void frrzmq_thread_cancel(struct frrzmq_cb **cb, struct cb_core *core); diff --git a/lib/frrlua.c b/lib/frrlua.c index 9f9cf8c1f6..d8aaa3aa3c 100644 --- a/lib/frrlua.c +++ b/lib/frrlua.c @@ -2,128 +2,365 @@ * This file defines the lua interface into * FRRouting. * - * Copyright (C) 2016 Cumulus Networks, Inc. - * Donald Sharp + * Copyright (C) 2016-2019 Cumulus Networks, Inc. + * Donald Sharp, Quentin Young * - * This file is part of FRRouting (FRR). + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. * - * FRR is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2, or (at your option) any later version. - * - * FRR is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. * * You should have received a copy of the GNU General Public License along - * with FRR; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <zebra.h> -#if defined(HAVE_LUA) +#ifdef HAVE_SCRIPTING + #include "prefix.h" #include "frrlua.h" #include "log.h" +#include "buffer.h" + +/* Lua stuff */ -static int lua_zlog_debug(lua_State *L) +/* + * FRR convenience functions. + * + * This section has convenience functions used to make interacting with the Lua + * stack easier. + */ + +int frrlua_table_get_integer(lua_State *L, const char *key) { - int debug_lua = 1; - const char *string = lua_tostring(L, 1); + int result; - if (debug_lua) - zlog_debug("%s", string); + lua_pushstring(L, key); + lua_gettable(L, -2); - return 0; + result = lua_tointeger(L, -1); + lua_pop(L, 1); + + return result; } -const char *get_string(lua_State *L, const char *key) +/* + * Encoders. + * + * This section has functions that convert internal FRR datatypes into Lua + * datatypes. + */ + +void lua_pushprefix(lua_State *L, const struct prefix *prefix) { - const char *str; + char buffer[PREFIX_STRLEN]; - lua_pushstring(L, key); - lua_gettable(L, -2); + lua_newtable(L); + lua_pushstring(L, prefix2str(prefix, buffer, PREFIX_STRLEN)); + lua_setfield(L, -2, "network"); + lua_pushinteger(L, prefix->prefixlen); + lua_setfield(L, -2, "length"); + lua_pushinteger(L, prefix->family); + lua_setfield(L, -2, "family"); +} - str = (const char *)lua_tostring(L, -1); +void *lua_toprefix(lua_State *L, int idx) +{ + struct prefix *p = XCALLOC(MTYPE_TMP, sizeof(struct prefix)); + + lua_getfield(L, idx, "network"); + (void)str2prefix(lua_tostring(L, -1), p); lua_pop(L, 1); - return str; + return p; } -int get_integer(lua_State *L, const char *key) +void lua_pushinterface(lua_State *L, const struct interface *ifp) { - int result; + lua_newtable(L); + lua_pushstring(L, ifp->name); + lua_setfield(L, -2, "name"); + lua_pushinteger(L, ifp->ifindex); + lua_setfield(L, -2, "ifindex"); + lua_pushinteger(L, ifp->status); + lua_setfield(L, -2, "status"); + lua_pushinteger(L, ifp->flags); + lua_setfield(L, -2, "flags"); + lua_pushinteger(L, ifp->metric); + lua_setfield(L, -2, "metric"); + lua_pushinteger(L, ifp->speed); + lua_setfield(L, -2, "speed"); + lua_pushinteger(L, ifp->mtu); + lua_setfield(L, -2, "mtu"); + lua_pushinteger(L, ifp->mtu6); + lua_setfield(L, -2, "mtu6"); + lua_pushinteger(L, ifp->bandwidth); + lua_setfield(L, -2, "bandwidth"); + lua_pushinteger(L, ifp->link_ifindex); + lua_setfield(L, -2, "link_ifindex"); + lua_pushinteger(L, ifp->ll_type); + lua_setfield(L, -2, "linklayer_type"); +} - lua_pushstring(L, key); - lua_gettable(L, -2); +void *lua_tointerface(lua_State *L, int idx) +{ + struct interface *ifp = XCALLOC(MTYPE_TMP, sizeof(struct interface)); - result = lua_tointeger(L, -1); + lua_getfield(L, idx, "name"); + strlcpy(ifp->name, lua_tostring(L, -1), sizeof(ifp->name)); + lua_pop(L, 1); + lua_getfield(L, idx, "ifindex"); + ifp->ifindex = lua_tointeger(L, -1); + lua_pop(L, 1); + lua_getfield(L, idx, "status"); + ifp->status = lua_tointeger(L, -1); + lua_pop(L, 1); + lua_getfield(L, idx, "flags"); + ifp->flags = lua_tointeger(L, -1); + lua_pop(L, 1); + lua_getfield(L, idx, "metric"); + ifp->metric = lua_tointeger(L, -1); + lua_pop(L, 1); + lua_getfield(L, idx, "speed"); + ifp->speed = lua_tointeger(L, -1); + lua_pop(L, 1); + lua_getfield(L, idx, "mtu"); + ifp->mtu = lua_tointeger(L, -1); + lua_pop(L, 1); + lua_getfield(L, idx, "mtu6"); + ifp->mtu6 = lua_tointeger(L, -1); + lua_pop(L, 1); + lua_getfield(L, idx, "bandwidth"); + ifp->bandwidth = lua_tointeger(L, -1); + lua_pop(L, 1); + lua_getfield(L, idx, "link_ifindex"); + ifp->link_ifindex = lua_tointeger(L, -1); + lua_pop(L, 1); + lua_getfield(L, idx, "linklayer_type"); + ifp->ll_type = lua_tointeger(L, -1); lua_pop(L, 1); - return result; + return ifp; } -static void *lua_alloc(void *ud, void *ptr, size_t osize, - size_t nsize) +void lua_pushinaddr(lua_State *L, const struct in_addr *addr) { - (void)ud; (void)osize; /* not used */ - if (nsize == 0) { - free(ptr); - return NULL; - } else - return realloc(ptr, nsize); + char buf[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, addr, buf, sizeof(buf)); + + lua_newtable(L); + lua_pushinteger(L, addr->s_addr); + lua_setfield(L, -2, "value"); + lua_pushstring(L, buf); + lua_setfield(L, -2, "string"); } -lua_State *lua_initialize(const char *file) +void *lua_toinaddr(lua_State *L, int idx) { - int status; - lua_State *L = lua_newstate(lua_alloc, NULL); + struct in_addr *inaddr = XCALLOC(MTYPE_TMP, sizeof(struct in_addr)); - zlog_debug("Newstate: %p", L); - luaL_openlibs(L); - zlog_debug("Opened lib"); - status = luaL_loadfile(L, file); - if (status) { - zlog_debug("Failure to open %s %d", file, status); - lua_close(L); - return NULL; - } + lua_getfield(L, idx, "value"); + inaddr->s_addr = lua_tointeger(L, -1); + lua_pop(L, 1); - lua_pcall(L, 0, LUA_MULTRET, 0); - zlog_debug("Setting global function"); - lua_pushcfunction(L, lua_zlog_debug); - lua_setglobal(L, "zlog_debug"); + return inaddr; +} + + +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); + lua_pushlstring(L, (const char *)addr->s6_addr, 16); + lua_setfield(L, -2, "value"); + lua_pushstring(L, buf); + lua_setfield(L, -2, "string"); +} + +void *lua_toin6addr(lua_State *L, int idx) +{ + 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); - return L; + return in6addr; } -void lua_setup_prefix_table(lua_State *L, const struct prefix *prefix) +void lua_pushsockunion(lua_State *L, const union sockunion *su) { - char buffer[100]; + char buf[SU_ADDRSTRLEN]; + sockunion2str(su, buf, sizeof(buf)); lua_newtable(L); - lua_pushstring(L, prefix2str(prefix, buffer, 100)); - lua_setfield(L, -2, "route"); - lua_pushinteger(L, prefix->family); - lua_setfield(L, -2, "family"); - lua_setglobal(L, "prefix"); + lua_pushlstring(L, (const char *)sockunion_get_addr(su), + sockunion_get_addrlen(su)); + lua_setfield(L, -2, "value"); + lua_pushstring(L, buf); + lua_setfield(L, -2, "string"); } -enum lua_rm_status lua_run_rm_rule(lua_State *L, const char *rule) +void *lua_tosockunion(lua_State *L, int idx) { - int status; + union sockunion *su = XCALLOC(MTYPE_TMP, sizeof(union sockunion)); + + lua_getfield(L, idx, "string"); + str2sockunion(lua_tostring(L, -1), su); + + return su; +} - lua_getglobal(L, rule); - status = lua_pcall(L, 0, 1, 0); - if (status) { - zlog_debug("Executing Failure with function: %s: %d", - rule, status); - return LUA_RM_FAILURE; +void lua_pushtimet(lua_State *L, const time_t *time) +{ + lua_pushinteger(L, *time); +} + +void *lua_totimet(lua_State *L, int idx) +{ + time_t *t = XCALLOC(MTYPE_TMP, sizeof(time_t)); + + *t = lua_tointeger(L, idx); + + return t; +} + +void lua_pushintegerp(lua_State *L, const long long *num) +{ + lua_pushinteger(L, *num); +} + +void *lua_tointegerp(lua_State *L, int idx) +{ + int isnum; + long long *num = XCALLOC(MTYPE_TMP, sizeof(long long)); + + *num = lua_tonumberx(L, idx, &isnum); + assert(isnum); + + return num; +} + +void *lua_tostringp(lua_State *L, int idx) +{ + char *string = XSTRDUP(MTYPE_TMP, lua_tostring(L, idx)); + + return string; +} + +/* + * Logging. + * + * Lua-compatible wrappers for FRR logging functions. + */ +static const char *frrlua_log_thunk(lua_State *L) +{ + int nargs; + + nargs = lua_gettop(L); + assert(nargs == 1); + + return lua_tostring(L, 1); +} + +static int frrlua_log_debug(lua_State *L) +{ + zlog_debug("%s", frrlua_log_thunk(L)); + return 0; +} + +static int frrlua_log_info(lua_State *L) +{ + zlog_info("%s", frrlua_log_thunk(L)); + return 0; +} + +static int frrlua_log_notice(lua_State *L) +{ + zlog_notice("%s", frrlua_log_thunk(L)); + return 0; +} + +static int frrlua_log_warn(lua_State *L) +{ + zlog_warn("%s", frrlua_log_thunk(L)); + return 0; +} + +static int frrlua_log_error(lua_State *L) +{ + zlog_err("%s", frrlua_log_thunk(L)); + return 0; +} + +static const luaL_Reg log_funcs[] = { + {"debug", frrlua_log_debug}, + {"info", frrlua_log_info}, + {"notice", frrlua_log_notice}, + {"warn", frrlua_log_warn}, + {"error", frrlua_log_error}, + {}, +}; + +void frrlua_export_logging(lua_State *L) +{ + lua_newtable(L); + luaL_setfuncs(L, log_funcs, 0); + lua_setglobal(L, "log"); +} + +/* + * Debugging. + */ + +char *frrlua_stackdump(lua_State *L) +{ + int top = lua_gettop(L); + + char tmpbuf[64]; + struct buffer *buf = buffer_new(4098); + + for (int i = 1; i <= top; i++) { + int t = lua_type(L, i); + + switch (t) { + case LUA_TSTRING: /* strings */ + snprintf(tmpbuf, sizeof(tmpbuf), "\"%s\"\n", + lua_tostring(L, i)); + buffer_putstr(buf, tmpbuf); + break; + case LUA_TBOOLEAN: /* booleans */ + snprintf(tmpbuf, sizeof(tmpbuf), "%s\n", + lua_toboolean(L, i) ? "true" : "false"); + buffer_putstr(buf, tmpbuf); + break; + case LUA_TNUMBER: /* numbers */ + snprintf(tmpbuf, sizeof(tmpbuf), "%g\n", + lua_tonumber(L, i)); + buffer_putstr(buf, tmpbuf); + break; + default: /* other values */ + snprintf(tmpbuf, sizeof(tmpbuf), "%s\n", + lua_typename(L, t)); + buffer_putstr(buf, tmpbuf); + break; + } } - status = lua_tonumber(L, -1); - return status; + char *result = XSTRDUP(MTYPE_TMP, buffer_getstr(buf)); + + buffer_free(buf); + + return result; } -#endif + +#endif /* HAVE_SCRIPTING */ diff --git a/lib/frrlua.h b/lib/frrlua.h index 40c7a67b89..6fb30938b0 100644 --- a/lib/frrlua.h +++ b/lib/frrlua.h @@ -1,88 +1,184 @@ /* - * This file defines the lua interface into - * FRRouting. + * Copyright (C) 2016-2019 Cumulus Networks, Inc. + * Donald Sharp, Quentin Young * - * Copyright (C) 2016 Cumulus Networks, Inc. - * Donald Sharp + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. * - * This file is part of FRRouting (FRR). - * - * FRR is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2, or (at your option) any later version. - * - * FRR is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. * * You should have received a copy of the GNU General Public License along - * with FRR; see the file COPYING. If not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef __LUA_H__ -#define __LUA_H__ +#ifndef __FRRLUA_H__ +#define __FRRLUA_H__ + +#include <zebra.h> -#if defined(HAVE_LUA) +#ifdef HAVE_SCRIPTING -#include "lua.h" -#include "lualib.h" -#include "lauxlib.h" +#include <lua.h> +#include <lualib.h> +#include <lauxlib.h> + +#include "prefix.h" +#include "frrscript.h" #ifdef __cplusplus extern "C" { #endif /* - * These functions are helper functions that - * try to glom some of the lua_XXX functionality - * into what we actually need, instead of having - * to make multiple calls to set up what - * we want + * gcc-10 is complaining about the wrapper function + * not being compatible with lua_pushstring returning + * a char *. Let's wrapper it here to make our life + * easier + */ +static inline void lua_pushstring_wrapper(lua_State *L, const char *str) +{ + (void)lua_pushstring(L, str); +} + +/* + * Converts a prefix to a Lua value and pushes it on the stack. + */ +void lua_pushprefix(lua_State *L, const struct prefix *prefix); + +/* + * Converts the Lua value at idx to a prefix. + * + * Returns: + * struct prefix allocated with MTYPE_TMP + */ +void *lua_toprefix(lua_State *L, int idx); + +/* + * Converts an interface to a Lua value and pushes it on the stack. + */ +void lua_pushinterface(lua_State *L, const struct interface *ifp); + +/* + * Converts the Lua value at idx to an interface. + * + * Returns: + * struct interface allocated with MTYPE_TMP. This interface is not hooked + * to anything, nor is it inserted in the global interface tree. */ -enum lua_rm_status { - /* - * Script function run failure. This will translate into a - * deny - */ - LUA_RM_FAILURE = 0, - /* - * No Match was found for the route map function - */ - LUA_RM_NOMATCH, - /* - * Match was found but no changes were made to the - * incoming data. - */ - LUA_RM_MATCH, - /* - * Match was found and data was modified, so - * figure out what changed - */ - LUA_RM_MATCH_AND_CHANGE, -}; +void *lua_tointerface(lua_State *L, int idx); /* - * Open up the lua.scr file and parse - * initial global values, if any. + * Converts an in_addr to a Lua value and pushes it on the stack. */ -lua_State *lua_initialize(const char *file); +void lua_pushinaddr(lua_State *L, const struct in_addr *addr); -void lua_setup_prefix_table(lua_State *L, const struct prefix *prefix); +/* + * Converts the Lua value at idx to an in_addr. + * + * Returns: + * struct in_addr allocated with MTYPE_TMP. + */ +void *lua_toinaddr(lua_State *L, int idx); -enum lua_rm_status lua_run_rm_rule(lua_State *L, const char *rule); +/* + * Converts an in6_addr to a Lua value and pushes it on the stack. + */ +void lua_pushin6addr(lua_State *L, const struct in6_addr *addr); /* - * Get particular string/integer information - * from a table. It is *assumed* that - * the table has already been selected + * Converts the Lua value at idx to an in6_addr. + * + * Returns: + * struct in6_addr allocated with MTYPE_TMP. */ -const char *get_string(lua_State *L, const char *key); -int get_integer(lua_State *L, const char *key); +void *lua_toin6addr(lua_State *L, int idx); + +/* + * Converts a time_t to a Lua value and pushes it on the stack. + */ +void lua_pushtimet(lua_State *L, const time_t *time); + +/* + * Converts the Lua value at idx to a time_t. + * + * Returns: + * time_t allocated with MTYPE_TMP. + */ +void *lua_totimet(lua_State *L, int idx); + +/* + * Converts a sockunion to a Lua value and pushes it on the stack. + */ +void lua_pushsockunion(lua_State *L, const union sockunion *su); + +/* + * Converts the Lua value at idx to a sockunion. + * + * Returns: + * sockunion allocated with MTYPE_TMP. + */ +void *lua_tosockunion(lua_State *L, int idx); + +/* + * Converts an int to a Lua value and pushes it on the stack. + */ +void lua_pushintegerp(lua_State *L, const long long *num); + +/* + * Converts the Lua value at idx to an int. + * + * Returns: + * int allocated with MTYPE_TMP. + */ +void *lua_tointegerp(lua_State *L, int idx); + +/* + * Pop string. + * + * Sets *string to a copy of the string at the top of the stack. The copy is + * allocated with MTYPE_TMP and the caller is responsible for freeing it. + */ +void *lua_tostringp(lua_State *L, int idx); + +/* + * Retrieve an integer from table on the top of the stack. + * + * key + * Key of string value in table + */ +int frrlua_table_get_integer(lua_State *L, const char *key); + +/* + * Exports a new table containing bindings to FRR zlog functions into the + * global namespace. + * + * From Lua, these functions may be accessed as: + * + * - log.debug() + * - log.info() + * - log.warn() + * - log.error() + * + * They take a single string argument. + */ +void frrlua_export_logging(lua_State *L); + +/* + * Dump Lua stack to a string. + * + * Return value must be freed with XFREE(MTYPE_TMP, ...); + */ +char *frrlua_stackdump(lua_State *L); #ifdef __cplusplus } #endif -#endif -#endif +#endif /* HAVE_SCRIPTING */ + +#endif /* __FRRLUA_H__ */ diff --git a/lib/frrscript.c b/lib/frrscript.c new file mode 100644 index 0000000000..10d400886d --- /dev/null +++ b/lib/frrscript.c @@ -0,0 +1,272 @@ +/* Scripting foo + * Copyright (C) 2020 NVIDIA Corporation + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <zebra.h> + +#ifdef HAVE_SCRIPTING + +#include <stdarg.h> +#include <lua.h> + +#include "frrscript.h" +#include "frrlua.h" +#include "memory.h" +#include "hash.h" +#include "log.h" + + +DEFINE_MTYPE_STATIC(LIB, SCRIPT, "Scripting"); + +/* Codecs */ + +struct frrscript_codec frrscript_codecs_lib[] = { + {.typename = "integer", + .encoder = (encoder_func)lua_pushintegerp, + .decoder = lua_tointegerp}, + {.typename = "string", + .encoder = (encoder_func)lua_pushstring_wrapper, + .decoder = lua_tostringp}, + {.typename = "prefix", + .encoder = (encoder_func)lua_pushprefix, + .decoder = lua_toprefix}, + {.typename = "interface", + .encoder = (encoder_func)lua_pushinterface, + .decoder = lua_tointerface}, + {.typename = "in_addr", + .encoder = (encoder_func)lua_pushinaddr, + .decoder = lua_toinaddr}, + {.typename = "in6_addr", + .encoder = (encoder_func)lua_pushin6addr, + .decoder = lua_toin6addr}, + {.typename = "sockunion", + .encoder = (encoder_func)lua_pushsockunion, + .decoder = lua_tosockunion}, + {.typename = "time_t", + .encoder = (encoder_func)lua_pushtimet, + .decoder = lua_totimet}, + {}}; + +/* Type codecs */ + +struct hash *codec_hash; +char scriptdir[MAXPATHLEN]; + +static unsigned int codec_hash_key(const void *data) +{ + const struct frrscript_codec *c = data; + + return string_hash_make(c->typename); +} + +static bool codec_hash_cmp(const void *d1, const void *d2) +{ + const struct frrscript_codec *e1 = d1; + const struct frrscript_codec *e2 = d2; + + return strmatch(e1->typename, e2->typename); +} + +static void *codec_alloc(void *arg) +{ + struct frrscript_codec *tmp = arg; + + struct frrscript_codec *e = + XCALLOC(MTYPE_SCRIPT, sizeof(struct frrscript_codec)); + e->typename = XSTRDUP(MTYPE_SCRIPT, tmp->typename); + e->encoder = tmp->encoder; + e->decoder = tmp->decoder; + + return e; +} + +#if 0 +static void codec_free(struct codec *c) +{ + XFREE(MTYPE_TMP, c->typename); + XFREE(MTYPE_TMP, c); +} +#endif + +/* Generic script APIs */ + +int frrscript_call(struct frrscript *fs, struct frrscript_env *env) +{ + struct frrscript_codec c = {}; + const void *arg; + const char *bindname; + + /* 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; + + struct frrscript_codec *codec = hash_lookup(codec_hash, &c); + assert(codec && "No encoder for type"); + codec->encoder(fs->L, arg); + + lua_setglobal(fs->L, bindname); + } + + int ret = lua_pcall(fs->L, 0, 0, 0); + + switch (ret) { + case LUA_OK: + break; + case LUA_ERRRUN: + zlog_err("Script '%s' runtime error: %s", fs->name, + lua_tostring(fs->L, -1)); + break; + case LUA_ERRMEM: + zlog_err("Script '%s' memory error: %s", fs->name, + lua_tostring(fs->L, -1)); + break; + case LUA_ERRERR: + zlog_err("Script '%s' error handler error: %s", fs->name, + lua_tostring(fs->L, -1)); + break; + case LUA_ERRGCMM: + zlog_err("Script '%s' garbage collector error: %s", fs->name, + lua_tostring(fs->L, -1)); + break; + default: + zlog_err("Script '%s' unknown error: %s", fs->name, + lua_tostring(fs->L, -1)); + break; + } + + if (ret != LUA_OK) { + lua_pop(fs->L, 1); + goto done; + } + +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 *r; + struct frrscript_codec c = {.typename = result->typename}; + + struct frrscript_codec *codec = hash_lookup(codec_hash, &c); + assert(codec && "No encoder for type"); + + if (!codec->decoder) { + zlog_err("No script decoder for type '%s'", result->typename); + return NULL; + } + + lua_getglobal(fs->L, result->name); + r = codec->decoder(fs->L, -1); + lua_pop(fs->L, 1); + + return r; +} + +void frrscript_register_type_codec(struct frrscript_codec *codec) +{ + struct frrscript_codec c = *codec; + + if (hash_lookup(codec_hash, &c)) { + zlog_backtrace(LOG_ERR); + assert(!"Type codec double-registered."); + } + + assert(hash_get(codec_hash, &c, codec_alloc)); +} + +void frrscript_register_type_codecs(struct frrscript_codec *codecs) +{ + for (int i = 0; codecs[i].typename != NULL; i++) + frrscript_register_type_codec(&codecs[i]); +} + +struct frrscript *frrscript_load(const char *name, + int (*load_cb)(struct frrscript *)) +{ + struct frrscript *fs = XCALLOC(MTYPE_SCRIPT, sizeof(struct frrscript)); + + fs->name = XSTRDUP(MTYPE_SCRIPT, name); + fs->L = luaL_newstate(); + frrlua_export_logging(fs->L); + + char fname[MAXPATHLEN * 2]; + snprintf(fname, sizeof(fname), "%s/%s.lua", scriptdir, fs->name); + + int ret = luaL_loadfile(fs->L, fname); + + switch (ret) { + case LUA_OK: + break; + case LUA_ERRSYNTAX: + zlog_err("Failed loading script '%s': syntax error: %s", fname, + lua_tostring(fs->L, -1)); + break; + case LUA_ERRMEM: + zlog_err("Failed loading script '%s': out-of-memory error: %s", + fname, lua_tostring(fs->L, -1)); + break; + case LUA_ERRGCMM: + zlog_err( + "Failed loading script '%s': garbage collector error: %s", + fname, lua_tostring(fs->L, -1)); + break; + case LUA_ERRFILE: + zlog_err("Failed loading script '%s': file read error: %s", + fname, lua_tostring(fs->L, -1)); + break; + default: + zlog_err("Failed loading script '%s': unknown error: %s", fname, + lua_tostring(fs->L, -1)); + break; + } + + if (ret != LUA_OK) + goto fail; + + if (load_cb && (*load_cb)(fs) != 0) + goto fail; + + return fs; +fail: + frrscript_unload(fs); + return NULL; +} + +void frrscript_unload(struct frrscript *fs) +{ + lua_close(fs->L); + XFREE(MTYPE_SCRIPT, fs->name); + XFREE(MTYPE_SCRIPT, fs); +} + +void frrscript_init(const char *sd) +{ + codec_hash = hash_create(codec_hash_key, codec_hash_cmp, + "Lua type encoders"); + + strlcpy(scriptdir, sd, sizeof(scriptdir)); + + /* Register core library types */ + frrscript_register_type_codecs(frrscript_codecs_lib); +} + +#endif /* HAVE_SCRIPTING */ diff --git a/lib/frrscript.h b/lib/frrscript.h new file mode 100644 index 0000000000..f4057f531b --- /dev/null +++ b/lib/frrscript.h @@ -0,0 +1,138 @@ +/* Scripting foo + * Copyright (C) 2020 NVIDIA Corporation + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __FRRSCRIPT_H__ +#define __FRRSCRIPT_H__ + +#include <zebra.h> + +#ifdef HAVE_SCRIPTING + +#include <lua.h> +#include "frrlua.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*encoder_func)(lua_State *, const void *); +typedef void *(*decoder_func)(lua_State *, int); + +struct frrscript_codec { + const char *typename; + encoder_func encoder; + decoder_func decoder; +}; + +struct frrscript { + /* Script name */ + char *name; + + /* Lua state */ + struct lua_State *L; +}; + +struct frrscript_env { + /* Value type */ + const char *typename; + + /* Binding name */ + const char *name; + + /* Value */ + const void *val; +}; + +/* + * Create new FRR script. + */ +struct frrscript *frrscript_load(const char *name, + int (*load_cb)(struct frrscript *)); + +/* + * Destroy FRR script. + */ +void frrscript_unload(struct frrscript *fs); + +/* + * Register a Lua codec for a type. + * + * tname + * Name of type; e.g., "peer", "ospf_interface", etc. Chosen at will. + * + * codec(s) + * Function pointer to codec struct. Encoder function should push a Lua + * table representing the passed argument - which will have the C type + * associated with the chosen 'tname' to the provided stack. The decoder + * function should pop a value from the top of the stack and return a heap + * chunk containing that value. Allocations should be made with MTYPE_TMP. + * + * If using the plural function variant, pass a NULL-terminated array. + * + */ +void frrscript_register_type_codec(struct frrscript_codec *codec); +void frrscript_register_type_codecs(struct frrscript_codec *codecs); + +/* + * Initialize scripting subsystem. Call this before anything else. + * + * scriptdir + * Directory in which to look for scripts + */ +void frrscript_init(const char *scriptdir); + + +/* + * Call script. + * + * fs + * The script to call; this is obtained from frrscript_load(). + * + * env + * The script's environment. Specify this as an array of frrscript_env. + * + * Returns: + * 0 if the script ran successfully, nonzero otherwise. + */ +int frrscript_call(struct frrscript *fs, struct frrscript_env *env); + + +/* + * Get result from finished script. + * + * 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. + * + * Returns: + * The script result of the specified name and type, or NULL. + */ +void *frrscript_get_result(struct frrscript *fs, + const struct frrscript_env *result); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* HAVE_SCRIPTING */ + +#endif /* __FRRSCRIPT_H__ */ diff --git a/lib/hash.c b/lib/hash.c index ed429b77d0..ec616ee724 100644 --- a/lib/hash.c +++ b/lib/hash.c @@ -32,7 +32,7 @@ #include "libfrr_trace.h" DEFINE_MTYPE_STATIC(LIB, HASH, "Hash") -DEFINE_MTYPE_STATIC(LIB, HASH_BACKET, "Hash Bucket") +DEFINE_MTYPE_STATIC(LIB, HASH_BUCKET, "Hash Bucket") DEFINE_MTYPE_STATIC(LIB, HASH_INDEX, "Hash Index") static pthread_mutex_t _hashes_mtx = PTHREAD_MUTEX_INITIALIZER; @@ -168,7 +168,7 @@ void *hash_get(struct hash *hash, void *data, void *(*alloc_func)(void *)) index = key & (hash->size - 1); } - bucket = XCALLOC(MTYPE_HASH_BACKET, sizeof(struct hash_bucket)); + bucket = XCALLOC(MTYPE_HASH_BUCKET, sizeof(struct hash_bucket)); bucket->data = newdata; bucket->key = key; bucket->next = hash->index[index]; @@ -239,7 +239,7 @@ void *hash_release(struct hash *hash, void *data) hash_update_ssq(hash, oldlen, newlen); ret = bucket->data; - XFREE(MTYPE_HASH_BACKET, bucket); + XFREE(MTYPE_HASH_BUCKET, bucket); hash->count--; break; } @@ -302,7 +302,7 @@ void hash_clean(struct hash *hash, void (*free_func)(void *)) if (free_func) (*free_func)(hb->data); - XFREE(MTYPE_HASH_BACKET, hb); + XFREE(MTYPE_HASH_BUCKET, hb); hash->count--; } hash->index[i] = NULL; diff --git a/lib/hash.h b/lib/hash.h index 23e93b6d7d..47d951a34b 100644 --- a/lib/hash.h +++ b/lib/hash.h @@ -76,7 +76,7 @@ struct hash { /* Data compare function. */ bool (*hash_cmp)(const void *, const void *); - /* Backet alloc. */ + /* Bucket alloc. */ unsigned long count; struct hashstats stats; @@ -351,6 +351,40 @@ struct interface *if_lookup_by_index(ifindex_t ifindex, vrf_id_t vrf_id) return NULL; } +/* Interface existance check by index. */ +struct interface *if_vrf_lookup_by_index_next(ifindex_t ifindex, + vrf_id_t vrf_id) +{ + struct vrf *vrf = vrf_lookup_by_id(vrf_id); + struct interface *tmp_ifp; + bool found = false; + + if (!vrf) + return NULL; + + if (ifindex == 0) { + tmp_ifp = RB_MIN(if_index_head, &vrf->ifaces_by_index); + /* skip the vrf interface */ + if (tmp_ifp && if_is_vrf(tmp_ifp)) + ifindex = tmp_ifp->ifindex; + else + return tmp_ifp; + } + + RB_FOREACH (tmp_ifp, if_index_head, &vrf->ifaces_by_index) { + if (found) { + /* skip the vrf interface */ + if (tmp_ifp && if_is_vrf(tmp_ifp)) + continue; + else + return tmp_ifp; + } + if (tmp_ifp->ifindex == ifindex) + found = true; + } + return NULL; +} + const char *ifindex2ifname(ifindex_t ifindex, vrf_id_t vrf_id) { struct interface *ifp; @@ -802,70 +836,6 @@ void if_dump_all(void) if_dump(ifp); } -#if 0 -/* For debug purpose. */ -DEFUN (show_address, - show_address_cmd, - "show address [vrf NAME]", - SHOW_STR - "address\n" - VRF_CMD_HELP_STR) -{ - int idx_vrf = 3; - struct listnode *node; - struct interface *ifp; - struct connected *ifc; - struct prefix *p; - vrf_id_t vrf_id = VRF_DEFAULT; - - if (argc > 2) - VRF_GET_ID (vrf_id, argv[idx_vrf]->arg); - - FOR_ALL_INTERFACES (vrf, ifp) { - for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, ifc)) { - p = ifc->address; - - if (p->family == AF_INET) - vty_out (vty, "%pFX\n", p); - } - } - return CMD_SUCCESS; -} - -DEFUN (show_address_vrf_all, - show_address_vrf_all_cmd, - "show address vrf all", - SHOW_STR - "address\n" - VRF_ALL_CMD_HELP_STR) -{ - struct vrf *vrf; - struct listnode *node; - struct interface *ifp; - struct connected *ifc; - struct prefix *p; - - RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) - { - if (RB_EMPTY (if_name_head, &vrf->ifaces_by_name)) - continue; - - vty_out (vty, "\nVRF %s(%u)\n\n", - VRF_LOGNAME(vrf), vrf->vrf_id); - - FOR_ALL_INTERFACES (vrf, ifp) { - for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, ifc)) { - p = ifc->address; - - if (p->family == AF_INET) - vty_out (vty, "%pFX\n", p); - } - } - } - return CMD_SUCCESS; -} -#endif - /* Allocate connected structure. */ struct connected *connected_new(void) { @@ -1083,84 +1053,6 @@ struct connected *connected_get_linklocal(struct interface *ifp) return c; } -#if 0 /* this route_table of struct connected's is unused \ - * however, it would be good to use a route_table rather than \ - * a list.. \ - */ -/* Interface looking up by interface's address. */ -/* Interface's IPv4 address reverse lookup table. */ -struct route_table *ifaddr_ipv4_table; -/* struct route_table *ifaddr_ipv6_table; */ - -static void -ifaddr_ipv4_add (struct in_addr *ifaddr, struct interface *ifp) -{ - struct route_node *rn; - struct prefix_ipv4 p; - - p.family = AF_INET; - p.prefixlen = IPV4_MAX_PREFIXLEN; - p.prefix = *ifaddr; - - rn = route_node_get (ifaddr_ipv4_table, (struct prefix *) &p); - if (rn) - { - route_unlock_node (rn); - zlog_info("ifaddr_ipv4_add(): address %pI4 is already added", - ifaddr); - return; - } - rn->info = ifp; -} - -static void -ifaddr_ipv4_delete (struct in_addr *ifaddr, struct interface *ifp) -{ - struct route_node *rn; - struct prefix_ipv4 p; - - p.family = AF_INET; - p.prefixlen = IPV4_MAX_PREFIXLEN; - p.prefix = *ifaddr; - - rn = route_node_lookup (ifaddr_ipv4_table, (struct prefix *) &p); - if (! rn) - { - zlog_info("%s: can't find address %pI4", __func__, ifaddr); - return; - } - rn->info = NULL; - route_unlock_node (rn); - route_unlock_node (rn); -} - -/* Lookup interface by interface's IP address or interface index. */ -static struct interface * -ifaddr_ipv4_lookup (struct in_addr *addr, ifindex_t ifindex) -{ - struct prefix_ipv4 p; - struct route_node *rn; - struct interface *ifp; - - if (addr) - { - p.family = AF_INET; - p.prefixlen = IPV4_MAX_PREFIXLEN; - p.prefix = *addr; - - rn = route_node_lookup (ifaddr_ipv4_table, (struct prefix *) &p); - if (! rn) - return NULL; - - ifp = rn->info; - route_unlock_node (rn); - return ifp; - } - else - return if_lookup_by_index(ifindex, VRF_DEFAULT); -} -#endif /* ifaddr_ipv4_table */ - void if_terminate(struct vrf *vrf) { struct interface *ifp; @@ -513,6 +513,8 @@ extern struct interface *if_create_name(const char *name, vrf_id_t vrf_id); /* Create new interface, adds to index list only */ extern struct interface *if_create_ifindex(ifindex_t ifindex, vrf_id_t vrf_id); extern struct interface *if_lookup_by_index(ifindex_t, vrf_id_t vrf_id); +extern struct interface *if_vrf_lookup_by_index_next(ifindex_t ifindex, + vrf_id_t vrf_id); extern struct interface *if_lookup_by_index_all_vrf(ifindex_t); extern struct interface *if_lookup_exact_address(const void *matchaddr, int family, vrf_id_t vrf_id); diff --git a/lib/lib_vty.c b/lib/lib_vty.c index cd8b5c9809..128261a39c 100644 --- a/lib/lib_vty.c +++ b/lib/lib_vty.c @@ -43,10 +43,14 @@ #include "vty.h" #include "command.h" -#ifdef HAVE_MALLINFO +#if defined(HAVE_MALLINFO2) || defined(HAVE_MALLINFO) static int show_memory_mallinfo(struct vty *vty) { +#if defined(HAVE_MALLINFO2) + struct mallinfo2 minfo = mallinfo2(); +#elif defined(HAVE_MALLINFO) struct mallinfo minfo = mallinfo(); +#endif char buf[MTYPE_MEMSTR_LEN]; vty_out(vty, "System allocator statistics:\n"); diff --git a/lib/libfrr.c b/lib/libfrr.c index 8e7777a1a9..51b97369c9 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -43,6 +43,7 @@ #include "frrcu.h" #include "frr_pthread.h" #include "defaults.h" +#include "frrscript.h" DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm)) DEFINE_HOOK(frr_very_late_init, (struct thread_master * tm), (tm)) @@ -55,6 +56,7 @@ char frr_vtydir[256]; const char frr_dbdir[] = DAEMON_DB_DIR; #endif const char frr_moduledir[] = MODULE_PATH; +const char frr_scriptdir[] = SCRIPT_PATH; char frr_protoname[256] = "NONE"; char frr_protonameinst[256] = "NONE"; @@ -69,6 +71,7 @@ static char vtypath_default[512]; bool debug_memstats_at_exit = false; static bool nodetach_term, nodetach_daemon; +static uint64_t startup_fds; static char comb_optstr[256]; static struct option comb_lo[64]; @@ -100,6 +103,7 @@ static void opt_extend(const struct optspec *os) #define OPTION_DB_FILE 1006 #define OPTION_LOGGING 1007 #define OPTION_LIMIT_FDS 1008 +#define OPTION_SCRIPTDIR 1009 static const struct option lo_always[] = { {"help", no_argument, NULL, 'h'}, @@ -110,6 +114,7 @@ static const struct option lo_always[] = { {"pathspace", required_argument, NULL, 'N'}, {"vty_socket", required_argument, NULL, OPTION_VTYSOCK}, {"moduledir", required_argument, NULL, OPTION_MODULEDIR}, + {"scriptdir", required_argument, NULL, OPTION_SCRIPTDIR}, {"log", required_argument, NULL, OPTION_LOG}, {"log-level", required_argument, NULL, OPTION_LOGLEVEL}, {"tcli", no_argument, NULL, OPTION_TCLI}, @@ -126,6 +131,7 @@ static const struct optspec os_always = { " -N, --pathspace Insert prefix into config & socket paths\n" " --vty_socket Override vty socket path\n" " --moduledir Override modules directory\n" + " --scriptdir Override scripts directory\n" " --log Set Logging to stdout, syslog, or file:<name>\n" " --log-level Set Logging Level to use, debug, info, warn, etc\n" " --tcli Use transaction-based CLI\n" @@ -336,6 +342,28 @@ void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv) strlcpy(frr_protonameinst, di->logname, sizeof(frr_protonameinst)); di->cli_mode = FRR_CLI_CLASSIC; + + /* we may be starting with extra FDs open for whatever purpose, + * e.g. logging, some module, etc. Recording them here allows later + * checking whether an fd is valid for such extension purposes, + * without this we could end up e.g. logging to a BGP session fd. + */ + startup_fds = 0; + for (int i = 0; i < 64; i++) { + struct stat st; + + if (fstat(i, &st)) + continue; + if (S_ISDIR(st.st_mode) || S_ISBLK(st.st_mode)) + continue; + + startup_fds |= UINT64_C(0x1) << (uint64_t)i; + } +} + +bool frr_is_startup_fd(int fd) +{ + return !!(startup_fds & (UINT64_C(0x1) << (uint64_t)fd)); } void frr_opt_add(const char *optstr, const struct option *longopts, @@ -533,6 +561,14 @@ static int frr_opt(int opt) } di->module_path = optarg; break; + case OPTION_SCRIPTDIR: + if (di->script_path) { + fprintf(stderr, "--scriptdir option specified more than once!\n"); + errors++; + break; + } + di->script_path = optarg; + break; case OPTION_TCLI: di->cli_mode = FRR_CLI_TRANSACTIONAL; break; @@ -717,6 +753,9 @@ struct thread_master *frr_init(void) lib_cmd_init(); frr_pthread_init(); +#ifdef HAVE_SCRIPTING + frrscript_init(di->script_path ? di->script_path : frr_scriptdir); +#endif log_ref_init(); log_ref_vty_init(); diff --git a/lib/libfrr.h b/lib/libfrr.h index 2e4dcbe093..825f502bdf 100644 --- a/lib/libfrr.h +++ b/lib/libfrr.h @@ -81,6 +81,7 @@ struct frr_daemon_info { #endif const char *vty_path; const char *module_path; + const char *script_path; const char *pathspace; bool zpathspace; @@ -137,7 +138,8 @@ extern __attribute__((__noreturn__)) void frr_help_exit(int status); extern struct thread_master *frr_init(void); extern const char *frr_get_progname(void); extern enum frr_cli_mode frr_get_cli_mode(void); -uint32_t frr_get_fd_limit(void); +extern uint32_t frr_get_fd_limit(void); +extern bool frr_is_startup_fd(int fd); DECLARE_HOOK(frr_late_init, (struct thread_master * tm), (tm)) DECLARE_HOOK(frr_very_late_init, (struct thread_master * tm), (tm)) @@ -162,6 +164,7 @@ extern char frr_zclientpath[256]; extern const char frr_sysconfdir[]; extern char frr_vtydir[256]; extern const char frr_moduledir[]; +extern const char frr_scriptdir[]; extern char frr_protoname[]; extern char frr_protonameinst[]; diff --git a/lib/link_state.c b/lib/link_state.c new file mode 100644 index 0000000000..6bd7ef703c --- /dev/null +++ b/lib/link_state.c @@ -0,0 +1,1282 @@ +/* + * Link State Database - link_state.c + * + * Author: Olivier Dugeon <olivier.dugeon@orange.com> + * + * Copyright (C) 2020 Orange http://www.orange.com + * + * This file is part of Free Range Routing (FRR). + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "if.h" +#include "linklist.h" +#include "log.h" +#include "command.h" +#include "termtable.h" +#include "memory.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "zclient.h" +#include "stream.h" +#include "link_state.h" + +/* Link State Memory allocation */ +DEFINE_MTYPE_STATIC(LIB, LS_DB, "Link State Database") + +/** + * Link State Node management functions + */ +struct ls_node *ls_node_new(struct ls_node_id adv, struct in_addr rid, + struct in6_addr rid6) +{ + struct ls_node *new; + + if (adv.origin == NONE) + return NULL; + + new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_node)); + new->adv = adv; + if (!IPV4_NET0(rid.s_addr)) { + new->router_id = rid; + SET_FLAG(new->flags, LS_NODE_ROUTER_ID); + } else { + if (adv.origin == OSPFv2 || adv.origin == STATIC + || adv.origin == DIRECT) { + new->router_id = adv.id.ip.addr; + SET_FLAG(new->flags, LS_NODE_ROUTER_ID); + } + } + if (!IN6_IS_ADDR_UNSPECIFIED(&rid6)) { + new->router6_id = rid6; + SET_FLAG(new->flags, LS_NODE_ROUTER_ID6); + } + return new; +} + +void ls_node_del(struct ls_node *node) +{ + XFREE(MTYPE_LS_DB, node); + node = NULL; +} + +int ls_node_same(struct ls_node *n1, struct ls_node *n2) +{ + if ((n1 && !n2) || (!n1 && n2)) + return 0; + + if (n1 == n2) + return 1; + + if (n1->flags != n2->flags) + return 0; + + if (n1->adv.origin != n2->adv.origin) + return 0; + + if (!memcmp(&n1->adv.id, &n2->adv.id, sizeof(struct ls_node_id))) + return 0; + + /* Do we need to test individually each field, instead performing a + * global memcmp? There is a risk that an old value that is bit masked + * i.e. corresponding flag = 0, will result into a false negative + */ + if (!memcmp(n1, n2, sizeof(struct ls_node))) + return 0; + else + return 1; +} + +/** + * Link State Attributes management functions + */ +struct ls_attributes *ls_attributes_new(struct ls_node_id adv, + struct in_addr local, + struct in6_addr local6, + uint32_t local_id) +{ + struct ls_attributes *new; + + if (adv.origin == NONE) + return NULL; + + new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_attributes)); + new->adv = adv; + if (!IPV4_NET0(local.s_addr)) { + new->standard.local = local; + SET_FLAG(new->flags, LS_ATTR_LOCAL_ADDR); + } + if (!IN6_IS_ADDR_UNSPECIFIED(&local6)) { + new->standard.local6 = local6; + SET_FLAG(new->flags, LS_ATTR_LOCAL_ADDR6); + } + if (local_id != 0) { + new->standard.local_id = local_id; + SET_FLAG(new->flags, LS_ATTR_LOCAL_ID); + } + + /* Check that almost one identifier is set */ + if (!CHECK_FLAG(new->flags, LS_ATTR_LOCAL_ADDR | LS_ATTR_LOCAL_ADDR6 + | LS_ATTR_LOCAL_ID)) { + XFREE(MTYPE_LS_DB, new); + return NULL; + } + + return new; +} + +void ls_attributes_del(struct ls_attributes *attr) +{ + if (!attr) + return; + + if (attr->srlgs) + XFREE(MTYPE_LS_DB, attr->srlgs); + + XFREE(MTYPE_LS_DB, attr); + attr = NULL; +} + +int ls_attributes_same(struct ls_attributes *l1, struct ls_attributes *l2) +{ + if ((l1 && !l2) || (!l1 && l2)) + return 0; + + if (l1 == l2) + return 1; + + if (l1->flags != l2->flags) + return 0; + + if (l1->adv.origin != l2->adv.origin) + return 0; + + if (!memcmp(&l1->adv.id, &l2->adv.id, sizeof(struct ls_node_id))) + return 0; + + /* Do we need to test individually each field, instead performing a + * global memcmp? There is a risk that an old value that is bit masked + * i.e. corresponding flag = 0, will result into a false negative + */ + if (!memcmp(l1, l2, sizeof(struct ls_attributes))) + return 0; + else + return 1; +} + +/** + * Link State Vertices management functions + */ +struct ls_vertex *ls_vertex_new(struct ls_node *node) +{ + struct ls_vertex *new; + + if (node == NULL) + return NULL; + + new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_vertex)); + new->node = node; + new->incoming_edges = list_new(); + new->outgoing_edges = list_new(); + new->prefixes = list_new(); + + return new; +} + +void ls_vertex_del(struct ls_vertex *vertex) +{ + if (vertex == NULL) + return; + + list_delete_all_node(vertex->incoming_edges); + list_delete_all_node(vertex->outgoing_edges); + list_delete_all_node(vertex->prefixes); + XFREE(MTYPE_LS_DB, vertex); + vertex = NULL; +} + +struct ls_vertex *ls_vertex_add(struct ls_ted *ted, struct ls_node *node) +{ + struct ls_vertex *new; + + if ((ted == NULL) || (node == NULL)) + return NULL; + + new = ls_vertex_new(node); + if (!new) + return NULL; + + /* set Key as the IPv4/Ipv6 Router ID or ISO System ID */ + switch (node->adv.origin) { + case OSPFv2: + case STATIC: + case DIRECT: + memcpy(&new->key, &node->adv.id.ip.addr, IPV4_MAX_BYTELEN); + break; + case ISIS_L1: + case ISIS_L2: + memcpy(&new->key, &node->adv.id.iso.sys_id, ISO_SYS_ID_LEN); + break; + default: + new->key = 0; + break; + } + + /* Remove Vertex if key is not set */ + if (new->key == 0) { + ls_vertex_del(new); + return NULL; + } + + /* Add Vertex to TED */ + vertices_add(&ted->vertices, new); + + return new; +} + +struct ls_vertex *ls_vertex_update(struct ls_ted *ted, struct ls_node *node) +{ + struct ls_vertex *old; + + if (node == NULL) + return NULL; + + old = ls_find_vertex_by_id(ted, node->adv); + if (old) { + if (!ls_node_same(old->node, node)) { + ls_node_del(old->node); + old->node = node; + } + return old; + } + + return ls_vertex_add(ted, node); +} + +void ls_vertex_remove(struct ls_ted *ted, struct ls_vertex *vertex) +{ + vertices_del(&ted->vertices, vertex); + ls_vertex_del(vertex); +} + +struct ls_vertex *ls_find_vertex_by_key(struct ls_ted *ted, const uint64_t key) +{ + struct ls_vertex node = {}; + + if (key == 0) + return NULL; + + node.key = key; + return vertices_find(&ted->vertices, &node); +} + +struct ls_vertex *ls_find_vertex_by_id(struct ls_ted *ted, + struct ls_node_id nid) +{ + struct ls_vertex node = {}; + + switch (nid.origin) { + case OSPFv2: + case STATIC: + case DIRECT: + memcpy(&node.key, &nid.id.ip.addr, IPV4_MAX_BYTELEN); + break; + case ISIS_L1: + case ISIS_L2: + memcpy(&node.key, &nid.id.iso.sys_id, ISO_SYS_ID_LEN); + break; + default: + return NULL; + } + + return vertices_find(&ted->vertices, &node); +} + +int ls_vertex_same(struct ls_vertex *v1, struct ls_vertex *v2) +{ + if ((v1 && !v2) || (!v1 && v2)) + return 0; + + if (!v1 && !v2) + return 1; + + if (v1->key != v2->key) + return 0; + + if (v1->node == v2->node) + return 1; + + return ls_node_same(v1->node, v2->node); +} + +/** + * Link State Edges management functions + */ + +/** + * This function allows to connect the Edge to the vertices present in the TED. + * A temporary vertex that corresponds to the source of this Edge i.e. the + * advertised router, is created if not found in the Data Base. If a Edge that + * corresponds to the reverse path is found, the Edge is attached to the + * destination vertex as destination and reverse Edge is attached to the source + * vertex as source. + * + * @param ted Link State Data Base + * @param edge Link State Edge to be attached + */ +static void ls_edge_connect_to(struct ls_ted *ted, struct ls_edge *edge) +{ + struct ls_vertex *vertex = NULL; + struct ls_node *node; + struct ls_edge *dst; + const struct in_addr inaddr_any = {.s_addr = INADDR_ANY}; + + /* First, search if there is a Vertex that correspond to the Node ID */ + vertex = ls_find_vertex_by_id(ted, edge->attributes->adv); + if (vertex == NULL) { + /* Create a new temporary Node & Vertex if not found */ + node = ls_node_new(edge->attributes->adv, inaddr_any, + in6addr_any); + vertex = ls_vertex_add(ted, node); + } + /* and attach the edge as source to the vertex */ + listnode_add(vertex->outgoing_edges, edge); + edge->source = vertex; + + /* Then search if there is a reverse Edge */ + dst = ls_find_edge_by_destination(ted, edge->attributes); + /* attach the destination edge to the vertex */ + if (dst) { + listnode_add(vertex->incoming_edges, dst); + dst->destination = vertex; + /* and destination vertex to this edge */ + vertex = dst->source; + listnode_add(vertex->incoming_edges, edge); + edge->destination = vertex; + } +} + +struct ls_edge *ls_edge_add(struct ls_ted *ted, + struct ls_attributes *attributes) +{ + struct ls_edge *new; + + if (attributes == NULL) + return NULL; + + new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_edge)); + new->attributes = attributes; + /* Key is the IPv4 local address */ + if (!IPV4_NET0(attributes->standard.local.s_addr)) + new->key = ((uint64_t)attributes->standard.local.s_addr) + & 0xffffffff; + /* or the IPv6 local address if IPv4 is not defined */ + else if (!IN6_IS_ADDR_UNSPECIFIED(&attributes->standard.local6)) + new->key = (uint64_t)(attributes->standard.local6.s6_addr32[0] + & 0xffffffff) + | ((uint64_t)attributes->standard.local6.s6_addr32[1] + << 32); + /* of local identifier if no IP addresses are defined */ + else if (attributes->standard.local_id != 0) + new->key = (uint64_t)( + (attributes->standard.local_id & 0xffffffff) + | ((uint64_t)attributes->standard.remote_id << 32)); + + /* Remove Edge if key is not known */ + if (new->key == 0) { + XFREE(MTYPE_LS_DB, new); + return NULL; + } + + edges_add(&ted->edges, new); + + /* Finally, connect edge to vertices */ + ls_edge_connect_to(ted, new); + + return new; +} + +struct ls_edge *ls_find_edge_by_key(struct ls_ted *ted, const uint64_t key) +{ + struct ls_edge edge = {}; + + if (key == 0) + return NULL; + + edge.key = key; + return edges_find(&ted->edges, &edge); +} + +struct ls_edge *ls_find_edge_by_source(struct ls_ted *ted, + struct ls_attributes *attributes) +{ + struct ls_edge edge = {}; + + if (attributes == NULL) + return NULL; + + /* Key is the IPv4 local address */ + if (!IPV4_NET0(attributes->standard.local.s_addr)) + edge.key = ((uint64_t)attributes->standard.local.s_addr) + & 0xffffffff; + /* or the IPv6 local address if IPv4 is not defined */ + else if (!IN6_IS_ADDR_UNSPECIFIED(&attributes->standard.local6)) + edge.key = (uint64_t)(attributes->standard.local6.s6_addr32[0] + & 0xffffffff) + | ((uint64_t)attributes->standard.local6.s6_addr32[1] + << 32); + /* of local identifier if no IP addresses are defined */ + else if (attributes->standard.local_id != 0) + edge.key = (uint64_t)( + (attributes->standard.local_id & 0xffffffff) + | ((uint64_t)attributes->standard.remote_id << 32)); + + if (edge.key == 0) + return NULL; + + return edges_find(&ted->edges, &edge); +} + +struct ls_edge *ls_find_edge_by_destination(struct ls_ted *ted, + struct ls_attributes *attributes) +{ + struct ls_edge edge = {}; + + if (attributes == NULL) + return NULL; + + /* Key is the IPv4 local address */ + if (!IPV4_NET0(attributes->standard.remote.s_addr)) + edge.key = ((uint64_t)attributes->standard.remote.s_addr) + & 0xffffffff; + /* or the IPv6 local address if IPv4 is not defined */ + else if (!IN6_IS_ADDR_UNSPECIFIED(&attributes->standard.remote6)) + edge.key = + (uint64_t)(attributes->standard.remote6.s6_addr32[0] + & 0xffffffff) + | ((uint64_t)attributes->standard.remote6.s6_addr32[1] + << 32); + /* of local identifier if no IP addresses are defined */ + else if (attributes->standard.remote_id != 0) + edge.key = (uint64_t)( + (attributes->standard.remote_id & 0xffffffff) + | ((uint64_t)attributes->standard.local_id << 32)); + + if (edge.key == 0) + return NULL; + + return edges_find(&ted->edges, &edge); +} + +struct ls_edge *ls_edge_update(struct ls_ted *ted, + struct ls_attributes *attributes) +{ + struct ls_edge *old; + + if (attributes == NULL) + return NULL; + + /* First, search for an existing Edge */ + old = ls_find_edge_by_source(ted, attributes); + if (old) { + /* Check if attributes are similar */ + if (!ls_attributes_same(old->attributes, attributes)) { + ls_attributes_del(old->attributes); + old->attributes = attributes; + } + return old; + } + + /* If not found, add new Edge from the attributes */ + return ls_edge_add(ted, attributes); +} + +void ls_edge_del(struct ls_ted *ted, struct ls_edge *edge) +{ + /* Fist disconnect Edge */ + ls_disconnect_edge(edge); + /* Then remove it from the Data Base */ + edges_del(&ted->edges, edge); + XFREE(MTYPE_LS_DB, edge); +} + +/** + * Link State Subnet Management functions. + */ +struct ls_subnet *ls_subnet_add(struct ls_ted *ted, + struct ls_prefix *ls_pref) +{ + struct ls_subnet *new; + struct ls_vertex *vertex; + struct ls_node *node; + const struct in_addr inaddr_any = {.s_addr = INADDR_ANY}; + + if (ls_pref == NULL) + return NULL; + + new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_subnet)); + new->ls_pref = ls_pref; + new->key = ls_pref->pref; + + /* Find Vertex */ + vertex = ls_find_vertex_by_id(ted, ls_pref->adv); + if (vertex == NULL) { + /* Create a new temporary Node & Vertex if not found */ + node = ls_node_new(ls_pref->adv, inaddr_any, in6addr_any); + vertex = ls_vertex_add(ted, node); + } + /* And attach the subnet to the corresponding Vertex */ + new->vertex = vertex; + listnode_add(vertex->prefixes, new); + + subnets_add(&ted->subnets, new); + + return new; +} + +void ls_subnet_del(struct ls_ted *ted, struct ls_subnet *subnet) +{ + subnets_del(&ted->subnets, subnet); + XFREE(MTYPE_LS_DB, subnet); +} + +struct ls_subnet *ls_find_subnet(struct ls_ted *ted, const struct prefix prefix) +{ + struct ls_subnet subnet = {}; + + subnet.key = prefix; + return subnets_find(&ted->subnets, &subnet); +} + +/** + * Link State TED management functions + */ +struct ls_ted *ls_ted_new(const uint32_t key, const char *name, + uint32_t as_number) +{ + 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; + new->as_number = as_number; + strlcpy(new->name, name, MAX_NAME_LENGTH); + + /* Initialize the various RB tree */ + vertices_init(&new->vertices); + edges_init(&new->edges); + subnets_init(&new->subnets); + + return new; +} + +void ls_ted_del(struct ls_ted *ted) +{ + if (ted == NULL) + return; + + /* Release RB Tree */ + vertices_fini(&ted->vertices); + edges_fini(&ted->edges); + subnets_fini(&ted->subnets); + + XFREE(MTYPE_LS_DB, ted); + ted = NULL; +} + +void ls_connect(struct ls_vertex *vertex, struct ls_edge *edge, bool source) +{ + if (vertex == NULL || edge == NULL) + return; + + if (source) { + listnode_add(vertex->outgoing_edges, edge); + edge->source = vertex; + } else { + listnode_add(vertex->incoming_edges, edge); + edge->destination = vertex; + } +} + +void ls_disconnect(struct ls_vertex *vertex, struct ls_edge *edge, bool source) +{ + + if (vertex == NULL || edge == NULL) + return; + + if (source) { + listnode_delete(vertex->outgoing_edges, edge); + edge->source = NULL; + } else { + listnode_delete(vertex->incoming_edges, edge); + edge->destination = NULL; + } +} + +void ls_connect_vertices(struct ls_vertex *src, struct ls_vertex *dst, + struct ls_edge *edge) +{ + if (edge == NULL) + return; + + edge->source = src; + edge->destination = dst; + + if (src != NULL) + listnode_add(src->outgoing_edges, edge); + + if (dst != NULL) + listnode_add(dst->incoming_edges, edge); + +} + +void ls_disconnect_edge(struct ls_edge *edge) +{ + if (edge == NULL) + return; + + ls_disconnect(edge->source, edge, true); + ls_disconnect(edge->destination, edge, false); +} + +/** + * Link State Message management functions + */ + +static struct ls_node *ls_parse_node(struct stream *s) +{ + struct ls_node *node; + 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); + if (CHECK_FLAG(node->flags, LS_NODE_NAME)) { + STREAM_GETC(s, len); + STREAM_GET(node->name, s, len); + } + if (CHECK_FLAG(node->flags, LS_NODE_ROUTER_ID)) + node->router_id.s_addr = stream_get_ipv4(s); + if (CHECK_FLAG(node->flags, LS_NODE_ROUTER_ID6)) + STREAM_GET(&node->router6_id, s, IPV6_MAX_BYTELEN); + if (CHECK_FLAG(node->flags, LS_NODE_FLAG)) + STREAM_GETC(s, node->node_flag); + if (CHECK_FLAG(node->flags, LS_NODE_TYPE)) + STREAM_GETC(s, node->type); + if (CHECK_FLAG(node->flags, LS_NODE_AS_NUMBER)) + STREAM_GETL(s, node->as_number); + if (CHECK_FLAG(node->flags, LS_NODE_SR)) { + STREAM_GETL(s, node->srgb.lower_bound); + STREAM_GETL(s, node->srgb.range_size); + STREAM_GETC(s, node->srgb.flag); + STREAM_GET(node->algo, s, 2); + } + if (CHECK_FLAG(node->flags, LS_NODE_SRLB)) { + STREAM_GETL(s, node->srlb.lower_bound); + STREAM_GETL(s, node->srlb.range_size); + } + if (CHECK_FLAG(node->flags, LS_NODE_MSD)) + STREAM_GETC(s, node->msd); + + return node; + +stream_failure: + zlog_err("LS(%s): Could not parse Link State Node. Abort!", __func__); + XFREE(MTYPE_LS_DB, node); + return NULL; +} + +static struct ls_attributes *ls_parse_attributes(struct stream *s) +{ + struct ls_attributes *attr; + 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)); + STREAM_GETL(s, attr->flags); + if (CHECK_FLAG(attr->flags, LS_ATTR_NAME)) { + STREAM_GETC(s, len); + STREAM_GET(attr->name, s, len); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_METRIC)) + STREAM_GETL(s, attr->standard.metric); + if (CHECK_FLAG(attr->flags, LS_ATTR_TE_METRIC)) + STREAM_GETL(s, attr->standard.te_metric); + if (CHECK_FLAG(attr->flags, LS_ATTR_ADM_GRP)) + STREAM_GETL(s, attr->standard.admin_group); + if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR)) + attr->standard.local.s_addr = stream_get_ipv4(s); + if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR)) + attr->standard.remote.s_addr = stream_get_ipv4(s); + if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6)) + STREAM_GET(&attr->standard.local6, s, IPV6_MAX_BYTELEN); + if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR6)) + STREAM_GET(&attr->standard.remote6, s, IPV6_MAX_BYTELEN); + if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID)) + STREAM_GETL(s, attr->standard.local_id); + if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ID)) + STREAM_GETL(s, attr->standard.remote_id); + if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_BW)) + STREAM_GETF(s, attr->standard.max_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_RSV_BW)) + STREAM_GETF(s, attr->standard.max_rsv_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_UNRSV_BW)) + for (len = 0; len < MAX_CLASS_TYPE; len++) + STREAM_GETF(s, attr->standard.unrsv_bw[len]); + if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_AS)) + STREAM_GETL(s, attr->standard.remote_as); + if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR)) + attr->standard.remote_addr.s_addr = stream_get_ipv4(s); + if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR6)) + STREAM_GET(&attr->standard.remote_addr6, s, IPV6_MAX_BYTELEN); + if (CHECK_FLAG(attr->flags, LS_ATTR_DELAY)) + STREAM_GETL(s, attr->extended.delay); + if (CHECK_FLAG(attr->flags, LS_ATTR_MIN_MAX_DELAY)) { + STREAM_GETL(s, attr->extended.min_delay); + STREAM_GETL(s, attr->extended.max_delay); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_JITTER)) + STREAM_GETL(s, attr->extended.jitter); + if (CHECK_FLAG(attr->flags, LS_ATTR_PACKET_LOSS)) + STREAM_GETL(s, attr->extended.pkt_loss); + if (CHECK_FLAG(attr->flags, LS_ATTR_AVA_BW)) + STREAM_GETF(s, attr->extended.ava_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_RSV_BW)) + STREAM_GETF(s, attr->extended.rsv_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_USE_BW)) + STREAM_GETF(s, attr->extended.used_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_ADJ_SID)) { + STREAM_GETL(s, attr->adj_sid[0].sid); + STREAM_GETC(s, attr->adj_sid[0].flags); + STREAM_GETC(s, attr->adj_sid[0].weight); + if (attr->adv.origin == ISIS_L1 || attr->adv.origin == ISIS_L2) + STREAM_GET(attr->adj_sid[0].neighbor.sysid, s, + ISO_SYS_ID_LEN); + else if (attr->adv.origin == OSPFv2) + attr->adj_sid[0].neighbor.addr.s_addr = + stream_get_ipv4(s); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SID)) { + STREAM_GETL(s, attr->adj_sid[1].sid); + STREAM_GETC(s, attr->adj_sid[1].flags); + STREAM_GETC(s, attr->adj_sid[1].weight); + if (attr->adv.origin == ISIS_L1 || attr->adv.origin == ISIS_L2) + STREAM_GET(attr->adj_sid[1].neighbor.sysid, s, + ISO_SYS_ID_LEN); + else if (attr->adv.origin == OSPFv2) + attr->adj_sid[1].neighbor.addr.s_addr = + stream_get_ipv4(s); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_SRLG)) { + STREAM_GETC(s, len); + attr->srlgs = XCALLOC(MTYPE_LS_DB, len*sizeof(uint32_t)); + attr->srlg_len = len; + for (len = 0; len < attr->srlg_len; len++) + STREAM_GETL(s, attr->srlgs[len]); + } + + return attr; + +stream_failure: + zlog_err("LS(%s): Could not parse Link State Attributes. Abort!", + __func__); + /* Clean memeory allocation */ + if (attr->srlgs != NULL) + XFREE(MTYPE_LS_DB, attr->srlgs); + XFREE(MTYPE_LS_DB, attr); + return NULL; + +} + +static struct ls_prefix *ls_parse_prefix(struct stream *s) +{ + struct ls_prefix *ls_pref; + 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); + STREAM_GETC(s, ls_pref->pref.family); + STREAM_GETW(s, ls_pref->pref.prefixlen); + len = prefix_blen(&ls_pref->pref); + STREAM_GET(&ls_pref->pref.u.prefix, s, len); + if (CHECK_FLAG(ls_pref->flags, LS_PREF_IGP_FLAG)) + STREAM_GETC(s, ls_pref->igp_flag); + if (CHECK_FLAG(ls_pref->flags, LS_PREF_ROUTE_TAG)) + STREAM_GETL(s, ls_pref->route_tag); + if (CHECK_FLAG(ls_pref->flags, LS_PREF_EXTENDED_TAG)) + STREAM_GETQ(s, ls_pref->extended_tag); + if (CHECK_FLAG(ls_pref->flags, LS_PREF_METRIC)) + STREAM_GETL(s, ls_pref->metric); + if (CHECK_FLAG(ls_pref->flags, LS_PREF_SR)) { + STREAM_GETL(s, ls_pref->sr.sid); + STREAM_GETC(s, ls_pref->sr.sid_flag); + STREAM_GETC(s, ls_pref->sr.algo); + } + + return ls_pref; + +stream_failure: + zlog_err("LS(%s): Could not parse Link State Prefix. Abort!", __func__); + XFREE(MTYPE_LS_DB, ls_pref); + return NULL; +} + +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); + STREAM_GETC(s, msg->type); + STREAM_GET(&msg->remote_id, s, sizeof(struct ls_node_id)); + + /* Read Message Payload */ + switch (msg->type) { + case LS_MSG_TYPE_NODE: + msg->data.node = ls_parse_node(s); + break; + case LS_MSG_TYPE_ATTRIBUTES: + msg->data.attr = ls_parse_attributes(s); + break; + case LS_MSG_TYPE_PREFIX: + msg->data.prefix = ls_parse_prefix(s); + break; + default: + zlog_err("Unsupported Payload"); + goto stream_failure; + } + + if (msg->data.node == NULL || msg->data.attr == NULL + || msg->data.prefix == NULL) + goto stream_failure; + + return msg; + +stream_failure: + zlog_err("LS(%s): Could not parse LS message. Abort!", __func__); + XFREE(MTYPE_LS_DB, msg); + return NULL; +} + +static int ls_format_node(struct stream *s, struct ls_node *node) +{ + size_t len; + + /* Push Advertise node information first */ + stream_put(s, &node->adv, sizeof(struct ls_node_id)); + + /* Push Flags & Origin then Node information if there are present */ + stream_putw(s, node->flags); + if (CHECK_FLAG(node->flags, LS_NODE_NAME)) { + len = strlen(node->name); + stream_putc(s, len + 1); + stream_put(s, node->name, len); + stream_putc(s, '\0'); + } + if (CHECK_FLAG(node->flags, LS_NODE_ROUTER_ID)) + stream_put_ipv4(s, node->router_id.s_addr); + if (CHECK_FLAG(node->flags, LS_NODE_ROUTER_ID6)) + stream_put(s, &node->router6_id, IPV6_MAX_BYTELEN); + if (CHECK_FLAG(node->flags, LS_NODE_FLAG)) + stream_putc(s, node->node_flag); + if (CHECK_FLAG(node->flags, LS_NODE_TYPE)) + stream_putc(s, node->type); + if (CHECK_FLAG(node->flags, LS_NODE_AS_NUMBER)) + stream_putl(s, node->as_number); + if (CHECK_FLAG(node->flags, LS_NODE_SR)) { + stream_putl(s, node->srgb.lower_bound); + stream_putl(s, node->srgb.range_size); + stream_putc(s, node->srgb.flag); + stream_put(s, node->algo, 2); + } + if (CHECK_FLAG(node->flags, LS_NODE_SRLB)) { + stream_putl(s, node->srlb.lower_bound); + stream_putl(s, node->srlb.range_size); + } + if (CHECK_FLAG(node->flags, LS_NODE_MSD)) + stream_putc(s, node->msd); + + return 0; +} + +static int ls_format_attributes(struct stream *s, struct ls_attributes *attr) +{ + size_t len; + + /* Push Advertise node information first */ + stream_put(s, &attr->adv, sizeof(struct ls_node_id)); + + /* Push Flags & Origin then LS attributes if there are present */ + stream_putl(s, attr->flags); + if (CHECK_FLAG(attr->flags, LS_ATTR_NAME)) { + len = strlen(attr->name); + stream_putc(s, len + 1); + stream_put(s, attr->name, len); + stream_putc(s, '\0'); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_METRIC)) + stream_putl(s, attr->standard.metric); + if (CHECK_FLAG(attr->flags, LS_ATTR_TE_METRIC)) + stream_putl(s, attr->standard.te_metric); + if (CHECK_FLAG(attr->flags, LS_ATTR_ADM_GRP)) + stream_putl(s, attr->standard.admin_group); + if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR)) + stream_put_ipv4(s, attr->standard.local.s_addr); + if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR)) + stream_put_ipv4(s, attr->standard.remote.s_addr); + if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6)) + stream_put(s, &attr->standard.local6, IPV6_MAX_BYTELEN); + if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR6)) + stream_put(s, &attr->standard.remote6, IPV6_MAX_BYTELEN); + if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID)) + stream_putl(s, attr->standard.local_id); + if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ID)) + stream_putl(s, attr->standard.remote_id); + if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_BW)) + stream_putf(s, attr->standard.max_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_RSV_BW)) + stream_putf(s, attr->standard.max_rsv_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_UNRSV_BW)) + for (len = 0; len < MAX_CLASS_TYPE; len++) + stream_putf(s, attr->standard.unrsv_bw[len]); + if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_AS)) + stream_putl(s, attr->standard.remote_as); + if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR)) + stream_put_ipv4(s, attr->standard.remote_addr.s_addr); + if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR6)) + stream_put(s, &attr->standard.remote_addr6, IPV6_MAX_BYTELEN); + if (CHECK_FLAG(attr->flags, LS_ATTR_DELAY)) + stream_putl(s, attr->extended.delay); + if (CHECK_FLAG(attr->flags, LS_ATTR_MIN_MAX_DELAY)) { + stream_putl(s, attr->extended.min_delay); + stream_putl(s, attr->extended.max_delay); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_JITTER)) + stream_putl(s, attr->extended.jitter); + if (CHECK_FLAG(attr->flags, LS_ATTR_PACKET_LOSS)) + stream_putl(s, attr->extended.pkt_loss); + if (CHECK_FLAG(attr->flags, LS_ATTR_AVA_BW)) + stream_putf(s, attr->extended.ava_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_RSV_BW)) + stream_putf(s, attr->extended.rsv_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_USE_BW)) + stream_putf(s, attr->extended.used_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_ADJ_SID)) { + stream_putl(s, attr->adj_sid[0].sid); + stream_putc(s, attr->adj_sid[0].flags); + stream_putc(s, attr->adj_sid[0].weight); + if (attr->adv.origin == ISIS_L1 || attr->adv.origin == ISIS_L2) + stream_put(s, attr->adj_sid[0].neighbor.sysid, + ISO_SYS_ID_LEN); + else if (attr->adv.origin == OSPFv2) + stream_put_ipv4(s, + attr->adj_sid[0].neighbor.addr.s_addr); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SID)) { + stream_putl(s, attr->adj_sid[1].sid); + stream_putc(s, attr->adj_sid[1].flags); + stream_putc(s, attr->adj_sid[1].weight); + if (attr->adv.origin == ISIS_L1 || attr->adv.origin == ISIS_L2) + stream_put(s, attr->adj_sid[1].neighbor.sysid, + ISO_SYS_ID_LEN); + else if (attr->adv.origin == OSPFv2) + stream_put_ipv4(s, + attr->adj_sid[1].neighbor.addr.s_addr); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_SRLG)) { + stream_putc(s, attr->srlg_len); + for (len = 0; len < attr->srlg_len; len++) + stream_putl(s, attr->srlgs[len]); + } + + return 0; +} + +static int ls_format_prefix(struct stream *s, struct ls_prefix *ls_pref) +{ + size_t len; + + /* Push Advertise node information first */ + stream_put(s, &ls_pref->adv, sizeof(struct ls_node_id)); + + /* Push Flags, Origin & Prefix then information if there are present */ + stream_putw(s, ls_pref->flags); + stream_putc(s, ls_pref->pref.family); + stream_putw(s, ls_pref->pref.prefixlen); + len = prefix_blen(&ls_pref->pref); + stream_put(s, &ls_pref->pref.u.prefix, len); + if (CHECK_FLAG(ls_pref->flags, LS_PREF_IGP_FLAG)) + stream_putc(s, ls_pref->igp_flag); + if (CHECK_FLAG(ls_pref->flags, LS_PREF_ROUTE_TAG)) + stream_putl(s, ls_pref->route_tag); + if (CHECK_FLAG(ls_pref->flags, LS_PREF_EXTENDED_TAG)) + stream_putq(s, ls_pref->extended_tag); + if (CHECK_FLAG(ls_pref->flags, LS_PREF_METRIC)) + stream_putl(s, ls_pref->metric); + if (CHECK_FLAG(ls_pref->flags, LS_PREF_SR)) { + stream_putl(s, ls_pref->sr.sid); + stream_putc(s, ls_pref->sr.sid_flag); + stream_putc(s, ls_pref->sr.algo); + } + + return 0; +} + +static int ls_format_msg(struct stream *s, struct ls_message *msg) +{ + + /* Prepare Link State header */ + stream_putc(s, msg->event); + stream_putc(s, msg->type); + stream_put(s, &msg->remote_id, sizeof(struct ls_node_id)); + + /* Add Message Payload */ + switch (msg->type) { + case LS_MSG_TYPE_NODE: + return ls_format_node(s, msg->data.node); + case LS_MSG_TYPE_ATTRIBUTES: + return ls_format_attributes(s, msg->data.attr); + case LS_MSG_TYPE_PREFIX: + return ls_format_prefix(s, msg->data.prefix); + default: + zlog_warn("Unsupported Payload"); + break; + } + + return -1; +} + +int ls_send_msg(struct zclient *zclient, struct ls_message *msg, + struct zapi_opaque_reg_info *dst) +{ + struct stream *s; + uint16_t flags = 0; + + /* Check buffer size */ + if (STREAM_SIZE(zclient->obuf) < + (ZEBRA_HEADER_SIZE + sizeof(uint32_t) + sizeof(msg))) + return -1; + + s = zclient->obuf; + stream_reset(s); + + zclient_create_header(s, ZEBRA_OPAQUE_MESSAGE, VRF_DEFAULT); + + /* Send sub-type, flags and destination for unicast message */ + stream_putl(s, LINK_STATE_UPDATE); + if (dst != NULL) { + SET_FLAG(flags, ZAPI_OPAQUE_FLAG_UNICAST); + stream_putw(s, flags); + /* Send destination client info */ + stream_putc(s, dst->proto); + stream_putw(s, dst->instance); + stream_putl(s, dst->session_id); + } else + stream_putw(s, flags); + + /* Format Link State message */ + if (ls_format_msg(s, msg) < 0) { + stream_reset(s); + return -1; + } + + /* Put length into the header at the start of the stream. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + return zclient_send_message(zclient); +} + +struct ls_message *ls_vertex2msg(struct ls_message *msg, + struct ls_vertex *vertex) +{ + /* Allocate space if needed */ + if (msg == NULL) + msg = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_message)); + else + memset(msg, 0, sizeof(*msg)); + + msg->type = LS_MSG_TYPE_NODE; + msg->data.node = vertex->node; + msg->remote_id.origin = NONE; + + return msg; +} + +struct ls_message *ls_edge2msg(struct ls_message *msg, struct ls_edge *edge) +{ + /* Allocate space if needed */ + if (msg == NULL) + msg = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_message)); + else + memset(msg, 0, sizeof(*msg)); + + msg->type = LS_MSG_TYPE_ATTRIBUTES; + msg->data.attr = edge->attributes; + if (edge->destination != NULL) + msg->remote_id = edge->destination->node->adv; + else + msg->remote_id.origin = NONE; + + return msg; +} + +struct ls_message *ls_subnet2msg(struct ls_message *msg, + struct ls_subnet *subnet) +{ + /* Allocate space if needed */ + if (msg == NULL) + msg = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_message)); + else + memset(msg, 0, sizeof(*msg)); + + msg->type = LS_MSG_TYPE_PREFIX; + msg->data.prefix = subnet->ls_pref; + msg->remote_id.origin = NONE; + + return msg; +} + +void ls_delete_msg(struct ls_message *msg) +{ + if (msg == NULL) + return; + + switch (msg->type) { + case LS_MSG_TYPE_NODE: + if (msg->data.node) + XFREE(MTYPE_LS_DB, msg->data.node); + break; + case LS_MSG_TYPE_ATTRIBUTES: + if (msg->data.attr) + XFREE(MTYPE_LS_DB, msg->data.attr); + break; + case LS_MSG_TYPE_PREFIX: + if (msg->data.prefix) + XFREE(MTYPE_LS_DB, msg->data.prefix); + break; + default: + break; + } + + XFREE(MTYPE_LS_DB, msg); +} + +int ls_sync_ted(struct ls_ted *ted, struct zclient *zclient, + struct zapi_opaque_reg_info *dst) +{ + struct ls_vertex *vertex; + struct ls_edge *edge; + struct ls_subnet *subnet; + struct ls_message msg; + + /* Prepare message */ + msg.event = LS_MSG_EVENT_SYNC; + + /* Loop TED, start sending Node, then Attributes and finally Prefix */ + frr_each(vertices, &ted->vertices, vertex) { + ls_vertex2msg(&msg, vertex); + ls_send_msg(zclient, &msg, dst); + } + frr_each(edges, &ted->edges, edge) { + ls_edge2msg(&msg, edge); + ls_send_msg(zclient, &msg, dst); + } + frr_each(subnets, &ted->subnets, subnet) { + ls_subnet2msg(&msg, subnet); + ls_send_msg(zclient, &msg, dst); + } + return 0; +} + +void ls_dump_ted(struct ls_ted *ted) +{ + struct ls_vertex *vertex; + struct ls_edge *edge; + struct ls_subnet *subnet; + struct ls_message msg; + + zlog_debug("(%s) Ted init", __func__); + /* Prepare message */ + msg.event = LS_MSG_EVENT_SYNC; + + /* Loop TED, start printing Node, then Attributes and finally Prefix */ + frr_each(vertices, &ted->vertices, vertex) { + ls_vertex2msg(&msg, vertex); + zlog_debug(" Ted node (%s %pI4 %s)", + vertex->node->name[0] ? vertex->node->name + : "no name node", + &vertex->node->router_id, + vertex->node->adv.origin == DIRECT ? "DIRECT" + : "NO DIRECT"); + struct listnode *lst_node; + struct ls_edge *vertex_edge; + + for (ALL_LIST_ELEMENTS_RO(vertex->incoming_edges, lst_node, + vertex_edge)) { + zlog_debug( + " inc edge key:%lldn attr key:%pI4 loc:(%pI4) rmt:(%pI4)", + vertex_edge->key, + &vertex_edge->attributes->adv.id.ip.addr, + &vertex_edge->attributes->standard.local, + &vertex_edge->attributes->standard.remote); + } + for (ALL_LIST_ELEMENTS_RO(vertex->outgoing_edges, lst_node, + vertex_edge)) { + zlog_debug( + " out edge key:%lld attr key:%pI4 loc:(%pI4) rmt:(%pI4)", + vertex_edge->key, + &vertex_edge->attributes->adv.id.ip.addr, + &vertex_edge->attributes->standard.local, + &vertex_edge->attributes->standard.remote); + } + } + frr_each(edges, &ted->edges, edge) { + ls_edge2msg(&msg, edge); + zlog_debug(" Ted edge key:%lld src:%s dst:%s", edge->key, + edge->source ? edge->source->node->name + : "no_source", + edge->destination ? edge->destination->node->name + : "no_dest"); + } + frr_each(subnets, &ted->subnets, subnet) { + ls_subnet2msg(&msg, subnet); + zlog_debug( + " Ted subnet key:%pFX vertex:%pI4 pfx:%pFX", + &subnet->key, + &subnet->vertex->node->adv.id.ip.addr, + &subnet->ls_pref->pref); + } + zlog_debug("(%s) Ted end", __func__); +} diff --git a/lib/link_state.h b/lib/link_state.h new file mode 100644 index 0000000000..93669f5b23 --- /dev/null +++ b/lib/link_state.h @@ -0,0 +1,780 @@ +/* + * Link State Database definition - ted.h + * + * Author: Olivier Dugeon <olivier.dugeon@orange.com> + * + * Copyright (C) 2020 Orange http://www.orange.com + * + * This file is part of Free Range Routing (FRR). + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _FRR_LINK_STATE_H_ +#define _FRR_LINK_STATE_H_ + +#include "typesafe.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * This file defines the model used to implement a Link State Database + * suitable to be used by various protocol like RSVP-TE, BGP-LS, PCEP ... + * This database is normally fulfill by the link state routing protocol, + * commonly OSPF or ISIS, carrying Traffic Engineering information within + * Link State Attributes. See, RFC3630.(OSPF-TE) and RFC5305 (ISIS-TE). + * + * At least, 3 types of Link State structure are defined: + * - Link State Node that groups all information related to a node + * - Link State Attributes that groups all information related to a link + * - Link State Prefix that groups all information related to a prefix + * + * These 3 types of structures are those handled by BGP-LS (see RFC7752). + * + * Each structure, in addition to the specific parameters, embed the node + * identifier which advertises the Link State and a bit mask as flags to + * indicates which parameters are valid i.e. for which the value corresponds + * to a Link State information convey by the routing protocol. + * Node identifier is composed of the route id as IPv4 address plus the area + * id for OSPF and the ISO System id plus the IS-IS level for IS-IS. + */ + +/* Link State Common definitions */ +#define MAX_NAME_LENGTH 256 +#define ISO_SYS_ID_LEN 6 + +/* Type of Node */ +enum ls_node_type { + STANDARD, /* a P or PE node */ + ABR, /* an Array Border Node */ + ASBR, /* an Autonomous System Border Node */ + PSEUDO, /* a Pseudo Node */ +}; + +/* Origin of the Link State information */ +enum ls_origin {NONE = 0, ISIS_L1, ISIS_L2, OSPFv2, DIRECT, STATIC}; + +/** + * Link State Node Identifier as: + * - IPv4 address + Area ID for OSPF + * - ISO System ID + ISIS Level for ISIS + */ +struct ls_node_id { + enum ls_origin origin; /* Origin of the LS information */ + union { + struct { + struct in_addr addr; /* OSPF Router IS */ + struct in_addr area_id; /* OSPF Area ID */ + } ip; + struct { + uint8_t sys_id[ISO_SYS_ID_LEN]; /* ISIS System ID */ + uint8_t level; /* ISIS Level */ + uint8_t padding; + } iso; + } id __attribute__((aligned(8))); +}; + +/* Link State flags to indicate which Node parameters are valid */ +#define LS_NODE_UNSET 0x0000 +#define LS_NODE_NAME 0x0001 +#define LS_NODE_ROUTER_ID 0x0002 +#define LS_NODE_ROUTER_ID6 0x0004 +#define LS_NODE_FLAG 0x0008 +#define LS_NODE_TYPE 0x0010 +#define LS_NODE_AS_NUMBER 0x0020 +#define LS_NODE_SR 0x0040 +#define LS_NODE_SRLB 0x0080 +#define LS_NODE_MSD 0x0100 + +/* Link State Node structure */ +struct ls_node { + uint16_t flags; /* Flag for parameters validity */ + struct ls_node_id adv; /* Adv. Router of this Link State */ + char name[MAX_NAME_LENGTH]; /* Name of the Node (IS-IS only) */ + struct in_addr router_id; /* IPv4 Router ID */ + struct in6_addr router6_id; /* IPv6 Router ID */ + uint8_t node_flag; /* IS-IS or OSPF Node flag */ + enum node_type type; /* Type of Node */ + uint32_t as_number; /* Local or neighbor AS number */ + struct { /* Segment Routing Global Block */ + uint32_t lower_bound; /* MPLS label lower bound */ + uint32_t range_size; /* MPLS label range size */ + uint8_t flag; /* IS-IS SRGB flags */ + } srgb; +#define LS_NODE_SRGB_SIZE 9 + struct { /* Segment Routing Local Block */ + uint32_t lower_bound; /* MPLS label lower bound */ + uint32_t range_size; /* MPLS label range size */ + } srlb; +#define LS_NODE_SRLB_SIZE 8 + uint8_t algo[2]; /* Segment Routing Algorithms */ + uint8_t msd; /* Maximum Stack Depth */ +}; + +/* Link State flags to indicate which Attribute parameters are valid */ +#define LS_ATTR_UNSET 0x00000000 +#define LS_ATTR_NAME 0x00000001 +#define LS_ATTR_METRIC 0x00000002 +#define LS_ATTR_TE_METRIC 0x00000004 +#define LS_ATTR_ADM_GRP 0x00000008 +#define LS_ATTR_LOCAL_ADDR 0x00000010 +#define LS_ATTR_NEIGH_ADDR 0x00000020 +#define LS_ATTR_LOCAL_ADDR6 0x00000040 +#define LS_ATTR_NEIGH_ADDR6 0x00000080 +#define LS_ATTR_LOCAL_ID 0x00000100 +#define LS_ATTR_NEIGH_ID 0x00000200 +#define LS_ATTR_MAX_BW 0x00000400 +#define LS_ATTR_MAX_RSV_BW 0x00000800 +#define LS_ATTR_UNRSV_BW 0x00001000 +#define LS_ATTR_REMOTE_AS 0x00002000 +#define LS_ATTR_REMOTE_ADDR 0x00004000 +#define LS_ATTR_REMOTE_ADDR6 0x00008000 +#define LS_ATTR_DELAY 0x00010000 +#define LS_ATTR_MIN_MAX_DELAY 0x00020000 +#define LS_ATTR_JITTER 0x00040000 +#define LS_ATTR_PACKET_LOSS 0x00080000 +#define LS_ATTR_AVA_BW 0x00100000 +#define LS_ATTR_RSV_BW 0x00200000 +#define LS_ATTR_USE_BW 0x00400000 +#define LS_ATTR_ADJ_SID 0x00800000 +#define LS_ATTR_BCK_ADJ_SID 0x01000000 +#define LS_ATTR_SRLG 0x02000000 + +/* Link State Attributes */ +struct ls_attributes { + uint32_t flags; /* Flag for parameters validity */ + struct ls_node_id adv; /* Adv. Router of this Link State */ + char name[MAX_NAME_LENGTH]; /* Name of the Edge. Could be null */ + struct { /* Standard TE metrics */ + uint32_t metric; /* IGP standard metric */ + uint32_t te_metric; /* Traffic Engineering metric */ + uint32_t admin_group; /* Administrative Group */ + struct in_addr local; /* Local IPv4 address */ + struct in_addr remote; /* Remote IPv4 address */ + struct in6_addr local6; /* Local IPv6 address */ + struct in6_addr remote6; /* Remote IPv6 address */ + uint32_t local_id; /* Local Identifier */ + uint32_t remote_id; /* Remote Identifier */ + float max_bw; /* Maximum Link Bandwidth */ + float max_rsv_bw; /* Maximum Reservable BW */ + float unrsv_bw[8]; /* Unreserved BW per CT (8) */ + uint32_t remote_as; /* Remote AS number */ + struct in_addr remote_addr; /* Remote IPv4 address */ + struct in6_addr remote_addr6; /* Remote IPv6 address */ + } standard; +#define LS_ATTR_STANDARD_SIZE 124 + struct { /* Extended TE Metrics */ + uint32_t delay; /* Unidirectional average delay */ + uint32_t min_delay; /* Unidirectional minimum delay */ + uint32_t max_delay; /* Unidirectional maximum delay */ + uint32_t jitter; /* Unidirectional delay variation */ + uint32_t pkt_loss; /* Unidirectional packet loss */ + float ava_bw; /* Available Bandwidth */ + float rsv_bw; /* Reserved Bandwidth */ + float used_bw; /* Utilized Bandwidth */ + } extended; +#define LS_ATTR_EXTENDED_SIZE 32 + struct { /* (LAN)-Adjacency SID for OSPF */ + uint32_t sid; /* SID as MPLS label or index */ + uint8_t flags; /* Flags */ + uint8_t weight; /* Administrative weight */ + union { + struct in_addr addr; /* Neighbor @IP for OSPF */ + uint8_t sysid[ISO_SYS_ID_LEN]; /* or Sys-ID for ISIS */ + } neighbor; + } adj_sid[2]; /* Primary & Backup (LAN)-Adj. SID */ +#define LS_ATTR_ADJ_SID_SIZE 120 + uint32_t *srlgs; /* List of Shared Risk Link Group */ + uint8_t srlg_len; /* number of SRLG in the list */ +}; + +/* Link State flags to indicate which Prefix parameters are valid */ +#define LS_PREF_UNSET 0x00 +#define LS_PREF_IGP_FLAG 0x01 +#define LS_PREF_ROUTE_TAG 0x02 +#define LS_PREF_EXTENDED_TAG 0x04 +#define LS_PREF_METRIC 0x08 +#define LS_PREF_SR 0x10 + +/* Link State Prefix */ +struct ls_prefix { + uint8_t flags; /* Flag for parameters validity */ + struct ls_node_id adv; /* Adv. Router of this Link State */ + struct prefix pref; /* IPv4 or IPv6 prefix */ + uint8_t igp_flag; /* IGP Flags associated to the prefix */ + uint32_t route_tag; /* IGP Route Tag */ + uint64_t extended_tag; /* IGP Extended Route Tag */ + uint32_t metric; /* Route metric for this prefix */ + struct { + uint32_t sid; /* Segment Routing ID */ + uint8_t sid_flag; /* Segment Routing Flags */ + uint8_t algo; /* Algorithm for Segment Routing */ + } sr; +}; + +/** + * Create a new Link State Node. Structure is dynamically allocated. + * + * @param adv Mandatory Link State Node ID i.e. advertise router information + * @param rid Router ID as IPv4 address + * @param rid6 Router ID as IPv6 address + * + * @return New Link State Node + */ +extern struct ls_node *ls_node_new(struct ls_node_id adv, struct in_addr rid, + struct in6_addr rid6); + +/** + * Remove Link State Node. Data structure is freed. + * + * @param node Pointer to a valid Link State Node structure + */ +extern void ls_node_del(struct ls_node *node); + +/** + * Check if two Link State Nodes are equal. Note that this routine has the same + * return value sense as '==' (which is different from a comparison). + * + * @param n1 First Link State Node to be compare + * @param n2 Second Link State Node to be compare + * + * @return 1 if equal, 0 otherwise + */ +extern int ls_node_same(struct ls_node *n1, struct ls_node *n2); + +/** + * Create a new Link State Attributes. Structure is dynamically allocated. + * At least one of parameters MUST be valid and not equal to 0. + * + * @param adv Mandatory Link State Node ID i.e. advertise router ID + * @param local Local IPv4 address + * @param local6 Local Ipv6 address + * @param local_id Local Identifier + * + * @return New Link State Attributes + */ +extern struct ls_attributes *ls_attributes_new(struct ls_node_id adv, + struct in_addr local, + struct in6_addr local6, + uint32_t local_id); + +/** + * Remove Link State Attributes. Data structure is freed. + * + * @param attr Pointer to a valid Link State Attribute structure + */ +extern void ls_attributes_del(struct ls_attributes *attr); + +/** + * Check if two Link State Attributes are equal. Note that this routine has the + * same return value sense as '==' (which is different from a comparison). + * + * @param a1 First Link State Attributes to be compare + * @param a2 Second Link State Attributes to be compare + * + * @return 1 if equal, 0 otherwise + */ +extern int ls_attributes_same(struct ls_attributes *a1, + struct ls_attributes *a2); + +/** + * In addition a Graph model is defined as an overlay on top of link state + * database in order to ease Path Computation algorithm implementation. + * Denoted G(V, E), a graph is composed by a list of Vertices (V) which + * represents the network Node and a list of Edges (E) which represents node + * Link. An additional list of prefixes (P) is also added. + * A prefix (P) is also attached to the Vertex (V) which advertise it. + * + * Vertex (V) contains the list of outgoing Edges (E) that connect this Vertex + * with its direct neighbors and the list of incoming Edges (E) that connect + * the direct neighbors to this Vertex. Indeed, the Edge (E) is unidirectional, + * thus, it is necessary to add 2 Edges to model a bidirectional relation + * between 2 Vertices. + * + * Edge (E) contains the source and destination Vertex that this Edge + * is connecting. + * + * A unique Key is used to identify both Vertices and Edges within the Graph. + * An easy way to build this key is to used the IP address: i.e. loopback + * address for Vertices and link IP address for Edges. + * + * -------------- --------------------------- -------------- + * | Connected |---->| Connected Edge Va to Vb |--->| Connected | + * --->| Vertex | --------------------------- | Vertex |----> + * | | | | + * | - Key (Va) | | - Key (Vb) | + * <---| - Vertex | --------------------------- | - Vertex |<---- + * | |<----| Connected Edge Vb to Va |<---| | + * -------------- --------------------------- -------------- + * + */ + +/* Link State Vertex structure */ +PREDECL_RBTREE_UNIQ(vertices) +struct ls_vertex { + struct vertices_item entry; /* Entry in RB Tree */ + uint64_t key; /* Unique Key identifier */ + struct ls_node *node; /* Link State Node */ + struct list *incoming_edges; /* List of incoming Link State links */ + struct list *outgoing_edges; /* List of outgoing Link State links */ + struct list *prefixes; /* List of advertised prefix */ +}; + +/* Link State Edge structure */ +PREDECL_RBTREE_UNIQ(edges) +struct ls_edge { + struct edges_item entry; /* Entry in RB tree */ + uint64_t key; /* Unique Key identifier */ + struct ls_attributes *attributes; /* Link State attributes */ + struct ls_vertex *source; /* Pointer to the source Vertex */ + struct ls_vertex *destination; /* Pointer to the destination Vertex */ +}; + +/* Link State Subnet structure */ +PREDECL_RBTREE_UNIQ(subnets) +struct ls_subnet { + struct subnets_item entry; /* Entry in RB tree */ + struct prefix key; /* Unique Key identifier */ + struct ls_vertex *vertex; /* Back pointer to the Vertex owner */ + struct ls_prefix *ls_pref; /* Link State Prefix */ +}; + +/* Declaration of Vertices, Edges and Prefixes RB Trees */ +macro_inline int vertex_cmp(const struct ls_vertex *node1, + const struct ls_vertex *node2) +{ + return (node1->key - node2->key); +} +DECLARE_RBTREE_UNIQ(vertices, struct ls_vertex, entry, vertex_cmp) + +macro_inline int edge_cmp(const struct ls_edge *edge1, + const struct ls_edge *edge2) +{ + return (edge1->key - edge2->key); +} +DECLARE_RBTREE_UNIQ(edges, struct ls_edge, entry, edge_cmp) + +macro_inline int subnet_cmp(const struct ls_subnet *a, + const struct ls_subnet *b) +{ + return prefix_cmp(&a->key, &b->key); +} +DECLARE_RBTREE_UNIQ(subnets, struct ls_subnet, entry, subnet_cmp) + +/* Link State TED Structure */ +struct ls_ted { + uint32_t key; /* Unique identifier */ + char name[MAX_NAME_LENGTH]; /* Name of this graph. Could be null */ + uint32_t as_number; /* AS number of the modeled network */ + struct ls_vertex *self; /* Vertex of the FRR instance */ + struct vertices_head vertices; /* List of Vertices */ + struct edges_head edges; /* List of Edges */ + struct subnets_head subnets; /* List of Subnets */ +}; + +/** + * Create a new Link State Vertex structure and initialize is with the Link + * State Node parameter. + * + * @param node Link State Node + * + * @return New Vertex + */ +extern struct ls_vertex *ls_vertex_new(struct ls_node *node); + +/** + * Delete Link State Vertex. This function clean internal Vertex lists (incoming + * and outgoing Link State Edge and Link State Subnet). Note that referenced + * objects of the different lists (Edges & SubNet) are not removed as they could + * be connected to other Vertices. + * + * @param vertex Link State Vertex to be removed + */ +extern void ls_vertex_del(struct ls_vertex *vertex); + +/** + * Add new vertex to the Link State DB. Vertex is created from the Link State + * Node. Vertex data structure is dynamically allocated. + * + * @param ted Traffic Engineering Database structure + * @param node Link State Node + * + * @return New Vertex or NULL in case of error + */ +extern struct ls_vertex *ls_vertex_add(struct ls_ted *ted, + struct ls_node *node); + +/** + * Update Vertex with the Link State Node. A new vertex is created if no one + * corresponds to the Link State Node. + * + * @param ted Link State Data Base + * @param node Link State Node to be updated + * + * @return Updated Link State Vertex or Null in case of error + */ +extern struct ls_vertex *ls_vertex_update(struct ls_ted *ted, + struct ls_node *node); + +/** + * Remove Vertex from the Link State DB. Vertex Data structure is freed but + * not the Link State Node. Link State DB is not modified if Vertex is NULL or + * not found in the Data Base. + * + * @param ted Link State Data Base + * @param vertex Vertex to be removed + */ +extern void ls_vertex_remove(struct ls_ted *ted, struct ls_vertex *vertex); + +/** + * Find Vertex in the Link State DB by its unique key. + * + * @param ted Link State Data Base + * @param key Vertex Key different from 0 + * + * @return Vertex if found, NULL otherwise + */ +extern struct ls_vertex *ls_find_vertex_by_key(struct ls_ted *ted, + const uint64_t key); + +/** + * Find Vertex in the Link State DB by its Link State Node. + * + * @param ted Link State Data Base + * @param nid Link State Node ID + * + * @return Vertex if found, NULL otherwise + */ +extern struct ls_vertex *ls_find_vertex_by_id(struct ls_ted *ted, + struct ls_node_id nid); + +/** + * Check if two Vertices are equal. Note that this routine has the same return + * value sense as '==' (which is different from a comparison). + * + * @param v1 First vertex to compare + * @param v2 Second vertex to compare + * + * @return 1 if equal, 0 otherwise + */ +extern int ls_vertex_same(struct ls_vertex *v1, struct ls_vertex *v2); + +/** + * Add new Edge to the Link State DB. Edge is created from the Link State + * Attributes. Edge data structure is dynamically allocated. + * + * @param ted Link State Data Base + * @param attributes Link State attributes + * + * @return New Edge or NULL in case of error + */ +extern struct ls_edge *ls_edge_add(struct ls_ted *ted, + struct ls_attributes *attributes); + +/** + * Update the Link State Attributes information of an existing Edge. If there is + * no corresponding Edge in the Link State Data Base, a new Edge is created. + * + * @param ted Link State Data Base + * @param attributes Link State Attributes + * + * @return Updated Link State Edge, or NULL in case of error + */ +extern struct ls_edge *ls_edge_update(struct ls_ted *ted, + struct ls_attributes *attributes); + +/** + * Remove Edge from the Link State DB. Edge data structure is freed but not the + * Link State Attributes data structure. Link State DB is not modified if Edge + * is NULL or not found in the Data Base. + * + * @param ted Link State Data Base + * @param edge Edge to be removed + */ +extern void ls_edge_del(struct ls_ted *ted, struct ls_edge *edge); + +/** + * Find Edge in the Link State Data Base by Edge key. + * + * @param ted Link State Data Base + * @param key Edge key + * + * @return Edge if found, NULL otherwise + */ +extern struct ls_edge *ls_find_edge_by_key(struct ls_ted *ted, + const uint64_t key); + +/** + * Find Edge in the Link State Data Base by the source (local IPv4 or IPv6 + * address or local ID) informations of the Link + * State Attributes + * + * @param ted Link State Data Base + * @param attributes Link State Attributes + * + * @return Edge if found, NULL otherwise + */ +extern struct ls_edge * +ls_find_edge_by_source(struct ls_ted *ted, struct ls_attributes *attributes); + +/** + * Find Edge in the Link State Data Base by the destination (remote IPv4 or IPv6 + * address of remote ID) information of the Link State Attributes + * + * @param ted Link State Data Base + * @param attributes Link State Attributes + * + * @return Edge if found, NULL otherwise + */ +extern struct ls_edge * +ls_find_edge_by_destination(struct ls_ted *ted, + struct ls_attributes *attributes); + +/** + * Add new Subnet to the Link State DB. Subnet is created from the Link State + * prefix. Subnet data structure is dynamically allocated. + * + * @param ted Link State Data Base + * @param pref Link State Prefix + * + * @return New Subnet + */ +extern struct ls_subnet *ls_subnet_add(struct ls_ted *ted, + struct ls_prefix *pref); + +/** + * Remove Subnet from the Link State DB. Subnet data structure is freed but + * not the Link State prefix data structure. Link State DB is not modified + * if Subnet is NULL or not found in the Data Base. + * + * @param ted Link State Data Base + * @param subnet Subnet to be removed + */ +extern void ls_subnet_del(struct ls_ted *ted, struct ls_subnet *subnet); + +/** + * Find Subnet in the Link State Data Base by prefix. + * + * @param ted Link State Data Base + * @param prefix Link State Prefix + * + * @return Subnet if found, NULL otherwise + */ +extern struct ls_subnet *ls_find_subnet(struct ls_ted *ted, + const struct prefix prefix); + +/** + * Create a new Link State Data Base. + * + * @param key Unique key of the data base. Must be different from 0 + * @param name Name of the data base (may be NULL) + * @param asn AS Number for this data base. Must be different from 0 + * + * @return New Link State Database or NULL in case of error + */ +extern struct ls_ted *ls_ted_new(const uint32_t key, const char *name, + uint32_t asn); + +/** + * Delete existing Link State Data Base. + * + * @param ted Link State Data Base + */ +extern void ls_ted_del(struct ls_ted *ted); + +/** + * Connect Source and Destination Vertices by given Edge. Only non NULL source + * and destination vertices are connected. + * + * @param src Link State Source Vertex + * @param dst Link State Destination Vertex + * @param edge Link State Edge. Must not be NULL + */ +extern void ls_connect_vertices(struct ls_vertex *src, struct ls_vertex *dst, + struct ls_edge *edge); + +/** + * Connect Link State Edge to the Link State Vertex which could be a Source or + * a Destination Vertex. + * + * @param vertex Link State Vertex to be connected. Must not be NULL + * @param edge Link State Edge connection. Must not be NULL + * @param source True for a Source, false for a Destination Vertex + */ +extern void ls_connect(struct ls_vertex *vertex, struct ls_edge *edge, + bool source); + +/** + * Disconnect Link State Edge from the Link State Vertex which could be a + * Source or a Destination Vertex. + * + * @param vertex Link State Vertex to be connected. Must not be NULL + * @param edge Link State Edge connection. Must not be NULL + * @param source True for a Source, false for a Destination Vertex + */ +extern void ls_disconnect(struct ls_vertex *vertex, struct ls_edge *edge, + bool source); + +/** + * Disconnect Link State Edge from both Source and Destination Vertex. + * + * @param edge Link State Edge to be disconnected + */ +extern void ls_disconnect_edge(struct ls_edge *edge); + + +/** + * The Link State Message is defined to convey Link State parameters from + * the routing protocol (OSPF or IS-IS) to other daemons e.g. BGP. + * + * The structure is composed of: + * - Event of the message: + * - Sync: Send the whole LS DB following a request + * - Add: Send the a new Link State element + * - Update: Send an update of an existing Link State element + * - Delete: Indicate that the given Link State element is removed + * - Type of Link State element: Node, Attribute or Prefix + * - Remote node id when known + * - Data: Node, Attributes or Prefix + * + * A Link State Message can carry only one Link State Element (Node, Attributes + * of Prefix) at once, and only one Link State Message is sent through ZAPI + * Opaque Link State type at once. + */ + +/* ZAPI Opaque Link State Message Event */ +#define LS_MSG_EVENT_SYNC 1 +#define LS_MSG_EVENT_ADD 2 +#define LS_MSG_EVENT_UPDATE 3 +#define LS_MSG_EVENT_DELETE 4 + +/* ZAPI Opaque Link State Message sub-Type */ +#define LS_MSG_TYPE_NODE 1 +#define LS_MSG_TYPE_ATTRIBUTES 2 +#define LS_MSG_TYPE_PREFIX 3 + +/* Link State Message */ +struct ls_message { + uint8_t event; /* Message Event: Sync, Add, Update, Delete */ + uint8_t type; /* Message Data Type: Node, Attribute, Prefix */ + struct ls_node_id remote_id; /* Remote Link State Node ID */ + union { + struct ls_node *node; /* Link State Node */ + struct ls_attributes *attr; /* Link State Attributes */ + struct ls_prefix *prefix; /* Link State Prefix */ + } data; +}; + +/** + * Parse Link State Message from stream. Used this function once receiving a + * new ZAPI Opaque message of type Link State. + * + * @param s Stream buffer. Must not be NULL. + * + * @return New Link State Message or NULL in case of error + */ +extern struct ls_message *ls_parse_msg(struct stream *s); + +/** + * Delete existing message, freeing all substructure. + * + * @param msg Link state message to be deleted + */ +extern void ls_delete_msg(struct ls_message *msg); + +/** + * Send Link State Message as new ZAPI Opaque message of type Link State. + * If destination is not NULL, message is sent as Unicast otherwise it is + * broadcast to all registered daemon. + * + * @param zclient Zebra Client + * @param msg Link State Message to be sent + * @param dst Destination daemon for unicast message, + * NULL for broadcast message + * + * @return 0 on success, -1 otherwise + */ +extern int ls_send_msg(struct zclient *zclient, struct ls_message *msg, + struct zapi_opaque_reg_info *dst); + +/** + * Create a new Link State Message from a Link State Vertex. If Link State + * Message is NULL, a new data structure is dynamically allocated. + * + * @param msg Link State Message to be filled or NULL + * @param vertex Link State Vertex. Must not be NULL + * + * @return New Link State Message msg parameter is NULL or pointer + * to the provided Link State Message + */ +extern struct ls_message *ls_vertex2msg(struct ls_message *msg, + struct ls_vertex *vertex); + +/** + * Create a new Link State Message from a Link State Edge. If Link State + * Message is NULL, a new data structure is dynamically allocated. + * + * @param msg Link State Message to be filled or NULL + * @param edge Link State Edge. Must not be NULL + * + * @return New Link State Message msg parameter is NULL or pointer + * to the provided Link State Message + */ +extern struct ls_message *ls_edge2msg(struct ls_message *msg, + struct ls_edge *edge); + +/** + * Create a new Link State Message from a Link State Subnet. If Link State + * Message is NULL, a new data structure is dynamically allocated. + * + * @param msg Link State Message to be filled or NULL + * @param subnet Link State Subnet. Must not be NULL + * + * @return New Link State Message msg parameter is NULL or pointer + * to the provided Link State Message + */ +extern struct ls_message *ls_subnet2msg(struct ls_message *msg, + struct ls_subnet *subnet); + +/** + * Send all the content of the Link State Data Base to the given destination. + * Link State content is sent is this order: Vertices, Edges, Subnet. + * This function must be used when a daemon request a Link State Data Base + * Synchronization. + * + * @param ted Link State Data Base. Must not be NULL + * @param zclient Zebra Client. Must not be NULL + * @param dst Destination FRR daemon. Must not be NULL + * + * @return 0 on success, -1 otherwise + */ +extern int ls_sync_ted(struct ls_ted *ted, struct zclient *zclient, + struct zapi_opaque_reg_info *dst); + +/** + * Dump all Link State Data Base elements for debugging purposes + * + * @param ted Link State Data Base. Must not be NULL + * + */ +extern void ls_dump_ted(struct ls_ted *ted); + +#ifdef __cplusplus +} +#endif + +#endif /* _FRR_LINK_STATE_H_ */ @@ -161,8 +161,9 @@ void zlog_signal(int signo, const char *action, void *siginfo_v, if (!tc) bprintfrr(&fb, "no thread information available\n"); else - bprintfrr(&fb, "in thread %s scheduled from %s:%d\n", - tc->funcname, tc->schedfrom, tc->schedfrom_line); + bprintfrr(&fb, "in thread %s scheduled from %s:%d %s()\n", + tc->xref->funcname, tc->xref->xref.file, + tc->xref->xref.line, tc->xref->xref.func); zlog_sigsafe(fb.buf, fb.pos - fb.buf); } @@ -179,6 +180,9 @@ void zlog_backtrace_sigsafe(int priority, void *program_counter) unw_word_t ip, off, sp; Dl_info dlinfo; + memset(&uc, 0, sizeof(uc)); + memset(&cursor, 0, sizeof(cursor)); + unw_getcontext(&uc); unw_init_local(&cursor, &uc); while (unw_step(&cursor) > 0) { @@ -300,8 +304,9 @@ void zlog_thread_info(int log_level) if (tc) zlog(log_level, - "Current thread function %s, scheduled from file %s, line %u", - tc->funcname, tc->schedfrom, tc->schedfrom_line); + "Current thread function %s, scheduled from file %s, line %u in %s()", + tc->xref->funcname, tc->xref->xref.file, + tc->xref->xref.line, tc->xref->xref.func); else zlog(log_level, "Current thread not known/applicable"); } @@ -62,16 +62,6 @@ struct message { const char *str; }; -/* For logs which have error codes associated with them */ -#define flog_err(ferr_id, format, ...) \ - zlog_err("[EC %u] " format, ferr_id, ##__VA_ARGS__) -#define flog_err_sys(ferr_id, format, ...) \ - flog_err(ferr_id, format, ##__VA_ARGS__) -#define flog_warn(ferr_id, format, ...) \ - zlog_warn("[EC %u] " format, ferr_id, ##__VA_ARGS__) -#define flog(priority, ferr_id, format, ...) \ - zlog(priority, "[EC %u] " format, ferr_id, ##__VA_ARGS__) - extern void zlog_thread_info(int log_level); #define ZLOG_FILTERS_MAX 100 /* Max # of filters at once */ diff --git a/lib/module.c b/lib/module.c index 14d5cfd44f..3d299a6a2e 100644 --- a/lib/module.c +++ b/lib/module.c @@ -43,6 +43,8 @@ union _frrmod_runtime_u frrmod_default = { }, }; +XREF_SETUP() + // if defined(HAVE_SYS_WEAK_ALIAS_ATTRIBUTE) // union _frrmod_runtime_u _frrmod_this_module // __attribute__((weak, alias("frrmod_default"))); diff --git a/lib/module.h b/lib/module.h index 79cf52d75a..5d8d9cfbcc 100644 --- a/lib/module.h +++ b/lib/module.h @@ -20,6 +20,9 @@ #include <stdint.h> #include <stdbool.h> +#include "compiler.h" +#include "xref.h" + #ifdef __cplusplus extern "C" { #endif @@ -75,7 +78,10 @@ extern union _frrmod_runtime_u _frrmod_this_module; DSO_LOCAL union _frrmod_runtime_u _frrmod_this_module = {{ \ NULL, \ &_frrmod_info, \ - }}; + }}; \ + XREF_SETUP() \ + /* end */ + #define FRR_MODULE_SETUP(...) \ FRR_COREMOD_SETUP(__VA_ARGS__) \ DSO_SELF struct frrmod_runtime *frr_module = &_frrmod_this_module.r; diff --git a/lib/network.c b/lib/network.c index d2482bd55e..411661a5e1 100644 --- a/lib/network.c +++ b/lib/network.c @@ -121,21 +121,3 @@ float ntohf(float net) { return htonf(net); } - -/** - * Helper function that returns a random long value. The main purpose of - * this function is to hide a `random()` call that gets flagged by coverity - * scan and put it into one place. - * - * The main usage of this function should be for generating jitter or weak - * random values for simple purposes. - * - * See 'man 3 random' for more information. - * - * \returns random long integer. - */ -long frr_weak_random(void) -{ - /* coverity[dont_call] */ - return random(); -} diff --git a/lib/network.h b/lib/network.h index 83c9e59e76..4a9666984f 100644 --- a/lib/network.h +++ b/lib/network.h @@ -45,7 +45,23 @@ extern int set_cloexec(int fd); extern float htonf(float); extern float ntohf(float); -extern long frr_weak_random(void); +/** + * Helper function that returns a random long value. The main purpose of + * this function is to hide a `random()` call that gets flagged by coverity + * scan and put it into one place. + * + * The main usage of this function should be for generating jitter or weak + * random values for simple purposes. + * + * See 'man 3 random' for more information. + * + * \returns random long integer. + */ +static inline long frr_weak_random(void) +{ + /* coverity[dont_call] */ + return random(); +} #ifdef __cplusplus } diff --git a/lib/northbound.h b/lib/northbound.h index 3f6e4dc46e..8dd6b4c337 100644 --- a/lib/northbound.h +++ b/lib/northbound.h @@ -552,7 +552,7 @@ struct nb_node { * from working properly on shared libraries. For those compilers, use a fixed * size array to work around the problem. */ -#define YANG_MODULE_MAX_NODES 1400 +#define YANG_MODULE_MAX_NODES 2000 struct frr_yang_module_info { /* YANG module name. */ @@ -600,6 +600,7 @@ enum nb_client { NB_CLIENT_CONFD, NB_CLIENT_SYSREPO, NB_CLIENT_GRPC, + NB_CLIENT_PCEP, }; /* Northbound context. */ @@ -621,6 +622,8 @@ struct nb_context { } sysrepo; struct { } grpc; + struct { + } pcep; } client_data; #endif }; diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c index 7048df99fb..ad7dad5cb2 100644 --- a/lib/northbound_cli.c +++ b/lib/northbound_cli.c @@ -693,6 +693,12 @@ static int nb_write_config(struct nb_config *config, enum nb_cfg_format format, __func__, safe_strerror(errno)); return -1; } + if (fchmod(fd, CONFIGFILE_MASK) != 0) { + flog_warn(EC_LIB_SYSTEM_CALL, + "%s: fchmod() failed: %s(%d):", __func__, + safe_strerror(errno), errno); + return -1; + } /* Make vty for configuration file. */ file_vty = vty_new(); @@ -1820,20 +1826,20 @@ static struct cmd_node nb_debug_node = { void nb_cli_install_default(int node) { - install_element(node, &show_config_candidate_section_cmd); + _install_element(node, &show_config_candidate_section_cmd); if (frr_get_cli_mode() != FRR_CLI_TRANSACTIONAL) return; - install_element(node, &config_commit_cmd); - install_element(node, &config_commit_comment_cmd); - install_element(node, &config_commit_check_cmd); - install_element(node, &config_update_cmd); - install_element(node, &config_discard_cmd); - install_element(node, &show_config_running_cmd); - install_element(node, &show_config_candidate_cmd); - install_element(node, &show_config_compare_cmd); - install_element(node, &show_config_transaction_cmd); + _install_element(node, &config_commit_cmd); + _install_element(node, &config_commit_comment_cmd); + _install_element(node, &config_commit_check_cmd); + _install_element(node, &config_update_cmd); + _install_element(node, &config_discard_cmd); + _install_element(node, &show_config_running_cmd); + _install_element(node, &show_config_candidate_cmd); + _install_element(node, &show_config_compare_cmd); + _install_element(node, &show_config_transaction_cmd); } /* YANG module autocomplete. */ diff --git a/lib/printf/printf-pos.c b/lib/printf/printf-pos.c index cc03f7ef9a..ac775bea4e 100644 --- a/lib/printf/printf-pos.c +++ b/lib/printf/printf-pos.c @@ -384,6 +384,7 @@ reswitch: switch (ch) { goto error; break; #endif /* !NO_FLOATING_POINT */ +#ifdef DANGEROUS_PERCENT_N case 'n': if (flags & INTMAXT) error = addtype(&types, TP_INTMAXT); @@ -404,6 +405,7 @@ reswitch: switch (ch) { if (error) goto error; continue; /* no output */ +#endif case 'O': flags |= LONGINT; /*FALLTHROUGH*/ @@ -576,6 +578,7 @@ reswitch: switch (ch) { goto error; break; #endif /* !NO_FLOATING_POINT */ +#ifdef DANGEROUS_PERCENT_N case 'n': if (flags & INTMAXT) error = addtype(&types, TP_INTMAXT); @@ -596,6 +599,7 @@ reswitch: switch (ch) { if (error) goto error; continue; /* no output */ +#endif case 'O': flags |= LONGINT; /*FALLTHROUGH*/ diff --git a/lib/printf/vfprintf.c b/lib/printf/vfprintf.c index 6ffccb3811..a0634cde4b 100644 --- a/lib/printf/vfprintf.c +++ b/lib/printf/vfprintf.c @@ -503,6 +503,11 @@ reswitch: switch (ch) { size = (prec >= 0) ? strnlen(cp, prec) : strlen(cp); sign = '\0'; break; +#ifdef DANGEROUS_PERCENT_N + /* FRR does not use %n in printf formats. This is just left + * here in case someone tries to use %n and starts debugging + * why the f* it doesn't work + */ case 'n': /* * Assignment-like behavior is specified if the @@ -526,6 +531,7 @@ reswitch: switch (ch) { else *GETARG(int *) = ret; continue; /* no output */ +#endif case 'O': flags |= LONGINT; /*FALLTHROUGH*/ diff --git a/lib/privs.c b/lib/privs.c index 1bb5d059c8..5ca3c0d886 100644 --- a/lib/privs.c +++ b/lib/privs.c @@ -587,6 +587,8 @@ void zprivs_preinit(struct zebra_privs_t *zprivs) } } +struct zebra_privs_t *lib_privs; + void zprivs_init(struct zebra_privs_t *zprivs) { gid_t groups[NGROUPS_MAX] = {}; @@ -598,6 +600,8 @@ void zprivs_init(struct zebra_privs_t *zprivs) || zprivs->cap_num_i)) return; + lib_privs = zprivs; + if (zprivs->user) { ngroups = array_size(groups); if (getgrouplist(zprivs->user, zprivs_state.zgid, groups, @@ -701,6 +705,8 @@ void zprivs_terminate(struct zebra_privs_t *zprivs) { struct zebra_privs_refs_t *refs; + lib_privs = NULL; + if (!zprivs) { fprintf(stderr, "%s: no privs struct given, terminating", __func__); diff --git a/lib/privs.h b/lib/privs.h index 18ba8e8888..2dcdbe2e6c 100644 --- a/lib/privs.h +++ b/lib/privs.h @@ -100,6 +100,8 @@ struct zprivs_ids_t { gid_t gid_vty; /* vty gid */ }; +extern struct zebra_privs_t *lib_privs; + /* initialise zebra privileges */ extern void zprivs_preinit(struct zebra_privs_t *zprivs); extern void zprivs_init(struct zebra_privs_t *zprivs); diff --git a/lib/resolver.c b/lib/resolver.c index e5caadb2d0..c01284e29e 100644 --- a/lib/resolver.c +++ b/lib/resolver.c @@ -19,6 +19,9 @@ #include "lib_errors.h" #include "resolver.h" #include "command.h" +#include "xref.h" + +XREF_SETUP() struct resolver_state { ares_channel channel; diff --git a/lib/route_types.pl b/lib/route_types.pl index 759e9b4729..8c216e13bd 100755 --- a/lib/route_types.pl +++ b/lib/route_types.pl @@ -130,7 +130,7 @@ sub codelist { $str =~ s/ $//; push @lines, $str . "\\n\" \\\n"; push @lines, " \" > - selected route, * - FIB route, q - queued, r - rejected, b - backup\\n\""; - push @lines, " \" t - trapped, o - offload failure\\n\""; + push @lines, " \" t - trapped, o - offload failure\\n\\n\""; return join("", @lines); diff --git a/lib/routemap.c b/lib/routemap.c index 004beb3628..7714086672 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -1347,7 +1347,7 @@ enum rmap_compile_rets route_map_add_match(struct route_map_index *index, get_route_map_delete_event(type); route_map_upd8_dependency( delete_rmap_event_type, - rule_key, + rule->rule_str, index->map->name); } @@ -2240,7 +2240,7 @@ static void route_map_pentry_update(route_map_event_t event, } } -static void route_map_pentry_process_dependency(struct hash_bucket *backet, +static void route_map_pentry_process_dependency(struct hash_bucket *bucket, void *data) { char *rmap_name = NULL; @@ -2253,7 +2253,7 @@ static void route_map_pentry_process_dependency(struct hash_bucket *backet, (struct route_map_pentry_dep *)data; unsigned char family = pentry_dep->pentry->prefix.family; - dep_data = (struct route_map_dep_data *)backet->data; + dep_data = (struct route_map_dep_data *)bucket->data; if (!dep_data) return; @@ -2399,6 +2399,7 @@ route_map_result_t route_map_apply(struct route_map *map, index = route_map_get_index(map, prefix, object, (uint8_t *)&match_ret); if (index) { + index->applied++; if (rmap_debug) zlog_debug( "Best match route-map: %s, sequence: %d for pfx: %pFX, result: %s", @@ -2586,22 +2587,23 @@ static void route_map_clear_reference(struct hash_bucket *bucket, void *arg) struct route_map_dep *dep = bucket->data; struct route_map_dep_data *dep_data = NULL, tmp_dep_data; - if (arg) { - memset(&tmp_dep_data, 0, sizeof(struct route_map_dep_data)); - tmp_dep_data.rname = arg; - dep_data = hash_release(dep->dep_rmap_hash, - &tmp_dep_data); - if (dep_data) { - XFREE(MTYPE_ROUTE_MAP_NAME, dep_data->rname); - XFREE(MTYPE_ROUTE_MAP_DEP_DATA, dep_data); - } - if (!dep->dep_rmap_hash->count) { - dep = hash_release(dep->this_hash, - (void *)dep->dep_name); - hash_free(dep->dep_rmap_hash); - XFREE(MTYPE_ROUTE_MAP_NAME, dep->dep_name); - XFREE(MTYPE_ROUTE_MAP_DEP, dep); - } + memset(&tmp_dep_data, 0, sizeof(struct route_map_dep_data)); + tmp_dep_data.rname = arg; + dep_data = hash_release(dep->dep_rmap_hash, &tmp_dep_data); + if (dep_data) { + if (rmap_debug) + zlog_debug("Clearing reference for %s to %s count: %d", + dep->dep_name, tmp_dep_data.rname, + dep_data->refcnt); + + XFREE(MTYPE_ROUTE_MAP_NAME, dep_data->rname); + XFREE(MTYPE_ROUTE_MAP_DEP_DATA, dep_data); + } + if (!dep->dep_rmap_hash->count) { + dep = hash_release(dep->this_hash, (void *)dep->dep_name); + hash_free(dep->dep_rmap_hash); + XFREE(MTYPE_ROUTE_MAP_NAME, dep->dep_name); + XFREE(MTYPE_ROUTE_MAP_DEP, dep); } } @@ -2609,6 +2611,9 @@ static void route_map_clear_all_references(char *rmap_name) { int i; + if (rmap_debug) + zlog_debug("Clearing references for %s", rmap_name); + for (i = 1; i < ROUTE_MAP_DEP_MAX; i++) { hash_iterate(route_map_dep_hash[i], route_map_clear_reference, (void *)rmap_name); @@ -2763,12 +2768,19 @@ static int route_map_dep_update(struct hash *dephash, const char *dep_name, memset(&tmp_dep_data, 0, sizeof(struct route_map_dep_data)); tmp_dep_data.rname = rname; dep_data = hash_lookup(dep->dep_rmap_hash, &tmp_dep_data); - - if (!dep_data) + /* + * If dep_data is NULL then something has gone seriously + * wrong in route-map handling. Note it and prevent + * the crash. + */ + if (!dep_data) { + zlog_warn( + "route-map dependency for route-map %s: %s is not correct", + rmap_name, dep_name); goto out; + } - if (dep_data->refcnt) - dep_data->refcnt--; + dep_data->refcnt--; if (!dep_data->refcnt) { ret_dep_data = hash_release(dep->dep_rmap_hash, diff --git a/lib/sigevent.c b/lib/sigevent.c index 8d583096f6..64cec1385d 100644 --- a/lib/sigevent.c +++ b/lib/sigevent.c @@ -258,7 +258,7 @@ core_handler(int signo, siginfo_t *siginfo, void *context) static void trap_default_signals(void) { static const int core_signals[] = { - SIGQUIT, SIGILL, + SIGQUIT, SIGILL, SIGABRT, #ifdef SIGEMT SIGEMT, #endif diff --git a/lib/smux.h b/lib/smux.h index 6896f02354..e07df2369f 100644 --- a/lib/smux.h +++ b/lib/smux.h @@ -44,6 +44,29 @@ extern "C" { #define IN_ADDR_SIZE sizeof(struct in_addr) +/* IANAipRouteProtocol */ +#define IANAIPROUTEPROTOCOLOTHER 1 +#define IANAIPROUTEPROTOCOLLOCAL 2 +#define IANAIPROUTEPROTOCOLNETMGMT 3 +#define IANAIPROUTEPROTOCOLICMP 4 +#define IANAIPROUTEPROTOCOLEGP 5 +#define IANAIPROUTEPROTOCOLGGP 6 +#define IANAIPROUTEPROTOCOLHELLO 7 +#define IANAIPROUTEPROTOCOLRIP 8 +#define IANAIPROUTEPROTOCOLISIS 9 +#define IANAIPROUTEPROTOCOLESIS 10 +#define IANAIPROUTEPROTOCOLCISCOIGRP 11 +#define IANAIPROUTEPROTOCOLBBNSPFIGP 12 +#define IANAIPROUTEPROTOCOLOSPF 13 +#define IANAIPROUTEPROTOCOLBGP 14 +#define IANAIPROUTEPROTOCOLIDPR 15 +#define IANAIPROUTEPROTOCOLCISCOEIGRP 16 +#define IANAIPROUTEPROTOCOLDVMRP 17 + +#define INETADDRESSTYPEUNKNOWN 0 +#define INETADDRESSTYPEIPV4 1 +#define INETADDRESSTYPEIPV6 2 + #undef REGISTER_MIB #define REGISTER_MIB(descr, var, vartype, theoid) \ smux_register_mib(descr, (struct variable *)var, \ @@ -56,19 +79,29 @@ struct trap_object { oid name[MAX_OID_LEN]; }; +struct index_oid { + int indexlen; + oid indexname[MAX_OID_LEN]; +}; /* Declare SMUX return value. */ #define SNMP_LOCAL_VARIABLES \ static long snmp_int_val __attribute__((unused)); \ static struct in_addr snmp_in_addr_val __attribute__((unused)); - + static uint8_t snmp_octet_val __attribute__((unused)); #define SNMP_INTEGER(V) \ (*var_len = sizeof(snmp_int_val), snmp_int_val = V, \ (uint8_t *)&snmp_int_val) +#define SNMP_OCTET(V) \ + (*var_len = sizeof(snmp_octet_val), snmp_octet_val = V, \ + (uint8_t *)&snmp_octet_val) + #define SNMP_IPADDRESS(V) \ (*var_len = sizeof(struct in_addr), snmp_in_addr_val = V, \ (uint8_t *)&snmp_in_addr_val) +#define SNMP_IP6ADDRESS(V) (*var_len = sizeof(struct in6_addr), (uint8_t *)&V) + extern void smux_init(struct thread_master *tm); extern void smux_register_mib(const char *, struct variable *, size_t, int, oid[], size_t); @@ -98,14 +131,24 @@ extern int smux_header_table(struct variable *, oid *, size_t *, int, size_t *, The use of the arguments may differ depending on the implementation used. */ -extern int smux_trap(struct variable *, size_t, const oid *, size_t, - const oid *, size_t, const oid *, size_t, - const struct trap_object *, size_t, uint8_t); - +extern void smux_trap(struct variable *, size_t, const oid *, size_t, + const oid *, size_t, const oid *, size_t, + const struct trap_object *, size_t, uint8_t); + +extern int smux_trap_multi_index(struct variable *vp, size_t vp_len, + const oid *ename, size_t enamelen, + const oid *name, size_t namelen, + struct index_oid *iname, size_t index_len, + const struct trap_object *trapobj, + size_t trapobjlen, uint8_t sptrap); extern int oid_compare(const oid *, int, const oid *, int); extern void oid2in_addr(oid[], int, struct in_addr *); +extern void oid2int(oid oid[], int *dest); extern void *oid_copy(void *, const void *, size_t); extern void oid_copy_addr(oid[], const struct in_addr *, int); +extern void oid_copy_int(oid oid[], int *val); +extern void oid2string(oid oid[], int len, char *string); +extern void oid_copy_str(oid oid[], const char *string, int len); #ifdef __cplusplus } diff --git a/lib/snmp.c b/lib/snmp.c index 736a3c62b8..e92f622bb9 100644 --- a/lib/snmp.c +++ b/lib/snmp.c @@ -64,6 +64,19 @@ void oid2in_addr(oid oid[], int len, struct in_addr *addr) *pnt++ = oid[i]; } +void oid2int(oid oid[], int *dest) +{ + uint8_t i; + uint8_t *pnt; + int network_dest; + + pnt = (uint8_t *)&network_dest; + + for (i = 0; i < sizeof(int); i++) + *pnt++ = oid[i]; + *dest = ntohl(network_dest); +} + void oid_copy_addr(oid oid[], const struct in_addr *addr, int len) { int i; @@ -78,6 +91,47 @@ void oid_copy_addr(oid oid[], const struct in_addr *addr, int len) oid[i] = *pnt++; } +void oid_copy_int(oid oid[], int *val) +{ + uint8_t i; + const uint8_t *pnt; + int network_val; + + network_val = htonl(*val); + pnt = (uint8_t *)&network_val; + + for (i = 0; i < sizeof(int); i++) + oid[i] = *pnt++; +} + +void oid2string(oid oid[], int len, char *string) +{ + int i; + uint8_t *pnt; + + if (len == 0) + return; + + pnt = (uint8_t *)string; + + for (i = 0; i < len; i++) + *pnt++ = (uint8_t)oid[i]; +} + +void oid_copy_str(oid oid[], const char *string, int len) +{ + int i; + const uint8_t *pnt; + + if (len == 0) + return; + + pnt = (uint8_t *)string; + + for (i = 0; i < len; i++) + oid[i] = *pnt++; +} + int smux_header_generic(struct variable *v, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { diff --git a/lib/sockunion.c b/lib/sockunion.c index c999845659..1dbf77efa4 100644 --- a/lib/sockunion.c +++ b/lib/sockunion.c @@ -708,3 +708,20 @@ static ssize_t printfrr_psu(char *buf, size_t bsz, const char *fmt, fb.pos[0] = '\0'; return consumed; } + +int sockunion_is_null(const union sockunion *su) +{ + unsigned char null_s6_addr[16] = {0}; + + switch (sockunion_family(su)) { + case AF_UNSPEC: + return 1; + case AF_INET: + return (su->sin.sin_addr.s_addr == 0); + case AF_INET6: + return !memcmp(su->sin6.sin6_addr.s6_addr, null_s6_addr, + sizeof(null_s6_addr)); + default: + return 0; + } +} diff --git a/lib/sockunion.h b/lib/sockunion.h index 72f12b77ca..5e80ba1090 100644 --- a/lib/sockunion.h +++ b/lib/sockunion.h @@ -102,6 +102,7 @@ extern union sockunion *sockunion_getpeername(int); extern union sockunion *sockunion_dup(const union sockunion *); extern void sockunion_free(union sockunion *); extern void sockunion_init(union sockunion *); +extern int sockunion_is_null(const union sockunion *su); #ifdef _FRR_ATTRIBUTE_PRINTFRR #pragma FRR printfrr_ext "%pSU" (union sockunion *) diff --git a/lib/stream.c b/lib/stream.c index e4e37b7315..ef73c2fdc9 100644 --- a/lib/stream.c +++ b/lib/stream.c @@ -57,7 +57,7 @@ DEFINE_MTYPE_STATIC(LIB, STREAM_FIFO, "Stream FIFO") #define STREAM_WARN_OFFSETS(S) \ do { \ flog_warn(EC_LIB_STREAM, \ - "&(struct stream): %p, size: %lu, getp: %lu, endp: %lu\n", \ + "&(struct stream): %p, size: %lu, getp: %lu, endp: %lu", \ (void *)(S), (unsigned long)(S)->size, \ (unsigned long)(S)->getp, (unsigned long)(S)->endp); \ zlog_backtrace(LOG_WARNING); \ @@ -93,7 +93,7 @@ DEFINE_MTYPE_STATIC(LIB, STREAM_FIFO, "Stream FIFO") if (((S)->endp + (Z)) > (S)->size) { \ flog_warn( \ EC_LIB_STREAM, \ - "CHECK_SIZE: truncating requested size %lu\n", \ + "CHECK_SIZE: truncating requested size %lu", \ (unsigned long)(Z)); \ STREAM_WARN_OFFSETS(S); \ (Z) = (S)->size - (S)->endp; \ diff --git a/lib/subdir.am b/lib/subdir.am index 038282a99b..d5ffa08546 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -26,6 +26,7 @@ lib_libfrr_la_SOURCES = \ lib/filter_nb.c \ lib/frrcu.c \ lib/frrlua.c \ + lib/frrscript.c \ lib/frr_pthread.c \ lib/frrstr.c \ lib/getopt.c \ @@ -48,6 +49,7 @@ lib_libfrr_la_SOURCES = \ lib/libfrr.c \ lib/libfrr_trace.c \ lib/linklist.c \ + lib/link_state.c \ lib/log.c \ lib/log_filter.c \ lib/log_vty.c \ @@ -101,6 +103,7 @@ lib_libfrr_la_SOURCES = \ lib/vty.c \ lib/wheel.c \ lib/workqueue.c \ + lib/xref.c \ lib/yang.c \ lib/yang_translator.c \ lib/yang_wrappers.c \ @@ -184,6 +187,7 @@ pkginclude_HEADERS += \ lib/filter.h \ lib/freebsd-queue.h \ lib/frrlua.h \ + lib/frrscript.h \ lib/frr_pthread.h \ lib/frratomic.h \ lib/frrcu.h \ @@ -208,6 +212,7 @@ pkginclude_HEADERS += \ lib/libfrr_trace.h \ lib/libospf.h \ lib/linklist.h \ + lib/link_state.h \ lib/log.h \ lib/log_vty.h \ lib/md5.h \ @@ -264,6 +269,7 @@ pkginclude_HEADERS += \ lib/vxlan.h \ lib/wheel.h \ lib/workqueue.h \ + lib/xref.h \ lib/yang.h \ lib/yang_translator.h \ lib/yang_wrappers.h \ @@ -407,6 +413,7 @@ lib_clippy_CFLAGS = $(PYTHON_CFLAGS) lib_clippy_LDADD = $(PYTHON_LIBS) $(UST_LIBS) lib_clippy_LDFLAGS = -export-dynamic lib_clippy_SOURCES = \ + lib/jhash.c \ lib/clippy.c \ lib/command_graph.c \ lib/command_lex.l \ diff --git a/lib/thread.c b/lib/thread.c index c886058355..e0d734a951 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -119,7 +119,7 @@ static void vty_out_cpu_thread_history(struct vty *vty, a->total_active, a->cpu.total / 1000, a->cpu.total % 1000, a->total_calls, (a->cpu.total / a->total_calls), a->cpu.max, (a->real.total / a->total_calls), a->real.max); - vty_out(vty, " %c%c%c%c%c %s\n", + vty_out(vty, " %c%c%c%c%c %s\n", a->types & (1 << THREAD_READ) ? 'R' : ' ', a->types & (1 << THREAD_WRITE) ? 'W' : ' ', a->types & (1 << THREAD_TIMER) ? 'T' : ' ', @@ -188,7 +188,7 @@ static void cpu_record_print(struct vty *vty, uint8_t filter) name); vty_out(vty, "-------------------------------%s\n", underline); - vty_out(vty, "%21s %18s %18s\n", "", + vty_out(vty, "%30s %18s %18s\n", "", "CPU (user+system):", "Real (wall-clock):"); vty_out(vty, "Active Runtime(ms) Invoked Avg uSec Max uSecs"); @@ -211,7 +211,7 @@ static void cpu_record_print(struct vty *vty, uint8_t filter) vty_out(vty, "\n"); vty_out(vty, "Total thread statistics\n"); vty_out(vty, "-------------------------\n"); - vty_out(vty, "%21s %18s %18s\n", "", + vty_out(vty, "%30s %18s %18s\n", "", "CPU (user+system):", "Real (wall-clock):"); vty_out(vty, "Active Runtime(ms) Invoked Avg uSec Max uSecs"); vty_out(vty, " Avg uSec Max uSecs"); @@ -342,7 +342,7 @@ static void show_thread_poll_helper(struct vty *vty, struct thread_master *m) if (!thread) vty_out(vty, "ERROR "); else - vty_out(vty, "%s ", thread->funcname); + vty_out(vty, "%s ", thread->xref->funcname); } else vty_out(vty, " "); @@ -352,7 +352,7 @@ static void show_thread_poll_helper(struct vty *vty, struct thread_master *m) if (!thread) vty_out(vty, "ERROR\n"); else - vty_out(vty, "%s\n", thread->funcname); + vty_out(vty, "%s\n", thread->xref->funcname); } else vty_out(vty, "\n"); } @@ -633,9 +633,6 @@ unsigned long thread_timer_remain_second(struct thread *thread) return thread_timer_remain_msec(thread) / 1000LL; } -#define debugargdef const char *funcname, const char *schedfrom, int fromln -#define debugargpass funcname, schedfrom, fromln - struct timeval thread_timer_remain(struct thread *thread) { struct timeval remain; @@ -678,7 +675,7 @@ char *thread_timer_to_hhmmss(char *buf, int buf_size, /* Get new thread. */ static struct thread *thread_get(struct thread_master *m, uint8_t type, int (*func)(struct thread *), void *arg, - debugargdef) + const struct xref_threadsched *xref) { struct thread *thread = thread_list_pop(&m->unuse); struct cpu_thread_history tmp; @@ -707,18 +704,17 @@ static struct thread *thread_get(struct thread_master *m, uint8_t type, * This hopefully saves us some serious * hash_get lookups. */ - if (thread->funcname != funcname || thread->func != func) { + if ((thread->xref && thread->xref->funcname != xref->funcname) + || thread->func != func) { tmp.func = func; - tmp.funcname = funcname; + tmp.funcname = xref->funcname; thread->hist = hash_get(m->cpu_record, &tmp, (void *(*)(void *))cpu_record_hash_alloc); } thread->hist->total_active++; thread->func = func; - thread->funcname = funcname; - thread->schedfrom = schedfrom; - thread->schedfrom_line = fromln; + thread->xref = xref; return thread; } @@ -832,21 +828,23 @@ done: } /* Add new read thread. */ -struct thread *funcname_thread_add_read_write(int dir, struct thread_master *m, - int (*func)(struct thread *), - void *arg, int fd, - struct thread **t_ptr, - debugargdef) +struct thread *_thread_add_read_write(const struct xref_threadsched *xref, + struct thread_master *m, + int (*func)(struct thread *), + void *arg, int fd, struct thread **t_ptr) { + int dir = xref->thread_type; struct thread *thread = NULL; struct thread **thread_array; if (dir == THREAD_READ) - frrtrace(9, frr_libfrr, schedule_read, m, funcname, schedfrom, - fromln, t_ptr, fd, 0, arg, 0); + frrtrace(9, frr_libfrr, schedule_read, m, + xref->funcname, xref->xref.file, xref->xref.line, + t_ptr, fd, 0, arg, 0); else - frrtrace(9, frr_libfrr, schedule_write, m, funcname, schedfrom, - fromln, t_ptr, fd, 0, arg, 0); + frrtrace(9, frr_libfrr, schedule_write, m, + xref->funcname, xref->xref.file, xref->xref.line, + t_ptr, fd, 0, arg, 0); assert(fd >= 0 && fd < m->fd_limit); frr_with_mutex(&m->mtx) { @@ -882,7 +880,7 @@ struct thread *funcname_thread_add_read_write(int dir, struct thread_master *m, /* make sure we have room for this fd + pipe poker fd */ assert(queuepos + 1 < m->handler.pfdsize); - thread = thread_get(m, dir, func, arg, debugargpass); + thread = thread_get(m, dir, func, arg, xref); m->handler.pfds[queuepos].fd = fd; m->handler.pfds[queuepos].events |= @@ -910,10 +908,10 @@ struct thread *funcname_thread_add_read_write(int dir, struct thread_master *m, } static struct thread * -funcname_thread_add_timer_timeval(struct thread_master *m, - int (*func)(struct thread *), int type, - void *arg, struct timeval *time_relative, - struct thread **t_ptr, debugargdef) +_thread_add_timer_timeval(const struct xref_threadsched *xref, + struct thread_master *m, int (*func)(struct thread *), + int type, void *arg, struct timeval *time_relative, + struct thread **t_ptr) { struct thread *thread; @@ -922,7 +920,8 @@ funcname_thread_add_timer_timeval(struct thread_master *m, assert(type == THREAD_TIMER); assert(time_relative); - frrtrace(9, frr_libfrr, schedule_timer, m, funcname, schedfrom, fromln, + frrtrace(9, frr_libfrr, schedule_timer, m, + xref->funcname, xref->xref.file, xref->xref.line, t_ptr, 0, 0, arg, (long)time_relative->tv_sec); frr_with_mutex(&m->mtx) { @@ -930,7 +929,7 @@ funcname_thread_add_timer_timeval(struct thread_master *m, /* thread is already scheduled; don't reschedule */ return NULL; - thread = thread_get(m, type, func, arg, debugargpass); + thread = thread_get(m, type, func, arg, xref); frr_with_mutex(&thread->mtx) { monotime(&thread->u.sands); @@ -951,10 +950,10 @@ funcname_thread_add_timer_timeval(struct thread_master *m, /* Add timer event thread. */ -struct thread *funcname_thread_add_timer(struct thread_master *m, - int (*func)(struct thread *), - void *arg, long timer, - struct thread **t_ptr, debugargdef) +struct thread *_thread_add_timer(const struct xref_threadsched *xref, + struct thread_master *m, + int (*func)(struct thread *), + void *arg, long timer, struct thread **t_ptr) { struct timeval trel; @@ -963,16 +962,16 @@ struct thread *funcname_thread_add_timer(struct thread_master *m, trel.tv_sec = timer; trel.tv_usec = 0; - return funcname_thread_add_timer_timeval(m, func, THREAD_TIMER, arg, - &trel, t_ptr, debugargpass); + return _thread_add_timer_timeval(xref, m, func, THREAD_TIMER, arg, + &trel, t_ptr); } /* Add timer event thread with "millisecond" resolution */ -struct thread *funcname_thread_add_timer_msec(struct thread_master *m, - int (*func)(struct thread *), - void *arg, long timer, - struct thread **t_ptr, - debugargdef) +struct thread *_thread_add_timer_msec(const struct xref_threadsched *xref, + struct thread_master *m, + int (*func)(struct thread *), + void *arg, long timer, + struct thread **t_ptr) { struct timeval trel; @@ -981,29 +980,31 @@ struct thread *funcname_thread_add_timer_msec(struct thread_master *m, trel.tv_sec = timer / 1000; trel.tv_usec = 1000 * (timer % 1000); - return funcname_thread_add_timer_timeval(m, func, THREAD_TIMER, arg, - &trel, t_ptr, debugargpass); + return _thread_add_timer_timeval(xref, m, func, THREAD_TIMER, arg, + &trel, t_ptr); } /* Add timer event thread with "millisecond" resolution */ -struct thread *funcname_thread_add_timer_tv(struct thread_master *m, - int (*func)(struct thread *), - void *arg, struct timeval *tv, - struct thread **t_ptr, debugargdef) +struct thread *_thread_add_timer_tv(const struct xref_threadsched *xref, + struct thread_master *m, + int (*func)(struct thread *), + void *arg, struct timeval *tv, + struct thread **t_ptr) { - return funcname_thread_add_timer_timeval(m, func, THREAD_TIMER, arg, tv, - t_ptr, debugargpass); + return _thread_add_timer_timeval(xref, m, func, THREAD_TIMER, arg, tv, + t_ptr); } /* Add simple event thread. */ -struct thread *funcname_thread_add_event(struct thread_master *m, - int (*func)(struct thread *), - void *arg, int val, - struct thread **t_ptr, debugargdef) +struct thread *_thread_add_event(const struct xref_threadsched *xref, + struct thread_master *m, + int (*func)(struct thread *), + void *arg, int val, struct thread **t_ptr) { struct thread *thread = NULL; - frrtrace(9, frr_libfrr, schedule_event, m, funcname, schedfrom, fromln, + frrtrace(9, frr_libfrr, schedule_event, m, + xref->funcname, xref->xref.file, xref->xref.line, t_ptr, 0, val, arg, 0); assert(m != NULL); @@ -1013,7 +1014,7 @@ struct thread *funcname_thread_add_event(struct thread_master *m, /* thread is already scheduled; don't reschedule */ break; - thread = thread_get(m, THREAD_EVENT, func, arg, debugargpass); + thread = thread_get(m, THREAD_EVENT, func, arg, xref); frr_with_mutex(&thread->mtx) { thread->u.val = val; thread_list_add_tail(&m->event, thread); @@ -1239,8 +1240,9 @@ void thread_cancel(struct thread **thread) master = (*thread)->master; - frrtrace(9, frr_libfrr, thread_cancel, master, (*thread)->funcname, - (*thread)->schedfrom, (*thread)->schedfrom_line, NULL, (*thread)->u.fd, + frrtrace(9, frr_libfrr, thread_cancel, master, + (*thread)->xref->funcname, (*thread)->xref->xref.file, + (*thread)->xref->xref.line, NULL, (*thread)->u.fd, (*thread)->u.val, (*thread)->arg, (*thread)->u.sands.tv_sec); assert(master->owner == pthread_self()); @@ -1287,8 +1289,8 @@ void thread_cancel_async(struct thread_master *master, struct thread **thread, if (thread && *thread) frrtrace(9, frr_libfrr, thread_cancel_async, master, - (*thread)->funcname, (*thread)->schedfrom, - (*thread)->schedfrom_line, NULL, (*thread)->u.fd, + (*thread)->xref->funcname, (*thread)->xref->xref.file, + (*thread)->xref->xref.line, NULL, (*thread)->u.fd, (*thread)->u.val, (*thread)->arg, (*thread)->u.sands.tv_sec); else @@ -1363,7 +1365,7 @@ static int thread_process_io_helper(struct thread_master *m, if (!thread) { if ((actual_state & (POLLHUP|POLLIN)) != POLLHUP) flog_err(EC_LIB_NO_THREAD, - "Attempting to process an I/O event but for fd: %d(%d) no thread to handle this!\n", + "Attempting to process an I/O event but for fd: %d(%d) no thread to handle this!", m->handler.pfds[pos].fd, actual_state); return 0; } @@ -1673,8 +1675,9 @@ void thread_call(struct thread *thread) GETRUSAGE(&before); thread->real = before.real; - frrtrace(9, frr_libfrr, thread_call, thread->master, thread->funcname, - thread->schedfrom, thread->schedfrom_line, NULL, thread->u.fd, + frrtrace(9, frr_libfrr, thread_call, thread->master, + thread->xref->funcname, thread->xref->xref.file, + thread->xref->xref.line, NULL, thread->u.fd, thread->u.val, thread->arg, thread->u.sands.tv_sec); pthread_setspecific(thread_current, thread); @@ -1724,7 +1727,7 @@ void thread_call(struct thread *thread) flog_warn( EC_LIB_SLOW_THREAD, "SLOW THREAD: task %s (%lx) ran for %lums (cpu time %lums)", - thread->funcname, (unsigned long)thread->func, + thread->xref->funcname, (unsigned long)thread->func, realtime / 1000, cputime / 1000); } #endif /* CONSUMED_TIME_CHECK */ @@ -1732,15 +1735,15 @@ void thread_call(struct thread *thread) } /* Execute thread */ -void funcname_thread_execute(struct thread_master *m, - int (*func)(struct thread *), void *arg, int val, - debugargdef) +void _thread_execute(const struct xref_threadsched *xref, + struct thread_master *m, int (*func)(struct thread *), + void *arg, int val) { struct thread *thread; /* Get or allocate new thread to execute. */ frr_with_mutex(&m->mtx) { - thread = thread_get(m, THREAD_EVENT, func, arg, debugargpass); + thread = thread_get(m, THREAD_EVENT, func, arg, xref); /* Set its event value. */ frr_with_mutex(&thread->mtx) { diff --git a/lib/thread.h b/lib/thread.h index eb1b107e7b..6b510fc4c9 100644 --- a/lib/thread.h +++ b/lib/thread.h @@ -27,6 +27,7 @@ #include "monotime.h" #include "frratomic.h" #include "typesafe.h" +#include "xref.h" #ifdef __cplusplus extern "C" { @@ -66,6 +67,14 @@ struct cancel_req { struct thread **threadref; }; +struct xref_threadsched { + struct xref xref; + + const char *funcname; + const char *dest; + uint32_t thread_type; +}; + /* Master of the theads. */ struct thread_master { char *name; @@ -107,9 +116,7 @@ struct thread { struct timeval real; struct cpu_thread_history *hist; /* cache pointer to cpu_history */ unsigned long yield; /* yield time in microseconds */ - const char *funcname; /* name of thread function */ - const char *schedfrom; /* source file thread was scheduled from */ - int schedfrom_line; /* line number of source file */ + const struct xref_threadsched *xref; /* origin location */ pthread_mutex_t mtx; /* mutex for thread.c functions */ }; @@ -156,17 +163,45 @@ struct cpu_thread_history { thread_cancel(&(thread)); \ } while (0) -#define debugargdef const char *funcname, const char *schedfrom, int fromln - -#define thread_add_read(m,f,a,v,t) funcname_thread_add_read_write(THREAD_READ,m,f,a,v,t,#f,__FILE__,__LINE__) -#define thread_add_write(m,f,a,v,t) funcname_thread_add_read_write(THREAD_WRITE,m,f,a,v,t,#f,__FILE__,__LINE__) -#define thread_add_timer(m,f,a,v,t) funcname_thread_add_timer(m,f,a,v,t,#f,__FILE__,__LINE__) -#define thread_add_timer_msec(m,f,a,v,t) funcname_thread_add_timer_msec(m,f,a,v,t,#f,__FILE__,__LINE__) -#define thread_add_timer_tv(m,f,a,v,t) funcname_thread_add_timer_tv(m,f,a,v,t,#f,__FILE__,__LINE__) -#define thread_add_event(m,f,a,v,t) funcname_thread_add_event(m,f,a,v,t,#f,__FILE__,__LINE__) -#define thread_execute(m,f,a,v) funcname_thread_execute(m,f,a,v,#f,__FILE__,__LINE__) -#define thread_execute_name(m, f, a, v, n) \ - funcname_thread_execute(m, f, a, v, n, __FILE__, __LINE__) +/* + * Macro wrappers to generate xrefs for all thread add calls. Includes + * file/line/function info for debugging/tracing. + */ +#include "lib/xref.h" + +#define _xref_t_a(addfn, type, m, f, a, v, t) \ + ({ \ + static const struct xref_threadsched _xref \ + __attribute__((used)) = { \ + .xref = XREF_INIT(XREFT_THREADSCHED, NULL, __func__), \ + .funcname = #f, \ + .dest = #t, \ + .thread_type = THREAD_ ## type, \ + }; \ + XREF_LINK(_xref.xref); \ + _thread_add_ ## addfn(&_xref, m, f, a, v, t); \ + }) \ + /* end */ + +#define thread_add_read(m,f,a,v,t) _xref_t_a(read_write, READ, m,f,a,v,t) +#define thread_add_write(m,f,a,v,t) _xref_t_a(read_write, WRITE, m,f,a,v,t) +#define thread_add_timer(m,f,a,v,t) _xref_t_a(timer, TIMER, m,f,a,v,t) +#define thread_add_timer_msec(m,f,a,v,t) _xref_t_a(timer_msec, TIMER, m,f,a,v,t) +#define thread_add_timer_tv(m,f,a,v,t) _xref_t_a(timer_tv, TIMER, m,f,a,v,t) +#define thread_add_event(m,f,a,v,t) _xref_t_a(event, TIMER, m,f,a,v,t) + +#define thread_execute(m,f,a,v) \ + ({ \ + static const struct xref_threadsched _xref \ + __attribute__((used)) = { \ + .xref = XREF_INIT(XREFT_THREADSCHED, NULL, __func__), \ + .funcname = #f, \ + .dest = NULL, \ + .thread_type = THREAD_EXECUTE, \ + }; \ + XREF_LINK(_xref.xref); \ + _thread_execute(&_xref, m, f, a, v); \ + }) /* end */ /* Prototypes. */ extern struct thread_master *thread_master_create(const char *); @@ -174,35 +209,30 @@ void thread_master_set_name(struct thread_master *master, const char *name); extern void thread_master_free(struct thread_master *); extern void thread_master_free_unused(struct thread_master *); -extern struct thread * -funcname_thread_add_read_write(int dir, struct thread_master *, - int (*)(struct thread *), void *, int, - struct thread **, debugargdef); - -extern struct thread *funcname_thread_add_timer(struct thread_master *, - int (*)(struct thread *), - void *, long, struct thread **, - debugargdef); - -extern struct thread * -funcname_thread_add_timer_msec(struct thread_master *, int (*)(struct thread *), - void *, long, struct thread **, debugargdef); - -extern struct thread *funcname_thread_add_timer_tv(struct thread_master *, - int (*)(struct thread *), - void *, struct timeval *, - struct thread **, - debugargdef); - -extern struct thread *funcname_thread_add_event(struct thread_master *, - int (*)(struct thread *), - void *, int, struct thread **, - debugargdef); - -extern void funcname_thread_execute(struct thread_master *, - int (*)(struct thread *), void *, int, - debugargdef); -#undef debugargdef +extern struct thread *_thread_add_read_write( + const struct xref_threadsched *xref, struct thread_master *master, + int (*fn)(struct thread *), void *arg, int fd, struct thread **tref); + +extern struct thread *_thread_add_timer( + const struct xref_threadsched *xref, struct thread_master *master, + int (*fn)(struct thread *), void *arg, long t, struct thread **tref); + +extern struct thread *_thread_add_timer_msec( + const struct xref_threadsched *xref, struct thread_master *master, + int (*fn)(struct thread *), void *arg, long t, struct thread **tref); + +extern struct thread *_thread_add_timer_tv( + const struct xref_threadsched *xref, struct thread_master *master, + int (*fn)(struct thread *), void *arg, struct timeval *tv, + struct thread **tref); + +extern struct thread *_thread_add_event( + const struct xref_threadsched *xref, struct thread_master *master, + int (*fn)(struct thread *), void *arg, int val, struct thread **tref); + +extern void _thread_execute(const struct xref_threadsched *xref, + struct thread_master *master, + int (*fn)(struct thread *), void *arg, int val); extern void thread_cancel(struct thread **event); extern void thread_cancel_async(struct thread_master *, struct thread **, @@ -214,6 +214,53 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name) return vrf; } +/* Update a VRF. If not found, create one. + * Arg: + * name - The name of the vrf. + * vrf_id - The vrf_id of the vrf. + * Description: This function first finds the vrf using its name. If the vrf is + * found and the vrf-id of the existing vrf does not match the new vrf id, it + * will disable the existing vrf and update it with new vrf-id. If the vrf is + * not found, it will create the vrf with given name and the new vrf id. + */ +struct vrf *vrf_update(vrf_id_t new_vrf_id, const char *name) +{ + struct vrf *vrf = NULL; + + /*Treat VRF add for existing vrf as update + * Update VRF ID and also update in VRF ID table + */ + if (name) + vrf = vrf_lookup_by_name(name); + if (vrf && new_vrf_id != VRF_UNKNOWN && vrf->vrf_id != VRF_UNKNOWN + && vrf->vrf_id != new_vrf_id) { + if (debug_vrf) { + zlog_debug( + "Vrf Update event: %s old id: %u, new id: %u", + name, vrf->vrf_id, new_vrf_id); + } + + /*Disable the vrf to simulate implicit delete + * so that all stale routes are deleted + * This vrf will be enabled down the line + */ + vrf_disable(vrf); + + + RB_REMOVE(vrf_id_head, &vrfs_by_id, vrf); + vrf->vrf_id = new_vrf_id; + RB_INSERT(vrf_id_head, &vrfs_by_id, vrf); + + } else { + + /* + * vrf_get is implied creation if it does not exist + */ + vrf = vrf_get(new_vrf_id, name); + } + return vrf; +} + /* Delete a VRF. This is called when the underlying VRF goes away, a * pre-configured VRF is deleted or when shutting down (vrf_terminate()). */ @@ -628,12 +675,12 @@ int vrf_handler_create(struct vty *vty, const char *vrfname, if (strlen(vrfname) > VRF_NAMSIZ) { if (vty) vty_out(vty, - "%% VRF name %s invalid: length exceeds %d bytes\n", + "%% VRF name %s invalid: length exceeds %d bytes", vrfname, VRF_NAMSIZ); else flog_warn( EC_LIB_VRF_LENGTH, - "%% VRF name %s invalid: length exceeds %d bytes\n", + "%% VRF name %s invalid: length exceeds %d bytes", vrfname, VRF_NAMSIZ); return CMD_WARNING_CONFIG_FAILED; } @@ -1064,6 +1111,7 @@ static int lib_vrf_create(struct nb_cb_create_args *args) vrfp = vrf_get(VRF_UNKNOWN, vrfname); + vrf_set_user_cfged(vrfp); nb_running_set_entry(args->dnode, vrfp); return NB_OK; @@ -1089,7 +1137,7 @@ static int lib_vrf_destroy(struct nb_cb_destroy_args *args) vrfp = nb_running_unset_entry(args->dnode); /* Clear configured flag and invoke delete. */ - UNSET_FLAG(vrfp->status, VRF_CONFIGURED); + vrf_reset_user_cfged(vrfp); vrf_delete(vrfp); break; } @@ -114,6 +114,7 @@ extern struct vrf_name_head vrfs_by_name; extern struct vrf *vrf_lookup_by_id(vrf_id_t); extern struct vrf *vrf_lookup_by_name(const char *); extern struct vrf *vrf_get(vrf_id_t, const char *); +extern struct vrf *vrf_update(vrf_id_t new_vrf_id, const char *name); extern const char *vrf_id_to_name(vrf_id_t vrf_id); extern vrf_id_t vrf_name_to_id(const char *); @@ -167,6 +168,20 @@ static inline void vrf_reset_user_cfged(struct vrf *vrf) UNSET_FLAG(vrf->status, VRF_CONFIGURED); } +static inline uint32_t vrf_interface_count(struct vrf *vrf) +{ + uint32_t count = 0; + struct interface *ifp; + + RB_FOREACH (ifp, if_name_head, &vrf->ifaces_by_name) { + /* skip the l3mdev */ + if (strncmp(ifp->name, vrf->name, VRF_NAMSIZ) == 0) + continue; + count++; + } + return count; +} + /* * Utilities to obtain the user data */ @@ -73,7 +73,8 @@ enum event { #endif /* VTYSH */ }; -static void vty_event(enum event, int, struct vty *); +static void vty_event_serv(enum event event, int sock); +static void vty_event(enum event, struct vty *); /* Extern host structure from command.c */ extern struct host host; @@ -396,16 +397,6 @@ static void vty_do_window_size(struct vty *vty) vty_out(vty, "%s", cmd); } -#if 0 /* Currently not used. */ -/* Make don't use lflow vty interface. */ -static void -vty_dont_lflow_ahead (struct vty *vty) -{ - unsigned char cmd[] = { IAC, DONT, TELOPT_LFLOW, '\0' }; - vty_out (vty, "%s", cmd); -} -#endif /* 0 */ - /* Authentication of vty */ static void vty_auth(struct vty *vty, char *buf) { @@ -1090,11 +1081,6 @@ static void vty_describe_command(struct vty *vty) vector_free(varcomps); } -#if 0 - vty_out (vty, " %-*s %s\n", width - desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, - desc->str ? desc->str : ""); -#endif /* 0 */ } if ((token = token_cr)) { @@ -1299,6 +1285,7 @@ static int vty_execute(struct vty *vty) #define VTY_NORMAL 0 #define VTY_PRE_ESCAPE 1 #define VTY_ESCAPE 2 +#define VTY_CR 3 /* Escape character command map. */ static void vty_escape_map(unsigned char c, struct vty *vty) @@ -1340,14 +1327,13 @@ static int vty_read(struct thread *thread) int nbytes; unsigned char buf[VTY_READ_BUFSIZ]; - int vty_sock = THREAD_FD(thread); struct vty *vty = THREAD_ARG(thread); /* Read raw data from socket */ if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) { if (nbytes < 0) { if (ERRNO_IO_RETRY(errno)) { - vty_event(VTY_READ, vty_sock, vty); + vty_event(VTY_READ, vty); return 0; } vty->monitor = 0; /* disable monitoring to avoid @@ -1396,12 +1382,6 @@ static int vty_read(struct thread *thread) case 'Q': vty_buffer_reset(vty); break; -#if 0 /* More line does not work for "show ip bgp". */ - case '\n': - case '\r': - vty->status = VTY_MORELINE; - break; -#endif default: break; } @@ -1444,6 +1424,17 @@ static int vty_read(struct thread *thread) continue; } + if (vty->escape == VTY_CR) { + /* if we get CR+NL, the NL results in an extra empty + * prompt line being printed without this; just drop + * the NL if it immediately follows CR. + */ + vty->escape = VTY_NORMAL; + + if (buf[i] == '\n') + continue; + } + switch (buf[i]) { case CONTROL('A'): vty_beginning_of_line(vty); @@ -1488,9 +1479,12 @@ static int vty_read(struct thread *thread) case CONTROL('Z'): vty_end_config(vty); break; - case '\n': case '\r': + vty->escape = VTY_CR; + /* fallthru */ + case '\n': vty_out(vty, "\n"); + buffer_flush_available(vty->obuf, vty->wfd); vty_execute(vty); break; case '\t': @@ -1521,8 +1515,8 @@ static int vty_read(struct thread *thread) if (vty->status == VTY_CLOSE) vty_close(vty); else { - vty_event(VTY_WRITE, vty->wfd, vty); - vty_event(VTY_READ, vty_sock, vty); + vty_event(VTY_WRITE, vty); + vty_event(VTY_READ, vty); } return 0; } @@ -1532,7 +1526,6 @@ static int vty_flush(struct thread *thread) { int erase; buffer_status_t flushrc; - int vty_sock = THREAD_FD(thread); struct vty *vty = THREAD_ARG(thread); /* Tempolary disable read thread. */ @@ -1544,20 +1537,20 @@ static int vty_flush(struct thread *thread) /* N.B. if width is 0, that means we don't know the window size. */ if ((vty->lines == 0) || (vty->width == 0) || (vty->height == 0)) - flushrc = buffer_flush_available(vty->obuf, vty_sock); + flushrc = buffer_flush_available(vty->obuf, vty->wfd); else if (vty->status == VTY_MORELINE) - flushrc = buffer_flush_window(vty->obuf, vty_sock, vty->width, + flushrc = buffer_flush_window(vty->obuf, vty->wfd, vty->width, 1, erase, 0); else flushrc = buffer_flush_window( - vty->obuf, vty_sock, vty->width, + vty->obuf, vty->wfd, vty->width, vty->lines >= 0 ? vty->lines : vty->height, erase, 0); switch (flushrc) { case BUFFER_ERROR: vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ - zlog_info("buffer_flush failed on vty client fd %d, closing", - vty->fd); + zlog_info("buffer_flush failed on vty client fd %d/%d, closing", + vty->fd, vty->wfd); buffer_reset(vty->lbuf); buffer_reset(vty->obuf); vty_close(vty); @@ -1568,14 +1561,14 @@ static int vty_flush(struct thread *thread) else { vty->status = VTY_NORMAL; if (vty->lines == 0) - vty_event(VTY_READ, vty_sock, vty); + vty_event(VTY_READ, vty); } break; case BUFFER_PENDING: /* There is more data waiting to be written. */ vty->status = VTY_MORE; if (vty->lines == 0) - vty_event(VTY_WRITE, vty_sock, vty); + vty_event(VTY_WRITE, vty); break; } @@ -1678,8 +1671,8 @@ static struct vty *vty_create(int vty_sock, union sockunion *su) vty_prompt(vty); /* Add read/write thread. */ - vty_event(VTY_WRITE, vty_sock, vty); - vty_event(VTY_READ, vty_sock, vty); + vty_event(VTY_WRITE, vty); + vty_event(VTY_READ, vty); return vty; } @@ -1735,7 +1728,6 @@ void vty_stdio_resume(void) termios = stdio_orig_termios; termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); - termios.c_oflag &= ~OPOST; termios.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN); termios.c_cflag &= ~(CSIZE | PARENB); termios.c_cflag |= CS8; @@ -1746,8 +1738,8 @@ void vty_stdio_resume(void) vty_prompt(stdio_vty); /* Add read/write thread. */ - vty_event(VTY_WRITE, 1, stdio_vty); - vty_event(VTY_READ, 0, stdio_vty); + vty_event(VTY_WRITE, stdio_vty); + vty_event(VTY_READ, stdio_vty); } void vty_stdio_close(void) @@ -1796,7 +1788,7 @@ static int vty_accept(struct thread *thread) accept_sock = THREAD_FD(thread); /* We continue hearing vty socket. */ - vty_event(VTY_SERV, accept_sock, NULL); + vty_event_serv(VTY_SERV, accept_sock); memset(&su, 0, sizeof(union sockunion)); @@ -1826,7 +1818,7 @@ static int vty_accept(struct thread *thread) close(vty_sock); /* continue accepting connections */ - vty_event(VTY_SERV, accept_sock, NULL); + vty_event_serv(VTY_SERV, accept_sock); return 0; } @@ -1842,7 +1834,7 @@ static int vty_accept(struct thread *thread) close(vty_sock); /* continue accepting connections */ - vty_event(VTY_SERV, accept_sock, NULL); + vty_event_serv(VTY_SERV, accept_sock); return 0; } @@ -1915,7 +1907,7 @@ static void vty_serv_sock_addrinfo(const char *hostname, unsigned short port) continue; } - vty_event(VTY_SERV, sock, NULL); + vty_event_serv(VTY_SERV, sock); } while ((ainfo = ainfo->ai_next) != NULL); freeaddrinfo(ainfo_save); @@ -1993,7 +1985,7 @@ static void vty_serv_un(const char *path) } } - vty_event(VTYSH_SERV, sock, NULL); + vty_event_serv(VTYSH_SERV, sock); } /* #define VTYSH_DEBUG 1 */ @@ -2008,7 +2000,7 @@ static int vtysh_accept(struct thread *thread) accept_sock = THREAD_FD(thread); - vty_event(VTYSH_SERV, accept_sock, NULL); + vty_event_serv(VTYSH_SERV, accept_sock); memset(&client, 0, sizeof(struct sockaddr_un)); client_len = sizeof(struct sockaddr_un); @@ -2042,7 +2034,7 @@ static int vtysh_accept(struct thread *thread) vty->type = VTY_SHELL_SERV; vty->node = VIEW_NODE; - vty_event(VTYSH_READ, sock, vty); + vty_event(VTYSH_READ, vty); return 0; } @@ -2051,7 +2043,7 @@ static int vtysh_flush(struct vty *vty) { switch (buffer_flush_available(vty->obuf, vty->wfd)) { case BUFFER_PENDING: - vty_event(VTYSH_WRITE, vty->wfd, vty); + vty_event(VTYSH_WRITE, vty); break; case BUFFER_ERROR: vty->monitor = @@ -2084,7 +2076,7 @@ static int vtysh_read(struct thread *thread) if ((nbytes = read(sock, buf, VTY_READ_BUFSIZ)) <= 0) { if (nbytes < 0) { if (ERRNO_IO_RETRY(errno)) { - vty_event(VTYSH_READ, sock, vty); + vty_event(VTYSH_READ, vty); return 0; } vty->monitor = 0; /* disable monitoring to avoid @@ -2150,7 +2142,7 @@ static int vtysh_read(struct thread *thread) if (vty->status == VTY_CLOSE) vty_close(vty); else - vty_event(VTYSH_READ, sock, vty); + vty_event(VTYSH_READ, vty); return 0; } @@ -2657,33 +2649,44 @@ int vty_config_node_exit(struct vty *vty) /* Master of the threads. */ static struct thread_master *vty_master; -static void vty_event(enum event event, int sock, struct vty *vty) +static void vty_event_serv(enum event event, int sock) { struct thread *vty_serv_thread = NULL; switch (event) { case VTY_SERV: - vty_serv_thread = thread_add_read(vty_master, vty_accept, vty, - sock, NULL); + vty_serv_thread = thread_add_read(vty_master, vty_accept, + NULL, sock, NULL); vector_set_index(Vvty_serv_thread, sock, vty_serv_thread); break; #ifdef VTYSH case VTYSH_SERV: - vty_serv_thread = thread_add_read(vty_master, vtysh_accept, vty, - sock, NULL); + vty_serv_thread = thread_add_read(vty_master, vtysh_accept, + NULL, sock, NULL); vector_set_index(Vvty_serv_thread, sock, vty_serv_thread); break; +#endif /* VTYSH */ + default: + assert(!"vty_event_serv() called incorrectly"); + } +} + +static void vty_event(enum event event, struct vty *vty) +{ + switch (event) { +#ifdef VTYSH case VTYSH_READ: - thread_add_read(vty_master, vtysh_read, vty, sock, + thread_add_read(vty_master, vtysh_read, vty, vty->fd, &vty->t_read); break; case VTYSH_WRITE: - thread_add_write(vty_master, vtysh_write, vty, sock, + thread_add_write(vty_master, vtysh_write, vty, vty->wfd, &vty->t_write); break; #endif /* VTYSH */ case VTY_READ: - thread_add_read(vty_master, vty_read, vty, sock, &vty->t_read); + thread_add_read(vty_master, vty_read, vty, vty->fd, + &vty->t_read); /* Time out treatment. */ if (vty->v_timeout) { @@ -2693,7 +2696,7 @@ static void vty_event(enum event event, int sock, struct vty *vty) } break; case VTY_WRITE: - thread_add_write(vty_master, vty_flush, vty, sock, + thread_add_write(vty_master, vty_flush, vty, vty->wfd, &vty->t_write); break; case VTY_TIMEOUT_RESET: @@ -2702,6 +2705,8 @@ static void vty_event(enum event event, int sock, struct vty *vty) thread_add_timer(vty_master, vty_timeout, vty, vty->v_timeout, &vty->t_timeout); break; + default: + assert(!"vty_event() called incorrectly"); } } @@ -2748,7 +2753,7 @@ static int exec_timeout(struct vty *vty, const char *min_str, vty_timeout_val = timeout; vty->v_timeout = timeout; - vty_event(VTY_TIMEOUT_RESET, 0, vty); + vty_event(VTY_TIMEOUT_RESET, vty); return CMD_SUCCESS; diff --git a/lib/wheel.c b/lib/wheel.c index f5e5cc52c3..5bdd6292f9 100644 --- a/lib/wheel.c +++ b/lib/wheel.c @@ -73,8 +73,7 @@ static int wheel_timer_thread(struct thread *t) wheel = THREAD_ARG(t); - thread_execute_name(wheel->master, wheel_timer_thread_helper, - wheel, 0, wheel->name); + thread_execute(wheel->master, wheel_timer_thread_helper, wheel, 0); return 0; } diff --git a/lib/workqueue.c b/lib/workqueue.c index f8e4677220..8eabdf52e7 100644 --- a/lib/workqueue.c +++ b/lib/workqueue.c @@ -377,11 +377,6 @@ stats: if (yielded) wq->yields++; -#if 0 - printf ("%s: cycles %d, new: best %d, worst %d\n", - __func__, cycles, wq->cycles.best, wq->cycles.granularity); -#endif - /* Is the queue done yet? If it is, call the completion callback. */ if (!work_queue_empty(wq)) { if (ret == WQ_RETRY_LATER || diff --git a/lib/xref.c b/lib/xref.c new file mode 100644 index 0000000000..40efe51363 --- /dev/null +++ b/lib/xref.c @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2017-20 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <pthread.h> +#include <signal.h> +#include <inttypes.h> + +#include "xref.h" +#include "vty.h" +#include "jhash.h" +#include "sha256.h" +#include "memory.h" +#include "hash.h" + +struct xref_block *xref_blocks; +static struct xref_block **xref_block_last = &xref_blocks; + +static void base32(uint8_t **inpos, int *bitpos, + char *out, size_t n_chars) +{ + static const char base32ch[] = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; + + char *opos = out; + uint8_t *in = *inpos; + int bp = *bitpos; + + while (opos < out + n_chars) { + uint32_t bits = in[0] | (in[1] << 8); + + if (bp == -1) + bits |= 0x10; + else + bits >>= bp; + + *opos++ = base32ch[bits & 0x1f]; + + bp += 5; + if (bp >= 8) + in++, bp -= 8; + } + *opos = '\0'; + *inpos = in; + *bitpos = bp; +} + +static void xref_add_one(const struct xref *xref) +{ + SHA256_CTX sha; + struct xrefdata *xrefdata; + + const char *filename, *p, *q; + uint8_t hash[32], *h = hash; + uint32_t be_val; + int bitpos; + + if (!xref || !xref->xrefdata) + return; + + xrefdata = xref->xrefdata; + xrefdata->xref = xref; + + if (!xrefdata->hashstr) + return; + + /* as far as the unique ID is concerned, only use the last + * directory name + filename, e.g. "bgpd/bgp_route.c". This + * gives a little leeway in moving things and avoids IDs being + * screwed up by out of tree builds or absolute pathnames. + */ + filename = xref->file; + p = strrchr(filename, '/'); + if (p) { + q = memrchr(filename, '/', p - filename); + if (q) + filename = q + 1; + else + filename = p + 1; + } + + SHA256_Init(&sha); + SHA256_Update(&sha, filename, strlen(filename)); + SHA256_Update(&sha, xrefdata->hashstr, + strlen(xrefdata->hashstr)); + be_val = htonl(xrefdata->hashu32[0]); + SHA256_Update(&sha, &be_val, sizeof(be_val)); + be_val = htonl(xrefdata->hashu32[1]); + SHA256_Update(&sha, &be_val, sizeof(be_val)); + SHA256_Final(hash, &sha); + + bitpos = -1; + base32(&h, &bitpos, &xrefdata->uid[0], 5); + xrefdata->uid[5] = '-'; + base32(&h, &bitpos, &xrefdata->uid[6], 5); +} + +void xref_gcc_workaround(const struct xref *xref) +{ + xref_add_one(xref); +} + +void xref_block_add(struct xref_block *block) +{ + const struct xref * const *xrefp; + + *xref_block_last = block; + xref_block_last = &block->next; + + for (xrefp = block->start; xrefp < block->stop; xrefp++) + xref_add_one(*xrefp); +} diff --git a/lib/xref.h b/lib/xref.h new file mode 100644 index 0000000000..b3243fa058 --- /dev/null +++ b/lib/xref.h @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2017-20 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _FRR_XREF_H +#define _FRR_XREF_H + +#include <stdint.h> +#include <stdlib.h> +#include <limits.h> +#include <errno.h> +#include "compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum xref_type { + XREFT_NONE = 0, + + XREFT_THREADSCHED = 0x100, + + XREFT_LOGMSG = 0x200, + + XREFT_DEFUN = 0x300, + XREFT_INSTALL_ELEMENT = 0x301, +}; + +/* struct xref is the "const" part; struct xrefdata is the writable part. */ +struct xref; +struct xrefdata; + +struct xref { + /* this may be NULL, depending on the type of the xref. + * if it is NULL, the xref has no unique ID and cannot be accessed + * through that mechanism. + */ + struct xrefdata *xrefdata; + + /* type isn't generally needed at runtime */ + enum xref_type type; + + /* code location */ + int line; + const char *file; + const char *func; + + /* -- 32 bytes (on 64bit) -- */ + + /* type-specific bits appended by embedding this struct */ +}; + +struct xrefdata { + /* pointer back to the const part; this will be initialized at + * program startup by xref_block_add(). (Creating structs with + * cyclic pointers to each other is not easily possible for + * function-scoped static variables.) + * + * There is no xrefdata w/o xref, but there are xref w/o xrefdata. + */ + const struct xref *xref; + + /* base32(crockford) of unique ID. not all bytes are used, but + * let's pad to 16 for simplicity + */ + char uid[16]; + + /* hash/uid input + * if hashstr is NULL, no UID is assigned/calculated. Use macro + * string concatenation if multiple values need to be fed in. + * (This is here to not make the UID calculation independent of + * xref type.) + */ + const char *hashstr; + uint32_t hashu32[2]; + + /* -- 32 bytes (on 64bit) -- */ +}; + +/* linker "magic" is used to create an array of pointers to struct xref. + * the result is a contiguous block of pointers, each pointing to an xref + * somewhere in the code. The linker gives us start and end pointers, we + * stuff those into the struct below and hook up a constructor to run at + * program startup with the struct passed. + * + * Placing the xrefs themselves into an array doesn't work because they'd + * need to be constant size, but we're embedding struct xref into other + * container structs with extra data. Also this means that external code + * (like the python xref dumper) can safely ignore extra data at the end of + * xrefs without needing to account for size in iterating the array. + * + * If you're curious, this is also how __attribute__((constructor)) (and + * destructor) are implemented - there are 2 arrays, ".init_array" and + * ".fini_array", containing function pointers. The magic turns out to be + * quite mundane, actually ;) + * + * The slightly tricky bit is that this is a per-object (i.e. per shared + * library & daemon) thing and we need a bit of help (in XREF_SETUP) to + * initialize correctly. + */ + +struct xref_block { + struct xref_block *next; + const struct xref * const *start; + const struct xref * const *stop; +}; + +extern struct xref_block *xref_blocks; +extern void xref_block_add(struct xref_block *block); +extern void xref_gcc_workaround(const struct xref *xref); + +#ifndef HAVE_SECTION_SYMS +/* we have a build system patch to use GNU ld on Solaris; if that doesn't + * work we end up on Solaris ld which doesn't support the section start/end + * symbols. + */ +#define XREF_SETUP() \ + CPP_NOTICE("Missing linker support for section arrays. Solaris ld?") +#else +/* the actual symbols that the linker provides for us. Note these are + * _symbols_ referring to the actual section start/end, i.e. they are very + * much NOT _pointers_, rather the symbol *value* is the pointer. Declaring + * them as size-1 arrays is the "best" / "right" thing. + */ +extern const struct xref * const __start_xref_array[1] DSO_LOCAL; +extern const struct xref * const __stop_xref_array[1] DSO_LOCAL; + +/* this macro is invoked once for each standalone DSO through + * FRR_MODULE_SETUP \ + * }-> FRR_COREMOD_SETUP -> XREF_SETUP + * FRR_DAEMON_INFO / + */ +#define XREF_SETUP() \ + static const struct xref _dummy_xref = { \ + /* .xrefdata = */ NULL, \ + /* .type = */ XREFT_NONE, \ + /* .line = */ __LINE__, \ + /* .file = */ __FILE__, \ + /* .func = */ "dummy", \ + }; \ + static const struct xref * const _dummy_xref_p \ + __attribute__((used, section("xref_array"))) \ + = &_dummy_xref; \ + static void __attribute__((used, _CONSTRUCTOR(1100))) \ + _xref_init(void) { \ + static struct xref_block _xref_block = { \ + .start = __start_xref_array, \ + .stop = __stop_xref_array, \ + }; \ + xref_block_add(&_xref_block); \ + } \ + asm(XREF_NOTE); \ + /* end */ + +/* the following blurb emits an ELF note indicating start and end of the xref + * array in the binary. This is technically the "correct" entry point for + * external tools reading xrefs out of an ELF shared library or executable. + * + * right now, the extraction tools use the section header for "xref_array" + * instead; however, section headers are technically not necessarily preserved + * for fully linked libraries or executables. (In practice they are only + * stripped by obfuscation tools.) + * + * conversely, for reading xrefs out of a single relocatable object file (e.g. + * bar.o), section headers are the right thing to look at since the note is + * only emitted for the final binary once. + * + * FRR itself does not need this note to operate correctly, so if you have + * some build issue with it just add -DFRR_XREF_NO_NOTE to your build flags + * to disable it. + */ +#ifdef FRR_XREF_NO_NOTE +#define XREF_NOTE "" +#else + +#if __SIZEOF_POINTER__ == 4 +#define _NOTE_2PTRSIZE "8" +#define _NOTE_PTR ".long" +#elif __SIZEOF_POINTER__ == 8 +#define _NOTE_2PTRSIZE "16" +#define _NOTE_PTR ".quad" +#else +#error unsupported pointer size +#endif + +#ifdef __arm__ +# define asmspecial "%" +#else +# define asmspecial "@" +#endif + +#define XREF_NOTE \ + "" "\n"\ + " .type _frr_xref_note," asmspecial "object" "\n"\ + " .pushsection .note.FRR,\"a\"," asmspecial "note" "\n"\ + " .p2align 2" "\n"\ + "_frr_xref_note:" "\n"\ + " .long 9" "\n"\ + " .long " _NOTE_2PTRSIZE "\n"\ + " .ascii \"XREF\"" "\n"\ + " .ascii \"FRRouting\\0\\0\\0\"" "\n"\ + " " _NOTE_PTR " __start_xref_array-." "\n"\ + " " _NOTE_PTR " __stop_xref_array-." "\n"\ + " .size _frr_xref_note, .-_frr_xref_note" "\n"\ + " .popsection" "\n"\ + "" "\n"\ + /* end */ +#endif + +#endif /* HAVE_SECTION_SYMS */ + +/* emit the array entry / pointer to xref */ +#if defined(__clang__) || !defined(__cplusplus) +#define XREF_LINK(dst) \ + static const struct xref * const NAMECTR(xref_p_) \ + __attribute__((used, section("xref_array"))) \ + = &(dst) \ + /* end */ + +#else /* GCC && C++ */ +/* workaround for GCC bug 41091 (dated 2009), added in 2021... + * + * this breaks extraction of xrefs with xrelfo.py (because the xref_array + * entry will be missing), but provides full runtime functionality. To get + * the proper list of xrefs from C++ code, build with clang... + */ +struct _xref_p { + const struct xref * const ptr; + + _xref_p(const struct xref *_ptr) : ptr(_ptr) + { + xref_gcc_workaround(_ptr); + } +}; + +#define XREF_LINK(dst) \ + static const struct _xref_p __attribute__((used)) \ + NAMECTR(xref_p_)(&(dst)) \ + /* end */ +#endif + +/* initializer for a "struct xref" */ +#define XREF_INIT(type_, xrefdata_, func_) \ + { \ + /* .xrefdata = */ (xrefdata_), \ + /* .type = */ (type_), \ + /* .line = */ __LINE__, \ + /* .file = */ __FILE__, \ + /* .func = */ func_, \ + } \ + /* end */ + +/* use with XREF_INIT when outside of a function, i.e. no __func__ */ +#define XREF_NO_FUNC "<global>" + +#ifdef __cplusplus +} +#endif + +#endif /* _FRR_XREF_H */ diff --git a/lib/yang.c b/lib/yang.c index a3e2a395d7..383dc9f5eb 100644 --- a/lib/yang.c +++ b/lib/yang.c @@ -92,6 +92,7 @@ static const char *const frr_native_modules[] = { "frr-isisd", "frr-vrrpd", "frr-zebra", + "frr-pathd", }; /* clang-format on */ diff --git a/lib/zclient.c b/lib/zclient.c index cb4555650d..20c285cf7f 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -40,6 +40,7 @@ #include "nexthop_group.h" #include "lib_errors.h" #include "srte.h" +#include "printfrr.h" DEFINE_MTYPE_STATIC(LIB, ZCLIENT, "Zclient") DEFINE_MTYPE_STATIC(LIB, REDIST_INST, "Redistribution instance IDs") @@ -995,17 +996,24 @@ done: return ret; } -int zapi_nhg_encode(struct stream *s, int cmd, struct zapi_nhg *api_nhg) +static int zapi_nhg_encode(struct stream *s, int cmd, struct zapi_nhg *api_nhg) { int i; if (cmd != ZEBRA_NHG_DEL && cmd != ZEBRA_NHG_ADD) { flog_err(EC_LIB_ZAPI_ENCODE, - "%s: Specified zapi NHG command (%d) doesn't exist\n", + "%s: Specified zapi NHG command (%d) doesn't exist", __func__, cmd); return -1; } + if (api_nhg->nexthop_num >= MULTIPATH_NUM || + api_nhg->backup_nexthop_num >= MULTIPATH_NUM) { + flog_err(EC_LIB_ZAPI_ENCODE, + "%s: zapi NHG encode with invalid input", __func__); + return -1; + } + stream_reset(s); zclient_create_header(s, cmd, VRF_DEFAULT); @@ -1023,7 +1031,6 @@ int zapi_nhg_encode(struct stream *s, int cmd, struct zapi_nhg *api_nhg) zapi_nexthop_encode(s, &api_nhg->nexthops[i], 0, 0); /* Backup nexthops */ - stream_putw(s, api_nhg->backup_nexthop_num); for (i = 0; i < api_nhg->backup_nexthop_num; i++) @@ -1058,7 +1065,7 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) if (api->type >= ZEBRA_ROUTE_MAX) { flog_err(EC_LIB_ZAPI_ENCODE, - "%s: Specified route type (%u) is not a legal value\n", + "%s: Specified route type (%u) is not a legal value", __func__, api->type); return -1; } @@ -1070,7 +1077,7 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) if (api->safi < SAFI_UNICAST || api->safi >= SAFI_MAX) { flog_err(EC_LIB_ZAPI_ENCODE, - "%s: Specified route SAFI (%u) is not a legal value\n", + "%s: Specified route SAFI (%u) is not a legal value", __func__, api->safi); return -1; } @@ -1285,7 +1292,7 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) STREAM_GETC(s, api->type); if (api->type >= ZEBRA_ROUTE_MAX) { flog_err(EC_LIB_ZAPI_ENCODE, - "%s: Specified route type: %d is not a legal value\n", + "%s: Specified route type: %d is not a legal value", __func__, api->type); return -1; } @@ -1296,7 +1303,7 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) STREAM_GETC(s, api->safi); if (api->safi < SAFI_UNICAST || api->safi >= SAFI_MAX) { flog_err(EC_LIB_ZAPI_ENCODE, - "%s: Specified route SAFI (%u) is not a legal value\n", + "%s: Specified route SAFI (%u) is not a legal value", __func__, api->safi); return -1; } @@ -3297,7 +3304,7 @@ static void zclient_capability_decode(ZAPI_CALLBACK_ARGS) if (vrf_backend < 0 || vrf_configure_backend(vrf_backend)) { flog_err(EC_LIB_ZAPI_ENCODE, - "%s: Garbage VRF backend type: %d\n", __func__, + "%s: Garbage VRF backend type: %d", __func__, vrf_backend); goto stream_failure; } @@ -4121,3 +4128,51 @@ uint32_t zclient_get_nhg_start(uint32_t proto) return ZEBRA_NHG_PROTO_SPACING * proto; } + +char *zclient_dump_route_flags(uint32_t flags, char *buf, size_t len) +{ + if (flags == 0) { + snprintfrr(buf, len, "None "); + return buf; + } + + snprintfrr( + buf, len, "%s%s%s%s%s%s%s%s%s%s", + CHECK_FLAG(flags, ZEBRA_FLAG_ALLOW_RECURSION) ? "Recursion " + : "", + CHECK_FLAG(flags, ZEBRA_FLAG_SELFROUTE) ? "Self " : "", + CHECK_FLAG(flags, ZEBRA_FLAG_IBGP) ? "iBGP " : "", + CHECK_FLAG(flags, ZEBRA_FLAG_SELECTED) ? "Selected " : "", + CHECK_FLAG(flags, ZEBRA_FLAG_FIB_OVERRIDE) ? "Override " : "", + CHECK_FLAG(flags, ZEBRA_FLAG_EVPN_ROUTE) ? "Evpn " : "", + CHECK_FLAG(flags, ZEBRA_FLAG_RR_USE_DISTANCE) ? "RR Distance " + : "", + CHECK_FLAG(flags, ZEBRA_FLAG_TRAPPED) ? "Trapped " : "", + CHECK_FLAG(flags, ZEBRA_FLAG_OFFLOADED) ? "Offloaded " : "", + CHECK_FLAG(flags, ZEBRA_FLAG_OFFLOAD_FAILED) ? "Offload Failed " + : ""); + return buf; +} + +char *zclient_evpn_dump_macip_flags(uint8_t flags, char *buf, size_t len) +{ + if (flags == 0) { + snprintfrr(buf, len, "None "); + return buf; + } + + snprintfrr( + buf, len, "%s%s%s%s%s%s%s", + CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY) ? "Sticky MAC " : "", + CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW) ? "Gateway MAC " : "", + CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG) ? "Router " + : "", + CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_OVERRIDE_FLAG) ? "Override " + : "", + CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_SVI_IP) ? "SVI MAC " : "", + CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT) ? "Proxy " + : "", + CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_SYNC_PATH) ? "Sync " : ""); + + return buf; +} diff --git a/lib/zclient.h b/lib/zclient.h index 2af448a20c..cf52ea91a0 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -479,6 +479,7 @@ struct zapi_route { uint8_t type; unsigned short instance; + /* If you add flags, update zclient_dump_route_flags */ uint32_t flags; /* * Cause Zebra to consider this routes nexthops recursively @@ -580,6 +581,8 @@ struct zapi_route { } opaque; }; +extern char *zclient_dump_route_flags(uint32_t flags, char *buf, size_t len); + struct zapi_labels { uint8_t message; #define ZAPI_LABELS_FTN 0x01 @@ -634,6 +637,52 @@ struct zapi_pw_status { uint32_t status; }; +/* IGP instance data associated to a RLFA. */ +struct zapi_rlfa_igp { + vrf_id_t vrf_id; + int protocol; + union { + struct { + char area_tag[32]; + struct { + int tree_id; + int level; + unsigned int run_id; + } spf; + } isis; + }; +}; + +/* IGP -> LDP RLFA (un)registration message. */ +struct zapi_rlfa_request { + /* IGP instance data. */ + struct zapi_rlfa_igp igp; + + /* Destination prefix. */ + struct prefix destination; + + /* PQ node address. */ + struct in_addr pq_address; +}; + +/* LDP -> IGP RLFA label update. */ +struct zapi_rlfa_response { + /* IGP instance data. */ + struct zapi_rlfa_igp igp; + + /* Destination prefix. */ + struct prefix destination; + + /* Resolved LDP labels. */ + mpls_label_t pq_label; + uint16_t nexthop_num; + struct { + int family; + union g_addr gate; + mpls_label_t label; + } nexthops[MULTIPATH_NUM]; +}; + enum zapi_route_notify_owner { ZAPI_ROUTE_FAIL_INSTALL, ZAPI_ROUTE_BETTER_ADMIN_WON, @@ -722,8 +771,11 @@ zapi_rule_notify_owner2str(enum zapi_rule_notify_owner note) #define ZEBRA_MACIP_TYPE_PROXY_ADVERT 0x20 /* Not locally active */ #define ZEBRA_MACIP_TYPE_SYNC_PATH 0x40 /* sync path */ /* XXX - flags is an u8; that needs to be changed to u32 if you need - * to allocate past 0x80 + * to allocate past 0x80. Additionally touch zclient_evpn_dump_macip_flags */ +#define MACIP_BUF_SIZE 128 +extern char *zclient_evpn_dump_macip_flags(uint8_t flags, char *buf, + size_t len); /* Zebra ES VTEP flags (ZEBRA_REMOTE_ES_VTEP_ADD) */ /* ESR has been rxed from the VTEP. Only VTEPs that have advertised the @@ -969,9 +1021,7 @@ bool zapi_ipset_notify_decode(struct stream *s, uint32_t *unique, enum zapi_ipset_notify_owner *note); - -extern int zapi_nhg_encode(struct stream *s, int cmd, struct zapi_nhg *api_nhg); -extern int zapi_nhg_decode(struct stream *s, int cmd, struct zapi_nhg *api_nhg); +/* Nexthop-group message apis */ extern enum zclient_send_status zclient_nhg_send(struct zclient *zclient, int cmd, struct zapi_nhg *api_nhg); @@ -1091,6 +1141,12 @@ enum zapi_opaque_registry { LDP_IGP_SYNC_IF_STATE_UPDATE = 4, /* Announce that LDP is up */ LDP_IGP_SYNC_ANNOUNCE_UPDATE = 5, + /* Register RLFA with LDP */ + LDP_RLFA_REGISTER = 7, + /* Unregister all RLFAs with LDP */ + LDP_RLFA_UNREGISTER_ALL = 8, + /* Announce LDP labels associated to a previously registered RLFA */ + LDP_RLFA_LABELS = 9, }; /* Send the hello message. diff --git a/lib/zlog.c b/lib/zlog.c index e77feec5f2..51509e24f4 100644 --- a/lib/zlog.c +++ b/lib/zlog.c @@ -94,6 +94,7 @@ struct zlog_msg { const char *fmt; va_list args; + const struct xref_logmsg *xref; char *stackbuf; size_t stackbufsz; @@ -349,12 +350,14 @@ void zlog_tls_buffer_flush(void) } -static void vzlog_notls(int prio, const char *fmt, va_list ap) +static void vzlog_notls(const struct xref_logmsg *xref, int prio, + const char *fmt, va_list ap) { struct zlog_target *zt; struct zlog_msg stackmsg = { .prio = prio & LOG_PRIMASK, .fmt = fmt, + .xref = xref, }, *msg = &stackmsg; char stackbuf[512]; @@ -379,8 +382,8 @@ static void vzlog_notls(int prio, const char *fmt, va_list ap) XFREE(MTYPE_LOG_MESSAGE, msg->text); } -static void vzlog_tls(struct zlog_tls *zlog_tls, int prio, - const char *fmt, va_list ap) +static void vzlog_tls(struct zlog_tls *zlog_tls, const struct xref_logmsg *xref, + int prio, const char *fmt, va_list ap) { struct zlog_target *zt; struct zlog_msg *msg; @@ -413,6 +416,7 @@ static void vzlog_tls(struct zlog_tls *zlog_tls, int prio, msg->stackbufsz = TLS_LOG_BUF_SIZE - zlog_tls->bufpos - 1; msg->fmt = fmt; msg->prio = prio & LOG_PRIMASK; + msg->xref = xref; if (msg->prio < LOG_INFO) immediate = true; @@ -447,7 +451,8 @@ static void vzlog_tls(struct zlog_tls *zlog_tls, int prio, XFREE(MTYPE_LOG_MESSAGE, msg->text); } -void vzlog(int prio, const char *fmt, va_list ap) +void vzlogx(const struct xref_logmsg *xref, int prio, + const char *fmt, va_list ap) { struct zlog_tls *zlog_tls = zlog_tls_get(); @@ -480,9 +485,9 @@ void vzlog(int prio, const char *fmt, va_list ap) #endif if (zlog_tls) - vzlog_tls(zlog_tls, prio, fmt, ap); + vzlog_tls(zlog_tls, xref, prio, fmt, ap); else - vzlog_notls(prio, fmt, ap); + vzlog_notls(xref, prio, fmt, ap); } void zlog_sigsafe(const char *text, size_t len) @@ -516,6 +521,11 @@ int zlog_msg_prio(struct zlog_msg *msg) return msg->prio; } +const struct xref_logmsg *zlog_msg_xref(struct zlog_msg *msg) +{ + return msg->xref; +} + const char *zlog_msg_text(struct zlog_msg *msg, size_t *textlen) { if (!msg->text) { diff --git a/lib/zlog.h b/lib/zlog.h index 1c5013746b..bdf59fa68e 100644 --- a/lib/zlog.h +++ b/lib/zlog.h @@ -38,6 +38,20 @@ extern char zlog_prefix[]; extern size_t zlog_prefixsz; extern int zlog_tmpdirfd; +struct xref_logmsg { + struct xref xref; + + const char *fmtstring; + uint32_t priority; + uint32_t ec; +}; + +struct xrefdata_logmsg { + struct xrefdata xrefdata; + + /* nothing more here right now */ +}; + /* These functions are set up to write to stdout/stderr without explicit * initialization and/or before config load. There is no need to call e.g. * fprintf(stderr, ...) just because it's "too early" at startup. Depending @@ -45,7 +59,9 @@ extern int zlog_tmpdirfd; * determine wether something is a log message or something else. */ -extern void vzlog(int prio, const char *fmt, va_list ap); +extern void vzlogx(const struct xref_logmsg *xref, int prio, + const char *fmt, va_list ap); +#define vzlog(prio, ...) vzlogx(NULL, prio, __VA_ARGS__) PRINTFRR(2, 3) static inline void zlog(int prio, const char *fmt, ...) @@ -57,11 +73,61 @@ static inline void zlog(int prio, const char *fmt, ...) va_end(ap); } -#define zlog_err(...) zlog(LOG_ERR, __VA_ARGS__) -#define zlog_warn(...) zlog(LOG_WARNING, __VA_ARGS__) -#define zlog_info(...) zlog(LOG_INFO, __VA_ARGS__) -#define zlog_notice(...) zlog(LOG_NOTICE, __VA_ARGS__) -#define zlog_debug(...) zlog(LOG_DEBUG, __VA_ARGS__) +PRINTFRR(2, 3) +static inline void zlog_ref(const struct xref_logmsg *xref, + const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vzlogx(xref, xref->priority, fmt, ap); + va_end(ap); +} + +#define _zlog_ref(prio, msg, ...) do { \ + static struct xrefdata _xrefdata = { \ + .hashstr = (msg), \ + .hashu32 = { (prio), 0 }, \ + }; \ + static const struct xref_logmsg _xref __attribute__((used)) = {\ + .xref = XREF_INIT(XREFT_LOGMSG, &_xrefdata, __func__), \ + .fmtstring = (msg), \ + .priority = (prio), \ + }; \ + XREF_LINK(_xref.xref); \ + zlog_ref(&_xref, (msg), ## __VA_ARGS__); \ + } while (0) + +#define zlog_err(...) _zlog_ref(LOG_ERR, __VA_ARGS__) +#define zlog_warn(...) _zlog_ref(LOG_WARNING, __VA_ARGS__) +#define zlog_info(...) _zlog_ref(LOG_INFO, __VA_ARGS__) +#define zlog_notice(...) _zlog_ref(LOG_NOTICE, __VA_ARGS__) +#define zlog_debug(...) _zlog_ref(LOG_DEBUG, __VA_ARGS__) + +#define _zlog_ecref(ec_, prio, msg, ...) do { \ + static struct xrefdata _xrefdata = { \ + .hashstr = (msg), \ + .hashu32 = { (prio), (ec_) }, \ + }; \ + static const struct xref_logmsg _xref __attribute__((used)) = {\ + .xref = XREF_INIT(XREFT_LOGMSG, &_xrefdata, __func__), \ + .fmtstring = (msg), \ + .priority = (prio), \ + .ec = (ec_), \ + }; \ + XREF_LINK(_xref.xref); \ + zlog_ref(&_xref, "[EC %u] " msg, ec_, ## __VA_ARGS__); \ + } while (0) + +#define flog_err(ferr_id, format, ...) \ + _zlog_ecref(ferr_id, LOG_ERR, format, ## __VA_ARGS__) +#define flog_warn(ferr_id, format, ...) \ + _zlog_ecref(ferr_id, LOG_WARNING, format, ## __VA_ARGS__) + +#define flog_err_sys(ferr_id, format, ...) \ + flog_err(ferr_id, format, ##__VA_ARGS__) +#define flog(priority, ferr_id, format, ...) \ + zlog(priority, "[EC %u] " format, ferr_id, ##__VA_ARGS__) extern void zlog_sigsafe(const char *text, size_t len); @@ -83,6 +149,7 @@ extern void zlog_sigsafe(const char *text, size_t len); struct zlog_msg; extern int zlog_msg_prio(struct zlog_msg *msg); +extern const struct xref_logmsg *zlog_msg_xref(struct zlog_msg *msg); /* pass NULL as textlen if you don't need it. */ extern const char *zlog_msg_text(struct zlog_msg *msg, size_t *textlen); |
