summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/agentx.c12
-rw-r--r--lib/bitfield.h5
-rw-r--r--lib/clippy.c20
-rw-r--r--lib/command.c731
-rw-r--r--lib/command.h23
-rw-r--r--lib/command_match.c2
-rw-r--r--lib/csv.c6
-rw-r--r--lib/defaults.c7
-rw-r--r--lib/filter.c33
-rw-r--r--lib/frr_pthread.c5
-rw-r--r--lib/frrlua.c2
-rw-r--r--lib/frrlua.h2
-rw-r--r--lib/grammar_sandbox.c2
-rw-r--r--lib/grammar_sandbox_main.c10
-rw-r--r--lib/hook.c23
-rw-r--r--lib/hook.h28
-rw-r--r--lib/if.c183
-rw-r--r--lib/if.h99
-rw-r--r--lib/ipaddr.h2
-rw-r--r--lib/json.c5
-rw-r--r--lib/json.h3
-rw-r--r--lib/keychain.c24
-rw-r--r--lib/libfrr.c16
-rw-r--r--lib/log.c703
-rw-r--r--lib/log.h62
-rw-r--r--lib/log_filter.c156
-rw-r--r--lib/log_int.h59
-rw-r--r--lib/log_vty.c647
-rw-r--r--lib/log_vty.h14
-rw-r--r--lib/md5.c4
-rw-r--r--lib/memory.c3
-rw-r--r--lib/memory.h14
-rw-r--r--lib/mpls.c2
-rw-r--r--lib/mpls.h5
-rw-r--r--lib/network.c18
-rw-r--r--lib/network.h2
-rw-r--r--lib/nexthop.c44
-rw-r--r--lib/nexthop.h33
-rw-r--r--lib/nexthop_group.c207
-rw-r--r--lib/nexthop_group.h9
-rw-r--r--lib/northbound.c7
-rw-r--r--lib/northbound.h11
-rw-r--r--lib/northbound_cli.c9
-rw-r--r--lib/northbound_grpc.cpp21
-rw-r--r--lib/plist.c34
-rw-r--r--lib/plist_int.h2
-rw-r--r--lib/qobj.c5
-rw-r--r--lib/resolver.c10
-rw-r--r--lib/routemap.c10
-rw-r--r--lib/routemap_cli.c49
-rw-r--r--lib/routemap_northbound.c25
-rw-r--r--lib/skiplist.c7
-rw-r--r--lib/spf_backoff.c2
-rw-r--r--lib/spf_backoff.h2
-rw-r--r--lib/srcdest_table.c2
-rw-r--r--lib/srcdest_table.h2
-rw-r--r--lib/stream.c21
-rw-r--r--lib/stream.h20
-rw-r--r--lib/subdir.am11
-rw-r--r--lib/table.c2
-rw-r--r--lib/thread.c1
-rw-r--r--lib/typesafe.c3
-rw-r--r--lib/vrf.c223
-rw-r--r--lib/vrf.h17
-rw-r--r--lib/vty.c38
-rw-r--r--lib/vty.h1
-rw-r--r--lib/yang.c15
-rw-r--r--lib/yang.h12
-rw-r--r--lib/yang_translator.c4
-rw-r--r--lib/yang_wrappers.c50
-rw-r--r--lib/yang_wrappers.h10
-rw-r--r--lib/zclient.c342
-rw-r--r--lib/zclient.h29
-rw-r--r--lib/zlog.c701
-rw-r--r--lib/zlog.h186
-rw-r--r--lib/zlog_targets.c574
-rw-r--r--lib/zlog_targets.h66
77 files changed, 3916 insertions, 1833 deletions
diff --git a/lib/agentx.c b/lib/agentx.c
index d1b801fe8c..7c4bdcbe27 100644
--- a/lib/agentx.c
+++ b/lib/agentx.c
@@ -158,9 +158,13 @@ static void agentx_events_update(void)
}
/* AgentX node. */
-static struct cmd_node agentx_node = {SMUX_NODE,
- "", /* AgentX has no interface. */
- 1};
+static int config_write_agentx(struct vty *vty);
+static struct cmd_node agentx_node = {
+ .name = "smux",
+ .node = SMUX_NODE,
+ .prompt = "",
+ .config_write = config_write_agentx,
+};
/* Logging NetSNMP messages */
static int agentx_log_callback(int major, int minor, void *serverarg,
@@ -246,7 +250,7 @@ void smux_init(struct thread_master *tm)
agentx_log_callback, NULL);
init_agent(FRR_SMUX_NAME);
- install_node(&agentx_node, config_write_agentx);
+ install_node(&agentx_node);
install_element(CONFIG_NODE, &agentx_enable_cmd);
install_element(CONFIG_NODE, &no_agentx_cmd);
}
diff --git a/lib/bitfield.h b/lib/bitfield.h
index eebfc049d9..72980165f9 100644
--- a/lib/bitfield.h
+++ b/lib/bitfield.h
@@ -152,9 +152,8 @@ typedef unsigned int word_t;
*/
#define bf_free(v) \
do { \
- if ((v).data) { \
- free((v).data); \
- } \
+ free((v).data); \
+ (v).data = NULL; \
} while (0)
#ifdef __cplusplus
diff --git a/lib/clippy.c b/lib/clippy.c
index cd8067f5eb..2e09c24c66 100644
--- a/lib/clippy.c
+++ b/lib/clippy.c
@@ -107,21 +107,11 @@ int main(int argc, char **argv)
#include "log.h"
#include "zassert.h"
-#define ZLOG_FUNC(FUNCNAME) \
- void FUNCNAME(const char *format, ...) \
- { \
- va_list args; \
- va_start(args, format); \
- vfprintf(stderr, format, args); \
- fputs("\n", stderr); \
- va_end(args); \
- }
-
-ZLOG_FUNC(zlog_err)
-ZLOG_FUNC(zlog_warn)
-ZLOG_FUNC(zlog_info)
-ZLOG_FUNC(zlog_notice)
-ZLOG_FUNC(zlog_debug)
+void vzlog(int prio, const char *format, va_list args)
+{
+ vfprintf(stderr, format, args);
+ fputs("\n", stderr);
+}
void _zlog_assert_failed(const char *assertion, const char *file,
unsigned int line, const char *function)
diff --git a/lib/command.c b/lib/command.c
index 8811b3a791..fbbf10c796 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -31,7 +31,7 @@
#include "frrstr.h"
#include "memory.h"
#include "log.h"
-#include "log_int.h"
+#include "log_vty.h"
#include "thread.h"
#include "vector.h"
#include "linklist.h"
@@ -47,6 +47,7 @@
#include "hook.h"
#include "lib_errors.h"
#include "northbound_cli.h"
+#include "network.h"
DEFINE_MTYPE_STATIC(LIB, HOST, "Host config")
DEFINE_MTYPE(LIB, COMPLETION, "Completion item")
@@ -73,86 +74,6 @@ const struct message tokennames[] = {
item(END_TKN),
{0},
};
-
-const char *const node_names[] = {
- "auth", // AUTH_NODE,
- "view", // VIEW_NODE,
- "auth enable", // AUTH_ENABLE_NODE,
- "enable", // ENABLE_NODE,
- "config", // CONFIG_NODE,
- "debug", // DEBUG_NODE,
- "vrf debug", // VRF_DEBUG_NODE,
- "northbound debug", // NORTHBOUND_DEBUG_NODE,
- "vnc debug", // DEBUG_VNC_NODE,
- "route-map debug", /* RMAP_DEBUG_NODE */
- "resolver debug", /* RESOLVER_DEBUG_NODE */
- "aaa", // AAA_NODE,
- "keychain", // KEYCHAIN_NODE,
- "keychain key", // KEYCHAIN_KEY_NODE,
- "static ip", // IP_NODE,
- "vrf", // VRF_NODE,
- "interface", // INTERFACE_NODE,
- "nexthop-group", // NH_GROUP_NODE,
- "zebra", // ZEBRA_NODE,
- "table", // TABLE_NODE,
- "rip", // RIP_NODE,
- "ripng", // RIPNG_NODE,
- "babel", // BABEL_NODE,
- "eigrp", // EIGRP_NODE,
- "bgp", // BGP_NODE,
- "bgp vpnv4", // BGP_VPNV4_NODE,
- "bgp vpnv6", // BGP_VPNV6_NODE,
- "bgp ipv4 unicast", // BGP_IPV4_NODE,
- "bgp ipv4 multicast", // BGP_IPV4M_NODE,
- "bgp ipv4 labeled unicast", // BGP_IPV4L_NODE,
- "bgp ipv6", // BGP_IPV6_NODE,
- "bgp ipv6 multicast", // BGP_IPV6M_NODE,
- "bgp ipv6 labeled unicast", // BGP_IPV6L_NODE,
- "bgp vrf policy", // BGP_VRF_POLICY_NODE,
- "bgp vnc defaults", // BGP_VNC_DEFAULTS_NODE,
- "bgp vnc nve", // BGP_VNC_NVE_GROUP_NODE,
- "bgp vnc l2", // BGP_VNC_L2_GROUP_NODE,
- "rfp defaults", // RFP_DEFAULTS_NODE,
- "bgp evpn", // BGP_EVPN_NODE,
- "ospf", // OSPF_NODE,
- "ospf6", // OSPF6_NODE,
- "ldp", // LDP_NODE,
- "ldp ipv4", // LDP_IPV4_NODE,
- "ldp ipv6", // LDP_IPV6_NODE,
- "ldp ipv4 interface", // LDP_IPV4_IFACE_NODE,
- "ldp ipv6 interface", // LDP_IPV6_IFACE_NODE,
- "ldp l2vpn", // LDP_L2VPN_NODE,
- "ldp", // LDP_PSEUDOWIRE_NODE,
- "isis", // ISIS_NODE,
- "ipv4 access list", // ACCESS_NODE,
- "ipv4 prefix list", // PREFIX_NODE,
- "ipv6 access list", // ACCESS_IPV6_NODE,
- "MAC access list", // ACCESS_MAC_NODE,
- "ipv6 prefix list", // PREFIX_IPV6_NODE,
- "as list", // AS_LIST_NODE,
- "community list", // COMMUNITY_LIST_NODE,
- "routemap", // RMAP_NODE,
- "pbr-map", // PBRMAP_NODE,
- "smux", // SMUX_NODE,
- "dump", // DUMP_NODE,
- "forwarding", // FORWARDING_NODE,
- "protocol", // PROTOCOL_NODE,
- "mpls", // MPLS_NODE,
- "pw", // PW_NODE,
- "vty", // VTY_NODE,
- "link-params", // LINK_PARAMS_NODE,
- "bgp evpn vni", // BGP_EVPN_VNI_NODE,
- "rpki", // RPKI_NODE
- "bgp ipv4 flowspec", /* BGP_FLOWSPECV4_NODE
- */
- "bgp ipv6 flowspec", /* BGP_FLOWSPECV6_NODE
- */
- "bfd", /* BFD_NODE */
- "bfd peer", /* BFD_PEER_NODE */
- "openfabric", // OPENFABRIC_NODE
- "vrrp", /* VRRP_NODE */
- "bmp", /* BMP_NODE */
-};
/* clang-format on */
/* Command vector which includes some level of command lists. Normally
@@ -179,84 +100,45 @@ const char *cmd_domainname_get(void)
return host.domainname;
}
+static int root_on_exit(struct vty *vty);
+
/* Standard command node structures. */
static struct cmd_node auth_node = {
- AUTH_NODE, "Password: ",
+ .name = "auth",
+ .node = AUTH_NODE,
+ .prompt = "Password: ",
};
static struct cmd_node view_node = {
- VIEW_NODE, "%s> ",
+ .name = "view",
+ .node = VIEW_NODE,
+ .prompt = "%s> ",
+ .node_exit = root_on_exit,
};
static struct cmd_node auth_enable_node = {
- AUTH_ENABLE_NODE, "Password: ",
+ .name = "auth enable",
+ .node = AUTH_ENABLE_NODE,
+ .prompt = "Password: ",
};
static struct cmd_node enable_node = {
- ENABLE_NODE, "%s# ",
+ .name = "enable",
+ .node = ENABLE_NODE,
+ .prompt = "%s# ",
+ .node_exit = root_on_exit,
};
-static struct cmd_node config_node = {CONFIG_NODE, "%s(config)# ", 1};
-
-static const struct facility_map {
- int facility;
- const char *name;
- size_t match;
-} syslog_facilities[] = {
- {LOG_KERN, "kern", 1},
- {LOG_USER, "user", 2},
- {LOG_MAIL, "mail", 1},
- {LOG_DAEMON, "daemon", 1},
- {LOG_AUTH, "auth", 1},
- {LOG_SYSLOG, "syslog", 1},
- {LOG_LPR, "lpr", 2},
- {LOG_NEWS, "news", 1},
- {LOG_UUCP, "uucp", 2},
- {LOG_CRON, "cron", 1},
-#ifdef LOG_FTP
- {LOG_FTP, "ftp", 1},
-#endif
- {LOG_LOCAL0, "local0", 6},
- {LOG_LOCAL1, "local1", 6},
- {LOG_LOCAL2, "local2", 6},
- {LOG_LOCAL3, "local3", 6},
- {LOG_LOCAL4, "local4", 6},
- {LOG_LOCAL5, "local5", 6},
- {LOG_LOCAL6, "local6", 6},
- {LOG_LOCAL7, "local7", 6},
- {0, NULL, 0},
+static int config_write_host(struct vty *vty);
+static struct cmd_node config_node = {
+ .name = "config",
+ .node = CONFIG_NODE,
+ .parent_node = ENABLE_NODE,
+ .prompt = "%s(config)# ",
+ .config_write = config_write_host,
+ .node_exit = vty_config_node_exit,
};
-static const char *facility_name(int facility)
-{
- const struct facility_map *fm;
-
- for (fm = syslog_facilities; fm->name; fm++)
- if (fm->facility == facility)
- return fm->name;
- return "";
-}
-
-static int facility_match(const char *str)
-{
- const struct facility_map *fm;
-
- for (fm = syslog_facilities; fm->name; fm++)
- if (!strncmp(str, fm->name, fm->match))
- return fm->facility;
- return -1;
-}
-
-static int level_match(const char *s)
-{
- int level;
-
- for (level = 0; zlog_priority[level] != NULL; level++)
- if (!strncmp(s, zlog_priority[level], 2))
- return level;
- return ZLOG_DISABLED;
-}
-
/* This is called from main when a daemon is invoked with -v or --version. */
void print_version(const char *progname)
{
@@ -345,10 +227,9 @@ static bool cmd_hash_cmp(const void *a, const void *b)
}
/* Install top node of command vector. */
-void install_node(struct cmd_node *node, int (*func)(struct vty *))
+void install_node(struct cmd_node *node)
{
vector_set_index(cmdvec, node->node, node);
- node->func = func;
node->cmdgraph = graph_new();
node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
// add start node
@@ -386,9 +267,9 @@ void install_element(enum node_type ntype, const struct cmd_element *cmd)
if (cnode == NULL) {
fprintf(stderr,
"%s[%s]:\n"
- "\tnode %d (%s) does not exist.\n"
+ "\tnode %d does not exist.\n"
"\tplease call install_node() before install_element()\n",
- cmd->name, cmd->string, ntype, node_names[ntype]);
+ cmd->name, cmd->string, ntype);
exit(EXIT_FAILURE);
}
@@ -397,7 +278,7 @@ void install_element(enum node_type ntype, const struct cmd_element *cmd)
"%s[%s]:\n"
"\tnode %d (%s) already has this command installed.\n"
"\tduplicate install_element call?\n",
- cmd->name, cmd->string, ntype, node_names[ntype]);
+ cmd->name, cmd->string, ntype, cnode->name);
return;
}
@@ -435,9 +316,9 @@ void uninstall_element(enum node_type ntype, const struct cmd_element *cmd)
if (cnode == NULL) {
fprintf(stderr,
"%s[%s]:\n"
- "\tnode %d (%s) does not exist.\n"
+ "\tnode %d does not exist.\n"
"\tplease call install_node() before uninstall_element()\n",
- cmd->name, cmd->string, ntype, node_names[ntype]);
+ cmd->name, cmd->string, ntype);
exit(EXIT_FAILURE);
}
@@ -446,7 +327,7 @@ void uninstall_element(enum node_type ntype, const struct cmd_element *cmd)
"%s[%s]:\n"
"\tnode %d (%s) does not have this command installed.\n"
"\tduplicate uninstall_element call?\n",
- cmd->name, cmd->string, ntype, node_names[ntype]);
+ cmd->name, cmd->string, ntype, cnode->name);
return;
}
@@ -486,13 +367,15 @@ static char *zencrypt(const char *passwd)
gettimeofday(&tv, 0);
- to64(&salt[0], random(), 3);
+ to64(&salt[0], frr_weak_random(), 3);
to64(&salt[3], tv.tv_usec, 3);
salt[5] = '\0';
return crypt(passwd, salt);
}
+static bool full_cli;
+
/* This function write configuration of this host. */
static int config_write_host(struct vty *vty)
{
@@ -508,7 +391,7 @@ static int config_write_host(struct vty *vty)
* which would cause other daemons to then switch to syslog when they
* parse frr.conf.
*/
- if (strcmp(zlog_default->protoname, "WATCHFRR")) {
+ if (full_cli) {
if (host.encrypt) {
if (host.password_encrypt)
vty_out(vty, "password 8 %s\n",
@@ -523,59 +406,7 @@ static int config_write_host(struct vty *vty)
vty_out(vty, "enable password %s\n",
host.enable);
}
-
- if (host.logfile
- && (zlog_default->maxlvl[ZLOG_DEST_FILE]
- != ZLOG_DISABLED)) {
- vty_out(vty, "log file %s", host.logfile);
- if (zlog_default->maxlvl[ZLOG_DEST_FILE]
- != zlog_default->default_lvl)
- vty_out(vty, " %s",
- zlog_priority
- [zlog_default->maxlvl
- [ZLOG_DEST_FILE]]);
- vty_out(vty, "\n");
- }
-
- if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != ZLOG_DISABLED) {
- vty_out(vty, "log stdout");
- if (zlog_default->maxlvl[ZLOG_DEST_STDOUT]
- != zlog_default->default_lvl)
- vty_out(vty, " %s",
- zlog_priority
- [zlog_default->maxlvl
- [ZLOG_DEST_STDOUT]]);
- vty_out(vty, "\n");
- }
-
- if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
- vty_out(vty, "no log monitor\n");
- else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR]
- != zlog_default->default_lvl)
- vty_out(vty, "log monitor %s\n",
- zlog_priority[zlog_default->maxlvl
- [ZLOG_DEST_MONITOR]]);
-
- if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED) {
- vty_out(vty, "log syslog");
- if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG]
- != zlog_default->default_lvl)
- vty_out(vty, " %s",
- zlog_priority[zlog_default->maxlvl
- [ZLOG_DEST_SYSLOG]]);
- vty_out(vty, "\n");
- }
-
- if (zlog_default->facility != LOG_DAEMON)
- vty_out(vty, "log facility %s\n",
- facility_name(zlog_default->facility));
-
- if (zlog_default->record_priority == 1)
- vty_out(vty, "log record-priority\n");
-
- if (zlog_default->timestamp_precision > 0)
- vty_out(vty, "log timestamp precision %d\n",
- zlog_default->timestamp_precision);
+ log_config_write(vty);
if (host.advanced)
vty_out(vty, "service advanced-vty\n");
@@ -1448,90 +1279,25 @@ DEFUN (config_exit,
return CMD_SUCCESS;
}
+static int root_on_exit(struct vty *vty)
+{
+ if (vty_shell(vty))
+ exit(0);
+ else
+ vty->status = VTY_CLOSE;
+ return 0;
+}
+
void cmd_exit(struct vty *vty)
{
- switch (vty->node) {
- case VIEW_NODE:
- case ENABLE_NODE:
- if (vty_shell(vty))
- exit(0);
- else
- vty->status = VTY_CLOSE;
- break;
- case CONFIG_NODE:
- vty->node = ENABLE_NODE;
- vty_config_exit(vty);
- break;
- case INTERFACE_NODE:
- case PW_NODE:
- case VRF_NODE:
- case NH_GROUP_NODE:
- case ZEBRA_NODE:
- case BGP_NODE:
- case RIP_NODE:
- case EIGRP_NODE:
- case BABEL_NODE:
- case RIPNG_NODE:
- case OSPF_NODE:
- case OSPF6_NODE:
- case LDP_NODE:
- case LDP_L2VPN_NODE:
- case ISIS_NODE:
- case OPENFABRIC_NODE:
- case KEYCHAIN_NODE:
- case RMAP_NODE:
- case PBRMAP_NODE:
- case VTY_NODE:
- case BFD_NODE:
- vty->node = CONFIG_NODE;
- break;
- case BGP_IPV4_NODE:
- case BGP_IPV4M_NODE:
- case BGP_IPV4L_NODE:
- case BGP_VPNV4_NODE:
- case BGP_VPNV6_NODE:
- case BGP_FLOWSPECV4_NODE:
- case BGP_FLOWSPECV6_NODE:
- case BGP_VRF_POLICY_NODE:
- case BGP_VNC_DEFAULTS_NODE:
- case BGP_VNC_NVE_GROUP_NODE:
- case BGP_VNC_L2_GROUP_NODE:
- case BGP_IPV6_NODE:
- case BGP_IPV6M_NODE:
- case BGP_EVPN_NODE:
- case BGP_IPV6L_NODE:
- case BMP_NODE:
- vty->node = BGP_NODE;
- break;
- case BGP_EVPN_VNI_NODE:
- vty->node = BGP_EVPN_NODE;
- break;
- case LDP_IPV4_NODE:
- case LDP_IPV6_NODE:
- vty->node = LDP_NODE;
- break;
- case LDP_IPV4_IFACE_NODE:
- vty->node = LDP_IPV4_NODE;
- break;
- case LDP_IPV6_IFACE_NODE:
- vty->node = LDP_IPV6_NODE;
- break;
- case LDP_PSEUDOWIRE_NODE:
- vty->node = LDP_L2VPN_NODE;
- break;
- case KEYCHAIN_KEY_NODE:
- vty->node = KEYCHAIN_NODE;
- break;
- case LINK_PARAMS_NODE:
- vty->node = INTERFACE_NODE;
- break;
- case BFD_PEER_NODE:
- vty->node = BFD_NODE;
- break;
- default:
- break;
- }
+ struct cmd_node *cnode = vector_lookup(cmdvec, vty->node);
+ if (cnode->node_exit) {
+ if (!cnode->node_exit(vty))
+ return;
+ }
+ if (cnode->parent_node)
+ vty->node = cnode->parent_node;
if (vty->xpath_index > 0)
vty->xpath_index--;
}
@@ -1556,7 +1322,6 @@ DEFUN (config_end,
vty_config_exit(vty);
vty->node = ENABLE_NODE;
}
-
return CMD_SUCCESS;
}
@@ -1716,9 +1481,8 @@ static int vty_write_config(struct vty *vty)
vty_out(vty, "!\n");
for (i = 0; i < vector_active(cmdvec); i++)
- if ((node = vector_slot(cmdvec, i)) && node->func
- && (node->vtysh || vty->type != VTY_SHELL)) {
- if ((*node->func)(vty))
+ if ((node = vector_slot(cmdvec, i)) && node->config_write) {
+ if ((*node->config_write)(vty))
vty_out(vty, "!\n");
}
@@ -2273,7 +2037,8 @@ DEFUN (config_logmsg,
int level;
char *message;
- if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED)
+ level = log_level_match(argv[idx_log_level]->arg);
+ if (level == ZLOG_DISABLED)
return CMD_ERR_NO_MATCH;
zlog(level, "%s",
@@ -2284,348 +2049,6 @@ DEFUN (config_logmsg,
return CMD_SUCCESS;
}
-DEFUN (show_logging,
- show_logging_cmd,
- "show logging",
- SHOW_STR
- "Show current logging configuration\n")
-{
- struct zlog *zl = zlog_default;
-
- vty_out(vty, "Syslog logging: ");
- if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
- vty_out(vty, "disabled");
- else
- vty_out(vty, "level %s, facility %s, ident %s",
- zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
- facility_name(zl->facility), zl->ident);
- vty_out(vty, "\n");
-
- vty_out(vty, "Stdout logging: ");
- if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
- vty_out(vty, "disabled");
- else
- vty_out(vty, "level %s",
- zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
- vty_out(vty, "\n");
-
- vty_out(vty, "Monitor logging: ");
- if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
- vty_out(vty, "disabled");
- else
- vty_out(vty, "level %s",
- zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
- vty_out(vty, "\n");
-
- vty_out(vty, "File logging: ");
- if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
- vty_out(vty, "disabled");
- else
- vty_out(vty, "level %s, filename %s",
- zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
- zl->filename);
- vty_out(vty, "\n");
-
- vty_out(vty, "Protocol name: %s\n", zl->protoname);
- vty_out(vty, "Record priority: %s\n",
- (zl->record_priority ? "enabled" : "disabled"));
- vty_out(vty, "Timestamp precision: %d\n", zl->timestamp_precision);
-
- return CMD_SUCCESS;
-}
-
-DEFUN (config_log_stdout,
- config_log_stdout_cmd,
- "log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
- "Logging control\n"
- "Set stdout logging level\n"
- LOG_LEVEL_DESC)
-{
- int idx_log_level = 2;
-
- if (argc == idx_log_level) {
- zlog_set_level(ZLOG_DEST_STDOUT, zlog_default->default_lvl);
- return CMD_SUCCESS;
- }
- int level;
-
- if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED)
- return CMD_ERR_NO_MATCH;
- zlog_set_level(ZLOG_DEST_STDOUT, level);
- return CMD_SUCCESS;
-}
-
-DEFUN (no_config_log_stdout,
- no_config_log_stdout_cmd,
- "no log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
- NO_STR
- "Logging control\n"
- "Cancel logging to stdout\n"
- LOG_LEVEL_DESC)
-{
- zlog_set_level(ZLOG_DEST_STDOUT, ZLOG_DISABLED);
- return CMD_SUCCESS;
-}
-
-DEFUN (config_log_monitor,
- config_log_monitor_cmd,
- "log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
- "Logging control\n"
- "Set terminal line (monitor) logging level\n"
- LOG_LEVEL_DESC)
-{
- int idx_log_level = 2;
-
- if (argc == idx_log_level) {
- zlog_set_level(ZLOG_DEST_MONITOR, zlog_default->default_lvl);
- return CMD_SUCCESS;
- }
- int level;
-
- if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED)
- return CMD_ERR_NO_MATCH;
- zlog_set_level(ZLOG_DEST_MONITOR, level);
- return CMD_SUCCESS;
-}
-
-DEFUN (no_config_log_monitor,
- no_config_log_monitor_cmd,
- "no log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
- NO_STR
- "Logging control\n"
- "Disable terminal line (monitor) logging\n"
- LOG_LEVEL_DESC)
-{
- zlog_set_level(ZLOG_DEST_MONITOR, ZLOG_DISABLED);
- return CMD_SUCCESS;
-}
-
-static int set_log_file(struct vty *vty, const char *fname, int loglevel)
-{
- int ret;
- char *p = NULL;
- const char *fullpath;
-
- /* Path detection. */
- if (!IS_DIRECTORY_SEP(*fname)) {
- char cwd[MAXPATHLEN + 1];
- cwd[MAXPATHLEN] = '\0';
-
- if (getcwd(cwd, MAXPATHLEN) == NULL) {
- flog_err_sys(EC_LIB_SYSTEM_CALL,
- "config_log_file: Unable to alloc mem!");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- p = XMALLOC(MTYPE_TMP, strlen(cwd) + strlen(fname) + 2);
- sprintf(p, "%s/%s", cwd, fname);
- fullpath = p;
- } else
- fullpath = fname;
-
- ret = zlog_set_file(fullpath, loglevel);
-
- XFREE(MTYPE_TMP, p);
-
- if (!ret) {
- if (vty)
- vty_out(vty, "can't open logfile %s\n", fname);
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- XFREE(MTYPE_HOST, host.logfile);
-
- host.logfile = XSTRDUP(MTYPE_HOST, fname);
-
-#if defined(HAVE_CUMULUS)
- if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED)
- zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
-#endif
- return CMD_SUCCESS;
-}
-
-void command_setup_early_logging(const char *dest, const char *level)
-{
- char *token;
-
- if (level) {
- int nlevel = level_match(level);
-
- if (nlevel != ZLOG_DISABLED)
- zlog_default->default_lvl = nlevel;
- }
-
- if (!dest)
- return;
-
- if (strcmp(dest, "stdout") == 0) {
- zlog_set_level(ZLOG_DEST_STDOUT, zlog_default->default_lvl);
- return;
- }
-
- if (strcmp(dest, "syslog") == 0) {
- zlog_set_level(ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
- return;
- }
-
- token = strstr(dest, ":");
- if (token == NULL)
- return;
-
- token++;
-
- set_log_file(NULL, token, zlog_default->default_lvl);
-}
-
-DEFUN (config_log_file,
- config_log_file_cmd,
- "log file FILENAME [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
- "Logging control\n"
- "Logging to file\n"
- "Logging filename\n"
- LOG_LEVEL_DESC)
-{
- int idx_filename = 2;
- int idx_log_levels = 3;
- if (argc == 4) {
- int level;
- if ((level = level_match(argv[idx_log_levels]->arg))
- == ZLOG_DISABLED)
- return CMD_ERR_NO_MATCH;
- return set_log_file(vty, argv[idx_filename]->arg, level);
- } else
- return set_log_file(vty, argv[idx_filename]->arg,
- zlog_default->default_lvl);
-}
-
-static void disable_log_file(void)
-{
- zlog_reset_file();
-
- XFREE(MTYPE_HOST, host.logfile);
-}
-
-DEFUN (no_config_log_file,
- no_config_log_file_cmd,
- "no log file [FILENAME [LEVEL]]",
- NO_STR
- "Logging control\n"
- "Cancel logging to file\n"
- "Logging file name\n"
- "Logging level\n")
-{
- disable_log_file();
- return CMD_SUCCESS;
-}
-
-DEFUN (config_log_syslog,
- config_log_syslog_cmd,
- "log syslog [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
- "Logging control\n"
- "Set syslog logging level\n"
- LOG_LEVEL_DESC)
-{
- int idx_log_levels = 2;
-
- if (argc == 3) {
- int level;
- if ((level = level_match(argv[idx_log_levels]->arg))
- == ZLOG_DISABLED)
- return CMD_ERR_NO_MATCH;
- zlog_set_level(ZLOG_DEST_SYSLOG, level);
- return CMD_SUCCESS;
- } else {
- zlog_set_level(ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
- return CMD_SUCCESS;
- }
-}
-
-DEFUN (no_config_log_syslog,
- no_config_log_syslog_cmd,
- "no log syslog [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>] [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
- NO_STR
- "Logging control\n"
- "Cancel logging to syslog\n"
- LOG_FACILITY_DESC
- LOG_LEVEL_DESC)
-{
- zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
- return CMD_SUCCESS;
-}
-
-DEFUN (config_log_facility,
- config_log_facility_cmd,
- "log facility <kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>",
- "Logging control\n"
- "Facility parameter for syslog messages\n"
- LOG_FACILITY_DESC)
-{
- int idx_target = 2;
- int facility = facility_match(argv[idx_target]->arg);
-
- zlog_default->facility = facility;
- return CMD_SUCCESS;
-}
-
-DEFUN (no_config_log_facility,
- no_config_log_facility_cmd,
- "no log facility [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>]",
- NO_STR
- "Logging control\n"
- "Reset syslog facility to default (daemon)\n"
- LOG_FACILITY_DESC)
-{
- zlog_default->facility = LOG_DAEMON;
- return CMD_SUCCESS;
-}
-
-DEFUN (config_log_record_priority,
- config_log_record_priority_cmd,
- "log record-priority",
- "Logging control\n"
- "Log the priority of the message within the message\n")
-{
- zlog_default->record_priority = 1;
- return CMD_SUCCESS;
-}
-
-DEFUN (no_config_log_record_priority,
- no_config_log_record_priority_cmd,
- "no log record-priority",
- NO_STR
- "Logging control\n"
- "Do not log the priority of the message within the message\n")
-{
- zlog_default->record_priority = 0;
- return CMD_SUCCESS;
-}
-
-DEFUN (config_log_timestamp_precision,
- config_log_timestamp_precision_cmd,
- "log timestamp precision (0-6)",
- "Logging control\n"
- "Timestamp configuration\n"
- "Set the timestamp precision\n"
- "Number of subsecond digits\n")
-{
- int idx_number = 3;
- zlog_default->timestamp_precision =
- strtoul(argv[idx_number]->arg, NULL, 10);
- return CMD_SUCCESS;
-}
-
-DEFUN (no_config_log_timestamp_precision,
- no_config_log_timestamp_precision_cmd,
- "no log timestamp precision",
- NO_STR
- "Logging control\n"
- "Timestamp configuration\n"
- "Reset the timestamp precision to the default value of 0\n")
-{
- zlog_default->timestamp_precision = 0;
- return CMD_SUCCESS;
-}
-
DEFUN (debug_memstats,
debug_memstats_cmd,
"[no] debug memstats-at-exit",
@@ -2800,7 +2223,7 @@ DEFUN(find,
if (regexec(&exp, cli->string, 0, NULL, 0) == 0)
vty_out(vty, " (%s) %s\n",
- node_names[node->node], cli->string);
+ node->name, cli->string);
}
}
@@ -2848,9 +2271,6 @@ void cmd_init(int terminal)
{
struct utsname names;
- if (array_size(node_names) != NODE_TYPE_MAX)
- assert(!"Update the CLI node description array!");
-
uname(&names);
qobj_init();
@@ -2875,7 +2295,6 @@ void cmd_init(int terminal)
#endif
host.password = NULL;
host.enable = NULL;
- host.logfile = NULL;
host.config = NULL;
host.noconfig = (terminal < 0);
host.lines = -1;
@@ -2883,11 +2302,11 @@ void cmd_init(int terminal)
host.motdfile = NULL;
/* Install top nodes. */
- install_node(&view_node, NULL);
- install_node(&enable_node, NULL);
- install_node(&auth_node, NULL);
- install_node(&auth_enable_node, NULL);
- install_node(&config_node, config_write_host);
+ install_node(&view_node);
+ install_node(&enable_node);
+ install_node(&auth_node);
+ install_node(&auth_enable_node);
+ install_node(&config_node);
/* Each node's basic commands. */
install_element(VIEW_NODE, &show_version_cmd);
@@ -2903,7 +2322,6 @@ void cmd_init(int terminal)
install_element(VIEW_NODE, &config_enable_cmd);
install_element(VIEW_NODE, &config_terminal_length_cmd);
install_element(VIEW_NODE, &config_terminal_no_length_cmd);
- install_element(VIEW_NODE, &show_logging_cmd);
install_element(VIEW_NODE, &show_commandtree_cmd);
install_element(VIEW_NODE, &echo_cmd);
install_element(VIEW_NODE, &autocomplete_cmd);
@@ -2930,6 +2348,8 @@ void cmd_init(int terminal)
install_element(CONFIG_NODE, &no_domainname_cmd);
if (terminal > 0) {
+ full_cli = true;
+
install_element(CONFIG_NODE, &debug_memstats_cmd);
install_element(CONFIG_NODE, &password_cmd);
@@ -2937,23 +2357,6 @@ void cmd_init(int terminal)
install_element(CONFIG_NODE, &enable_password_cmd);
install_element(CONFIG_NODE, &no_enable_password_cmd);
- install_element(CONFIG_NODE, &config_log_stdout_cmd);
- install_element(CONFIG_NODE, &no_config_log_stdout_cmd);
- install_element(CONFIG_NODE, &config_log_monitor_cmd);
- install_element(CONFIG_NODE, &no_config_log_monitor_cmd);
- install_element(CONFIG_NODE, &config_log_file_cmd);
- install_element(CONFIG_NODE, &no_config_log_file_cmd);
- install_element(CONFIG_NODE, &config_log_syslog_cmd);
- install_element(CONFIG_NODE, &no_config_log_syslog_cmd);
- install_element(CONFIG_NODE, &config_log_facility_cmd);
- install_element(CONFIG_NODE, &no_config_log_facility_cmd);
- install_element(CONFIG_NODE, &config_log_record_priority_cmd);
- install_element(CONFIG_NODE,
- &no_config_log_record_priority_cmd);
- install_element(CONFIG_NODE,
- &config_log_timestamp_precision_cmd);
- install_element(CONFIG_NODE,
- &no_config_log_timestamp_precision_cmd);
install_element(CONFIG_NODE, &service_password_encrypt_cmd);
install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
install_element(CONFIG_NODE, &banner_motd_default_cmd);
@@ -2963,6 +2366,7 @@ void cmd_init(int terminal)
install_element(CONFIG_NODE, &service_terminal_length_cmd);
install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
+ log_cmd_init();
vrf_install_commands();
}
@@ -3000,7 +2404,6 @@ void cmd_terminate(void)
XFREE(MTYPE_HOST, host.password_encrypt);
XFREE(MTYPE_HOST, host.enable);
XFREE(MTYPE_HOST, host.enable_encrypt);
- XFREE(MTYPE_HOST, host.logfile);
XFREE(MTYPE_HOST, host.motdfile);
XFREE(MTYPE_HOST, host.config);
XFREE(MTYPE_HOST, host.motd);
diff --git a/lib/command.h b/lib/command.h
index ea8a76a964..725a201446 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -66,9 +66,6 @@ struct host {
/* System wide terminal lines. */
int lines;
- /* Log filename. */
- char *logfile;
-
/* config file name of this host */
char *config;
int noconfig;
@@ -149,6 +146,7 @@ enum node_type {
MPLS_NODE, /* MPLS config node */
PW_NODE, /* Pseudowire config node */
VTY_NODE, /* Vty node. */
+ FPM_NODE, /* Dataplane FPM node. */
LINK_PARAMS_NODE, /* Link-parameters node */
BGP_EVPN_VNI_NODE, /* BGP EVPN VNI */
RPKI_NODE, /* RPKI node for configuration of RPKI cache server
@@ -165,22 +163,29 @@ enum node_type {
extern vector cmdvec;
extern const struct message tokennames[];
-extern const char *const node_names[];
+
+/* for external users depending on struct layout */
+#define FRR_CMD_NODE_20200416
/* Node which has some commands and prompt string and configuration
function pointer . */
struct cmd_node {
+ const char *name;
+
/* Node index. */
enum node_type node;
+ enum node_type parent_node;
/* Prompt character at vty interface. */
const char *prompt;
- /* Is this node's configuration goes to vtysh ? */
- int vtysh;
-
/* Node's configuration write function */
- int (*func)(struct vty *);
+ int (*config_write)(struct vty *);
+
+ /* called when leaving the node on a VTY session.
+ * return 1 if normal exit processing should happen, 0 to suppress
+ */
+ int (*node_exit)(struct vty *);
/* Node's command graph */
struct graph *cmdgraph;
@@ -434,7 +439,7 @@ struct cmd_node {
#define NO_GR_NEIGHBOR_HELPER_CMD "Undo Graceful Restart Helper command for a neighbor\n"
/* Prototypes. */
-extern void install_node(struct cmd_node *node, int (*)(struct vty *));
+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 *);
diff --git a/lib/command_match.c b/lib/command_match.c
index 0195aebc17..801b05f157 100644
--- a/lib/command_match.c
+++ b/lib/command_match.c
@@ -88,7 +88,7 @@ enum matcher_rv command_match(struct graph *cmdgraph, vector vline,
// prepend a dummy token to match that pesky start node
vector vvline = vector_init(vline->alloced + 1);
- vector_set_index(vvline, 0, (void *)XSTRDUP(MTYPE_TMP, "dummy"));
+ vector_set_index(vvline, 0, XSTRDUP(MTYPE_TMP, "dummy"));
memcpy(vvline->index + 1, vline->index,
sizeof(void *) * vline->alloced);
vvline->active = vline->active + 1;
diff --git a/lib/csv.c b/lib/csv.c
index 445742807c..45ace9777c 100644
--- a/lib/csv.c
+++ b/lib/csv.c
@@ -1,5 +1,5 @@
/* CSV
- * Copyright (C) 2013 Cumulus Networks, Inc.
+ * Copyright (C) 2013,2020 Cumulus Networks, Inc.
*
* This file is part of Quagga.
*
@@ -22,6 +22,8 @@
#include "config.h"
#endif
+#include <zebra.h>
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -419,7 +421,7 @@ void csv_clone_record(csv_t *csv, csv_record_t *in_rec, csv_record_t **out_rec)
}
rec->record = curr;
rec->rec_len = in_rec->rec_len;
- strcpy(rec->record, in_rec->record);
+ strlcpy(rec->record, in_rec->record, csv->buflen);
/* decode record into fields */
csv_decode_record(rec);
diff --git a/lib/defaults.c b/lib/defaults.c
index 71ccc73cc6..7466aad5b1 100644
--- a/lib/defaults.c
+++ b/lib/defaults.c
@@ -100,10 +100,10 @@ static bool frr_match_version(const char *name, const char *vspec,
const char *version, bool check)
{
int cmp;
- static struct spec {
+ static const struct spec {
const char *str;
- bool dir, eq;
- } *s, specs[] = {
+ int dir, eq;
+ } specs[] = {
{"<=", -1, 1},
{">=", 1, 1},
{"==", 0, 1},
@@ -112,6 +112,7 @@ static bool frr_match_version(const char *name, const char *vspec,
{"=", 0, 1},
{NULL, 0, 0},
};
+ const struct spec *s;
if (!vspec)
/* NULL = all versions */
diff --git a/lib/filter.c b/lib/filter.c
index 3226fb2f5e..da02a77763 100644
--- a/lib/filter.c
+++ b/lib/filter.c
@@ -2294,6 +2294,7 @@ DEFUN (ipv6_access_list_exact,
if (argv_find(argv, argc, "exact-match", &idx))
exact = 1;
+ assert(prefix);
return filter_set_zebra(vty, argv[idx_word]->arg, seq, permit_deny,
AFI_IP6, prefix, exact, 1);
}
@@ -2812,9 +2813,13 @@ static int config_write_access(struct vty *vty, afi_t afi)
return write;
}
+static int config_write_access_mac(struct vty *vty);
static struct cmd_node access_mac_node = {
- ACCESS_MAC_NODE, "", /* Access list has no interface. */
- 1};
+ .name = "MAC access list",
+ .node = ACCESS_MAC_NODE,
+ .prompt = "",
+ .config_write = config_write_access_mac,
+};
static int config_write_access_mac(struct vty *vty)
{
@@ -2850,7 +2855,7 @@ static void access_list_reset_mac(void)
/* Install vty related command. */
static void access_list_init_mac(void)
{
- install_node(&access_mac_node, config_write_access_mac);
+ install_node(&access_mac_node);
install_element(ENABLE_NODE, &show_mac_access_list_cmd);
install_element(ENABLE_NODE, &show_mac_access_list_name_cmd);
@@ -2863,9 +2868,13 @@ static void access_list_init_mac(void)
}
/* Access-list node. */
-static struct cmd_node access_node = {ACCESS_NODE,
- "", /* Access list has no interface. */
- 1};
+static int config_write_access_ipv4(struct vty *vty);
+static struct cmd_node access_node = {
+ .name = "ipv4 access list",
+ .node = ACCESS_NODE,
+ .prompt = "",
+ .config_write = config_write_access_ipv4,
+};
static int config_write_access_ipv4(struct vty *vty)
{
@@ -2901,7 +2910,7 @@ static void access_list_reset_ipv4(void)
/* Install vty related command. */
static void access_list_init_ipv4(void)
{
- install_node(&access_node, config_write_access_ipv4);
+ install_node(&access_node);
install_element(ENABLE_NODE, &show_ip_access_list_cmd);
install_element(ENABLE_NODE, &show_ip_access_list_name_cmd);
@@ -2948,7 +2957,13 @@ static void access_list_init_ipv4(void)
install_element(CONFIG_NODE, &no_access_list_remark_comment_cmd);
}
-static struct cmd_node access_ipv6_node = {ACCESS_IPV6_NODE, "", 1};
+static int config_write_access_ipv6(struct vty *vty);
+static struct cmd_node access_ipv6_node = {
+ .name = "ipv6 access list",
+ .node = ACCESS_IPV6_NODE,
+ .prompt = "",
+ .config_write = config_write_access_ipv6,
+};
static int config_write_access_ipv6(struct vty *vty)
{
@@ -2983,7 +2998,7 @@ static void access_list_reset_ipv6(void)
static void access_list_init_ipv6(void)
{
- install_node(&access_ipv6_node, config_write_access_ipv6);
+ install_node(&access_ipv6_node);
install_element(ENABLE_NODE, &show_ipv6_access_list_cmd);
install_element(ENABLE_NODE, &show_ipv6_access_list_name_cmd);
diff --git a/lib/frr_pthread.c b/lib/frr_pthread.c
index 55f0b55ed6..e237934f81 100644
--- a/lib/frr_pthread.c
+++ b/lib/frr_pthread.c
@@ -27,6 +27,7 @@
#include "frr_pthread.h"
#include "memory.h"
#include "linklist.h"
+#include "zlog.h"
DEFINE_MTYPE_STATIC(LIB, FRR_PTHREAD, "FRR POSIX Thread")
DEFINE_MTYPE_STATIC(LIB, PTHREAD_PRIM, "POSIX sync primitives")
@@ -273,6 +274,8 @@ static void *fpt_run(void *arg)
struct frr_pthread *fpt = arg;
fpt->master->owner = pthread_self();
+ zlog_tls_buffer_init();
+
int sleeper[2];
pipe(sleeper);
thread_add_read(fpt->master, &fpt_dummy, NULL, sleeper[0], NULL);
@@ -294,5 +297,7 @@ static void *fpt_run(void *arg)
close(sleeper[1]);
close(sleeper[0]);
+ zlog_tls_buffer_fini();
+
return NULL;
}
diff --git a/lib/frrlua.c b/lib/frrlua.c
index 26610556dc..9f9cf8c1f6 100644
--- a/lib/frrlua.c
+++ b/lib/frrlua.c
@@ -5,7 +5,7 @@
* Copyright (C) 2016 Cumulus Networks, Inc.
* Donald Sharp
*
- * This file is part of FreeRangeRouting (FRR).
+ * 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
diff --git a/lib/frrlua.h b/lib/frrlua.h
index 374eb70311..40c7a67b89 100644
--- a/lib/frrlua.h
+++ b/lib/frrlua.h
@@ -5,7 +5,7 @@
* Copyright (C) 2016 Cumulus Networks, Inc.
* Donald Sharp
*
- * This file is part of FreeRangeRouting (FRR).
+ * 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
diff --git a/lib/grammar_sandbox.c b/lib/grammar_sandbox.c
index 8ccdbfcbc1..a40b815caa 100644
--- a/lib/grammar_sandbox.c
+++ b/lib/grammar_sandbox.c
@@ -399,7 +399,7 @@ DEFUN (grammar_findambig,
if (!nodegraph)
continue;
vty_out(vty, "scanning node %d (%s)\n", scannode - 1,
- node_names[scannode - 1]);
+ cnode->name);
}
commands = cmd_graph_permutations(nodegraph);
diff --git a/lib/grammar_sandbox_main.c b/lib/grammar_sandbox_main.c
index 4bd8f5138a..fbb97d2dd5 100644
--- a/lib/grammar_sandbox_main.c
+++ b/lib/grammar_sandbox_main.c
@@ -7,7 +7,7 @@
* Copyright (C) 2016 Cumulus Networks, Inc.
* Copyright (C) 2017 David Lamparter for NetDEF, Inc.
*
- * This file is part of FreeRangeRouting (FRR).
+ * 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
@@ -45,11 +45,7 @@ int main(int argc, char **argv)
master = thread_master_create(NULL);
- openzlog("grammar_sandbox", "NONE", 0, LOG_CONS | LOG_NDELAY | LOG_PID,
- LOG_DAEMON);
- zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
- zlog_set_level(ZLOG_DEST_STDOUT, LOG_DEBUG);
- zlog_set_level(ZLOG_DEST_MONITOR, ZLOG_DISABLED);
+ zlog_aux_init("NONE: ", LOG_DEBUG);
/* Library inits. */
cmd_init(1);
@@ -58,7 +54,7 @@ int main(int argc, char **argv)
vty_init(master, true);
lib_cmd_init();
- yang_init();
+ yang_init(true);
nb_init(master, NULL, 0);
vty_stdio(vty_do_exit);
diff --git a/lib/hook.c b/lib/hook.c
index 870d158aac..5a8ad00d66 100644
--- a/lib/hook.c
+++ b/lib/hook.c
@@ -18,16 +18,26 @@
#include "config.h"
#endif
+#include <string.h>
+
#include "memory.h"
#include "hook.h"
DEFINE_MTYPE_STATIC(LIB, HOOK_ENTRY, "Hook entry")
-void _hook_register(struct hook *hook, void *funcptr, void *arg, bool has_arg,
- struct frrmod_runtime *module, const char *funcname,
- int priority)
+void _hook_register(struct hook *hook, struct hookent *stackent, void *funcptr,
+ void *arg, bool has_arg, struct frrmod_runtime *module,
+ const char *funcname, int priority)
{
- struct hookent *he = XCALLOC(MTYPE_HOOK_ENTRY, sizeof(*he)), **pos;
+ struct hookent *he, **pos;
+
+ if (!stackent->ent_used)
+ he = stackent;
+ else {
+ he = XCALLOC(MTYPE_HOOK_ENTRY, sizeof(*he));
+ he->ent_on_heap = true;
+ }
+ he->ent_used = true;
he->hookfn = funcptr;
he->hookarg = arg;
he->has_arg = has_arg;
@@ -52,7 +62,10 @@ void _hook_unregister(struct hook *hook, void *funcptr, void *arg, bool has_arg)
if (he->hookfn == funcptr && he->hookarg == arg
&& he->has_arg == has_arg) {
*prev = he->next;
- XFREE(MTYPE_HOOK_ENTRY, he);
+ if (he->ent_on_heap)
+ XFREE(MTYPE_HOOK_ENTRY, he);
+ else
+ memset(he, 0, sizeof(*he));
break;
}
}
diff --git a/lib/hook.h b/lib/hook.h
index f7fb7b8a5c..3823cebe6a 100644
--- a/lib/hook.h
+++ b/lib/hook.h
@@ -114,7 +114,9 @@ struct hookent {
struct hookent *next;
void *hookfn; /* actually a function pointer */
void *hookarg;
- bool has_arg;
+ bool has_arg : 1;
+ bool ent_on_heap : 1;
+ bool ent_used : 1;
int priority;
struct frrmod_runtime *module;
const char *fnname;
@@ -133,21 +135,33 @@ struct hook {
* always use hook_register(), which uses the static inline helper from
* DECLARE_HOOK in order to get type safety
*/
-extern void _hook_register(struct hook *hook, void *funcptr, void *arg,
- bool has_arg, struct frrmod_runtime *module,
+extern void _hook_register(struct hook *hook, struct hookent *stackent,
+ void *funcptr, void *arg, bool has_arg,
+ struct frrmod_runtime *module,
const char *funcname, int priority);
+
+/* most hook_register calls are not in a loop or similar and can use a
+ * statically allocated "struct hookent" from the data segment
+ */
+#define _hook_reg_svar(hook, funcptr, arg, has_arg, module, funcname, prio) \
+ do { \
+ static struct hookent stack_hookent = { .ent_on_heap = 0, }; \
+ _hook_register(hook, &stack_hookent, funcptr, arg, has_arg, \
+ module, funcname, prio); \
+ } while (0)
+
#define hook_register(hookname, func) \
- _hook_register(&_hook_##hookname, _hook_typecheck_##hookname(func), \
+ _hook_reg_svar(&_hook_##hookname, _hook_typecheck_##hookname(func), \
NULL, false, THIS_MODULE, #func, HOOK_DEFAULT_PRIORITY)
#define hook_register_arg(hookname, func, arg) \
- _hook_register(&_hook_##hookname, \
+ _hook_reg_svar(&_hook_##hookname, \
_hook_typecheck_arg_##hookname(func), arg, true, \
THIS_MODULE, #func, HOOK_DEFAULT_PRIORITY)
#define hook_register_prio(hookname, prio, func) \
- _hook_register(&_hook_##hookname, _hook_typecheck_##hookname(func), \
+ _hook_reg_svar(&_hook_##hookname, _hook_typecheck_##hookname(func), \
NULL, false, THIS_MODULE, #func, prio)
#define hook_register_arg_prio(hookname, prio, func, arg) \
- _hook_register(&_hook_##hookname, \
+ _hook_reg_svar(&_hook_##hookname, \
_hook_typecheck_arg_##hookname(func), arg, true, \
THIS_MODULE, #func, prio)
diff --git a/lib/if.c b/lib/if.c
index dabf66799d..c887bb6265 100644
--- a/lib/if.c
+++ b/lib/if.c
@@ -582,23 +582,39 @@ struct interface *if_get_by_ifindex(ifindex_t ifindex, vrf_id_t vrf_id)
return NULL;
}
-void if_set_index(struct interface *ifp, ifindex_t ifindex)
+int if_set_index(struct interface *ifp, ifindex_t ifindex)
{
struct vrf *vrf;
+ if (ifp->ifindex == ifindex)
+ return 0;
+
vrf = vrf_get(ifp->vrf_id, NULL);
assert(vrf);
- if (ifp->ifindex == ifindex)
- return;
+ /*
+ * If there is already an interface with this ifindex, we will collide
+ * on insertion, so don't even try.
+ */
+ if (if_lookup_by_ifindex(ifindex, ifp->vrf_id))
+ return -1;
if (ifp->ifindex != IFINDEX_INTERNAL)
IFINDEX_RB_REMOVE(vrf, ifp);
ifp->ifindex = ifindex;
- if (ifp->ifindex != IFINDEX_INTERNAL)
- IFINDEX_RB_INSERT(vrf, ifp)
+ if (ifp->ifindex != IFINDEX_INTERNAL) {
+ /*
+ * This should never happen, since we checked if there was
+ * already an interface with the desired ifindex at the top of
+ * the function. Nevertheless.
+ */
+ if (IFINDEX_RB_INSERT(vrf, ifp))
+ return -1;
+ }
+
+ return 0;
}
void if_set_name(struct interface *ifp, const char *name)
@@ -1249,8 +1265,6 @@ struct if_link_params *if_link_params_get(struct interface *ifp)
struct if_link_params *iflp =
XCALLOC(MTYPE_IF_LINK_PARAMS, sizeof(struct if_link_params));
- if (iflp == NULL)
- return NULL;
/* Set TE metric equal to standard metric */
iflp->te_metric = ifp->metric;
@@ -1278,8 +1292,6 @@ struct if_link_params *if_link_params_get(struct interface *ifp)
void if_link_params_free(struct interface *ifp)
{
- if (ifp->link_params == NULL)
- return;
XFREE(MTYPE_IF_LINK_PARAMS, ifp->link_params);
}
@@ -1655,33 +1667,98 @@ static int lib_interface_description_destroy(enum nb_event event,
return NB_OK;
}
-/* clang-format off */
+/*
+ * XPath: /frr-interface:lib/interface/state/if-index
+ */
+struct yang_data *lib_interface_state_if_index_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ const struct interface *ifp = list_entry;
+
+ return yang_data_new_int32(xpath, ifp->ifindex);
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/state/mtu
+ */
+struct yang_data *lib_interface_state_mtu_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ const struct interface *ifp = list_entry;
-#if defined(__GNUC__) && ((__GNUC__ - 0) < 5) && !defined(__clang__)
-/* gcc versions before 5.x miscalculate the size for structs with variable
- * length arrays (they just count it as size 0)
+ return yang_data_new_uint16(xpath, ifp->mtu);
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/state/mtu6
+ */
+struct yang_data *lib_interface_state_mtu6_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ const struct interface *ifp = list_entry;
+
+ return yang_data_new_uint32(xpath, ifp->mtu6);
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/state/speed
*/
-struct frr_yang_module_info_size3 {
- /* YANG module name. */
- const char *name;
+struct yang_data *lib_interface_state_speed_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ const struct interface *ifp = list_entry;
- /* Northbound callbacks. */
- const struct {
- /* Data path of this YANG node. */
- const char *xpath;
+ return yang_data_new_uint32(xpath, ifp->speed);
+}
- /* Callbacks implemented for this node. */
- struct nb_callbacks cbs;
+/*
+ * XPath: /frr-interface:lib/interface/state/metric
+ */
+struct yang_data *lib_interface_state_metric_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ const struct interface *ifp = list_entry;
- /* Priority - lower priorities are processed first. */
- uint32_t priority;
- } nodes[3];
-};
+ return yang_data_new_uint32(xpath, ifp->metric);
+}
-const struct frr_yang_module_info_size3 frr_interface_info_size3 asm("frr_interface_info") = {
-#else
+/*
+ * XPath: /frr-interface:lib/interface/state/flags
+ */
+struct yang_data *lib_interface_state_flags_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ /* TODO: implement me. */
+ return NULL;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/state/type
+ */
+struct yang_data *lib_interface_state_type_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ /* TODO: implement me. */
+ return NULL;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/state/phy-address
+ */
+struct yang_data *
+lib_interface_state_phy_address_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ const struct interface *ifp = list_entry;
+ struct ethaddr macaddr;
+
+ memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN);
+
+ return yang_data_new_mac(xpath, &macaddr);
+}
+
+/* clang-format off */
const struct frr_yang_module_info frr_interface_info = {
-#endif
.name = "frr-interface",
.nodes = {
{
@@ -1704,6 +1781,54 @@ const struct frr_yang_module_info frr_interface_info = {
},
},
{
+ .xpath = "/frr-interface:lib/interface/state/if-index",
+ .cbs = {
+ .get_elem = lib_interface_state_if_index_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/state/mtu",
+ .cbs = {
+ .get_elem = lib_interface_state_mtu_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/state/mtu6",
+ .cbs = {
+ .get_elem = lib_interface_state_mtu6_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/state/speed",
+ .cbs = {
+ .get_elem = lib_interface_state_speed_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/state/metric",
+ .cbs = {
+ .get_elem = lib_interface_state_metric_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/state/flags",
+ .cbs = {
+ .get_elem = lib_interface_state_flags_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/state/type",
+ .cbs = {
+ .get_elem = lib_interface_state_type_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/state/phy-address",
+ .cbs = {
+ .get_elem = lib_interface_state_phy_address_get_elem,
+ }
+ },
+ {
.xpath = NULL,
},
}
diff --git a/lib/if.h b/lib/if.h
index 4ca2e79572..9afbb5acc8 100644
--- a/lib/if.h
+++ b/lib/if.h
@@ -308,33 +308,58 @@ RB_HEAD(if_index_head, interface);
RB_PROTOTYPE(if_index_head, interface, index_entry, if_cmp_index_func)
DECLARE_QOBJ_TYPE(interface)
-#define IFNAME_RB_INSERT(vrf, ifp) \
- if (RB_INSERT(if_name_head, &vrf->ifaces_by_name, (ifp))) \
- flog_err(EC_LIB_INTERFACE, \
- "%s(%s): corruption detected -- interface with this " \
- "name exists already in VRF %u!", \
- __func__, (ifp)->name, (ifp)->vrf_id);
-
-#define IFNAME_RB_REMOVE(vrf, ifp) \
- if (RB_REMOVE(if_name_head, &vrf->ifaces_by_name, (ifp)) == NULL) \
- flog_err(EC_LIB_INTERFACE, \
- "%s(%s): corruption detected -- interface with this " \
- "name doesn't exist in VRF %u!", \
- __func__, (ifp)->name, (ifp)->vrf_id);
-
-#define IFINDEX_RB_INSERT(vrf, ifp) \
- if (RB_INSERT(if_index_head, &vrf->ifaces_by_index, (ifp))) \
- flog_err(EC_LIB_INTERFACE, \
- "%s(%u): corruption detected -- interface with this " \
- "ifindex exists already in VRF %u!", \
- __func__, (ifp)->ifindex, (ifp)->vrf_id);
-
-#define IFINDEX_RB_REMOVE(vrf, ifp) \
- if (RB_REMOVE(if_index_head, &vrf->ifaces_by_index, (ifp)) == NULL) \
- flog_err(EC_LIB_INTERFACE, \
- "%s(%u): corruption detected -- interface with this " \
- "ifindex doesn't exist in VRF %u!", \
- __func__, (ifp)->ifindex, (ifp)->vrf_id);
+#define IFNAME_RB_INSERT(vrf, ifp) \
+ ({ \
+ struct interface *_iz = \
+ RB_INSERT(if_name_head, &vrf->ifaces_by_name, (ifp)); \
+ if (_iz) \
+ flog_err( \
+ EC_LIB_INTERFACE, \
+ "%s(%s): corruption detected -- interface with this " \
+ "name exists already in VRF %u!", \
+ __func__, (ifp)->name, (ifp)->vrf_id); \
+ _iz; \
+ })
+
+#define IFNAME_RB_REMOVE(vrf, ifp) \
+ ({ \
+ struct interface *_iz = \
+ RB_REMOVE(if_name_head, &vrf->ifaces_by_name, (ifp)); \
+ if (_iz == NULL) \
+ flog_err( \
+ EC_LIB_INTERFACE, \
+ "%s(%s): corruption detected -- interface with this " \
+ "name doesn't exist in VRF %u!", \
+ __func__, (ifp)->name, (ifp)->vrf_id); \
+ _iz; \
+ })
+
+
+#define IFINDEX_RB_INSERT(vrf, ifp) \
+ ({ \
+ struct interface *_iz = RB_INSERT( \
+ if_index_head, &vrf->ifaces_by_index, (ifp)); \
+ if (_iz) \
+ flog_err( \
+ EC_LIB_INTERFACE, \
+ "%s(%u): corruption detected -- interface with this " \
+ "ifindex exists already in VRF %u!", \
+ __func__, (ifp)->ifindex, (ifp)->vrf_id); \
+ _iz; \
+ })
+
+#define IFINDEX_RB_REMOVE(vrf, ifp) \
+ ({ \
+ struct interface *_iz = RB_REMOVE( \
+ if_index_head, &vrf->ifaces_by_index, (ifp)); \
+ if (_iz == NULL) \
+ flog_err( \
+ EC_LIB_INTERFACE, \
+ "%s(%u): corruption detected -- interface with this " \
+ "ifindex doesn't exist in VRF %u!", \
+ __func__, (ifp)->ifindex, (ifp)->vrf_id); \
+ _iz; \
+ })
#define FOR_ALL_INTERFACES(vrf, ifp) \
if (vrf) \
@@ -502,7 +527,7 @@ extern struct interface *if_get_by_name(const char *ifname, vrf_id_t vrf_id);
extern struct interface *if_get_by_ifindex(ifindex_t ifindex, vrf_id_t vrf_id);
/* Sets the index and adds to index list */
-extern void if_set_index(struct interface *ifp, ifindex_t ifindex);
+extern int if_set_index(struct interface *ifp, ifindex_t ifindex);
/* Sets the name and adds to name list */
extern void if_set_name(struct interface *ifp, const char *name);
@@ -577,6 +602,24 @@ extern void if_destroy_via_zapi(struct interface *ifp);
extern const struct frr_yang_module_info frr_interface_info;
+struct yang_data *lib_interface_state_if_index_get_elem(const char *xpath,
+ const void *list_entry);
+struct yang_data *lib_interface_state_mtu_get_elem(const char *xpath,
+ const void *list_entry);
+struct yang_data *lib_interface_state_mtu6_get_elem(const char *xpath,
+ const void *list_entry);
+struct yang_data *lib_interface_state_speed_get_elem(const char *xpath,
+ const void *list_entry);
+struct yang_data *lib_interface_state_metric_get_elem(const char *xpath,
+ const void *list_entry);
+struct yang_data *lib_interface_state_flags_get_elem(const char *xpath,
+ const void *list_entry);
+struct yang_data *lib_interface_state_type_get_elem(const char *xpath,
+ const void *list_entry);
+struct yang_data *
+lib_interface_state_phy_address_get_elem(const char *xpath,
+ const void *list_entry);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/ipaddr.h b/lib/ipaddr.h
index c6372f1abb..cd7f79a04e 100644
--- a/lib/ipaddr.h
+++ b/lib/ipaddr.h
@@ -112,7 +112,7 @@ static inline void ipv4_to_ipv4_mapped_ipv6(struct in6_addr *in6,
/*
* convert an ipv4 mapped ipv6 address back to ipv4 address
*/
-static inline void ipv4_mapped_ipv6_to_ipv4(struct in6_addr *in6,
+static inline void ipv4_mapped_ipv6_to_ipv4(const struct in6_addr *in6,
struct in_addr *in)
{
memset(in, 0, sizeof(struct in_addr));
diff --git a/lib/json.c b/lib/json.c
index 991240639a..6bea3982e3 100644
--- a/lib/json.c
+++ b/lib/json.c
@@ -50,6 +50,11 @@ void json_object_int_add(struct json_object *obj, const char *key, int64_t i)
json_object_object_add(obj, key, json_object_new_int64(i));
}
+void json_object_double_add(struct json_object *obj, const char *key, double i)
+{
+ json_object_object_add(obj, key, json_object_new_double(i));
+}
+
void json_object_boolean_false_add(struct json_object *obj, const char *key)
{
json_object_object_add(obj, key, json_object_new_boolean(0));
diff --git a/lib/json.h b/lib/json.h
index c8866c524a..afe0b175da 100644
--- a/lib/json.h
+++ b/lib/json.h
@@ -48,6 +48,9 @@ extern void json_object_int_add(struct json_object *obj, const char *key,
int64_t i);
void json_object_boolean_add(struct json_object *obj, const char *key,
bool val);
+
+extern void json_object_double_add(struct json_object *obj, const char *key,
+ double i);
extern void json_object_boolean_false_add(struct json_object *obj,
const char *key);
extern void json_object_boolean_true_add(struct json_object *obj,
diff --git a/lib/keychain.c b/lib/keychain.c
index ea512a2699..251211734b 100644
--- a/lib/keychain.c
+++ b/lib/keychain.c
@@ -959,11 +959,21 @@ DEFUN (no_send_lifetime,
return CMD_SUCCESS;
}
-static struct cmd_node keychain_node = {KEYCHAIN_NODE, "%s(config-keychain)# ",
- 1};
-
-static struct cmd_node keychain_key_node = {KEYCHAIN_KEY_NODE,
- "%s(config-keychain-key)# ", 1};
+static int keychain_config_write(struct vty *vty);
+static struct cmd_node keychain_node = {
+ .name = "keychain",
+ .node = KEYCHAIN_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-keychain)# ",
+ .config_write = keychain_config_write,
+};
+
+static struct cmd_node keychain_key_node = {
+ .name = "keychain key",
+ .node = KEYCHAIN_KEY_NODE,
+ .parent_node = KEYCHAIN_NODE,
+ .prompt = "%s(config-keychain-key)# ",
+};
static int keychain_strftime(char *buf, int bufsiz, time_t *time)
{
@@ -1042,8 +1052,8 @@ void keychain_init(void)
{
keychain_list = list_new();
- install_node(&keychain_node, keychain_config_write);
- install_node(&keychain_key_node, NULL);
+ install_node(&keychain_node);
+ install_node(&keychain_key_node);
install_default(KEYCHAIN_NODE);
install_default(KEYCHAIN_KEY_NODE);
diff --git a/lib/libfrr.c b/lib/libfrr.c
index 3622890e46..ac165f254e 100644
--- a/lib/libfrr.c
+++ b/lib/libfrr.c
@@ -33,7 +33,6 @@
#include "lib_vty.h"
#include "log_vty.h"
#include "zclient.h"
-#include "log_int.h"
#include "module.h"
#include "network.h"
#include "lib_errors.h"
@@ -630,6 +629,7 @@ struct thread_master *frr_init(void)
{
struct option_chain *oc;
struct frrmod_runtime *module;
+ struct zprivs_ids_t ids;
char moderr[256];
char p_instance[16] = "", p_pathspace[256] = "";
const char *dir;
@@ -657,9 +657,11 @@ struct thread_master *frr_init(void)
#endif
zprivs_preinit(di->privs);
+ zprivs_get_ids(&ids);
- openzlog(di->progname, di->logname, di->instance,
- LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON);
+ zlog_init(di->progname, di->logname, di->instance,
+ ids.uid_normal, ids.gid_normal);
+ zlog_tls_buffer_init();
command_setup_early_logging(di->early_logging, di->early_loglevel);
@@ -709,7 +711,6 @@ struct thread_master *frr_init(void)
vty_init(master, di->log_always);
lib_cmd_init();
- log_filter_cmd_init();
frr_pthread_init();
@@ -717,7 +718,7 @@ struct thread_master *frr_init(void)
log_ref_vty_init();
lib_error_init();
- yang_init();
+ yang_init(true);
debug_init_cli();
@@ -1086,7 +1087,7 @@ void frr_run(struct thread_master *master)
}
/* end fixed stderr startup logging */
- zlog_startup_stderr = false;
+ zlog_startup_end();
struct thread thread;
while (thread_fetch(master, &thread))
@@ -1119,7 +1120,8 @@ void frr_fini(void)
/* signal_init -> nothing needed */
thread_master_free(master);
master = NULL;
- closezlog();
+ zlog_tls_buffer_fini();
+ zlog_fini();
/* frrmod_init -> nothing needed / hooks */
rcu_shutdown();
diff --git a/lib/log.c b/lib/log.c
index b3be5216aa..9b0f5b3d85 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -25,7 +25,6 @@
#include "zclient.h"
#include "log.h"
-#include "log_int.h"
#include "memory.h"
#include "command.h"
#include "lib_errors.h"
@@ -33,154 +32,12 @@
#include "printfrr.h"
#include "frr_pthread.h"
-#ifndef SUNOS_5
-#include <sys/un.h>
-#endif
-/* for printstack on solaris */
-#ifdef HAVE_UCONTEXT_H
-#include <ucontext.h>
-#endif
-
#ifdef HAVE_LIBUNWIND
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#include <dlfcn.h>
#endif
-DEFINE_MTYPE_STATIC(LIB, ZLOG, "Logging")
-
-/* hook for external logging */
-DEFINE_HOOK(zebra_ext_log, (int priority, const char *format, va_list args),
- (priority, format, args));
-
-static int logfile_fd = -1; /* Used in signal handler. */
-
-struct zlog *zlog_default = NULL;
-bool zlog_startup_stderr = true;
-
-/* lock protecting zlog_default for mt-safe zlog */
-static pthread_mutex_t loglock = PTHREAD_MUTEX_INITIALIZER;
-
-const char *zlog_priority[] = {
- "emergencies", "alerts", "critical", "errors", "warnings",
- "notifications", "informational", "debugging", NULL,
-};
-
-static char zlog_filters[ZLOG_FILTERS_MAX][ZLOG_FILTER_LENGTH_MAX + 1];
-static uint8_t zlog_filter_count;
-
-/*
- * look for a match on the filter in the current filters, loglock must be held
- */
-static int zlog_filter_lookup(const char *lookup)
-{
- for (int i = 0; i < zlog_filter_count; i++) {
- if (strncmp(lookup, zlog_filters[i], sizeof(zlog_filters[0]))
- == 0)
- return i;
- }
- return -1;
-}
-
-void zlog_filter_clear(void)
-{
- frr_with_mutex(&loglock) {
- zlog_filter_count = 0;
- }
-}
-
-int zlog_filter_add(const char *filter)
-{
- frr_with_mutex(&loglock) {
- if (zlog_filter_count >= ZLOG_FILTERS_MAX)
- return 1;
-
- if (zlog_filter_lookup(filter) != -1)
- /* Filter already present */
- return -1;
-
- strlcpy(zlog_filters[zlog_filter_count], filter,
- sizeof(zlog_filters[0]));
-
- if (zlog_filters[zlog_filter_count][0] == '\0')
- /* Filter was either empty or didn't get copied
- * correctly
- */
- return -1;
-
- zlog_filter_count++;
- }
- return 0;
-}
-
-int zlog_filter_del(const char *filter)
-{
- frr_with_mutex(&loglock) {
- int found_idx = zlog_filter_lookup(filter);
- int last_idx = zlog_filter_count - 1;
-
- if (found_idx == -1)
- /* Didn't find the filter to delete */
- return -1;
-
- /* Adjust the filter array */
- memmove(zlog_filters[found_idx], zlog_filters[found_idx + 1],
- (last_idx - found_idx) * sizeof(zlog_filters[0]));
-
- zlog_filter_count--;
- }
- return 0;
-}
-
-/* Dump all filters to buffer, delimited by new line */
-int zlog_filter_dump(char *buf, size_t max_size)
-{
- int len = 0;
-
- frr_with_mutex(&loglock) {
- for (int i = 0; i < zlog_filter_count; i++) {
- int ret;
- ret = snprintf(buf + len, max_size - len, " %s\n",
- zlog_filters[i]);
- len += ret;
- if ((ret < 0) || ((size_t)len >= max_size))
- return -1;
- }
- }
-
- return len;
-}
-
-/*
- * write_wrapper
- *
- * glibc has declared that the return value from write *must* not be
- * ignored.
- * gcc see's this problem and issues a warning for the line.
- *
- * Why is this a big deal you say? Because both of them are right
- * and if you have -Werror enabled then all calls to write
- * generate a build error and the build stops.
- *
- * clang has helpfully allowed this construct:
- * (void)write(...)
- * to tell the compiler yeah I know it has a return value
- * I don't care about it at this time.
- * gcc doesn't have this ability.
- *
- * This code was written such that it didn't care about the
- * return value from write. At this time do I want
- * to go through and fix and test this code for correctness.
- * So just wrapper the bad behavior and move on.
- */
-static void write_wrapper(int fd, const void *buf, size_t count)
-{
- if (write(fd, buf, count) <= 0)
- return;
-
- return;
-}
-
/**
* Looks up a message in a message list by key.
*
@@ -264,274 +121,12 @@ size_t quagga_timestamp(int timestamp_precision, char *buf, size_t buflen)
return 0;
}
-static inline void timestamp_control_render(struct timestamp_control *ctl)
-{
- if (!ctl->already_rendered) {
- ctl->len = quagga_timestamp(ctl->precision, ctl->buf,
- sizeof(ctl->buf));
- ctl->already_rendered = 1;
- }
-}
-
-/* Utility routine for current time printing. */
-static void time_print(FILE *fp, struct timestamp_control *ctl)
-{
- timestamp_control_render(ctl);
- fprintf(fp, "%s ", ctl->buf);
-}
-
-static int time_print_buf(char *buf, int len, int max_size,
- struct timestamp_control *ctl)
-{
- timestamp_control_render(ctl);
-
- if (ctl->len + 1 >= (unsigned long)max_size)
- return -1;
-
- return snprintf(buf + len, max_size - len, "%s ", ctl->buf);
-}
-
-static void vzlog_file(struct zlog *zl, struct timestamp_control *tsctl,
- const char *proto_str, int record_priority, int priority,
- FILE *fp, const char *msg)
-{
- time_print(fp, tsctl);
- if (record_priority)
- fprintf(fp, "%s: ", zlog_priority[priority]);
-
- fprintf(fp, "%s%s\n", proto_str, msg);
- fflush(fp);
-}
-
-/* Search a buf for the filter strings, loglock must be held */
-static int search_buf(const char *buf)
-{
- char *found = NULL;
-
- for (int i = 0; i < zlog_filter_count; i++) {
- found = strstr(buf, zlog_filters[i]);
- if (found != NULL)
- return 0;
- }
-
- return -1;
-}
-
-/* Filter out a log */
-static int vzlog_filter(struct zlog *zl, struct timestamp_control *tsctl,
- const char *proto_str, int priority, const char *msg)
-{
- int len = 0;
- int ret = 0;
- char buf[1024] = "";
-
- ret = time_print_buf(buf, len, sizeof(buf), tsctl);
-
- len += ret;
- if ((ret < 0) || ((size_t)len >= sizeof(buf)))
- goto search;
-
- if (zl && zl->record_priority)
- snprintf(buf + len, sizeof(buf) - len, "%s: %s: %s",
- zlog_priority[priority], proto_str, msg);
- else
- snprintf(buf + len, sizeof(buf) - len, "%s: %s", proto_str,
- msg);
-
-search:
- return search_buf(buf);
-}
-
-/* va_list version of zlog. */
-void vzlog(int priority, const char *format, va_list args)
-{
- frr_mutex_lock_autounlock(&loglock);
-
- char proto_str[32] = "";
- int original_errno = errno;
- struct timestamp_control tsctl = {};
- tsctl.already_rendered = 0;
- struct zlog *zl = zlog_default;
- char buf[256], *msg;
-
- if (zl == NULL) {
- tsctl.precision = 0;
- } else {
- tsctl.precision = zl->timestamp_precision;
- if (zl->instance)
- sprintf(proto_str, "%s[%d]: ", zl->protoname,
- zl->instance);
- else
- sprintf(proto_str, "%s: ", zl->protoname);
- }
-
- msg = vasnprintfrr(MTYPE_TMP, buf, sizeof(buf), format, args);
-
- /* If it doesn't match on a filter, do nothing with the debug log */
- if ((priority == LOG_DEBUG) && zlog_filter_count
- && vzlog_filter(zl, &tsctl, proto_str, priority, msg))
- goto out;
-
- /* call external hook */
- hook_call(zebra_ext_log, priority, format, args);
-
- /* When zlog_default is also NULL, use stderr for logging. */
- if (zl == NULL) {
- time_print(stderr, &tsctl);
- fprintf(stderr, "%s: %s\n", "unknown", msg);
- fflush(stderr);
- goto out;
- }
-
- /* Syslog output */
- if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG])
- syslog(priority | zlog_default->facility, "%s", msg);
-
- /* File output. */
- if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp)
- vzlog_file(zl, &tsctl, proto_str, zl->record_priority, priority,
- zl->fp, msg);
-
- /* fixed-config logging to stderr while we're stating up & haven't
- * daemonized / reached mainloop yet
- *
- * note the "else" on stdout output -- we don't want to print the same
- * message to both stderr and stdout. */
- if (zlog_startup_stderr && priority <= LOG_WARNING)
- vzlog_file(zl, &tsctl, proto_str, 1, priority, stderr, msg);
- else if (priority <= zl->maxlvl[ZLOG_DEST_STDOUT])
- vzlog_file(zl, &tsctl, proto_str, zl->record_priority, priority,
- stdout, msg);
-
- /* Terminal monitor. */
- if (priority <= zl->maxlvl[ZLOG_DEST_MONITOR])
- vty_log((zl->record_priority ? zlog_priority[priority] : NULL),
- proto_str, msg, &tsctl);
-
-out:
- if (msg != buf)
- XFREE(MTYPE_TMP, msg);
- errno = original_errno;
-}
-
-int vzlog_test(int priority)
-{
- frr_mutex_lock_autounlock(&loglock);
-
- struct zlog *zl = zlog_default;
-
- /* When zlog_default is also NULL, use stderr for logging. */
- if (zl == NULL)
- return 1;
- /* Syslog output */
- else if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG])
- return 1;
- /* File output. */
- else if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp)
- return 1;
- /* stdout output. */
- else if (priority <= zl->maxlvl[ZLOG_DEST_STDOUT])
- return 1;
- /* Terminal monitor. */
- else if (priority <= zl->maxlvl[ZLOG_DEST_MONITOR])
- return 1;
-
- return 0;
-}
-
/*
* crash handling
*
* NB: only AS-Safe (async-signal) functions can be used here!
*/
-/* Needs to be enhanced to support Solaris. */
-static int syslog_connect(void)
-{
-#ifdef SUNOS_5
- return -1;
-#else
- int fd;
- struct sockaddr_un addr;
-
- if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
- return -1;
- addr.sun_family = AF_UNIX;
-#ifdef _PATH_LOG
-#define SYSLOG_SOCKET_PATH _PATH_LOG
-#else
-#define SYSLOG_SOCKET_PATH "/dev/log"
-#endif
- strlcpy(addr.sun_path, SYSLOG_SOCKET_PATH, sizeof(addr.sun_path));
-#undef SYSLOG_SOCKET_PATH
- if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
- close(fd);
- return -1;
- }
- return fd;
-#endif
-}
-
-static void syslog_sigsafe(int priority, const char *msg, size_t msglen)
-{
- static int syslog_fd = -1;
- char buf[sizeof("<1234567890>ripngd[1234567890]: ") + msglen + 50];
- struct fbuf fb = { .buf = buf, .pos = buf, .len = sizeof(buf) };
-
- if ((syslog_fd < 0) && ((syslog_fd = syslog_connect()) < 0))
- return;
-
- /* forget about the timestamp, too difficult in a signal handler */
- bprintfrr(&fb, "<%d>%s", priority, zlog_default->ident);
- if (zlog_default->syslog_options & LOG_PID)
- bprintfrr(&fb, "[%ld]", (long)getpid());
- bprintfrr(&fb, ": %s", msg);
- write_wrapper(syslog_fd, fb.buf, fb.pos - fb.buf);
-}
-
-static int open_crashlog(void)
-{
- char crashlog_buf[PATH_MAX];
- const char *crashlog_default = "/var/tmp/frr.crashlog", *crashlog;
-
- if (!zlog_default || !zlog_default->ident)
- crashlog = crashlog_default;
- else {
- snprintfrr(crashlog_buf, sizeof(crashlog_buf),
- "/var/tmp/frr.%s.crashlog", zlog_default->ident);
- crashlog = crashlog_buf;
- }
- return open(crashlog, O_WRONLY | O_CREAT | O_EXCL, LOGFILE_MASK);
-}
-
-/* N.B. implicit priority is most severe */
-#define PRI LOG_CRIT
-
-static void crash_write(struct fbuf *fb, char *msgstart)
-{
- if (fb->pos == fb->buf)
- return;
- if (!msgstart)
- msgstart = fb->buf;
-
- /* If no file logging configured, try to write to fallback log file. */
- if ((logfile_fd >= 0) || ((logfile_fd = open_crashlog()) >= 0))
- write(logfile_fd, fb->buf, fb->pos - fb->buf);
- if (!zlog_default)
- write(STDERR_FILENO, fb->buf, fb->pos - fb->buf);
- else {
- if (PRI <= zlog_default->maxlvl[ZLOG_DEST_STDOUT])
- write(STDOUT_FILENO, fb->buf, fb->pos - fb->buf);
- /* Remove trailing '\n' for monitor and syslog */
- fb->pos--;
- if (PRI <= zlog_default->maxlvl[ZLOG_DEST_MONITOR])
- vty_log_fixed(fb->buf, fb->pos - fb->buf);
- if (PRI <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG])
- syslog_sigsafe(PRI | zlog_default->facility, msgstart,
- fb->pos - msgstart);
- }
-}
-
/* Note: the goal here is to use only async-signal-safe functions. */
void zlog_signal(int signo, const char *action, void *siginfo_v,
void *program_counter)
@@ -540,14 +135,9 @@ void zlog_signal(int signo, const char *action, void *siginfo_v,
time_t now;
char buf[sizeof("DEFAULT: Received signal S at T (si_addr 0xP, PC 0xP); aborting...")
+ 100];
- char *msgstart;
struct fbuf fb = { .buf = buf, .pos = buf, .len = sizeof(buf) };
time(&now);
- if (zlog_default)
- bprintfrr(&fb, "%s: ", zlog_default->protoname);
-
- msgstart = fb.pos;
bprintfrr(&fb, "Received signal %d at %lld", signo, (long long)now);
if (program_counter)
@@ -559,9 +149,9 @@ void zlog_signal(int signo, const char *action, void *siginfo_v,
(ptrdiff_t)siginfo->si_addr);
bprintfrr(&fb, "; %s\n", action);
- crash_write(&fb, msgstart);
+ zlog_sigsafe(fb.buf, fb.pos - fb.buf);
- zlog_backtrace_sigsafe(PRI, program_counter);
+ zlog_backtrace_sigsafe(LOG_CRIT, program_counter);
fb.pos = buf;
@@ -574,7 +164,7 @@ void zlog_signal(int signo, const char *action, void *siginfo_v,
bprintfrr(&fb, "in thread %s scheduled from %s:%d\n",
tc->funcname, tc->schedfrom, tc->schedfrom_line);
- crash_write(&fb, NULL);
+ zlog_sigsafe(fb.buf, fb.pos - fb.buf);
}
/* Log a backtrace using only async-signal-safe functions.
@@ -609,85 +199,35 @@ void zlog_backtrace_sigsafe(int priority, void *program_counter)
bprintfrr(&fb, " %s (mapped at %p)",
dlinfo.dli_fname, dlinfo.dli_fbase);
bprintfrr(&fb, "\n");
- crash_write(&fb, NULL);
+ zlog_sigsafe(fb.buf, fb.pos - fb.buf);
}
-#elif defined(HAVE_GLIBC_BACKTRACE) || defined(HAVE_PRINTSTACK)
- static const char pclabel[] = "Program counter: ";
+#elif defined(HAVE_GLIBC_BACKTRACE)
void *array[64];
- int size;
+ int size, i;
char buf[128];
struct fbuf fb = { .buf = buf, .pos = buf, .len = sizeof(buf) };
char **bt = NULL;
-#ifdef HAVE_GLIBC_BACKTRACE
size = backtrace(array, array_size(array));
if (size <= 0 || (size_t)size > array_size(array))
return;
-#define DUMP(FD) \
- { \
- if (program_counter) { \
- write_wrapper(FD, pclabel, sizeof(pclabel) - 1); \
- backtrace_symbols_fd(&program_counter, 1, FD); \
- } \
- write_wrapper(FD, fb.buf, fb.pos - fb.buf); \
- backtrace_symbols_fd(array, size, FD); \
- }
-#elif defined(HAVE_PRINTSTACK)
- size = 0;
-
-#define DUMP(FD) \
- { \
- if (program_counter) \
- write_wrapper((FD), pclabel, sizeof(pclabel) - 1); \
- write_wrapper((FD), fb.buf, fb.pos - fb.buf); \
- printstack((FD)); \
- }
-#endif /* HAVE_GLIBC_BACKTRACE, HAVE_PRINTSTACK */
+ bprintfrr(&fb, "Backtrace for %d stack frames:", size);
+ zlog_sigsafe(fb.pos, fb.buf - fb.pos);
- bprintfrr(&fb, "Backtrace for %d stack frames:\n", size);
+ bt = backtrace_symbols(array, size);
- if ((logfile_fd >= 0) || ((logfile_fd = open_crashlog()) >= 0))
- DUMP(logfile_fd)
- if (!zlog_default)
- DUMP(STDERR_FILENO)
- else {
- if (priority <= zlog_default->maxlvl[ZLOG_DEST_STDOUT])
- DUMP(STDOUT_FILENO)
- /* Remove trailing '\n' for monitor and syslog */
- fb.pos--;
- if (priority <= zlog_default->maxlvl[ZLOG_DEST_MONITOR])
- vty_log_fixed(fb.buf, fb.pos - fb.buf);
- if (priority <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG])
- syslog_sigsafe(priority | zlog_default->facility,
- fb.buf, fb.pos - fb.buf);
- {
- int i;
-#ifdef HAVE_GLIBC_BACKTRACE
- bt = backtrace_symbols(array, size);
-#endif
- /* Just print the function addresses. */
- for (i = 0; i < size; i++) {
- fb.pos = buf;
- if (bt)
- bprintfrr(&fb, "%s", bt[i]);
- else
- bprintfrr(&fb, "[bt %d] 0x%tx", i,
- (ptrdiff_t)(array[i]));
- if (priority
- <= zlog_default->maxlvl[ZLOG_DEST_MONITOR])
- vty_log_fixed(fb.buf, fb.pos - fb.buf);
- if (priority
- <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG])
- syslog_sigsafe(priority
- | zlog_default->facility,
- fb.buf, fb.pos - fb.buf);
- }
- if (bt)
- free(bt);
- }
+ for (i = 0; i < size; i++) {
+ fb.pos = buf;
+ if (bt)
+ bprintfrr(&fb, "%s", bt[i]);
+ else
+ bprintfrr(&fb, "[bt %d] 0x%tx", i,
+ (ptrdiff_t)(array[i]));
+ zlog_sigsafe(fb.buf, fb.pos - fb.buf);
}
-#undef DUMP
+ if (bt)
+ free(bt);
#endif /* HAVE_STRACK_TRACE */
}
@@ -754,36 +294,6 @@ void zlog_backtrace(int priority)
#endif
}
-void zlog(int priority, const char *format, ...)
-{
- va_list args;
-
- va_start(args, format);
- vzlog(priority, format, args);
- va_end(args);
-}
-
-#define ZLOG_FUNC(FUNCNAME, PRIORITY) \
- void FUNCNAME(const char *format, ...) \
- { \
- va_list args; \
- va_start(args, format); \
- vzlog(PRIORITY, format, args); \
- va_end(args); \
- }
-
-ZLOG_FUNC(zlog_err, LOG_ERR)
-
-ZLOG_FUNC(zlog_warn, LOG_WARNING)
-
-ZLOG_FUNC(zlog_info, LOG_INFO)
-
-ZLOG_FUNC(zlog_notice, LOG_NOTICE)
-
-ZLOG_FUNC(zlog_debug, LOG_DEBUG)
-
-#undef ZLOG_FUNC
-
void zlog_thread_info(int log_level)
{
struct thread *tc;
@@ -801,11 +311,6 @@ void zlog_thread_info(int log_level)
void _zlog_assert_failed(const char *assertion, const char *file,
unsigned int line, const char *function)
{
- /* Force fallback file logging? */
- if (zlog_default && !zlog_default->fp
- && ((logfile_fd = open_crashlog()) >= 0)
- && ((zlog_default->fp = fdopen(logfile_fd, "w")) != NULL))
- zlog_default->maxlvl[ZLOG_DEST_FILE] = LOG_ERR;
zlog(LOG_CRIT, "Assertion `%s' failed in file %s, line %u, function %s",
assertion, file, line, (function ? function : "?"));
zlog_backtrace(LOG_CRIT);
@@ -816,174 +321,14 @@ void _zlog_assert_failed(const char *assertion, const char *file,
void memory_oom(size_t size, const char *name)
{
- flog_err_sys(EC_LIB_SYSTEM_CALL,
- "out of memory: failed to allocate %zu bytes for %s"
- "object",
- size, name);
- zlog_backtrace(LOG_ERR);
+ zlog(LOG_CRIT,
+ "out of memory: failed to allocate %zu bytes for %s object",
+ size, name);
+ zlog_backtrace(LOG_CRIT);
+ log_memstats(stderr, "log");
abort();
}
-/* Open log stream */
-void openzlog(const char *progname, const char *protoname,
- unsigned short instance, int syslog_flags, int syslog_facility)
-{
- struct zlog *zl;
- unsigned int i;
-
- zl = XCALLOC(MTYPE_ZLOG, sizeof(struct zlog));
-
- zl->ident = progname;
- zl->protoname = protoname;
- zl->instance = instance;
- zl->facility = syslog_facility;
- zl->syslog_options = syslog_flags;
-
- /* Set default logging levels. */
- for (i = 0; i < array_size(zl->maxlvl); i++)
- zl->maxlvl[i] = ZLOG_DISABLED;
- zl->maxlvl[ZLOG_DEST_MONITOR] = LOG_DEBUG;
- zl->default_lvl = LOG_DEBUG;
-
- openlog(progname, syslog_flags, zl->facility);
-
- frr_with_mutex(&loglock) {
- zlog_default = zl;
- }
-
-#ifdef HAVE_GLIBC_BACKTRACE
- /* work around backtrace() using lazily resolved dynamically linked
- * symbols, which will otherwise cause funny breakage in the SEGV
- * handler.
- * (particularly, the dynamic linker can call malloc(), which uses locks
- * in programs linked with -pthread, thus can deadlock.) */
- void *bt[4];
- backtrace(bt, array_size(bt));
- free(backtrace_symbols(bt, 0));
- backtrace_symbols_fd(bt, 0, 0);
-#endif
-}
-
-void closezlog(void)
-{
- frr_mutex_lock_autounlock(&loglock);
-
- struct zlog *zl = zlog_default;
-
- closelog();
-
- if (zl->fp != NULL)
- fclose(zl->fp);
-
- XFREE(MTYPE_ZLOG, zl->filename);
-
- XFREE(MTYPE_ZLOG, zl);
- zlog_default = NULL;
-}
-
-/* Called from command.c. */
-void zlog_set_level(zlog_dest_t dest, int log_level)
-{
- frr_with_mutex(&loglock) {
- zlog_default->maxlvl[dest] = log_level;
- }
-}
-
-int zlog_set_file(const char *filename, int log_level)
-{
- struct zlog *zl;
- FILE *fp;
- mode_t oldumask;
- int ret = 1;
-
- /* There is opend file. */
- zlog_reset_file();
-
- /* Open file. */
- oldumask = umask(0777 & ~LOGFILE_MASK);
- fp = fopen(filename, "a");
- umask(oldumask);
- if (fp == NULL) {
- ret = 0;
- } else {
- frr_with_mutex(&loglock) {
- zl = zlog_default;
-
- /* Set flags. */
- zl->filename = XSTRDUP(MTYPE_ZLOG, filename);
- zl->maxlvl[ZLOG_DEST_FILE] = log_level;
- zl->fp = fp;
- logfile_fd = fileno(fp);
- }
- }
-
- return ret;
-}
-
-/* Reset opend file. */
-int zlog_reset_file(void)
-{
- frr_mutex_lock_autounlock(&loglock);
-
- struct zlog *zl = zlog_default;
-
- if (zl->fp)
- fclose(zl->fp);
- zl->fp = NULL;
- logfile_fd = -1;
- zl->maxlvl[ZLOG_DEST_FILE] = ZLOG_DISABLED;
-
- XFREE(MTYPE_ZLOG, zl->filename);
-
- return 1;
-}
-
-/* Reopen log file. */
-int zlog_rotate(void)
-{
- pthread_mutex_lock(&loglock);
-
- struct zlog *zl = zlog_default;
- int level;
- int ret = 1;
-
- if (zl->fp)
- fclose(zl->fp);
- zl->fp = NULL;
- logfile_fd = -1;
- level = zl->maxlvl[ZLOG_DEST_FILE];
- zl->maxlvl[ZLOG_DEST_FILE] = ZLOG_DISABLED;
-
- if (zl->filename) {
- mode_t oldumask;
- int save_errno;
-
- oldumask = umask(0777 & ~LOGFILE_MASK);
- zl->fp = fopen(zl->filename, "a");
- save_errno = errno;
- umask(oldumask);
- if (zl->fp == NULL) {
-
- pthread_mutex_unlock(&loglock);
-
- flog_err_sys(
- EC_LIB_SYSTEM_CALL,
- "Log rotate failed: cannot open file %s for append: %s",
- zl->filename, safe_strerror(save_errno));
- ret = -1;
-
- pthread_mutex_lock(&loglock);
- } else {
- logfile_fd = fileno(zl->fp);
- zl->maxlvl[ZLOG_DEST_FILE] = level;
- }
- }
-
- pthread_mutex_unlock(&loglock);
-
- return ret;
-}
-
/* Wrapper around strerror to handle case where it returns NULL. */
const char *safe_strerror(int errnum)
{
diff --git a/lib/log.h b/lib/log.h
index d79ad9f805..b65ae5d5d5 100644
--- a/lib/log.h
+++ b/lib/log.h
@@ -22,21 +22,22 @@
#ifndef _ZEBRA_LOG_H
#define _ZEBRA_LOG_H
+#include "zassert.h"
+
#include <syslog.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdarg.h>
+
#include "lib/hook.h"
+#include "lib/zlog.h"
+#include "lib/zlog_targets.h"
#ifdef __cplusplus
extern "C" {
#endif
-/* Hook for external logging function */
-DECLARE_HOOK(zebra_ext_log, (int priority, const char *format, va_list args),
- (priority, format, args));
-
/* Here is some guidance on logging levels to use:
*
* LOG_DEBUG - For all messages that are enabled by optional debugging
@@ -53,19 +54,7 @@ DECLARE_HOOK(zebra_ext_log, (int priority, const char *format, va_list args),
* please use LOG_ERR instead.
*/
-/* If maxlvl is set to ZLOG_DISABLED, then no messages will be sent
- to that logging destination. */
-#define ZLOG_DISABLED (LOG_EMERG-1)
-
-typedef enum {
- ZLOG_DEST_SYSLOG = 0,
- ZLOG_DEST_STDOUT,
- ZLOG_DEST_MONITOR,
- ZLOG_DEST_FILE
-} zlog_dest_t;
-#define ZLOG_NUM_DESTS (ZLOG_DEST_FILE+1)
-
-extern bool zlog_startup_stderr;
+extern void zlog_rotate(void);
/* Message structure. */
struct message {
@@ -73,22 +62,6 @@ struct message {
const char *str;
};
-/* Open zlog function */
-extern void openzlog(const char *progname, const char *protoname,
- uint16_t instance, int syslog_options,
- int syslog_facility);
-
-/* Close zlog function. */
-extern void closezlog(void);
-
-/* Handy zlog functions. */
-extern void zlog_err(const char *format, ...) PRINTFRR(1, 2);
-extern void zlog_warn(const char *format, ...) PRINTFRR(1, 2);
-extern void zlog_info(const char *format, ...) PRINTFRR(1, 2);
-extern void zlog_notice(const char *format, ...) PRINTFRR(1, 2);
-extern void zlog_debug(const char *format, ...) PRINTFRR(1, 2);
-extern void zlog(int priority, const char *format, ...) PRINTFRR(2, 3);
-
/* For logs which have error codes associated with them */
#define flog_err(ferr_id, format, ...) \
zlog_err("[EC %" PRIu32 "] " format, ferr_id, ##__VA_ARGS__)
@@ -101,23 +74,16 @@ extern void zlog(int priority, const char *format, ...) PRINTFRR(2, 3);
extern void zlog_thread_info(int log_level);
-/* Set logging level for the given destination. If the log_level
- argument is ZLOG_DISABLED, then the destination is disabled.
- This function should not be used for file logging (use zlog_set_file
- or zlog_reset_file instead). */
-extern void zlog_set_level(zlog_dest_t, int log_level);
-
-/* Set logging to the given filename at the specified level. */
-extern int zlog_set_file(const char *filename, int log_level);
-/* Disable file logging. */
-extern int zlog_reset_file(void);
-
-/* Rotate log. */
-extern int zlog_rotate(void);
-
#define ZLOG_FILTERS_MAX 100 /* Max # of filters at once */
#define ZLOG_FILTER_LENGTH_MAX 80 /* 80 character filter limit */
+struct zlog_cfg_filterfile {
+ struct zlog_cfg_file parent;
+};
+
+extern void zlog_filterfile_init(struct zlog_cfg_filterfile *zcf);
+extern void zlog_filterfile_fini(struct zlog_cfg_filterfile *zcf);
+
/* Add/Del/Dump log filters */
extern void zlog_filter_clear(void);
extern int zlog_filter_add(const char *filter);
@@ -176,8 +142,6 @@ extern int proto_redistnum(int afi, const char *s);
extern const char *zserv_command_string(unsigned int command);
-extern int vzlog_test(int priority);
-
/* structure useful for avoiding repeated rendering of the same timestamp */
struct timestamp_control {
size_t len; /* length of rendered timestamp */
diff --git a/lib/log_filter.c b/lib/log_filter.c
new file mode 100644
index 0000000000..721e57a628
--- /dev/null
+++ b/lib/log_filter.c
@@ -0,0 +1,156 @@
+/*
+ * Logging - Filtered file log target
+ * Copyright (C) 2019 Cumulus Networks, Inc.
+ * Stephen Worley
+ *
+ * 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>
+
+#include "frr_pthread.h"
+#include "log.h"
+
+static pthread_mutex_t logfilterlock = PTHREAD_MUTEX_INITIALIZER;
+static char zlog_filters[ZLOG_FILTERS_MAX][ZLOG_FILTER_LENGTH_MAX + 1];
+static uint8_t zlog_filter_count;
+
+/*
+ * look for a match on the filter in the current filters,
+ * logfilterlock must be held
+ */
+static int zlog_filter_lookup(const char *lookup)
+{
+ for (int i = 0; i < zlog_filter_count; i++) {
+ if (strncmp(lookup, zlog_filters[i], sizeof(zlog_filters[0]))
+ == 0)
+ return i;
+ }
+ return -1;
+}
+
+void zlog_filter_clear(void)
+{
+ frr_with_mutex(&logfilterlock) {
+ zlog_filter_count = 0;
+ }
+}
+
+int zlog_filter_add(const char *filter)
+{
+ frr_with_mutex(&logfilterlock) {
+ if (zlog_filter_count >= ZLOG_FILTERS_MAX)
+ return 1;
+
+ if (zlog_filter_lookup(filter) != -1)
+ /* Filter already present */
+ return -1;
+
+ strlcpy(zlog_filters[zlog_filter_count], filter,
+ sizeof(zlog_filters[0]));
+
+ if (zlog_filters[zlog_filter_count][0] == '\0')
+ /* Filter was either empty or didn't get copied
+ * correctly
+ */
+ return -1;
+
+ zlog_filter_count++;
+ }
+ return 0;
+}
+
+int zlog_filter_del(const char *filter)
+{
+ frr_with_mutex(&logfilterlock) {
+ int found_idx = zlog_filter_lookup(filter);
+ int last_idx = zlog_filter_count - 1;
+
+ if (found_idx == -1)
+ /* Didn't find the filter to delete */
+ return -1;
+
+ /* Adjust the filter array */
+ memmove(zlog_filters[found_idx], zlog_filters[found_idx + 1],
+ (last_idx - found_idx) * sizeof(zlog_filters[0]));
+
+ zlog_filter_count--;
+ }
+ return 0;
+}
+
+/* Dump all filters to buffer, delimited by new line */
+int zlog_filter_dump(char *buf, size_t max_size)
+{
+ int len = 0;
+
+ frr_with_mutex(&logfilterlock) {
+ for (int i = 0; i < zlog_filter_count; i++) {
+ int ret;
+
+ ret = snprintf(buf + len, max_size - len, " %s\n",
+ zlog_filters[i]);
+ len += ret;
+ if ((ret < 0) || ((size_t)len >= max_size))
+ return -1;
+ }
+ }
+
+ return len;
+}
+
+static int search_buf(const char *buf)
+{
+ char *found = NULL;
+
+ frr_with_mutex(&logfilterlock) {
+ for (int i = 0; i < zlog_filter_count; i++) {
+ found = strstr(buf, zlog_filters[i]);
+ if (found != NULL)
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static void zlog_filterfile_fd(struct zlog_target *zt, struct zlog_msg *msgs[],
+ size_t nmsgs)
+{
+ struct zlog_msg *msgfilt[nmsgs];
+ size_t i, o = 0;
+
+ for (i = 0; i < nmsgs; i++) {
+ if (zlog_msg_prio(msgs[i]) >= LOG_DEBUG
+ && search_buf(zlog_msg_text(msgs[i], NULL)) < 0)
+ continue;
+
+ msgfilt[o++] = msgs[i];
+ }
+
+ if (o)
+ zlog_fd(zt, msgfilt, o);
+}
+
+void zlog_filterfile_init(struct zlog_cfg_filterfile *zcf)
+{
+ zlog_file_init(&zcf->parent);
+ zcf->parent.zlog_wrap = zlog_filterfile_fd;
+}
+
+void zlog_filterfile_fini(struct zlog_cfg_filterfile *zcf)
+{
+ zlog_file_fini(&zcf->parent);
+}
diff --git a/lib/log_int.h b/lib/log_int.h
deleted file mode 100644
index 287e626eab..0000000000
--- a/lib/log_int.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Zebra logging funcions.
- * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro
- *
- * This file is part of GNU Zebra.
- *
- * GNU Zebra 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.
- *
- * GNU Zebra 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 _ZEBRA_LOG_PRIVATE_H
-#define _ZEBRA_LOG_PRIVATE_H
-
-#include "log.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct zlog {
- const char *ident; /* daemon name (first arg to openlog) */
- const char *protoname;
- unsigned short instance;
- int maxlvl[ZLOG_NUM_DESTS]; /* maximum priority to send to associated
- logging destination */
- int default_lvl; /* maxlvl to use if none is specified */
- FILE *fp;
- char *filename;
- int facility; /* as per syslog facility */
- int record_priority; /* should messages logged through stdio include the
- priority of the message? */
- int syslog_options; /* 2nd arg to openlog */
- int timestamp_precision; /* # of digits of subsecond precision */
-};
-
-/* Default logging strucutre. */
-extern struct zlog *zlog_default;
-
-extern const char *zlog_priority[];
-
-/* Generic function for zlog. */
-extern void vzlog(int priority, const char *format, va_list args);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _ZEBRA_LOG_PRIVATE_H */
diff --git a/lib/log_vty.c b/lib/log_vty.c
index 68d598f565..97026e5dbc 100644
--- a/lib/log_vty.c
+++ b/lib/log_vty.c
@@ -22,12 +22,542 @@
#include "lib/log_vty.h"
#include "command.h"
-#include "lib/vty.h"
#include "lib/log.h"
+#include "lib/zlog_targets.h"
+#include "lib/lib_errors.h"
+#include "lib/printfrr.h"
+
#ifndef VTYSH_EXTRACT_PL
#include "lib/log_vty_clippy.c"
#endif
+#define ZLOG_MAXLVL(a, b) MAX(a, b)
+
+DEFINE_HOOK(zlog_rotate, (), ())
+
+static const int log_default_lvl = LOG_DEBUG;
+
+static int log_config_stdout_lvl = ZLOG_DISABLED;
+static int log_config_syslog_lvl = ZLOG_DISABLED;
+static int log_cmdline_stdout_lvl = ZLOG_DISABLED;
+static int log_cmdline_syslog_lvl = ZLOG_DISABLED;
+
+static struct zlog_cfg_file zt_file_cmdline = {
+ .prio_min = ZLOG_DISABLED,
+};
+static struct zlog_cfg_file zt_file = {
+ .prio_min = ZLOG_DISABLED,
+};
+static struct zlog_cfg_file zt_stdout = {
+ .prio_min = ZLOG_DISABLED,
+};
+static struct zlog_cfg_filterfile zt_filterfile = {
+ .parent = {
+ .prio_min = ZLOG_DISABLED,
+ },
+};
+
+static const char *zlog_progname;
+static const char *zlog_protoname;
+
+static const struct facility_map {
+ int facility;
+ const char *name;
+ size_t match;
+} syslog_facilities[] = {
+ {LOG_KERN, "kern", 1},
+ {LOG_USER, "user", 2},
+ {LOG_MAIL, "mail", 1},
+ {LOG_DAEMON, "daemon", 1},
+ {LOG_AUTH, "auth", 1},
+ {LOG_SYSLOG, "syslog", 1},
+ {LOG_LPR, "lpr", 2},
+ {LOG_NEWS, "news", 1},
+ {LOG_UUCP, "uucp", 2},
+ {LOG_CRON, "cron", 1},
+#ifdef LOG_FTP
+ {LOG_FTP, "ftp", 1},
+#endif
+ {LOG_LOCAL0, "local0", 6},
+ {LOG_LOCAL1, "local1", 6},
+ {LOG_LOCAL2, "local2", 6},
+ {LOG_LOCAL3, "local3", 6},
+ {LOG_LOCAL4, "local4", 6},
+ {LOG_LOCAL5, "local5", 6},
+ {LOG_LOCAL6, "local6", 6},
+ {LOG_LOCAL7, "local7", 6},
+ {0, NULL, 0},
+};
+
+static const char * const zlog_priority[] = {
+ "emergencies", "alerts", "critical", "errors", "warnings",
+ "notifications", "informational", "debugging", NULL,
+};
+
+static const char *facility_name(int facility)
+{
+ const struct facility_map *fm;
+
+ for (fm = syslog_facilities; fm->name; fm++)
+ if (fm->facility == facility)
+ return fm->name;
+ return "";
+}
+
+static int facility_match(const char *str)
+{
+ const struct facility_map *fm;
+
+ for (fm = syslog_facilities; fm->name; fm++)
+ if (!strncmp(str, fm->name, fm->match))
+ return fm->facility;
+ return -1;
+}
+
+int log_level_match(const char *s)
+{
+ int level;
+
+ for (level = 0; zlog_priority[level] != NULL; level++)
+ if (!strncmp(s, zlog_priority[level], 2))
+ return level;
+ return ZLOG_DISABLED;
+}
+
+void zlog_rotate(void)
+{
+ zlog_file_rotate(&zt_file);
+ zlog_file_rotate(&zt_filterfile.parent);
+ hook_call(zlog_rotate);
+}
+
+
+void log_show_syslog(struct vty *vty)
+{
+ int level = zlog_syslog_get_prio_min();
+
+ vty_out(vty, "Syslog logging: ");
+ if (level == ZLOG_DISABLED)
+ vty_out(vty, "disabled\n");
+ else
+ vty_out(vty, "level %s, facility %s, ident %s\n",
+ zlog_priority[level],
+ facility_name(zlog_syslog_get_facility()),
+ zlog_progname);
+}
+
+DEFUN (show_logging,
+ show_logging_cmd,
+ "show logging",
+ SHOW_STR
+ "Show current logging configuration\n")
+{
+ log_show_syslog(vty);
+
+ vty_out(vty, "Stdout logging: ");
+ if (zt_stdout.prio_min == ZLOG_DISABLED)
+ vty_out(vty, "disabled");
+ else
+ vty_out(vty, "level %s",
+ zlog_priority[zt_stdout.prio_min]);
+ vty_out(vty, "\n");
+
+ vty_out(vty, "File logging: ");
+ if (zt_file.prio_min == ZLOG_DISABLED || !zt_file.filename)
+ vty_out(vty, "disabled");
+ else
+ vty_out(vty, "level %s, filename %s",
+ zlog_priority[zt_file.prio_min], zt_file.filename);
+ vty_out(vty, "\n");
+
+ if (zt_filterfile.parent.prio_min != ZLOG_DISABLED
+ && zt_filterfile.parent.filename)
+ vty_out(vty, "Filtered-file logging: level %s, filename %s\n",
+ zlog_priority[zt_filterfile.parent.prio_min],
+ zt_filterfile.parent.filename);
+
+ if (log_cmdline_syslog_lvl != ZLOG_DISABLED)
+ vty_out(vty,
+ "From command line: \"--log syslog --log-level %s\"\n",
+ zlog_priority[log_cmdline_syslog_lvl]);
+ if (log_cmdline_stdout_lvl != ZLOG_DISABLED)
+ vty_out(vty,
+ "From command line: \"--log stdout --log-level %s\"\n",
+ zlog_priority[log_cmdline_stdout_lvl]);
+ if (zt_file_cmdline.prio_min != ZLOG_DISABLED)
+ vty_out(vty,
+ "From command line: \"--log file:%s --log-level %s\"\n",
+ zt_file_cmdline.filename,
+ zlog_priority[zt_file_cmdline.prio_min]);
+
+ vty_out(vty, "Protocol name: %s\n", zlog_protoname);
+ vty_out(vty, "Record priority: %s\n",
+ (zt_file.record_priority ? "enabled" : "disabled"));
+ vty_out(vty, "Timestamp precision: %d\n", zt_file.ts_subsec);
+ return CMD_SUCCESS;
+}
+
+DEFPY (config_log_stdout,
+ config_log_stdout_cmd,
+ "log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>$levelarg]",
+ "Logging control\n"
+ "Set stdout logging level\n"
+ LOG_LEVEL_DESC)
+{
+ int level;
+
+ if (levelarg) {
+ level = log_level_match(levelarg);
+ if (level == ZLOG_DISABLED)
+ return CMD_ERR_NO_MATCH;
+ } else
+ level = log_default_lvl;
+
+ log_config_stdout_lvl = level;
+ zt_stdout.prio_min = ZLOG_MAXLVL(log_config_stdout_lvl,
+ log_cmdline_stdout_lvl);
+ zlog_file_set_other(&zt_stdout);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_config_log_stdout,
+ no_config_log_stdout_cmd,
+ "no log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
+ NO_STR
+ "Logging control\n"
+ "Cancel logging to stdout\n"
+ LOG_LEVEL_DESC)
+{
+ log_config_stdout_lvl = ZLOG_DISABLED;
+ zt_stdout.prio_min = ZLOG_MAXLVL(log_config_stdout_lvl,
+ log_cmdline_stdout_lvl);
+ zlog_file_set_other(&zt_stdout);
+ return CMD_SUCCESS;
+}
+
+DEFUN_HIDDEN (config_log_monitor,
+ config_log_monitor_cmd,
+ "log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
+ "Logging control\n"
+ "Set terminal line (monitor) logging level\n"
+ LOG_LEVEL_DESC)
+{
+ vty_out(vty, "%% \"log monitor\" is deprecated and does nothing.\n");
+ return CMD_SUCCESS;
+}
+
+DEFUN_HIDDEN (no_config_log_monitor,
+ no_config_log_monitor_cmd,
+ "no log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
+ NO_STR
+ "Logging control\n"
+ "Disable terminal line (monitor) logging\n"
+ LOG_LEVEL_DESC)
+{
+ return CMD_SUCCESS;
+}
+
+static int set_log_file(struct zlog_cfg_file *target, struct vty *vty,
+ const char *fname, int loglevel)
+{
+ char *p = NULL;
+ const char *fullpath;
+ bool ok;
+
+ /* Path detection. */
+ if (!IS_DIRECTORY_SEP(*fname)) {
+ char cwd[MAXPATHLEN + 1];
+
+ cwd[MAXPATHLEN] = '\0';
+
+ if (getcwd(cwd, MAXPATHLEN) == NULL) {
+ flog_err_sys(EC_LIB_SYSTEM_CALL,
+ "config_log_file: Unable to alloc mem!");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ p = XMALLOC(MTYPE_TMP, strlen(cwd) + strlen(fname) + 2);
+ sprintf(p, "%s/%s", cwd, fname);
+ fullpath = p;
+ } else
+ fullpath = fname;
+
+ target->prio_min = loglevel;
+ ok = zlog_file_set_filename(target, fullpath);
+
+ XFREE(MTYPE_TMP, p);
+
+ if (!ok) {
+ if (vty)
+ vty_out(vty, "can't open logfile %s\n", fname);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ return CMD_SUCCESS;
+}
+
+void command_setup_early_logging(const char *dest, const char *level)
+{
+ int nlevel;
+ char *sep;
+ int len;
+ char type[8];
+
+ if (level) {
+ nlevel = log_level_match(level);
+
+ if (nlevel == ZLOG_DISABLED) {
+ fprintf(stderr, "invalid log level \"%s\"\n", level);
+ exit(1);
+ }
+ } else
+ nlevel = log_default_lvl;
+
+ if (!dest)
+ return;
+
+ sep = strchr(dest, ':');
+ len = sep ? (int)(sep - dest) : (int)strlen(dest);
+
+ snprintfrr(type, sizeof(type), "%.*s", len, dest);
+
+ if (strcmp(type, "stdout") == 0) {
+ log_cmdline_stdout_lvl = nlevel;
+ zt_stdout.prio_min = ZLOG_MAXLVL(log_config_stdout_lvl,
+ log_cmdline_stdout_lvl);
+ zlog_file_set_other(&zt_stdout);
+ return;
+ }
+ if (strcmp(type, "syslog") == 0) {
+ log_cmdline_syslog_lvl = nlevel;
+ zlog_syslog_set_prio_min(ZLOG_MAXLVL(log_config_syslog_lvl,
+ log_cmdline_syslog_lvl));
+ return;
+ }
+ if (strcmp(type, "file") == 0 && sep) {
+ sep++;
+ set_log_file(&zt_file_cmdline, NULL, sep, nlevel);
+ return;
+ }
+
+ fprintf(stderr, "invalid log target \"%s\" (\"%s\")\n", type, dest);
+ exit(1);
+}
+
+DEFUN (clear_log_cmdline,
+ clear_log_cmdline_cmd,
+ "clear log cmdline-targets",
+ CLEAR_STR
+ "Logging control\n"
+ "Disable log targets specified at startup by --log option\n")
+{
+ zt_file_cmdline.prio_min = ZLOG_DISABLED;
+ zlog_file_set_other(&zt_file_cmdline);
+
+ log_cmdline_syslog_lvl = ZLOG_DISABLED;
+ zlog_syslog_set_prio_min(ZLOG_MAXLVL(log_config_syslog_lvl,
+ log_cmdline_syslog_lvl));
+
+ log_cmdline_stdout_lvl = ZLOG_DISABLED;
+ zt_stdout.prio_min = ZLOG_MAXLVL(log_config_stdout_lvl,
+ log_cmdline_stdout_lvl);
+ zlog_file_set_other(&zt_stdout);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (config_log_file,
+ config_log_file_cmd,
+ "log file FILENAME [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>$levelarg]",
+ "Logging control\n"
+ "Logging to file\n"
+ "Logging filename\n"
+ LOG_LEVEL_DESC)
+{
+ int level = log_default_lvl;
+
+ if (levelarg) {
+ level = log_level_match(levelarg);
+ if (level == ZLOG_DISABLED)
+ return CMD_ERR_NO_MATCH;
+ }
+ return set_log_file(&zt_file, vty, filename, level);
+}
+
+DEFUN (no_config_log_file,
+ no_config_log_file_cmd,
+ "no log file [FILENAME [LEVEL]]",
+ NO_STR
+ "Logging control\n"
+ "Cancel logging to file\n"
+ "Logging file name\n"
+ "Logging level\n")
+{
+ zt_file.prio_min = ZLOG_DISABLED;
+ zlog_file_set_other(&zt_file);
+ return CMD_SUCCESS;
+}
+
+DEFPY (config_log_syslog,
+ config_log_syslog_cmd,
+ "log syslog [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>$levelarg]",
+ "Logging control\n"
+ "Set syslog logging level\n"
+ LOG_LEVEL_DESC)
+{
+ int level;
+
+ if (levelarg) {
+ level = log_level_match(levelarg);
+
+ if (level == ZLOG_DISABLED)
+ return CMD_ERR_NO_MATCH;
+ } else
+ level = log_default_lvl;
+
+ log_config_syslog_lvl = level;
+ zlog_syslog_set_prio_min(ZLOG_MAXLVL(log_config_syslog_lvl,
+ log_cmdline_syslog_lvl));
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_config_log_syslog,
+ no_config_log_syslog_cmd,
+ "no log syslog [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>] [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
+ NO_STR
+ "Logging control\n"
+ "Cancel logging to syslog\n"
+ LOG_FACILITY_DESC
+ LOG_LEVEL_DESC)
+{
+ log_config_syslog_lvl = ZLOG_DISABLED;
+ zlog_syslog_set_prio_min(ZLOG_MAXLVL(log_config_syslog_lvl,
+ log_cmdline_syslog_lvl));
+ return CMD_SUCCESS;
+}
+
+DEFPY (config_log_facility,
+ config_log_facility_cmd,
+ "log facility <kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>$facilityarg",
+ "Logging control\n"
+ "Facility parameter for syslog messages\n"
+ LOG_FACILITY_DESC)
+{
+ int facility = facility_match(facilityarg);
+
+ zlog_syslog_set_facility(facility);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_config_log_facility,
+ no_config_log_facility_cmd,
+ "no log facility [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>]",
+ NO_STR
+ "Logging control\n"
+ "Reset syslog facility to default (daemon)\n"
+ LOG_FACILITY_DESC)
+{
+ zlog_syslog_set_facility(LOG_DAEMON);
+ return CMD_SUCCESS;
+}
+
+DEFUN (config_log_record_priority,
+ config_log_record_priority_cmd,
+ "log record-priority",
+ "Logging control\n"
+ "Log the priority of the message within the message\n")
+{
+ zt_file.record_priority = true;
+ zlog_file_set_other(&zt_file);
+ zt_stdout.record_priority = true;
+ zlog_file_set_other(&zt_stdout);
+ zt_filterfile.parent.record_priority = true;
+ zlog_file_set_other(&zt_filterfile.parent);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_config_log_record_priority,
+ no_config_log_record_priority_cmd,
+ "no log record-priority",
+ NO_STR
+ "Logging control\n"
+ "Do not log the priority of the message within the message\n")
+{
+ zt_file.record_priority = false;
+ zlog_file_set_other(&zt_file);
+ zt_stdout.record_priority = false;
+ zlog_file_set_other(&zt_stdout);
+ zt_filterfile.parent.record_priority = false;
+ zlog_file_set_other(&zt_filterfile.parent);
+ return CMD_SUCCESS;
+}
+
+DEFPY (config_log_timestamp_precision,
+ config_log_timestamp_precision_cmd,
+ "log timestamp precision (0-6)",
+ "Logging control\n"
+ "Timestamp configuration\n"
+ "Set the timestamp precision\n"
+ "Number of subsecond digits\n")
+{
+ zt_file.ts_subsec = precision;
+ zlog_file_set_other(&zt_file);
+ zt_stdout.ts_subsec = precision;
+ zlog_file_set_other(&zt_stdout);
+ zt_filterfile.parent.ts_subsec = precision;
+ zlog_file_set_other(&zt_filterfile.parent);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_config_log_timestamp_precision,
+ no_config_log_timestamp_precision_cmd,
+ "no log timestamp precision [(0-6)]",
+ NO_STR
+ "Logging control\n"
+ "Timestamp configuration\n"
+ "Reset the timestamp precision to the default value of 0\n"
+ "Number of subsecond digits\n")
+{
+ zt_file.ts_subsec = 0;
+ zlog_file_set_other(&zt_file);
+ zt_stdout.ts_subsec = 0;
+ zlog_file_set_other(&zt_stdout);
+ zt_filterfile.parent.ts_subsec = 0;
+ zlog_file_set_other(&zt_filterfile.parent);
+ return CMD_SUCCESS;
+}
+
+DEFPY (config_log_filterfile,
+ config_log_filterfile_cmd,
+ "log filtered-file FILENAME [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>$levelarg]",
+ "Logging control\n"
+ "Logging to file with string filter\n"
+ "Logging filename\n"
+ LOG_LEVEL_DESC)
+{
+ int level = log_default_lvl;
+
+ if (levelarg) {
+ level = log_level_match(levelarg);
+ if (level == ZLOG_DISABLED)
+ return CMD_ERR_NO_MATCH;
+ }
+ return set_log_file(&zt_filterfile.parent, vty, filename, level);
+}
+
+DEFUN (no_config_log_filterfile,
+ no_config_log_filterfile_cmd,
+ "no log filtered-file [FILENAME [LEVEL]]",
+ NO_STR
+ "Logging control\n"
+ "Cancel logging to file with string filter\n"
+ "Logging file name\n"
+ "Logging level\n")
+{
+ zt_filterfile.parent.prio_min = ZLOG_DISABLED;
+ zlog_file_set_other(&zt_filterfile.parent);
+ return CMD_SUCCESS;
+}
+
DEFPY (log_filter,
log_filter_cmd,
"[no] log-filter WORD$filter",
@@ -89,9 +619,122 @@ DEFPY (show_log_filter,
return CMD_SUCCESS;
}
-void log_filter_cmd_init(void)
+void log_config_write(struct vty *vty)
{
+ bool show_cmdline_hint = false;
+
+ if (zt_file.prio_min != ZLOG_DISABLED && zt_file.filename) {
+ vty_out(vty, "log file %s", zt_file.filename);
+
+ if (zt_file.prio_min != log_default_lvl)
+ vty_out(vty, " %s", zlog_priority[zt_file.prio_min]);
+ vty_out(vty, "\n");
+ }
+
+ if (zt_filterfile.parent.prio_min != ZLOG_DISABLED
+ && zt_filterfile.parent.filename) {
+ vty_out(vty, "log filtered-file %s",
+ zt_filterfile.parent.filename);
+
+ if (zt_filterfile.parent.prio_min != log_default_lvl)
+ vty_out(vty, " %s",
+ zlog_priority[zt_filterfile.parent.prio_min]);
+ vty_out(vty, "\n");
+ }
+
+ if (log_config_stdout_lvl != ZLOG_DISABLED) {
+ vty_out(vty, "log stdout");
+
+ if (log_config_stdout_lvl != log_default_lvl)
+ vty_out(vty, " %s",
+ zlog_priority[log_config_stdout_lvl]);
+ vty_out(vty, "\n");
+ }
+
+ if (log_config_syslog_lvl != ZLOG_DISABLED) {
+ vty_out(vty, "log syslog");
+
+ if (log_config_syslog_lvl != log_default_lvl)
+ vty_out(vty, " %s",
+ zlog_priority[log_config_syslog_lvl]);
+ vty_out(vty, "\n");
+ }
+
+ if (log_cmdline_syslog_lvl != ZLOG_DISABLED) {
+ vty_out(vty,
+ "! \"log syslog %s\" enabled by \"--log\" startup option\n",
+ zlog_priority[log_cmdline_syslog_lvl]);
+ show_cmdline_hint = true;
+ }
+ if (log_cmdline_stdout_lvl != ZLOG_DISABLED) {
+ vty_out(vty,
+ "! \"log stdout %s\" enabled by \"--log\" startup option\n",
+ zlog_priority[log_cmdline_stdout_lvl]);
+ show_cmdline_hint = true;
+ }
+ if (zt_file_cmdline.prio_min != ZLOG_DISABLED) {
+ vty_out(vty,
+ "! \"log file %s %s\" enabled by \"--log\" startup option\n",
+ zt_file_cmdline.filename,
+ zlog_priority[zt_file_cmdline.prio_min]);
+ show_cmdline_hint = true;
+ }
+ if (show_cmdline_hint)
+ vty_out(vty,
+ "! use \"clear log cmdline-targets\" to remove this target\n");
+
+ if (zlog_syslog_get_facility() != LOG_DAEMON)
+ vty_out(vty, "log facility %s\n",
+ facility_name(zlog_syslog_get_facility()));
+
+ if (zt_file.record_priority == 1)
+ vty_out(vty, "log record-priority\n");
+
+ if (zt_file.ts_subsec > 0)
+ vty_out(vty, "log timestamp precision %d\n",
+ zt_file.ts_subsec);
+}
+
+static int log_vty_init(const char *progname, const char *protoname,
+ unsigned short instance, uid_t uid, gid_t gid)
+{
+ zlog_progname = progname;
+ zlog_protoname = protoname;
+
+ zlog_filterfile_init(&zt_filterfile);
+
+ zlog_file_set_fd(&zt_stdout, STDOUT_FILENO);
+ return 0;
+}
+
+__attribute__((_CONSTRUCTOR(475))) static void log_vty_preinit(void)
+{
+ hook_register(zlog_init, log_vty_init);
+}
+
+void log_cmd_init(void)
+{
+ install_element(VIEW_NODE, &show_logging_cmd);
+ install_element(ENABLE_NODE, &clear_log_cmdline_cmd);
+
+ install_element(CONFIG_NODE, &config_log_stdout_cmd);
+ install_element(CONFIG_NODE, &no_config_log_stdout_cmd);
+ install_element(CONFIG_NODE, &config_log_monitor_cmd);
+ install_element(CONFIG_NODE, &no_config_log_monitor_cmd);
+ install_element(CONFIG_NODE, &config_log_file_cmd);
+ install_element(CONFIG_NODE, &no_config_log_file_cmd);
+ install_element(CONFIG_NODE, &config_log_syslog_cmd);
+ install_element(CONFIG_NODE, &no_config_log_syslog_cmd);
+ install_element(CONFIG_NODE, &config_log_facility_cmd);
+ install_element(CONFIG_NODE, &no_config_log_facility_cmd);
+ install_element(CONFIG_NODE, &config_log_record_priority_cmd);
+ install_element(CONFIG_NODE, &no_config_log_record_priority_cmd);
+ install_element(CONFIG_NODE, &config_log_timestamp_precision_cmd);
+ install_element(CONFIG_NODE, &no_config_log_timestamp_precision_cmd);
+
install_element(VIEW_NODE, &show_log_filter_cmd);
install_element(CONFIG_NODE, &log_filter_cmd);
install_element(CONFIG_NODE, &log_filter_clear_cmd);
+ install_element(CONFIG_NODE, &config_log_filterfile_cmd);
+ install_element(CONFIG_NODE, &no_config_log_filterfile_cmd);
}
diff --git a/lib/log_vty.h b/lib/log_vty.h
index fa5627e4bd..0fd60e9b07 100644
--- a/lib/log_vty.h
+++ b/lib/log_vty.h
@@ -20,5 +20,17 @@
#ifndef __LOG_VTY_H__
#define __LOG_VTY_H__
-extern void log_filter_cmd_init(void);
+
+#include "lib/hook.h"
+
+struct vty;
+
+extern void log_cmd_init(void);
+extern void log_config_write(struct vty *vty);
+extern int log_level_match(const char *s);
+extern void log_show_syslog(struct vty *vty);
+
+DECLARE_HOOK(zlog_rotate, (), ())
+extern void zlog_rotate(void);
+
#endif /* __LOG_VTY_H__ */
diff --git a/lib/md5.c b/lib/md5.c
index 9c003a3af6..5c93c7bc1f 100644
--- a/lib/md5.c
+++ b/lib/md5.c
@@ -429,7 +429,7 @@ void hmac_md5(unsigned char *text, int text_len, unsigned char *key,
* pass */
MD5Update(&context, k_ipad, 64); /* start with inner pad */
MD5Update(&context, text, text_len); /* then text of datagram */
- MD5Final((uint8_t *)digest, &context); /* finish up 1st pass */
+ MD5Final(digest, &context); /* finish up 1st pass */
/*
* perform outer MD5
*/
@@ -438,5 +438,5 @@ void hmac_md5(unsigned char *text, int text_len, unsigned char *key,
MD5Update(&context, k_opad, 64); /* start with outer pad */
MD5Update(&context, digest, 16); /* then results of 1st
* hash */
- MD5Final((uint8_t *)digest, &context); /* finish up 2nd pass */
+ MD5Final(digest, &context); /* finish up 2nd pass */
}
diff --git a/lib/memory.c b/lib/memory.c
index 149e294d50..3a29404827 100644
--- a/lib/memory.c
+++ b/lib/memory.c
@@ -163,7 +163,8 @@ static int qmem_exit_walker(void *arg, struct memgroup *mg, struct memtype *mt)
} else if (mt->n_alloc) {
char size[32];
- eda->error++;
+ if (!mg->active_at_exit)
+ eda->error++;
snprintf(size, sizeof(size), "%10zu", mt->size);
fprintf(eda->fp, "%s: memstats: %-30s: %6zu * %s\n",
eda->prefix, mt->name, mt->n_alloc,
diff --git a/lib/memory.h b/lib/memory.h
index e4e05faa4f..13f2f9b11a 100644
--- a/lib/memory.h
+++ b/lib/memory.h
@@ -17,6 +17,7 @@
#ifndef _QUAGGA_MEMORY_H
#define _QUAGGA_MEMORY_H
+#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <frratomic.h>
@@ -48,6 +49,8 @@ struct memgroup {
struct memgroup *next, **ref;
struct memtype *types, **insert;
const char *name;
+ /* ignore group on dumping memleaks at exit */
+ bool active_at_exit;
};
/* macro usage:
@@ -76,7 +79,7 @@ struct memgroup {
*/
#define DECLARE_MGROUP(name) extern struct memgroup _mg_##name;
-#define DEFINE_MGROUP(mname, desc) \
+#define _DEFINE_MGROUP(mname, desc, ...) \
struct memgroup _mg_##mname \
__attribute__((section(".data.mgroups"))) = { \
.name = desc, \
@@ -84,6 +87,7 @@ struct memgroup {
.next = NULL, \
.insert = NULL, \
.ref = NULL, \
+ __VA_ARGS__ \
}; \
static void _mginit_##mname(void) __attribute__((_CONSTRUCTOR(1000))); \
static void _mginit_##mname(void) \
@@ -99,7 +103,13 @@ struct memgroup {
if (_mg_##mname.next) \
_mg_##mname.next->ref = _mg_##mname.ref; \
*_mg_##mname.ref = _mg_##mname.next; \
- }
+ } \
+ /* end */
+
+#define DEFINE_MGROUP(mname, desc) \
+ _DEFINE_MGROUP(mname, desc, )
+#define DEFINE_MGROUP_ACTIVEATEXIT(mname, desc) \
+ _DEFINE_MGROUP(mname, desc, .active_at_exit = true)
#define DECLARE_MTYPE(name) \
extern struct memtype MTYPE_##name[1]; \
diff --git a/lib/mpls.c b/lib/mpls.c
index 759fe1206d..ac5792a686 100644
--- a/lib/mpls.c
+++ b/lib/mpls.c
@@ -79,7 +79,7 @@ int mpls_str2label(const char *label_str, uint8_t *num_labels,
/*
* Label to string conversion, labels in string separated by '/'.
*/
-char *mpls_label2str(uint8_t num_labels, mpls_label_t *labels, char *buf,
+char *mpls_label2str(uint8_t num_labels, const mpls_label_t *labels, char *buf,
int len, int pretty)
{
char label_buf[BUFSIZ];
diff --git a/lib/mpls.h b/lib/mpls.h
index 635ecc77a1..05cf2935e8 100644
--- a/lib/mpls.h
+++ b/lib/mpls.h
@@ -209,10 +209,13 @@ static inline char *label2str(mpls_label_t label, char *buf, size_t len)
int mpls_str2label(const char *label_str, uint8_t *num_labels,
mpls_label_t *labels);
+/* Generic string buffer for label-stack-to-str */
+#define MPLS_LABEL_STRLEN 1024
+
/*
* Label to string conversion, labels in string separated by '/'.
*/
-char *mpls_label2str(uint8_t num_labels, mpls_label_t *labels, char *buf,
+char *mpls_label2str(uint8_t num_labels, const mpls_label_t *labels, char *buf,
int len, int pretty);
#ifdef __cplusplus
diff --git a/lib/network.c b/lib/network.c
index 411661a5e1..d2482bd55e 100644
--- a/lib/network.c
+++ b/lib/network.c
@@ -121,3 +121,21 @@ 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 a00c5a0a65..83c9e59e76 100644
--- a/lib/network.h
+++ b/lib/network.h
@@ -45,6 +45,8 @@ extern int set_cloexec(int fd);
extern float htonf(float);
extern float ntohf(float);
+extern long frr_weak_random(void);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/nexthop.c b/lib/nexthop.c
index c3be0a71e6..0d239e091b 100644
--- a/lib/nexthop.c
+++ b/lib/nexthop.c
@@ -23,11 +23,9 @@
#include "table.h"
#include "memory.h"
#include "command.h"
-#include "if.h"
#include "log.h"
#include "sockunion.h"
#include "linklist.h"
-#include "thread.h"
#include "prefix.h"
#include "nexthop.h"
#include "mpls.h"
@@ -155,7 +153,24 @@ static int _nexthop_cmp_no_labels(const struct nexthop *next1,
}
ret = _nexthop_source_cmp(next1, next2);
+ if (ret != 0)
+ goto done;
+
+ if (!CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) &&
+ CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP))
+ return -1;
+
+ if (CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) &&
+ !CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP))
+ return 1;
+
+ if (next1->backup_idx < next2->backup_idx)
+ return -1;
+
+ if (next1->backup_idx > next2->backup_idx)
+ return 1;
+done:
return ret;
}
@@ -240,7 +255,7 @@ struct nexthop *nexthop_new(void)
* The linux kernel does some weird stuff with adding +1 to
* all nexthop weights it gets over netlink.
* To handle this, just default everything to 1 right from
- * from the beggining so we don't have to special case
+ * from the beginning so we don't have to special case
* default weights in the linux netlink code.
*
* 1 should be a valid on all platforms anyway.
@@ -393,8 +408,8 @@ struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type)
}
/* Update nexthop with label information. */
-void nexthop_add_labels(struct nexthop *nexthop, enum lsp_types_t type,
- uint8_t num_labels, mpls_label_t *label)
+void nexthop_add_labels(struct nexthop *nexthop, enum lsp_types_t ltype,
+ uint8_t num_labels, const mpls_label_t *labels)
{
struct mpls_label_stack *nh_label;
int i;
@@ -402,13 +417,18 @@ void nexthop_add_labels(struct nexthop *nexthop, enum lsp_types_t type,
if (num_labels == 0)
return;
- nexthop->nh_label_type = type;
+ /* Enforce limit on label stack size */
+ if (num_labels > MPLS_MAX_LABELS)
+ num_labels = MPLS_MAX_LABELS;
+
+ nexthop->nh_label_type = ltype;
+
nh_label = XCALLOC(MTYPE_NH_LABEL,
sizeof(struct mpls_label_stack)
+ num_labels * sizeof(mpls_label_t));
nh_label->num_labels = num_labels;
for (i = 0; i < num_labels; i++)
- nh_label->label[i] = *(label + i);
+ nh_label->label[i] = *(labels + i);
nexthop->nh_label = nh_label;
}
@@ -503,6 +523,7 @@ unsigned int nexthop_level(struct nexthop *nexthop)
uint32_t nexthop_hash_quick(const struct nexthop *nexthop)
{
uint32_t key = 0x45afe398;
+ uint32_t val;
key = jhash_3words(nexthop->type, nexthop->vrf_id,
nexthop->nh_label_type, key);
@@ -532,8 +553,12 @@ uint32_t nexthop_hash_quick(const struct nexthop *nexthop)
key = jhash_1word(nexthop->nh_label->label[i], key);
}
- key = jhash_2words(nexthop->ifindex,
- CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK),
+ val = 0;
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP))
+ val = (uint32_t)nexthop->backup_idx;
+
+ key = jhash_3words(nexthop->ifindex,
+ CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK), val,
key);
return key;
@@ -573,6 +598,7 @@ void nexthop_copy_no_recurse(struct nexthop *copy,
copy->type = nexthop->type;
copy->flags = nexthop->flags;
copy->weight = nexthop->weight;
+ copy->backup_idx = nexthop->backup_idx;
memcpy(&copy->gate, &nexthop->gate, sizeof(nexthop->gate));
memcpy(&copy->src, &nexthop->src, sizeof(nexthop->src));
memcpy(&copy->rmap_src, &nexthop->rmap_src, sizeof(nexthop->rmap_src));
diff --git a/lib/nexthop.h b/lib/nexthop.h
index 267f9f28ad..9b71262589 100644
--- a/lib/nexthop.h
+++ b/lib/nexthop.h
@@ -25,6 +25,7 @@
#include "prefix.h"
#include "mpls.h"
+#include "vxlan.h"
#ifdef __cplusplus
extern "C" {
@@ -60,6 +61,10 @@ enum blackhole_type {
? (type) \
: ((type) | 1)
+enum nh_encap_type {
+ NET_VXLAN = 100, /* value copied from FPM_NH_ENCAP_VXLAN. */
+};
+
/* Nexthop structure. */
struct nexthop {
struct nexthop *next;
@@ -86,6 +91,8 @@ struct nexthop {
* active one
*/
#define NEXTHOP_FLAG_RNH_FILTERED (1 << 5) /* rmap filtered, used by rnh */
+#define NEXTHOP_FLAG_HAS_BACKUP (1 << 6) /* Backup nexthop index is set */
+
#define NEXTHOP_IS_ACTIVE(flags) \
(CHECK_FLAG(flags, NEXTHOP_FLAG_ACTIVE) \
&& !CHECK_FLAG(flags, NEXTHOP_FLAG_DUPLICATE))
@@ -116,15 +123,37 @@ struct nexthop {
/* Weight of the nexthop ( for unequal cost ECMP ) */
uint8_t weight;
+
+ /* Index of a corresponding backup nexthop in a backup list;
+ * only meaningful if the HAS_BACKUP flag is set.
+ */
+ uint8_t backup_idx;
+
+ /* Encapsulation information. */
+ enum nh_encap_type nh_encap_type;
+ union {
+ vni_t vni;
+ } nh_encap;
};
+/* Backup index value is limited */
+#define NEXTHOP_BACKUP_IDX_MAX 255
+
+/* Utility to append one nexthop to another. */
+#define NEXTHOP_APPEND(to, new) \
+ do { \
+ (to)->next = (new); \
+ (new)->prev = (to); \
+ (new)->next = NULL; \
+ } while (0)
+
struct nexthop *nexthop_new(void);
void nexthop_free(struct nexthop *nexthop);
void nexthops_free(struct nexthop *nexthop);
-void nexthop_add_labels(struct nexthop *, enum lsp_types_t, uint8_t,
- mpls_label_t *);
+void nexthop_add_labels(struct nexthop *nexthop, enum lsp_types_t ltype,
+ uint8_t num_labels, const mpls_label_t *labels);
void nexthop_del_labels(struct nexthop *);
/*
diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c
index d660428bcd..c23c57d2e1 100644
--- a/lib/nexthop_group.c
+++ b/lib/nexthop_group.c
@@ -43,8 +43,12 @@ struct nexthop_hold {
char *intf;
char *labels;
uint32_t weight;
+ int backup_idx; /* Index of backup nexthop, if >= 0 */
};
+/* Invalid/unset value for nexthop_hold's backup_idx */
+#define NHH_BACKUP_IDX_INVALID -1
+
struct nexthop_group_hooks {
void (*new)(const char *name);
void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
@@ -143,6 +147,59 @@ struct nexthop *nexthop_exists(const struct nexthop_group *nhg,
return NULL;
}
+/*
+ * Helper that locates a nexthop in an nhg config list. Note that
+ * this uses a specific matching / equality rule that's different from
+ * the complete match performed by 'nexthop_same()'.
+ */
+static struct nexthop *nhg_nh_find(const struct nexthop_group *nhg,
+ const struct nexthop *nh)
+{
+ struct nexthop *nexthop;
+ int ret;
+
+ /* We compare: vrf, gateway, and interface */
+
+ for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) {
+
+ /* Compare vrf and type */
+ if (nexthop->vrf_id != nh->vrf_id)
+ continue;
+ if (nexthop->type != nh->type)
+ continue;
+
+ /* Compare gateway */
+ switch (nexthop->type) {
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV6:
+ ret = nexthop_g_addr_cmp(nexthop->type,
+ &nexthop->gate, &nh->gate);
+ if (ret != 0)
+ continue;
+ break;
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ ret = nexthop_g_addr_cmp(nexthop->type,
+ &nexthop->gate, &nh->gate);
+ if (ret != 0)
+ continue;
+ /* Intentional Fall-Through */
+ case NEXTHOP_TYPE_IFINDEX:
+ if (nexthop->ifindex != nh->ifindex)
+ continue;
+ break;
+ case NEXTHOP_TYPE_BLACKHOLE:
+ if (nexthop->bh_type != nh->bh_type)
+ continue;
+ break;
+ }
+
+ return nexthop;
+ }
+
+ return NULL;
+}
+
static bool
nexthop_group_equal_common(const struct nexthop_group *nhg1,
const struct nexthop_group *nhg2,
@@ -225,6 +282,10 @@ void nexthop_group_copy(struct nexthop_group *to,
void nexthop_group_delete(struct nexthop_group **nhg)
{
+ /* OK to call with NULL group */
+ if ((*nhg) == NULL)
+ return;
+
if ((*nhg)->nexthop)
nexthops_free((*nhg)->nexthop);
@@ -322,6 +383,25 @@ void _nexthop_del(struct nexthop_group *nhg, struct nexthop *nh)
nh->next = NULL;
}
+/* Unlink a nexthop from the list it's on, unconditionally */
+static void nexthop_unlink(struct nexthop_group *nhg, struct nexthop *nexthop)
+{
+
+ if (nexthop->prev)
+ nexthop->prev->next = nexthop->next;
+ else {
+ assert(nhg->nexthop == nexthop);
+ assert(nexthop->prev == NULL);
+ nhg->nexthop = nexthop->next;
+ }
+
+ if (nexthop->next)
+ nexthop->next->prev = nexthop->prev;
+
+ nexthop->prev = NULL;
+ nexthop->next = NULL;
+}
+
/*
* Copy a list of nexthops in 'nh' to an nhg, enforcing canonical sort order
*/
@@ -567,11 +647,36 @@ DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NHGNAME",
return CMD_SUCCESS;
}
+DEFPY(nexthop_group_backup, nexthop_group_backup_cmd,
+ "backup-group WORD$name",
+ "Specify a group name containing backup nexthops\n"
+ "The name of the backup group\n")
+{
+ VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
+
+ strlcpy(nhgc->backup_list_name, name, sizeof(nhgc->backup_list_name));
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(no_nexthop_group_backup, no_nexthop_group_backup_cmd,
+ "no backup-group [WORD$name]",
+ NO_STR
+ "Clear group name containing backup nexthops\n"
+ "The name of the backup group\n")
+{
+ VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
+
+ nhgc->backup_list_name[0] = 0;
+
+ return CMD_SUCCESS;
+}
+
static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
const char *nhvrf_name,
const union sockunion *addr,
const char *intf, const char *labels,
- const uint32_t weight)
+ const uint32_t weight, int backup_idx)
{
struct nexthop_hold *nh;
@@ -588,14 +693,22 @@ static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
nh->weight = weight;
+ nh->backup_idx = backup_idx;
+
listnode_add_sort(nhgc->nhg_list, nh);
}
+/*
+ * Remove config info about a nexthop from group 'nhgc'. Note that we
+ * use only a subset of the available attributes here to determine
+ * a 'match'.
+ * Note that this doesn't change the list of nexthops, only the config
+ * information.
+ */
static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc,
const char *nhvrf_name,
const union sockunion *addr,
- const char *intf, const char *labels,
- const uint32_t weight)
+ const char *intf)
{
struct nexthop_hold *nh;
struct listnode *node;
@@ -603,9 +716,7 @@ static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc,
for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0
&& nhgc_addr_cmp_helper(addr, nh->addr) == 0
- && nhgc_cmp_helper(intf, nh->intf) == 0
- && nhgc_cmp_helper(labels, nh->labels) == 0
- && weight == nh->weight)
+ && nhgc_cmp_helper(intf, nh->intf) == 0)
break;
}
@@ -629,7 +740,7 @@ static bool nexthop_group_parse_nexthop(struct nexthop *nhop,
const union sockunion *addr,
const char *intf, const char *name,
const char *labels, int *lbl_ret,
- uint32_t weight)
+ uint32_t weight, int backup_idx)
{
int ret = 0;
struct vrf *vrf;
@@ -688,6 +799,15 @@ static bool nexthop_group_parse_nexthop(struct nexthop *nhop,
nhop->weight = weight;
+ if (backup_idx != NHH_BACKUP_IDX_INVALID) {
+ /* Validate index value */
+ if (backup_idx > NEXTHOP_BACKUP_IDX_MAX)
+ return false;
+
+ SET_FLAG(nhop->flags, NEXTHOP_FLAG_HAS_BACKUP);
+ nhop->backup_idx = backup_idx;
+ }
+
return true;
}
@@ -699,7 +819,7 @@ static bool nexthop_group_parse_nhh(struct nexthop *nhop,
{
return (nexthop_group_parse_nexthop(nhop, nhh->addr, nhh->intf,
nhh->nhvrf_name, nhh->labels, NULL,
- nhh->weight));
+ nhh->weight, nhh->backup_idx));
}
DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
@@ -712,6 +832,7 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
nexthop-vrf NAME$vrf_name \
|label WORD \
|weight (1-255) \
+ |backup-idx$bi_str (0-254)$idx \
}]",
NO_STR
"Specify one of the nexthops in this ECMP group\n"
@@ -724,16 +845,23 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
"Specify label(s) for this nexthop\n"
"One or more labels in the range (16-1048575) separated by '/'\n"
"Weight to be used by the nexthop for purposes of ECMP\n"
- "Weight value to be used\n")
+ "Weight value to be used\n"
+ "Backup nexthop index in another group\n"
+ "Nexthop index value\n")
{
VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
struct nexthop nhop;
struct nexthop *nh;
int lbl_ret = 0;
bool legal;
+ int backup_idx = idx;
+ bool yes = !no;
+
+ if (bi_str == NULL)
+ backup_idx = NHH_BACKUP_IDX_INVALID;
legal = nexthop_group_parse_nexthop(&nhop, addr, intf, vrf_name, label,
- &lbl_ret, weight);
+ &lbl_ret, weight, backup_idx);
if (nhop.type == NEXTHOP_TYPE_IPV6
&& IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
@@ -763,21 +891,30 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
return CMD_WARNING_CONFIG_FAILED;
}
- nh = nexthop_exists(&nhgc->nhg, &nhop);
+ /* Look for an existing nexthop in the config. Note that the test
+ * here tests only some attributes - it's not a complete comparison.
+ * Note that we've got two kinds of objects to manage: 'nexthop_hold'
+ * that represent config that may or may not be valid (yet), and
+ * actual nexthops that have been validated and parsed.
+ */
+ nh = nhg_nh_find(&nhgc->nhg, &nhop);
- if (no) {
- nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf, label,
- weight);
- if (nh) {
- _nexthop_del(&nhgc->nhg, nh);
+ /* Always attempt to remove old config info. */
+ nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf);
- if (nhg_hooks.del_nexthop)
- nhg_hooks.del_nexthop(nhgc, nh);
+ /* Remove any existing nexthop, for delete and replace cases. */
+ if (nh) {
+ nexthop_unlink(&nhgc->nhg, nh);
- nexthop_free(nh);
- }
- } else if (!nh) {
- /* must be adding new nexthop since !no and !nexthop_exists */
+ if (nhg_hooks.del_nexthop)
+ nhg_hooks.del_nexthop(nhgc, nh);
+
+ nexthop_free(nh);
+ }
+ if (yes) {
+ /* Add/replace case: capture nexthop if valid, and capture
+ * config info always.
+ */
if (legal) {
nh = nexthop_new();
@@ -785,8 +922,9 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
_nexthop_add(&nhgc->nhg.nexthop, nh);
}
+ /* Save config always */
nexthop_group_save_nhop(nhgc, vrf_name, addr, intf, label,
- weight);
+ weight, backup_idx);
if (legal && nhg_hooks.add_nexthop)
nhg_hooks.add_nexthop(nhgc, nh);
@@ -795,10 +933,13 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
return CMD_SUCCESS;
}
+static int nexthop_group_write(struct vty *vty);
static struct cmd_node nexthop_group_node = {
- NH_GROUP_NODE,
- "%s(config-nh-group)# ",
- 1
+ .name = "nexthop-group",
+ .node = NH_GROUP_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-nh-group)# ",
+ .config_write = nexthop_group_write,
};
void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh)
@@ -849,6 +990,9 @@ void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh)
if (nh->weight)
vty_out(vty, " weight %u", nh->weight);
+ if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP))
+ vty_out(vty, " backup-idx %d", nh->backup_idx);
+
vty_out(vty, "\n");
}
@@ -874,6 +1018,9 @@ static void nexthop_group_write_nexthop_internal(struct vty *vty,
if (nh->weight)
vty_out(vty, " weight %u", nh->weight);
+ if (nh->backup_idx != NHH_BACKUP_IDX_INVALID)
+ vty_out(vty, " backup-idx %d", nh->backup_idx);
+
vty_out(vty, "\n");
}
@@ -887,6 +1034,10 @@ static int nexthop_group_write(struct vty *vty)
vty_out(vty, "nexthop-group %s\n", nhgc->name);
+ if (nhgc->backup_list_name[0])
+ vty_out(vty, " backup-group %s\n",
+ nhgc->backup_list_name);
+
for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
vty_out(vty, " ");
nexthop_group_write_nexthop_internal(vty, nh);
@@ -1062,11 +1213,13 @@ void nexthop_group_init(void (*new)(const char *name),
cmd_variable_handler_register(nhg_name_handlers);
- install_node(&nexthop_group_node, nexthop_group_write);
+ install_node(&nexthop_group_node);
install_element(CONFIG_NODE, &nexthop_group_cmd);
install_element(CONFIG_NODE, &no_nexthop_group_cmd);
install_default(NH_GROUP_NODE);
+ install_element(NH_GROUP_NODE, &nexthop_group_backup_cmd);
+ install_element(NH_GROUP_NODE, &no_nexthop_group_backup_cmd);
install_element(NH_GROUP_NODE, &ecmp_nexthops_cmd);
memset(&nhg_hooks, 0, sizeof(nhg_hooks));
diff --git a/lib/nexthop_group.h b/lib/nexthop_group.h
index f99a53f694..3a5a1299c1 100644
--- a/lib/nexthop_group.h
+++ b/lib/nexthop_group.h
@@ -57,6 +57,8 @@ void copy_nexthops(struct nexthop **tnh, const struct nexthop *nh,
uint32_t nexthop_group_hash_no_recurse(const struct nexthop_group *nhg);
uint32_t nexthop_group_hash(const struct nexthop_group *nhg);
void nexthop_group_mark_duplicates(struct nexthop_group *nhg);
+
+/* Add a nexthop to a list, enforcing the canonical sort order. */
void nexthop_group_add_sorted(struct nexthop_group *nhg,
struct nexthop *nexthop);
@@ -79,11 +81,16 @@ void nexthop_group_add_sorted(struct nexthop_group *nhg,
(nhop) = nexthop_next(nhop)
+#define NHGC_NAME_SIZE 80
+
struct nexthop_group_cmd {
RB_ENTRY(nexthop_group_cmd) nhgc_entry;
- char name[80];
+ char name[NHGC_NAME_SIZE];
+
+ /* Name of group containing backup nexthops (if set) */
+ char backup_list_name[NHGC_NAME_SIZE];
struct nexthop_group nhg;
diff --git a/lib/northbound.c b/lib/northbound.c
index cebedcff09..85e723d7cf 100644
--- a/lib/northbound.c
+++ b/lib/northbound.c
@@ -1866,6 +1866,13 @@ static void nb_load_callbacks(const struct frr_yang_module_info *module)
struct nb_node *nb_node;
uint32_t priority;
+ if (i > YANG_MODULE_MAX_NODES) {
+ zlog_err(
+ "%s: %s.yang has more than %u nodes. Please increase YANG_MODULE_MAX_NODES to fix this problem.",
+ __func__, module->name, YANG_MODULE_MAX_NODES);
+ exit(1);
+ }
+
nb_node = nb_node_find(module->nodes[i].xpath);
if (!nb_node) {
flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
diff --git a/lib/northbound.h b/lib/northbound.h
index 76a11e518c..19a2ba0865 100644
--- a/lib/northbound.h
+++ b/lib/northbound.h
@@ -403,6 +403,13 @@ struct nb_node {
/* The YANG list doesn't contain key leafs. */
#define F_NB_NODE_KEYLESS_LIST 0x02
+/*
+ * HACK: old gcc versions (< 5.x) have a bug that prevents C99 flexible arrays
+ * from working properly on shared libraries. For those compilers, use a fixed
+ * size array to work around the problem.
+ */
+#define YANG_MODULE_MAX_NODES 1024
+
struct frr_yang_module_info {
/* YANG module name. */
const char *name;
@@ -417,7 +424,11 @@ struct frr_yang_module_info {
/* Priority - lower priorities are processed first. */
uint32_t priority;
+#if defined(__GNUC__) && ((__GNUC__ - 0) < 5) && !defined(__clang__)
+ } nodes[YANG_MODULE_MAX_NODES + 1];
+#else
} nodes[];
+#endif
};
/* Northbound error codes. */
diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c
index 17dc256281..d4467facaf 100644
--- a/lib/northbound_cli.c
+++ b/lib/northbound_cli.c
@@ -1674,7 +1674,12 @@ static int nb_debug_config_write(struct vty *vty)
}
static struct debug_callbacks nb_dbg_cbs = {.debug_set_all = nb_debug_set_all};
-static struct cmd_node nb_debug_node = {NORTHBOUND_DEBUG_NODE, "", 1};
+static struct cmd_node nb_debug_node = {
+ .name = "northbound debug",
+ .node = NORTHBOUND_DEBUG_NODE,
+ .prompt = "",
+ .config_write = nb_debug_config_write,
+};
void nb_cli_install_default(int node)
{
@@ -1738,7 +1743,7 @@ void nb_cli_init(struct thread_master *tm)
debug_init(&nb_dbg_cbs);
- install_node(&nb_debug_node, nb_debug_config_write);
+ install_node(&nb_debug_node);
install_element(ENABLE_NODE, &debug_nb_cmd);
install_element(CONFIG_NODE, &debug_nb_cmd);
diff --git a/lib/northbound_grpc.cpp b/lib/northbound_grpc.cpp
index 089899368d..b195f1aeca 100644
--- a/lib/northbound_grpc.cpp
+++ b/lib/northbound_grpc.cpp
@@ -884,7 +884,14 @@ static int frr_grpc_finish(void)
return 0;
}
-static int frr_grpc_module_late_init(struct thread_master *tm)
+/*
+ * This is done this way because module_init and module_late_init are both
+ * called during daemon pre-fork initialization. Because the GRPC library
+ * spawns threads internally, we need to delay initializing it until after
+ * fork. This is done by scheduling this init function as an event task, since
+ * the event loop doesn't run until after fork.
+ */
+static int frr_grpc_module_very_late_init(struct thread *thread)
{
static unsigned long port = GRPC_DEFAULT_PORT;
const char *args = THIS_MODULE->load_args;
@@ -910,15 +917,19 @@ static int frr_grpc_module_late_init(struct thread_master *tm)
if (frr_grpc_init(&port) < 0)
goto error;
- hook_register(frr_fini, frr_grpc_finish);
-
- return 0;
-
error:
flog_err(EC_LIB_GRPC_INIT, "failed to initialize the gRPC module");
return -1;
}
+static int frr_grpc_module_late_init(struct thread_master *tm)
+{
+ thread_add_event(tm, frr_grpc_module_very_late_init, NULL, 0, NULL);
+ hook_register(frr_fini, frr_grpc_finish);
+
+ return 0;
+}
+
static int frr_grpc_module_init(void)
{
hook_register(frr_late_init, frr_grpc_module_late_init);
diff --git a/lib/plist.c b/lib/plist.c
index b7a020c6f7..67a91e2a0d 100644
--- a/lib/plist.c
+++ b/lib/plist.c
@@ -348,14 +348,14 @@ static void prefix_list_delete(struct prefix_list *plist)
static struct prefix_list_entry *
prefix_list_entry_make(struct prefix *prefix, enum prefix_list_type type,
- int64_t seq, int le, int ge, int any)
+ int64_t seq, int le, int ge, bool any)
{
struct prefix_list_entry *pentry;
pentry = prefix_list_entry_new();
if (any)
- pentry->any = 1;
+ pentry->any = true;
prefix_copy(&pentry->prefix, prefix);
pentry->type = type;
@@ -851,7 +851,7 @@ static int vty_prefix_list_install(struct vty *vty, afi_t afi, const char *name,
struct prefix_list_entry *pentry;
struct prefix_list_entry *dup;
struct prefix p, p_tmp;
- int any = 0;
+ bool any = false;
int64_t seqnum = -1;
int lenum = 0;
int genum = 0;
@@ -889,7 +889,7 @@ static int vty_prefix_list_install(struct vty *vty, afi_t afi, const char *name,
(struct prefix_ipv4 *)&p);
genum = 0;
lenum = IPV4_MAX_BITLEN;
- any = 1;
+ any = true;
} else
ret = str2prefix_ipv4(prefix, (struct prefix_ipv4 *)&p);
@@ -908,7 +908,7 @@ static int vty_prefix_list_install(struct vty *vty, afi_t afi, const char *name,
ret = str2prefix_ipv6("::/0", (struct prefix_ipv6 *)&p);
genum = 0;
lenum = IPV6_MAX_BITLEN;
- any = 1;
+ any = true;
} else
ret = str2prefix_ipv6(prefix, (struct prefix_ipv6 *)&p);
@@ -1898,7 +1898,7 @@ int prefix_bgp_orf_set(char *name, afi_t afi, struct orf_prefix *orfp,
if (set) {
pentry = prefix_list_entry_make(
&orfp->p, (permit ? PREFIX_PERMIT : PREFIX_DENY),
- orfp->seq, orfp->le, orfp->ge, 0);
+ orfp->seq, orfp->le, orfp->ge, false);
if (prefix_entry_dup_check(plist, pentry)) {
prefix_list_entry_free(pentry);
@@ -2044,10 +2044,14 @@ static void prefix_list_reset_afi(afi_t afi, int orf)
}
+static int config_write_prefix_ipv4(struct vty *vty);
/* Prefix-list node. */
-static struct cmd_node prefix_node = {PREFIX_NODE,
- "", /* Prefix list has no interface. */
- 1};
+static struct cmd_node prefix_node = {
+ .name = "ipv4 prefix list",
+ .node = PREFIX_NODE,
+ .prompt = "",
+ .config_write = config_write_prefix_ipv4,
+};
static int config_write_prefix_ipv4(struct vty *vty)
{
@@ -2085,7 +2089,7 @@ static const struct cmd_variable_handler plist_var_handlers[] = {
static void prefix_list_init_ipv4(void)
{
- install_node(&prefix_node, config_write_prefix_ipv4);
+ install_node(&prefix_node);
install_element(CONFIG_NODE, &ip_prefix_list_cmd);
install_element(CONFIG_NODE, &no_ip_prefix_list_cmd);
@@ -2107,10 +2111,14 @@ static void prefix_list_init_ipv4(void)
install_element(ENABLE_NODE, &clear_ip_prefix_list_cmd);
}
+static int config_write_prefix_ipv6(struct vty *vty);
/* Prefix-list node. */
static struct cmd_node prefix_ipv6_node = {
- PREFIX_IPV6_NODE, "", /* Prefix list has no interface. */
- 1};
+ .name = "ipv6 prefix list",
+ .node = PREFIX_IPV6_NODE,
+ .prompt = "",
+ .config_write = config_write_prefix_ipv6,
+};
static int config_write_prefix_ipv6(struct vty *vty)
{
@@ -2119,7 +2127,7 @@ static int config_write_prefix_ipv6(struct vty *vty)
static void prefix_list_init_ipv6(void)
{
- install_node(&prefix_ipv6_node, config_write_prefix_ipv6);
+ install_node(&prefix_ipv6_node);
install_element(CONFIG_NODE, &ipv6_prefix_list_cmd);
install_element(CONFIG_NODE, &no_ipv6_prefix_list_cmd);
diff --git a/lib/plist_int.h b/lib/plist_int.h
index 443b0c614d..ec8bbe1315 100644
--- a/lib/plist_int.h
+++ b/lib/plist_int.h
@@ -59,7 +59,7 @@ struct prefix_list_entry {
enum prefix_list_type type;
- int any;
+ bool any;
struct prefix prefix;
unsigned long refcnt;
diff --git a/lib/qobj.c b/lib/qobj.c
index 1e48b541dc..cb3254cbe9 100644
--- a/lib/qobj.c
+++ b/lib/qobj.c
@@ -26,6 +26,7 @@
#include "log.h"
#include "qobj.h"
#include "jhash.h"
+#include "network.h"
static uint32_t qobj_hash(const struct qobj_node *node)
{
@@ -53,8 +54,8 @@ void qobj_reg(struct qobj_node *node, const struct qobj_nodetype *type)
node->type = type;
pthread_rwlock_wrlock(&nodes_lock);
do {
- node->nid = (uint64_t)random();
- node->nid ^= (uint64_t)random() << 32;
+ node->nid = (uint64_t)frr_weak_random();
+ node->nid ^= (uint64_t)frr_weak_random() << 32;
} while (!node->nid || qobj_nodes_find(&nodes, node));
qobj_nodes_add(&nodes, node);
pthread_rwlock_unlock(&nodes_lock);
diff --git a/lib/resolver.c b/lib/resolver.c
index 1be47bd6e1..e5caadb2d0 100644
--- a/lib/resolver.c
+++ b/lib/resolver.c
@@ -245,7 +245,13 @@ DEFUN(debug_resolver,
return CMD_SUCCESS;
}
-static struct cmd_node resolver_debug_node = {RESOLVER_DEBUG_NODE, "", 1};
+static int resolver_config_write_debug(struct vty *vty);
+static struct cmd_node resolver_debug_node = {
+ .name = "resolver debug",
+ .node = RESOLVER_DEBUG_NODE,
+ .prompt = "",
+ .config_write = resolver_config_write_debug,
+};
static int resolver_config_write_debug(struct vty *vty)
{
@@ -274,7 +280,7 @@ void resolver_init(struct thread_master *tm)
ARES_OPT_SOCK_STATE_CB | ARES_OPT_TIMEOUT
| ARES_OPT_TRIES);
- install_node(&resolver_debug_node, resolver_config_write_debug);
+ install_node(&resolver_debug_node);
install_element(CONFIG_NODE, &debug_resolver_cmd);
install_element(ENABLE_NODE, &debug_resolver_cmd);
}
diff --git a/lib/routemap.c b/lib/routemap.c
index e2baa36f24..210512212d 100644
--- a/lib/routemap.c
+++ b/lib/routemap.c
@@ -3015,7 +3015,13 @@ DEFUN (no_debug_rmap,
}
/* Debug node. */
-static struct cmd_node rmap_debug_node = {RMAP_DEBUG_NODE, "", 1};
+static int rmap_config_write_debug(struct vty *vty);
+static struct cmd_node rmap_debug_node = {
+ .name = "route-map debug",
+ .node = RMAP_DEBUG_NODE,
+ .prompt = "",
+ .config_write = rmap_config_write_debug,
+};
/* Configuration write function. */
static int rmap_config_write_debug(struct vty *vty)
@@ -3242,7 +3248,7 @@ void route_map_init(void)
route_map_cli_init();
/* Install route map top node. */
- install_node(&rmap_debug_node, rmap_config_write_debug);
+ install_node(&rmap_debug_node);
/* Install route map commands. */
install_element(CONFIG_NODE, &debug_rmap_cmd);
diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c
index 5b03b5266f..2c45f09751 100644
--- a/lib/routemap_cli.c
+++ b/lib/routemap_cli.c
@@ -148,6 +148,12 @@ void route_map_instance_show(struct vty *vty, struct lyd_node *dnode,
SKIP_RULE("ipv6 next-hop type");
SKIP_RULE("metric");
SKIP_RULE("tag");
+ /* Zebra specific match conditions. */
+ SKIP_RULE("ip address prefix-len");
+ SKIP_RULE("ipv6 address prefix-len");
+ SKIP_RULE("ip next-hop prefix-len");
+ SKIP_RULE("source-protocol");
+ SKIP_RULE("source-instance");
vty_out(vty, " match %s %s\n", rmr->cmd->str,
rmr->rule_str ? rmr->rule_str : "");
@@ -158,6 +164,8 @@ void route_map_instance_show(struct vty *vty, struct lyd_node *dnode,
/* Skip all sets implemented by northbound. */
SKIP_RULE("metric");
SKIP_RULE("tag");
+ /* Zebra specific set actions. */
+ SKIP_RULE("src");
vty_out(vty, " set %s %s\n", rmr->cmd->str,
rmr->rule_str ? rmr->rule_str : "");
@@ -666,8 +674,25 @@ void route_map_condition_show(struct vty *vty, struct lyd_node *dnode,
vty_out(vty, " match tag %s\n",
yang_dnode_get_string(dnode, "./tag"));
break;
- case 100:
- /* NOTHING: custom field, should be handled by daemon. */
+ case 100: /* ipv4-prefix-length */
+ vty_out(vty, " match ip address prefix-len %s\n",
+ yang_dnode_get_string(dnode,"./frr-zebra:ipv4-prefix-length"));
+ break;
+ case 101: /* ipv6-prefix-length */
+ vty_out(vty, " match ipv6 address prefix-len %s\n",
+ yang_dnode_get_string(dnode, "./frr-zebra:ipv6-prefix-length"));
+ break;
+ case 102: /* ipv4-next-hop-prefix-length */
+ vty_out(vty, " match ip next-hop prefix-len %s\n",
+ yang_dnode_get_string(dnode, "./frr-zebra:ipv4-prefix-length"));
+ break;
+ case 103: /* source-protocol */
+ vty_out(vty, " match source-protocol %s\n",
+ yang_dnode_get_string(dnode, "./frr-zebra:source-protocol"));
+ break;
+ case 104: /* source-instance */
+ vty_out(vty, " match source-instance %s\n",
+ yang_dnode_get_string(dnode, "./frr-zebra:source-instance"));
break;
}
}
@@ -868,8 +893,13 @@ void route_map_action_show(struct vty *vty, struct lyd_node *dnode,
vty_out(vty, " set tag %s\n",
yang_dnode_get_string(dnode, "./tag"));
break;
- case 100:
- /* NOTHING: custom field, should be handled by daemon. */
+ case 100: /* source */
+ if (yang_dnode_exists(dnode, "./frr-zebra:source-v4"))
+ vty_out(vty, " set src %s\n",
+ yang_dnode_get_string(dnode, "./frr-zebra:source-v4"));
+ else
+ vty_out(vty, " set src %s\n",
+ yang_dnode_get_string(dnode, "./frr-zebra:source-v6"));
break;
}
}
@@ -1034,7 +1064,14 @@ static int route_map_config_write(struct vty *vty)
}
/* Route map node structure. */
-static struct cmd_node rmap_node = {RMAP_NODE, "%s(config-route-map)# ", 1};
+static int route_map_config_write(struct vty *vty);
+static struct cmd_node rmap_node = {
+ .name = "routemap",
+ .node = RMAP_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-route-map)# ",
+ .config_write = route_map_config_write,
+};
static void rmap_autocomplete(vector comps, struct cmd_token *token)
{
@@ -1057,7 +1094,7 @@ void route_map_cli_init(void)
cmd_variable_handler_register(rmap_var_handlers);
/* CLI commands. */
- install_node(&rmap_node, route_map_config_write);
+ install_node(&rmap_node);
install_default(RMAP_NODE);
install_element(CONFIG_NODE, &route_map_cmd);
install_element(CONFIG_NODE, &no_route_map_cmd);
diff --git a/lib/routemap_northbound.c b/lib/routemap_northbound.c
index 69cebbd2a1..dd4cbd7d99 100644
--- a/lib/routemap_northbound.c
+++ b/lib/routemap_northbound.c
@@ -1221,32 +1221,7 @@ lib_route_map_entry_set_action_tag_destroy(enum nb_event event,
}
/* clang-format off */
-#if defined(__GNUC__) && ((__GNUC__ - 0) < 5) && !defined(__clang__)
-/*
- * gcc versions before 5.x miscalculate the size for structs with variable
- * length arrays (they just count it as size 0)
- */
-struct frr_yang_module_info_sizen {
- /* YANG module name. */
- const char *name;
-
- /* Northbound callbacks. */
- const struct {
- /* Data path of this YANG node. */
- const char *xpath;
-
- /* Callbacks implemented for this node. */
- struct nb_callbacks cbs;
-
- /* Priority - lower priorities are processed first. */
- uint32_t priority;
- } nodes[28];
-};
-
-const struct frr_yang_module_info_sizen frr_route_map_info_sizen asm("frr_route_map_info") = {
-#else
const struct frr_yang_module_info frr_route_map_info = {
-#endif
.name = "frr-route-map",
.nodes = {
{
diff --git a/lib/skiplist.c b/lib/skiplist.c
index fa25770efa..2bef18f525 100644
--- a/lib/skiplist.c
+++ b/lib/skiplist.c
@@ -61,6 +61,7 @@
#include "vty.h"
#include "skiplist.h"
#include "lib_errors.h"
+#include "network.h"
DEFINE_MTYPE_STATIC(LIB, SKIP_LIST, "Skip List")
DEFINE_MTYPE_STATIC(LIB, SKIP_LIST_NODE, "Skip Node")
@@ -95,7 +96,7 @@ static int randomLevel(void)
do {
if (randomsLeft <= 0) {
- randomBits = random();
+ randomBits = frr_weak_random();
randomsLeft = BitsInRandom / 2;
}
b = randomBits & 3;
@@ -624,7 +625,7 @@ void skiplist_test(struct vty *vty)
zlog_debug("%s: (%d:%d)", __func__, i, k);
}
// keys[k] = (void *)random();
- keys[k] = (void *)scramble(k);
+ keys[k] = scramble(k);
if (skiplist_insert(l, keys[k], keys[k]))
zlog_debug("error in insert #%d,#%d", i, k);
}
@@ -649,7 +650,7 @@ void skiplist_test(struct vty *vty)
zlog_debug("<%d:%d>", i, k);
if (skiplist_delete(l, keys[k], keys[k]))
zlog_debug("error in delete");
- keys[k] = (void *)scramble(k ^ 0xf0f0f0f0);
+ keys[k] = scramble(k ^ 0xf0f0f0f0);
if (skiplist_insert(l, keys[k], keys[k]))
zlog_debug("error in insert #%d,#%d", i, k);
}
diff --git a/lib/spf_backoff.c b/lib/spf_backoff.c
index 41d4e2bb57..4e74714489 100644
--- a/lib/spf_backoff.c
+++ b/lib/spf_backoff.c
@@ -7,7 +7,7 @@
* Copyright (C) 2017 Orange Labs http://www.orange.com/
* Copyright (C) 2017 by Christian Franke, Open Source Routing / NetDEF Inc.
*
- * This file is part of FreeRangeRouting (FRR)
+ * 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
diff --git a/lib/spf_backoff.h b/lib/spf_backoff.h
index 11b2701e3e..2617195d79 100644
--- a/lib/spf_backoff.h
+++ b/lib/spf_backoff.h
@@ -7,7 +7,7 @@
* Copyright (C) 2017 Orange Labs http://www.orange.com/
* Copyright (C) 2017 by Christian Franke, Open Source Routing / NetDEF Inc.
*
- * This file is part of FreeRangeRouting (FRR)
+ * 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
diff --git a/lib/srcdest_table.c b/lib/srcdest_table.c
index ee87d73077..66b735919b 100644
--- a/lib/srcdest_table.c
+++ b/lib/srcdest_table.c
@@ -4,7 +4,7 @@
* Copyright (C) 2017 by David Lamparter & Christian Franke,
* Open Source Routing / NetDEF Inc.
*
- * This file is part of FreeRangeRouting (FRR)
+ * 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
diff --git a/lib/srcdest_table.h b/lib/srcdest_table.h
index 90418944c7..7982260777 100644
--- a/lib/srcdest_table.h
+++ b/lib/srcdest_table.h
@@ -4,7 +4,7 @@
* Copyright (C) 2017 by David Lamparter & Christian Franke,
* Open Source Routing / NetDEF Inc.
*
- * This file is part of FreeRangeRouting (FRR)
+ * 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
diff --git a/lib/stream.c b/lib/stream.c
index f046572f41..683a130e44 100644
--- a/lib/stream.c
+++ b/lib/stream.c
@@ -543,6 +543,27 @@ uint64_t stream_getq(struct stream *s)
return q;
}
+bool stream_getq2(struct stream *s, uint64_t *q)
+{
+ STREAM_VERIFY_SANE(s);
+
+ if (STREAM_READABLE(s) < sizeof(uint64_t)) {
+ STREAM_BOUND_WARN2(s, "get uint64");
+ return false;
+ }
+
+ *q = ((uint64_t)s->data[s->getp++]) << 56;
+ *q |= ((uint64_t)s->data[s->getp++]) << 48;
+ *q |= ((uint64_t)s->data[s->getp++]) << 40;
+ *q |= ((uint64_t)s->data[s->getp++]) << 32;
+ *q |= ((uint64_t)s->data[s->getp++]) << 24;
+ *q |= ((uint64_t)s->data[s->getp++]) << 16;
+ *q |= ((uint64_t)s->data[s->getp++]) << 8;
+ *q |= ((uint64_t)s->data[s->getp++]);
+
+ return true;
+}
+
/* Get next long word from the stream. */
uint32_t stream_get_ipv4(struct stream *s)
{
diff --git a/lib/stream.h b/lib/stream.h
index 6fcf9a53cf..5c7d94fab8 100644
--- a/lib/stream.h
+++ b/lib/stream.h
@@ -215,6 +215,7 @@ extern bool stream_getl2(struct stream *s, uint32_t *l);
extern uint32_t stream_getl_from(struct stream *, size_t);
extern uint64_t stream_getq(struct stream *);
extern uint64_t stream_getq_from(struct stream *, size_t);
+bool stream_getq2(struct stream *s, uint64_t *q);
extern uint32_t stream_get_ipv4(struct stream *);
/* IEEE-754 floats */
@@ -402,6 +403,25 @@ static inline const uint8_t *ptr_get_be32(const uint8_t *ptr, uint32_t *out)
(P) = _pval; \
} while (0)
+#define STREAM_GETF(S, P) \
+ do { \
+ union { \
+ float r; \
+ uint32_t d; \
+ } _pval; \
+ if (stream_getl2((S), &_pval.d)) \
+ goto stream_failure; \
+ (P) = _pval.r; \
+ } while (0)
+
+#define STREAM_GETQ(S, P) \
+ do { \
+ uint64_t _pval; \
+ if (!stream_getq2((S), &_pval)) \
+ goto stream_failure; \
+ (P) = _pval; \
+ } while (0)
+
#define STREAM_GET(P, STR, SIZE) \
do { \
if (!stream_get2((P), (STR), (SIZE))) \
diff --git a/lib/subdir.am b/lib/subdir.am
index 4f62eb2264..ed6cf31b34 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -45,6 +45,7 @@ lib_libfrr_la_SOURCES = \
lib/libfrr.c \
lib/linklist.c \
lib/log.c \
+ lib/log_filter.c \
lib/log_vty.c \
lib/md5.c \
lib/memory.c \
@@ -100,6 +101,8 @@ lib_libfrr_la_SOURCES = \
lib/yang_translator.c \
lib/yang_wrappers.c \
lib/zclient.c \
+ lib/zlog.c \
+ lib/zlog_targets.c \
lib/printf/printf-pos.c \
lib/printf/vfprintf.c \
lib/printf/glue.c \
@@ -110,8 +113,13 @@ nodist_lib_libfrr_la_SOURCES = \
yang/frr-interface.yang.c \
yang/frr-route-map.yang.c \
yang/frr-route-types.yang.c \
+ yang/frr-vrf.yang.c \
+ yang/frr-routing.yang.c \
yang/ietf/ietf-routing-types.yang.c \
+ yang/ietf/ietf-interfaces.yang.c \
yang/frr-module-translator.yang.c \
+ yang/frr-nexthop.yang.c \
+ yang/frr-igmp.yang.c \
# end
vtysh_scan += \
@@ -254,6 +262,8 @@ pkginclude_HEADERS += \
lib/zassert.h \
lib/zclient.h \
lib/zebra.h \
+ lib/zlog.h \
+ lib/zlog_targets.h \
lib/pbr.h \
# end
@@ -265,7 +275,6 @@ nodist_pkginclude_HEADERS += \
noinst_HEADERS += \
lib/clippy.h \
- lib/log_int.h \
lib/plist_int.h \
lib/printf/printfcommon.h \
lib/printf/printflocal.h \
diff --git a/lib/table.c b/lib/table.c
index 1a89a95f4f..86347cbacd 100644
--- a/lib/table.c
+++ b/lib/table.c
@@ -160,7 +160,7 @@ static void route_common(const struct prefix *n, const struct prefix *p,
np = (const uint8_t *)&n->u.prefix;
pp = (const uint8_t *)&p->u.prefix;
- newp = (uint8_t *)&new->u.prefix;
+ newp = &new->u.prefix;
for (i = 0; i < p->prefixlen / 8; i++) {
if (np[i] == pp[i])
diff --git a/lib/thread.c b/lib/thread.c
index dbf668a699..4d689a9f88 100644
--- a/lib/thread.c
+++ b/lib/thread.c
@@ -724,6 +724,7 @@ static int fd_poll(struct thread_master *m, struct pollfd *pfds, nfds_t pfdsize,
< 0) // effect a poll (return immediately)
timeout = 0;
+ zlog_tls_buffer_flush();
rcu_read_unlock();
rcu_assert_read_unlocked();
diff --git a/lib/typesafe.c b/lib/typesafe.c
index 6635cf7506..a52b55b734 100644
--- a/lib/typesafe.c
+++ b/lib/typesafe.c
@@ -23,6 +23,7 @@
#include "typesafe.h"
#include "memory.h"
+#include "network.h"
DEFINE_MTYPE_STATIC(LIB, TYPEDHASH_BUCKET, "Typed-hash bucket")
DEFINE_MTYPE_STATIC(LIB, SKIPLIST_OFLOW, "Skiplist overflow")
@@ -196,7 +197,7 @@ struct sskip_item *typesafe_skiplist_add(struct sskip_head *head,
int cmpval;
/* level / newlevel are 1-counted here */
- newlevel = __builtin_ctz(random()) + 1;
+ newlevel = __builtin_ctz(frr_weak_random()) + 1;
if (newlevel > SKIPLIST_MAXDEPTH)
newlevel = SKIPLIST_MAXDEPTH;
diff --git a/lib/vrf.c b/lib/vrf.c
index 370a1b235a..79195453e6 100644
--- a/lib/vrf.c
+++ b/lib/vrf.c
@@ -36,6 +36,8 @@
#include "privs.h"
#include "nexthop_group.h"
#include "lib_errors.h"
+#include "northbound.h"
+#include "northbound_cli.h"
/* default VRF ID value used when VRF backend is not NETNS */
#define VRF_DEFAULT_INTERNAL 0
@@ -116,7 +118,7 @@ static void vrf_update_vrf_id(ns_id_t ns_id, void *opaqueptr)
vrf->vrf_id = vrf_id;
RB_INSERT(vrf_id_head, &vrfs_by_id, vrf);
if (old_vrf_id == VRF_UNKNOWN)
- vrf_enable((struct vrf *)vrf);
+ vrf_enable(vrf);
}
int vrf_switch_to_netns(vrf_id_t vrf_id)
@@ -324,10 +326,7 @@ const char *vrf_id_to_name(vrf_id_t vrf_id)
struct vrf *vrf;
vrf = vrf_lookup_by_id(vrf_id);
- if (vrf)
- return vrf->name;
-
- return "n/a";
+ return VRF_LOGNAME(vrf);
}
vrf_id_t vrf_name_to_id(const char *name)
@@ -593,16 +592,30 @@ int vrf_get_backend(void)
return vrf_backend;
}
-void vrf_configure_backend(int vrf_backend_netns)
+int vrf_configure_backend(enum vrf_backend_type backend)
{
- vrf_backend = vrf_backend_netns;
+ /* Work around issue in old gcc */
+ switch (backend) {
+ case VRF_BACKEND_UNKNOWN:
+ case VRF_BACKEND_NETNS:
+ case VRF_BACKEND_VRF_LITE:
+ break;
+ default:
+ return -1;
+ }
+
+ vrf_backend = backend;
vrf_backend_configured = 1;
+
+ return 0;
}
int vrf_handler_create(struct vty *vty, const char *vrfname,
struct vrf **vrf)
{
struct vrf *vrfp;
+ char xpath_list[XPATH_MAXLEN];
+ int ret;
if (strlen(vrfname) > VRF_NAMSIZ) {
if (vty)
@@ -617,13 +630,24 @@ int vrf_handler_create(struct vty *vty, const char *vrfname,
return CMD_WARNING_CONFIG_FAILED;
}
- vrfp = vrf_get(VRF_UNKNOWN, vrfname);
-
- if (vty)
- VTY_PUSH_CONTEXT(VRF_NODE, vrfp);
+ if (vty) {
+ snprintf(xpath_list, sizeof(xpath_list),
+ "/frr-vrf:lib/vrf[name='%s']", vrfname);
+
+ nb_cli_enqueue_change(vty, xpath_list, NB_OP_CREATE, NULL);
+ ret = nb_cli_apply_changes(vty, xpath_list);
+ if (ret == CMD_SUCCESS) {
+ VTY_PUSH_XPATH(VRF_NODE, xpath_list);
+ vrfp = vrf_lookup_by_name(vrfname);
+ if (vrfp)
+ VTY_PUSH_CONTEXT(VRF_NODE, vrfp);
+ }
+ } else {
+ vrfp = vrf_get(VRF_UNKNOWN, vrfname);
- if (vrf)
- *vrf = vrfp;
+ if (vrf)
+ *vrf = vrfp;
+ }
return CMD_SUCCESS;
}
@@ -726,6 +750,7 @@ DEFUN (no_vrf,
"VRF's name\n")
{
const char *vrfname = argv[2]->arg;
+ char xpath_list[XPATH_MAXLEN];
struct vrf *vrfp;
@@ -741,15 +766,20 @@ DEFUN (no_vrf,
return CMD_WARNING_CONFIG_FAILED;
}
- /* Clear configured flag and invoke delete. */
- UNSET_FLAG(vrfp->status, VRF_CONFIGURED);
- vrf_delete(vrfp);
+ snprintf(xpath_list, sizeof(xpath_list), "/frr-vrf:lib/vrf[name='%s']",
+ vrfname);
- return CMD_SUCCESS;
+ nb_cli_enqueue_change(vty, xpath_list, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, xpath_list);
}
-static struct cmd_node vrf_node = {VRF_NODE, "%s(config-vrf)# ", 1};
+static struct cmd_node vrf_node = {
+ .name = "vrf",
+ .node = VRF_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-vrf)# ",
+};
DEFUN_NOSH (vrf_netns,
vrf_netns_cmd,
@@ -839,11 +869,17 @@ static int vrf_write_host(struct vty *vty)
return 1;
}
-static struct cmd_node vrf_debug_node = {VRF_DEBUG_NODE, "", 1};
+static int vrf_write_host(struct vty *vty);
+static struct cmd_node vrf_debug_node = {
+ .name = "vrf debug",
+ .node = VRF_DEBUG_NODE,
+ .prompt = "",
+ .config_write = vrf_write_host,
+};
void vrf_install_commands(void)
{
- install_node(&vrf_debug_node, vrf_write_host);
+ install_node(&vrf_debug_node);
install_element(CONFIG_NODE, &vrf_debug_cmd);
install_element(ENABLE_NODE, &vrf_debug_cmd);
@@ -856,7 +892,8 @@ void vrf_cmd_init(int (*writefunc)(struct vty *vty),
{
install_element(CONFIG_NODE, &vrf_cmd);
install_element(CONFIG_NODE, &no_vrf_cmd);
- install_node(&vrf_node, writefunc);
+ vrf_node.config_write = writefunc;
+ install_node(&vrf_node);
install_default(VRF_NODE);
install_element(VRF_NODE, &vrf_exit_cmd);
if (vrf_is_backend_netns() && ns_have_netns()) {
@@ -1010,3 +1047,147 @@ vrf_id_t vrf_generate_id(void)
return ++vrf_id_local;
}
+
+/* ------- Northbound callbacks ------- */
+
+/*
+ * XPath: /frr-vrf:lib/vrf
+ */
+static int lib_vrf_create(enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ const char *vrfname;
+ struct vrf *vrfp;
+
+ vrfname = yang_dnode_get_string(dnode, "./name");
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ vrfp = vrf_get(VRF_UNKNOWN, vrfname);
+
+ nb_running_set_entry(dnode, vrfp);
+
+ return NB_OK;
+}
+
+static int lib_vrf_destroy(enum nb_event event, const struct lyd_node *dnode)
+{
+ struct vrf *vrfp;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ vrfp = nb_running_get_entry(dnode, NULL, true);
+ if (CHECK_FLAG(vrfp->status, VRF_ACTIVE)) {
+ zlog_debug("%s Only inactive VRFs can be deleted",
+ __func__);
+ return NB_ERR_VALIDATION;
+ }
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ vrfp = nb_running_unset_entry(dnode);
+
+ /* Clear configured flag and invoke delete. */
+ UNSET_FLAG(vrfp->status, VRF_CONFIGURED);
+ vrf_delete(vrfp);
+ break;
+ }
+
+ return NB_OK;
+}
+
+static const void *lib_vrf_get_next(const void *parent_list_entry,
+ const void *list_entry)
+{
+ struct vrf *vrfp = (struct vrf *)list_entry;
+
+ if (list_entry == NULL) {
+ vrfp = RB_MIN(vrf_name_head, &vrfs_by_name);
+ } else {
+ vrfp = RB_NEXT(vrf_name_head, vrfp);
+ }
+
+ return vrfp;
+}
+
+static int lib_vrf_get_keys(const void *list_entry, struct yang_list_keys *keys)
+{
+ struct vrf *vrfp = (struct vrf *)list_entry;
+
+ keys->num = 1;
+ strlcpy(keys->key[0], vrfp->name, sizeof(keys->key[0]));
+
+ return NB_OK;
+}
+
+static const void *lib_vrf_lookup_entry(const void *parent_list_entry,
+ const struct yang_list_keys *keys)
+{
+ const char *vrfname = keys->key[0];
+
+ struct vrf *vrf = vrf_lookup_by_name(vrfname);
+
+ return vrf;
+}
+
+/*
+ * XPath: /frr-vrf:lib/vrf/id
+ */
+static struct yang_data *lib_vrf_state_id_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ struct vrf *vrfp = (struct vrf *)list_entry;
+
+ return yang_data_new_uint32(xpath, vrfp->vrf_id);
+}
+
+/*
+ * XPath: /frr-vrf:lib/vrf/active
+ */
+static struct yang_data *lib_vrf_state_active_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ struct vrf *vrfp = (struct vrf *)list_entry;
+
+ if (vrfp->status == VRF_ACTIVE)
+ return yang_data_new_bool(
+ xpath, vrfp->status == VRF_ACTIVE ? true : false);
+
+ return NULL;
+}
+
+/* clang-format off */
+const struct frr_yang_module_info frr_vrf_info = {
+ .name = "frr-vrf",
+ .nodes = {
+ {
+ .xpath = "/frr-vrf:lib/vrf",
+ .cbs = {
+ .create = lib_vrf_create,
+ .destroy = lib_vrf_destroy,
+ .get_next = lib_vrf_get_next,
+ .get_keys = lib_vrf_get_keys,
+ .lookup_entry = lib_vrf_lookup_entry,
+ }
+ },
+ {
+ .xpath = "/frr-vrf:lib/vrf/state/id",
+ .cbs = {
+ .get_elem = lib_vrf_state_id_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-vrf:lib/vrf/state/active",
+ .cbs = {
+ .get_elem = lib_vrf_state_active_get_elem,
+ }
+ },
+ {
+ .xpath = NULL,
+ },
+ }
+};
+
diff --git a/lib/vrf.h b/lib/vrf.h
index f231d2433f..83ed16b48e 100644
--- a/lib/vrf.h
+++ b/lib/vrf.h
@@ -101,9 +101,12 @@ RB_PROTOTYPE(vrf_name_head, vrf, name_entry, vrf_name_compare)
DECLARE_QOBJ_TYPE(vrf)
/* Allow VRF with netns as backend */
-#define VRF_BACKEND_VRF_LITE 0
-#define VRF_BACKEND_NETNS 1
-#define VRF_BACKEND_UNKNOWN 2
+enum vrf_backend_type {
+ VRF_BACKEND_VRF_LITE,
+ VRF_BACKEND_NETNS,
+ VRF_BACKEND_UNKNOWN,
+ VRF_BACKEND_MAX,
+};
extern struct vrf_id_head vrfs_by_id;
extern struct vrf_name_head vrfs_by_name;
@@ -292,10 +295,10 @@ extern void vrf_install_commands(void);
* VRF utilities
*/
-/* API for configuring VRF backend
- * should be called from zebra only
+/*
+ * API for configuring VRF backend
*/
-extern void vrf_configure_backend(int vrf_backend_netns);
+extern int vrf_configure_backend(enum vrf_backend_type backend);
extern int vrf_get_backend(void);
extern int vrf_is_backend_netns(void);
@@ -322,6 +325,8 @@ extern int vrf_enable(struct vrf *vrf);
extern void vrf_delete(struct vrf *vrf);
extern vrf_id_t vrf_generate_id(void);
+extern const struct frr_yang_module_info frr_vrf_info;
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/vty.c b/lib/vty.c
index 8056236de9..86d0a36aa0 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -2199,6 +2199,9 @@ void vty_close(struct vty *vty)
int i;
bool was_stdio = false;
+ /* Drop out of configure / transaction if needed. */
+ vty_config_exit(vty);
+
/* Cancel threads.*/
THREAD_OFF(vty->t_read);
THREAD_OFF(vty->t_write);
@@ -2242,9 +2245,6 @@ void vty_close(struct vty *vty)
list_delete(&vty->error);
}
- /* Check configure. */
- vty_config_exit(vty);
-
/* OK free vty. */
XFREE(MTYPE_VTY, vty);
@@ -2605,6 +2605,28 @@ int vty_config_enter(struct vty *vty, bool private_config, bool exclusive)
void vty_config_exit(struct vty *vty)
{
+ enum node_type node = vty->node;
+ struct cmd_node *cnode;
+
+ /* unlock and jump up to ENABLE_NODE if -and only if- we're
+ * somewhere below CONFIG_NODE */
+ while (node && node != CONFIG_NODE) {
+ cnode = vector_lookup(cmdvec, node);
+ node = cnode->parent_node;
+ }
+ if (node != CONFIG_NODE)
+ /* called outside config, e.g. vty_close() in ENABLE_NODE */
+ return;
+
+ while (vty->node != ENABLE_NODE)
+ /* will call vty_config_node_exit() below */
+ cmd_exit(vty);
+}
+
+int vty_config_node_exit(struct vty *vty)
+{
+ vty->xpath_index = 0;
+
/* Check if there's a pending confirmed commit. */
if (vty->t_confirmed_commit_timeout) {
vty_out(vty,
@@ -2626,6 +2648,7 @@ void vty_config_exit(struct vty *vty)
}
vty->config = false;
+ return 1;
}
/* Master of the threads. */
@@ -2989,8 +3012,13 @@ static int vty_config_write(struct vty *vty)
return CMD_SUCCESS;
}
+static int vty_config_write(struct vty *vty);
struct cmd_node vty_node = {
- VTY_NODE, "%s(config-line)# ", 1,
+ .name = "vty",
+ .node = VTY_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-line)# ",
+ .config_write = vty_config_write,
};
/* Reset all VTY status. */
@@ -3084,7 +3112,7 @@ void vty_init(struct thread_master *master_thread, bool do_command_logging)
Vvty_serv_thread = vector_init(VECTOR_MIN_SIZE);
/* Install bgp top node. */
- install_node(&vty_node, vty_config_write);
+ install_node(&vty_node);
install_element(VIEW_NODE, &config_who_cmd);
install_element(VIEW_NODE, &show_history_cmd);
diff --git a/lib/vty.h b/lib/vty.h
index f90a35a260..694aac3944 100644
--- a/lib/vty.h
+++ b/lib/vty.h
@@ -323,6 +323,7 @@ extern void vty_log(const char *level, const char *proto, const char *msg,
extern int vty_config_enter(struct vty *vty, bool private_config,
bool exclusive);
extern void vty_config_exit(struct vty *);
+extern int vty_config_node_exit(struct vty *);
extern int vty_shell(struct vty *);
extern int vty_shell_serv(struct vty *);
extern void vty_hello(struct vty *);
diff --git a/lib/yang.c b/lib/yang.c
index 93e6db3055..c80bf20306 100644
--- a/lib/yang.c
+++ b/lib/yang.c
@@ -72,13 +72,17 @@ static const char *yang_module_imp_clb(const char *mod_name,
return NULL;
}
+/* clang-format off */
static const char *const frr_native_modules[] = {
"frr-interface",
+ "frr-vrf",
"frr-ripd",
"frr-ripngd",
"frr-isisd",
"frr-vrrpd",
+ "frr-zebra",
};
+/* clang-format on */
/* Generate the yang_modules tree. */
static inline int yang_module_compare(const struct yang_module *a,
@@ -628,7 +632,7 @@ void yang_debugging_set(bool enable)
}
}
-struct ly_ctx *yang_ctx_new_setup(void)
+struct ly_ctx *yang_ctx_new_setup(bool embedded_modules)
{
struct ly_ctx *ctx;
const char *yang_models_path = YANG_MODELS_PATH;
@@ -647,18 +651,21 @@ struct ly_ctx *yang_ctx_new_setup(void)
ctx = ly_ctx_new(yang_models_path, LY_CTX_DISABLE_SEARCHDIR_CWD);
if (!ctx)
return NULL;
- ly_ctx_set_module_imp_clb(ctx, yang_module_imp_clb, NULL);
+
+ if (embedded_modules)
+ ly_ctx_set_module_imp_clb(ctx, yang_module_imp_clb, NULL);
+
return ctx;
}
-void yang_init(void)
+void yang_init(bool embedded_modules)
{
/* Initialize libyang global parameters that affect all containers. */
ly_set_log_clb(ly_log_cb, 1);
ly_log_options(LY_LOLOG | LY_LOSTORE);
/* Initialize libyang container for native models. */
- ly_native_ctx = yang_ctx_new_setup();
+ ly_native_ctx = yang_ctx_new_setup(embedded_modules);
if (!ly_native_ctx) {
flog_err(EC_LIB_LIBYANG, "%s: ly_ctx_new() failed", __func__);
exit(1);
diff --git a/lib/yang.h b/lib/yang.h
index 6892e36019..126521707b 100644
--- a/lib/yang.h
+++ b/lib/yang.h
@@ -34,7 +34,7 @@ extern "C" {
#endif
/* Maximum XPath length. */
-#define XPATH_MAXLEN 256
+#define XPATH_MAXLEN 512
/* Maximum list key length. */
#define LIST_MAXKEYS 8
@@ -482,8 +482,11 @@ extern struct yang_data *yang_data_list_find(const struct list *list,
/*
* Create and set up a libyang context (for use by the translator)
+ *
+ * embedded_modules
+ * Specify whether libyang should attempt to look for embedded YANG modules.
*/
-extern struct ly_ctx *yang_ctx_new_setup(void);
+extern struct ly_ctx *yang_ctx_new_setup(bool embedded_modules);
/*
* Enable or disable libyang verbose debugging.
@@ -496,8 +499,11 @@ extern void yang_debugging_set(bool enable);
/*
* Initialize the YANG subsystem. Should be called only once during the
* daemon initialization process.
+ *
+ * embedded_modules
+ * Specify whether libyang should attempt to look for embedded YANG modules.
*/
-extern void yang_init(void);
+extern void yang_init(bool embedded_modules);
/*
* Finish the YANG subsystem gracefully. Should be called only when the daemon
diff --git a/lib/yang_translator.c b/lib/yang_translator.c
index 341420eeda..7dbb1f3f1a 100644
--- a/lib/yang_translator.c
+++ b/lib/yang_translator.c
@@ -171,7 +171,7 @@ struct yang_translator *yang_translator_load(const char *path)
RB_INSERT(yang_translators, &yang_translators, translator);
/* Initialize the translator libyang context. */
- translator->ly_ctx = yang_ctx_new_setup();
+ translator->ly_ctx = yang_ctx_new_setup(false);
if (!translator->ly_ctx) {
flog_warn(EC_LIB_LIBYANG, "%s: ly_ctx_new() failed", __func__);
goto error;
@@ -511,7 +511,7 @@ static unsigned int yang_module_nodes_count(const struct lys_module *module)
void yang_translator_init(void)
{
- ly_translator_ctx = yang_ctx_new_setup();
+ ly_translator_ctx = yang_ctx_new_setup(true);
if (!ly_translator_ctx) {
flog_err(EC_LIB_LIBYANG, "%s: ly_ctx_new() failed", __func__);
exit(1);
diff --git a/lib/yang_wrappers.c b/lib/yang_wrappers.c
index 2b502d635b..7d94a2f744 100644
--- a/lib/yang_wrappers.c
+++ b/lib/yang_wrappers.c
@@ -23,6 +23,7 @@
#include "lib_errors.h"
#include "northbound.h"
#include "printfrr.h"
+#include "nexthop.h"
static const char *yang_get_default_value(const char *xpath)
{
@@ -783,6 +784,14 @@ void yang_get_default_string_buf(char *buf, size_t size, const char *xpath_fmt,
}
/*
+ * Primitive type: empty.
+ */
+struct yang_data *yang_data_new_empty(const char *xpath)
+{
+ return yang_data_new(xpath, NULL);
+}
+
+/*
* Derived type: IP prefix.
*/
void yang_str2prefix(const char *value, union prefixptr prefix)
@@ -1114,3 +1123,44 @@ void yang_get_default_ip(struct ipaddr *var, const char *xpath_fmt, ...)
value = yang_get_default_value(xpath);
yang_str2ip(value, var);
}
+
+struct yang_data *yang_data_new_mac(const char *xpath,
+ const struct ethaddr *mac)
+{
+ char value_str[ETHER_ADDR_STRLEN];
+
+ prefix_mac2str(mac, value_str, sizeof(value_str));
+ return yang_data_new(xpath, value_str);
+}
+
+void yang_str2mac(const char *value, struct ethaddr *mac)
+{
+ (void)prefix_str2mac(value, mac);
+}
+
+const char *yang_nexthop_type2str(uint32_t ntype)
+{
+ switch (ntype) {
+ case NEXTHOP_TYPE_IFINDEX:
+ return "ifindex";
+ break;
+ case NEXTHOP_TYPE_IPV4:
+ return "ip4";
+ break;
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ return "ip4-ifindex";
+ break;
+ case NEXTHOP_TYPE_IPV6:
+ return "ip6";
+ break;
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ return "ip6-ifindex";
+ break;
+ case NEXTHOP_TYPE_BLACKHOLE:
+ return "blackhole";
+ break;
+ default:
+ return "unknown";
+ break;
+ }
+}
diff --git a/lib/yang_wrappers.h b/lib/yang_wrappers.h
index 10d1ea314f..d853b61ae1 100644
--- a/lib/yang_wrappers.h
+++ b/lib/yang_wrappers.h
@@ -114,6 +114,9 @@ extern const char *yang_get_default_string(const char *xpath_fmt, ...);
extern void yang_get_default_string_buf(char *buf, size_t size,
const char *xpath_fmt, ...);
+/* empty */
+extern struct yang_data *yang_data_new_empty(const char *xpath);
+
/* ip prefix */
extern void yang_str2prefix(const char *value, union prefixptr prefix);
extern struct yang_data *yang_data_new_prefix(const char *xpath,
@@ -172,4 +175,11 @@ extern void yang_dnode_get_ip(struct ipaddr *addr, const struct lyd_node *dnode,
const char *xpath_fmt, ...);
extern void yang_get_default_ip(struct ipaddr *var, const char *xpath_fmt, ...);
+/* mac */
+extern struct yang_data *yang_data_new_mac(const char *xpath,
+ const struct ethaddr *mac);
+extern void yang_str2mac(const char *value, struct ethaddr *mac);
+
+extern const char *yang_nexthop_type2str(uint32_t ntype);
+
#endif /* _FRR_NORTHBOUND_WRAPPERS_H_ */
diff --git a/lib/zclient.c b/lib/zclient.c
index 1ac0e49e13..be2c4e54a0 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -388,6 +388,7 @@ int zclient_send_hello(struct zclient *zclient)
zclient_create_header(s, ZEBRA_HELLO, VRF_DEFAULT);
stream_putc(s, zclient->redist_default);
stream_putw(s, zclient->instance);
+ stream_putl(s, zclient->session_id);
if (zclient->receive_notify)
stream_putc(s, 1);
else
@@ -904,6 +905,7 @@ int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh,
}
}
+ /* If present, set 'weight' flag before encoding flags */
if (api_nh->weight)
SET_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_WEIGHT);
@@ -948,6 +950,10 @@ int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh,
stream_put(s, &(api_nh->rmac),
sizeof(struct ethaddr));
+ /* Index of backup nexthop */
+ if (CHECK_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP))
+ stream_putc(s, api_nh->backup_idx);
+
done:
return ret;
}
@@ -985,7 +991,7 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api)
stream_putc(s, api->prefix.family);
psize = PSIZE(api->prefix.prefixlen);
stream_putc(s, api->prefix.prefixlen);
- stream_write(s, (uint8_t *)&api->prefix.u.prefix, psize);
+ stream_write(s, &api->prefix.u.prefix, psize);
if (CHECK_FLAG(api->message, ZAPI_MESSAGE_SRCPFX)) {
psize = PSIZE(api->src_prefix.prefixlen);
@@ -1007,6 +1013,10 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api)
return -1;
}
+ /* We canonicalize the nexthops by sorting them; this allows
+ * zebra to resolve the list of nexthops to a nexthop-group
+ * more efficiently.
+ */
zapi_nexthop_group_sort(api->nexthops, api->nexthop_num);
stream_putw(s, api->nexthop_num);
@@ -1033,6 +1043,50 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api)
}
}
+ /* Backup nexthops */
+ if (CHECK_FLAG(api->message, ZAPI_MESSAGE_BACKUP_NEXTHOPS)) {
+ /* limit the number of nexthops if necessary */
+ if (api->backup_nexthop_num > MULTIPATH_NUM) {
+ char buf[PREFIX2STR_BUFFER];
+
+ prefix2str(&api->prefix, buf, sizeof(buf));
+ flog_err(
+ EC_LIB_ZAPI_ENCODE,
+ "%s: prefix %s: can't encode %u backup nexthops (maximum is %u)",
+ __func__, buf, api->backup_nexthop_num,
+ MULTIPATH_NUM);
+ return -1;
+ }
+
+ /* Note that we do not sort the list of backup nexthops -
+ * this list is treated as an array and indexed by each
+ * primary nexthop that is associated with a backup.
+ */
+
+ stream_putw(s, api->backup_nexthop_num);
+
+ for (i = 0; i < api->backup_nexthop_num; i++) {
+ api_nh = &api->backup_nexthops[i];
+
+ /* MPLS labels for BGP-LU or Segment Routing */
+ if (api_nh->label_num > MPLS_MAX_LABELS) {
+ char buf[PREFIX2STR_BUFFER];
+
+ prefix2str(&api->prefix, buf, sizeof(buf));
+
+ flog_err(EC_LIB_ZAPI_ENCODE,
+ "%s: prefix %s: backup: can't encode %u labels (maximum is %u)",
+ __func__, buf,
+ api_nh->label_num,
+ MPLS_MAX_LABELS);
+ return -1;
+ }
+
+ if (zapi_nexthop_encode(s, api_nh, api->flags) != 0)
+ return -1;
+ }
+ }
+
/* Attributes. */
if (CHECK_FLAG(api->message, ZAPI_MESSAGE_DISTANCE))
stream_putc(s, api->distance);
@@ -1108,6 +1162,10 @@ static int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh,
STREAM_GET(&(api_nh->rmac), s,
sizeof(struct ethaddr));
+ /* Backup nexthop index */
+ if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP))
+ STREAM_GETC(s, api_nh->backup_idx);
+
/* Success */
ret = 0;
@@ -1214,6 +1272,24 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api)
}
}
+ /* Backup nexthops. */
+ if (CHECK_FLAG(api->message, ZAPI_MESSAGE_BACKUP_NEXTHOPS)) {
+ STREAM_GETW(s, api->backup_nexthop_num);
+ if (api->backup_nexthop_num > MULTIPATH_NUM) {
+ flog_err(EC_LIB_ZAPI_ENCODE,
+ "%s: invalid number of backup nexthops (%u)",
+ __func__, api->backup_nexthop_num);
+ return -1;
+ }
+
+ for (i = 0; i < api->backup_nexthop_num; i++) {
+ api_nh = &api->backup_nexthops[i];
+
+ if (zapi_nexthop_decode(s, api_nh, api->flags) != 0)
+ return -1;
+ }
+ }
+
/* Attributes. */
if (CHECK_FLAG(api->message, ZAPI_MESSAGE_DISTANCE))
STREAM_GETC(s, api->distance);
@@ -1388,7 +1464,7 @@ stream_failure:
return false;
}
-struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh)
+struct nexthop *nexthop_from_zapi_nexthop(const struct zapi_nexthop *znh)
{
struct nexthop *n = nexthop_new();
@@ -1405,6 +1481,11 @@ struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh)
znh->labels);
}
+ if (CHECK_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) {
+ SET_FLAG(n->flags, NEXTHOP_FLAG_HAS_BACKUP);
+ n->backup_idx = znh->backup_idx;
+ }
+
return n;
}
@@ -1420,10 +1501,16 @@ int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh,
znh->type = nh->type;
znh->vrf_id = nh->vrf_id;
+ znh->weight = nh->weight;
znh->ifindex = nh->ifindex;
znh->gate = nh->gate;
if (nh->nh_label && (nh->nh_label->num_labels > 0)) {
+
+ /* Validate */
+ if (nh->nh_label->num_labels > MPLS_MAX_LABELS)
+ return -1;
+
for (i = 0; i < nh->nh_label->num_labels; i++)
znh->labels[i] = nh->nh_label->label[i];
@@ -1431,10 +1518,31 @@ int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh,
SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_LABEL);
}
+ if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
+ SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP);
+ znh->backup_idx = nh->backup_idx;
+ }
+
return 0;
}
/*
+ * Wrapper that converts backup nexthop
+ */
+int zapi_backup_nexthop_from_nexthop(struct zapi_nexthop *znh,
+ const struct nexthop *nh)
+{
+ int ret;
+
+ /* Ensure that zapi flags are correct: backups don't have backups */
+ ret = zapi_nexthop_from_nexthop(znh, nh);
+ if (ret == 0)
+ UNSET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP);
+
+ return ret;
+}
+
+/*
* Decode the nexthop-tracking update message
*/
bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr)
@@ -1528,33 +1636,34 @@ int zebra_redistribute_default_send(int command, struct zclient *zclient,
}
/* Get prefix in ZServ format; family should be filled in on prefix */
-static void zclient_stream_get_prefix(struct stream *s, struct prefix *p)
+static int zclient_stream_get_prefix(struct stream *s, struct prefix *p)
{
size_t plen = prefix_blen(p);
uint8_t c;
p->prefixlen = 0;
if (plen == 0)
- return;
+ return -1;
- stream_get(&p->u.prefix, s, plen);
+ STREAM_GET(&p->u.prefix, s, plen);
STREAM_GETC(s, c);
p->prefixlen = MIN(plen * 8, c);
+ return 0;
stream_failure:
- return;
+ return -1;
}
/* Router-id update from zebra daemon. */
-void zebra_router_id_update_read(struct stream *s, struct prefix *rid)
+int zebra_router_id_update_read(struct stream *s, struct prefix *rid)
{
/* Fetch interface address. */
STREAM_GETC(s, rid->family);
- zclient_stream_get_prefix(s, rid);
+ return zclient_stream_get_prefix(s, rid);
stream_failure:
- return;
+ return -1;
}
/* Interface addition from zebra daemon. */
@@ -1603,24 +1712,36 @@ stream_failure:
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
-static void zclient_vrf_add(struct zclient *zclient, vrf_id_t vrf_id)
+static int zclient_vrf_add(struct zclient *zclient, vrf_id_t vrf_id)
{
struct vrf *vrf;
- char vrfname_tmp[VRF_NAMSIZ];
+ char vrfname_tmp[VRF_NAMSIZ + 1] = {};
struct vrf_data data;
- stream_get(&data, zclient->ibuf, sizeof(struct vrf_data));
+ STREAM_GET(&data, zclient->ibuf, sizeof(struct vrf_data));
/* Read interface name. */
- stream_get(vrfname_tmp, zclient->ibuf, VRF_NAMSIZ);
+ STREAM_GET(vrfname_tmp, zclient->ibuf, VRF_NAMSIZ);
- /* Lookup/create vrf by vrf_id. */
+ if (strlen(vrfname_tmp) == 0)
+ goto stream_failure;
+
+ /* Lookup/create vrf by name, then vrf_id. */
vrf = vrf_get(vrf_id, vrfname_tmp);
+
+ /* If there's already a VRF with this name, don't create vrf */
+ if (!vrf)
+ return 0;
+
vrf->data.l.table_id = data.l.table_id;
memcpy(vrf->data.l.netns_name, data.l.netns_name, NS_NAMSIZ);
/* overwrite default vrf */
if (vrf_id == VRF_DEFAULT)
vrf_set_default_name(vrfname_tmp, false);
vrf_enable(vrf);
+
+ return 0;
+stream_failure:
+ return -1;
}
static void zclient_vrf_delete(struct zclient *zclient, vrf_id_t vrf_id)
@@ -1641,21 +1762,32 @@ static void zclient_vrf_delete(struct zclient *zclient, vrf_id_t vrf_id)
vrf_delete(vrf);
}
-static void zclient_interface_add(struct zclient *zclient, vrf_id_t vrf_id)
+static int zclient_interface_add(struct zclient *zclient, vrf_id_t vrf_id)
{
struct interface *ifp;
- char ifname_tmp[INTERFACE_NAMSIZ];
+ char ifname_tmp[INTERFACE_NAMSIZ + 1] = {};
struct stream *s = zclient->ibuf;
/* Read interface name. */
- stream_get(ifname_tmp, s, INTERFACE_NAMSIZ);
+ STREAM_GET(ifname_tmp, s, INTERFACE_NAMSIZ);
/* Lookup/create interface by name. */
+ if (!vrf_get(vrf_id, NULL)) {
+ zlog_debug(
+ "Rx'd interface add from Zebra, but VRF %u does not exist",
+ vrf_id);
+ return -1;
+ }
+
ifp = if_get_by_name(ifname_tmp, vrf_id);
zebra_interface_if_set_value(s, ifp);
if_new_via_zapi(ifp);
+
+ return 0;
+stream_failure:
+ return -1;
}
/*
@@ -1667,10 +1799,10 @@ static void zclient_interface_add(struct zclient *zclient, vrf_id_t vrf_id)
struct interface *zebra_interface_state_read(struct stream *s, vrf_id_t vrf_id)
{
struct interface *ifp;
- char ifname_tmp[INTERFACE_NAMSIZ];
+ char ifname_tmp[INTERFACE_NAMSIZ + 1] = {};
/* Read interface name. */
- stream_get(ifname_tmp, s, INTERFACE_NAMSIZ);
+ STREAM_GET(ifname_tmp, s, INTERFACE_NAMSIZ);
/* Lookup this by interface index. */
ifp = if_lookup_by_name(ifname_tmp, vrf_id);
@@ -1684,6 +1816,8 @@ struct interface *zebra_interface_state_read(struct stream *s, vrf_id_t vrf_id)
zebra_interface_if_set_value(s, ifp);
return ifp;
+stream_failure:
+ return NULL;
}
static void zclient_interface_delete(struct zclient *zclient, vrf_id_t vrf_id)
@@ -1737,21 +1871,23 @@ static void zclient_handle_error(ZAPI_CALLBACK_ARGS)
(*zclient->handle_error)(error);
}
-static void link_params_set_value(struct stream *s, struct if_link_params *iflp)
+static int link_params_set_value(struct stream *s, struct if_link_params *iflp)
{
if (iflp == NULL)
- return;
+ return -1;
+
+ uint32_t bwclassnum;
- iflp->lp_status = stream_getl(s);
- iflp->te_metric = stream_getl(s);
- iflp->max_bw = stream_getf(s);
- iflp->max_rsv_bw = stream_getf(s);
- uint32_t bwclassnum = stream_getl(s);
+ STREAM_GETL(s, iflp->lp_status);
+ STREAM_GETL(s, iflp->te_metric);
+ STREAM_GETF(s, iflp->max_bw);
+ STREAM_GETF(s, iflp->max_rsv_bw);
+ STREAM_GETL(s, bwclassnum);
{
unsigned int i;
for (i = 0; i < bwclassnum && i < MAX_CLASS_TYPE; i++)
- iflp->unrsv_bw[i] = stream_getf(s);
+ STREAM_GETF(s, iflp->unrsv_bw[i]);
if (i < bwclassnum)
flog_err(
EC_LIB_ZAPI_MISSMATCH,
@@ -1759,19 +1895,23 @@ static void link_params_set_value(struct stream *s, struct if_link_params *iflp)
" - outdated library?",
__func__, bwclassnum, MAX_CLASS_TYPE);
}
- iflp->admin_grp = stream_getl(s);
- iflp->rmt_as = stream_getl(s);
+ STREAM_GETL(s, iflp->admin_grp);
+ STREAM_GETL(s, iflp->rmt_as);
iflp->rmt_ip.s_addr = stream_get_ipv4(s);
- iflp->av_delay = stream_getl(s);
- iflp->min_delay = stream_getl(s);
- iflp->max_delay = stream_getl(s);
- iflp->delay_var = stream_getl(s);
+ STREAM_GETL(s, iflp->av_delay);
+ STREAM_GETL(s, iflp->min_delay);
+ STREAM_GETL(s, iflp->max_delay);
+ STREAM_GETL(s, iflp->delay_var);
- iflp->pkt_loss = stream_getf(s);
- iflp->res_bw = stream_getf(s);
- iflp->ava_bw = stream_getf(s);
- iflp->use_bw = stream_getf(s);
+ STREAM_GETF(s, iflp->pkt_loss);
+ STREAM_GETF(s, iflp->res_bw);
+ STREAM_GETF(s, iflp->ava_bw);
+ STREAM_GETF(s, iflp->use_bw);
+
+ return 0;
+stream_failure:
+ return -1;
}
struct interface *zebra_interface_link_params_read(struct stream *s,
@@ -1780,9 +1920,7 @@ struct interface *zebra_interface_link_params_read(struct stream *s,
struct if_link_params *iflp;
ifindex_t ifindex;
- assert(s);
-
- ifindex = stream_getl(s);
+ STREAM_GETL(s, ifindex);
struct interface *ifp = if_lookup_by_index(ifindex, vrf_id);
@@ -1796,36 +1934,41 @@ struct interface *zebra_interface_link_params_read(struct stream *s,
if ((iflp = if_link_params_get(ifp)) == NULL)
return NULL;
- link_params_set_value(s, iflp);
+ if (link_params_set_value(s, iflp) != 0)
+ goto stream_failure;
return ifp;
+
+stream_failure:
+ return NULL;
}
static void zebra_interface_if_set_value(struct stream *s,
struct interface *ifp)
{
uint8_t link_params_status = 0;
- ifindex_t old_ifindex;
+ ifindex_t old_ifindex, new_ifindex;
old_ifindex = ifp->ifindex;
/* Read interface's index. */
- if_set_index(ifp, stream_getl(s));
- ifp->status = stream_getc(s);
+ STREAM_GETL(s, new_ifindex);
+ if_set_index(ifp, new_ifindex);
+ STREAM_GETC(s, ifp->status);
/* Read interface's value. */
- ifp->flags = stream_getq(s);
- ifp->ptm_enable = stream_getc(s);
- ifp->ptm_status = stream_getc(s);
- ifp->metric = stream_getl(s);
- ifp->speed = stream_getl(s);
- ifp->mtu = stream_getl(s);
- ifp->mtu6 = stream_getl(s);
- ifp->bandwidth = stream_getl(s);
- ifp->link_ifindex = stream_getl(s);
- ifp->ll_type = stream_getl(s);
- ifp->hw_addr_len = stream_getl(s);
+ STREAM_GETQ(s, ifp->flags);
+ STREAM_GETC(s, ifp->ptm_enable);
+ STREAM_GETC(s, ifp->ptm_status);
+ STREAM_GETL(s, ifp->metric);
+ STREAM_GETL(s, ifp->speed);
+ STREAM_GETL(s, ifp->mtu);
+ STREAM_GETL(s, ifp->mtu6);
+ STREAM_GETL(s, ifp->bandwidth);
+ STREAM_GETL(s, ifp->link_ifindex);
+ STREAM_GETL(s, ifp->ll_type);
+ STREAM_GETL(s, ifp->hw_addr_len);
if (ifp->hw_addr_len)
- stream_get(ifp->hw_addr, s,
+ STREAM_GET(ifp->hw_addr, s,
MIN(ifp->hw_addr_len, INTERFACE_HWADDR_MAX));
/* Read Traffic Engineering status */
@@ -1837,6 +1980,11 @@ static void zebra_interface_if_set_value(struct stream *s,
}
nexthop_group_interface_state_change(ifp, old_ifindex);
+
+ return;
+stream_failure:
+ zlog_err("Could not parse interface values; aborting");
+ assert(!"Failed to parse interface values");
}
size_t zebra_interface_link_params_write(struct stream *s,
@@ -1935,7 +2083,7 @@ struct connected *zebra_interface_address_read(int type, struct stream *s,
memset(&d, 0, sizeof(d));
/* Get interface index. */
- ifindex = stream_getl(s);
+ STREAM_GETL(s, ifindex);
/* Lookup index. */
ifp = if_lookup_by_index(ifindex, vrf_id);
@@ -1948,16 +2096,18 @@ struct connected *zebra_interface_address_read(int type, struct stream *s,
}
/* Fetch flag. */
- ifc_flags = stream_getc(s);
+ STREAM_GETC(s, ifc_flags);
/* Fetch interface address. */
- d.family = p.family = stream_getc(s);
+ STREAM_GETC(s, d.family);
+ p.family = d.family;
plen = prefix_blen(&d);
- zclient_stream_get_prefix(s, &p);
+ if (zclient_stream_get_prefix(s, &p) != 0)
+ goto stream_failure;
/* Fetch destination address. */
- stream_get(&d.u.prefix, s, plen);
+ STREAM_GET(&d.u.prefix, s, plen);
/* N.B. NULL destination pointers are encoded as all zeroes */
dp = memconstant(&d.u.prefix, 0, plen) ? NULL : &d;
@@ -1993,6 +2143,9 @@ struct connected *zebra_interface_address_read(int type, struct stream *s,
}
return ifc;
+
+stream_failure:
+ return NULL;
}
/*
@@ -2028,7 +2181,7 @@ zebra_interface_nbr_address_read(int type, struct stream *s, vrf_id_t vrf_id)
struct nbr_connected *ifc;
/* Get interface index. */
- ifindex = stream_getl(s);
+ STREAM_GETL(s, ifindex);
/* Lookup index. */
ifp = if_lookup_by_index(ifindex, vrf_id);
@@ -2041,9 +2194,9 @@ zebra_interface_nbr_address_read(int type, struct stream *s, vrf_id_t vrf_id)
return NULL;
}
- p.family = stream_getc(s);
- stream_get(&p.u.prefix, s, prefix_blen(&p));
- p.prefixlen = stream_getc(s);
+ STREAM_GETC(s, p.family);
+ STREAM_GET(&p.u.prefix, s, prefix_blen(&p));
+ STREAM_GETC(s, p.prefixlen);
if (type == ZEBRA_INTERFACE_NBR_ADDRESS_ADD) {
/* Currently only supporting P2P links, so any new RA source
@@ -2067,18 +2220,21 @@ zebra_interface_nbr_address_read(int type, struct stream *s, vrf_id_t vrf_id)
}
return ifc;
+
+stream_failure:
+ return NULL;
}
struct interface *zebra_interface_vrf_update_read(struct stream *s,
vrf_id_t vrf_id,
vrf_id_t *new_vrf_id)
{
- char ifname[INTERFACE_NAMSIZ];
+ char ifname[INTERFACE_NAMSIZ + 1] = {};
struct interface *ifp;
vrf_id_t new_id;
/* Read interface name. */
- stream_get(ifname, s, INTERFACE_NAMSIZ);
+ STREAM_GET(ifname, s, INTERFACE_NAMSIZ);
/* Lookup interface. */
ifp = if_lookup_by_name(ifname, vrf_id);
@@ -2090,10 +2246,13 @@ struct interface *zebra_interface_vrf_update_read(struct stream *s,
}
/* Fetch new VRF Id. */
- new_id = stream_getl(s);
+ STREAM_GETL(s, new_id);
*new_vrf_id = new_id;
return ifp;
+
+stream_failure:
+ return NULL;
}
/* filter unwanted messages until the expected one arrives */
@@ -2202,8 +2361,11 @@ int lm_label_manager_connect(struct zclient *zclient, int async)
s = zclient->ibuf;
/* read instance and proto */
- uint8_t proto = stream_getc(s);
- uint16_t instance = stream_getw(s);
+ uint8_t proto;
+ uint16_t instance;
+
+ STREAM_GETC(s, proto);
+ STREAM_GETW(s, instance);
/* sanity */
if (proto != zclient->redist_default)
@@ -2218,11 +2380,14 @@ int lm_label_manager_connect(struct zclient *zclient, int async)
instance, zclient->instance);
/* result code */
- result = stream_getc(s);
+ STREAM_GETC(s, result);
if (zclient_debug)
zlog_debug("LM connect-response received, result %u", result);
return (int)result;
+
+stream_failure:
+ return -1;
}
/*
@@ -2330,8 +2495,11 @@ int lm_get_label_chunk(struct zclient *zclient, uint8_t keep, uint32_t base,
s = zclient->ibuf;
/* read proto and instance */
- uint8_t proto = stream_getc(s);
- uint16_t instance = stream_getw(s);
+ uint8_t proto;
+ uint8_t instance;
+
+ STREAM_GETC(s, proto);
+ STREAM_GETW(s, instance);
/* sanities */
if (proto != zclient->redist_default)
@@ -2353,10 +2521,10 @@ int lm_get_label_chunk(struct zclient *zclient, uint8_t keep, uint32_t base,
}
/* keep */
- response_keep = stream_getc(s);
+ STREAM_GETC(s, response_keep);
/* start and end labels */
- *start = stream_getl(s);
- *end = stream_getl(s);
+ STREAM_GETL(s, *start);
+ STREAM_GETL(s, *end);
/* not owning this response */
if (keep != response_keep) {
@@ -2378,6 +2546,9 @@ int lm_get_label_chunk(struct zclient *zclient, uint8_t keep, uint32_t base,
response_keep);
return 0;
+
+stream_failure:
+ return -1;
}
/**
@@ -2767,7 +2938,7 @@ int zebra_send_pw(struct zclient *zclient, int command, struct zapi_pw *pw)
/*
* Receive PW status update from Zebra and send it to LDE process.
*/
-void zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS, struct zapi_pw_status *pw)
+int zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS, struct zapi_pw_status *pw)
{
struct stream *s;
@@ -2776,8 +2947,12 @@ void zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS, struct zapi_pw_status *pw)
/* Get data. */
stream_get(pw->ifname, s, IF_NAMESIZE);
- pw->ifindex = stream_getl(s);
- pw->status = stream_getl(s);
+ STREAM_GETL(s, pw->ifindex);
+ STREAM_GETL(s, pw->status);
+
+ return 0;
+stream_failure:
+ return -1;
}
static void zclient_capability_decode(ZAPI_CALLBACK_ARGS)
@@ -2788,7 +2963,14 @@ static void zclient_capability_decode(ZAPI_CALLBACK_ARGS)
uint8_t mpls_enabled;
STREAM_GETL(s, vrf_backend);
- vrf_configure_backend(vrf_backend);
+
+ if (vrf_backend < 0 || vrf_configure_backend(vrf_backend)) {
+ flog_err(EC_LIB_ZAPI_ENCODE,
+ "%s: Garbage VRF backend type: %d\n", __func__,
+ vrf_backend);
+ goto stream_failure;
+ }
+
memset(&cap, 0, sizeof(cap));
STREAM_GETC(s, mpls_enabled);
diff --git a/lib/zclient.h b/lib/zclient.h
index 4de42a35bb..6e8066381f 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -258,6 +258,9 @@ struct zclient {
/* Is this a synchronous client? */
bool synchronous;
+ /* Session id (optional) to support clients with multiple sessions */
+ uint32_t session_id;
+
/* Socket to zebra daemon. */
int sock;
@@ -341,6 +344,9 @@ struct zclient {
#define ZAPI_MESSAGE_TAG 0x08
#define ZAPI_MESSAGE_MTU 0x10
#define ZAPI_MESSAGE_SRCPFX 0x20
+/* Backup nexthops are present */
+#define ZAPI_MESSAGE_BACKUP_NEXTHOPS 0x40
+
/*
* This should only be used by a DAEMON that needs to communicate
* the table being used is not in the VRF. You must pass the
@@ -377,14 +383,21 @@ struct zapi_nexthop {
struct ethaddr rmac;
uint32_t weight;
+
+ /* Index of backup nexthop */
+ uint8_t backup_idx;
};
/*
- * ZAPI nexthop flags values
+ * ZAPI nexthop flags values - we're encoding a single octet
+ * initially, so ensure that the on-the-wire encoding continues
+ * to match the number of valid flags.
*/
+
#define ZAPI_NEXTHOP_FLAG_ONLINK 0x01
#define ZAPI_NEXTHOP_FLAG_LABEL 0x02
#define ZAPI_NEXTHOP_FLAG_WEIGHT 0x04
+#define ZAPI_NEXTHOP_FLAG_HAS_BACKUP 0x08 /* Nexthop has a backup */
/*
* Some of these data structures do not map easily to
@@ -448,6 +461,10 @@ struct zapi_route {
uint16_t nexthop_num;
struct zapi_nexthop nexthops[MULTIPATH_NUM];
+ /* Support backup routes for IP FRR, TI-LFA, traffic engineering */
+ uint16_t backup_nexthop_num;
+ struct zapi_nexthop backup_nexthops[MULTIPATH_NUM];
+
uint8_t distance;
uint32_t metric;
@@ -709,7 +726,7 @@ zebra_interface_nbr_address_read(int, struct stream *, vrf_id_t);
extern struct interface *zebra_interface_vrf_update_read(struct stream *s,
vrf_id_t vrf_id,
vrf_id_t *new_vrf_id);
-extern void zebra_router_id_update_read(struct stream *s, struct prefix *rid);
+extern int zebra_router_id_update_read(struct stream *s, struct prefix *rid);
extern struct interface *zebra_interface_link_params_read(struct stream *s,
vrf_id_t vrf_id);
@@ -738,7 +755,8 @@ extern int zapi_labels_decode(struct stream *s, struct zapi_labels *zl);
extern int zebra_send_pw(struct zclient *zclient, int command,
struct zapi_pw *pw);
-extern void zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS, struct zapi_pw_status *pw);
+extern int zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS,
+ struct zapi_pw_status *pw);
extern int zclient_route_send(uint8_t, struct zclient *, struct zapi_route *);
extern int zclient_send_rnh(struct zclient *zclient, int command,
@@ -769,9 +787,12 @@ bool zapi_iptable_notify_decode(struct stream *s,
uint32_t *unique,
enum zapi_iptable_notify_owner *note);
-extern struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh);
+extern struct nexthop *
+nexthop_from_zapi_nexthop(const struct zapi_nexthop *znh);
int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh,
const struct nexthop *nh);
+int zapi_backup_nexthop_from_nexthop(struct zapi_nexthop *znh,
+ const struct nexthop *nh);
extern bool zapi_nexthop_update_decode(struct stream *s,
struct zapi_route *nhr);
diff --git a/lib/zlog.c b/lib/zlog.c
new file mode 100644
index 0000000000..45726755f8
--- /dev/null
+++ b/lib/zlog.c
@@ -0,0 +1,701 @@
+/*
+ * Copyright (c) 2015-19 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.
+ */
+
+#include "zebra.h"
+
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <time.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <pthread.h>
+
+/* gettid() & co. */
+#ifdef HAVE_PTHREAD_NP_H
+#include <pthread_np.h>
+#endif
+#ifdef linux
+#include <sys/syscall.h>
+#endif
+#ifdef __FreeBSD__
+#include <sys/thr.h>
+#endif
+#ifdef __NetBSD__
+#include <lwp.h>
+#endif
+#ifdef __DragonFly__
+#include <sys/lwp.h>
+#endif
+#ifdef __APPLE__
+#include <mach/mach_traps.h>
+#endif
+
+#include "memory.h"
+#include "atomlist.h"
+#include "printfrr.h"
+#include "frrcu.h"
+#include "zlog.h"
+
+DEFINE_MTYPE_STATIC(LIB, LOG_MESSAGE, "log message")
+DEFINE_MTYPE_STATIC(LIB, LOG_TLSBUF, "log thread-local buffer")
+
+DEFINE_HOOK(zlog_init, (const char *progname, const char *protoname,
+ unsigned short instance, uid_t uid, gid_t gid),
+ (progname, protoname, instance, uid, gid))
+DEFINE_KOOH(zlog_fini, (), ())
+DEFINE_HOOK(zlog_aux_init, (const char *prefix, int prio_min),
+ (prefix, prio_min))
+
+char zlog_prefix[128];
+size_t zlog_prefixsz;
+int zlog_tmpdirfd = -1;
+
+/* these are kept around because logging is initialized (and directories
+ * & files created) before zprivs code switches to the FRR user; therefore
+ * we need to chown() things so we don't get permission errors later when
+ * trying to delete things on shutdown
+ */
+static uid_t zlog_uid = -1;
+static gid_t zlog_gid = -1;
+
+DECLARE_ATOMLIST(zlog_targets, struct zlog_target, head);
+static struct zlog_targets_head zlog_targets;
+
+/* cf. zlog.h for additional comments on this struct.
+ *
+ * Note: you MUST NOT pass the format string + va_list to non-FRR format
+ * string functions (e.g. vsyslog, sd_journal_printv, ...) since FRR uses an
+ * extended prinf() with additional formats (%pI4 and the like).
+ *
+ * Also remember to use va_copy() on args.
+ */
+
+struct zlog_msg {
+ struct timespec ts;
+ int prio;
+
+ const char *fmt;
+ va_list args;
+
+ char *stackbuf;
+ size_t stackbufsz;
+ char *text;
+ size_t textlen;
+
+ /* This is always ISO8601 with sub-second precision 9 here, it's
+ * converted for callers as needed. ts_dot points to the "."
+ * separating sub-seconds. ts_zonetail is "Z" or "+00:00" for the
+ * local time offset.
+ *
+ * Valid if ZLOG_TS_ISO8601 is set.
+ * (0 if timestamp has not been formatted yet)
+ */
+ uint32_t ts_flags;
+ char ts_str[32], *ts_dot, ts_zonetail[8];
+};
+
+/* thread-local log message buffering
+ *
+ * This is strictly optional and set up by calling zlog_tls_buffer_init()
+ * on a particular thread.
+ *
+ * If in use, this will create a temporary file in /var/tmp which is used as
+ * memory-mapped MAP_SHARED log message buffer. The idea there is that buffer
+ * access doesn't require any syscalls, but in case of a crash the kernel
+ * knows to sync the memory back to disk. This way the user can still get the
+ * last log messages if there were any left unwritten in the buffer.
+ *
+ * Sizing this dynamically isn't particularly useful, so here's an 8k buffer
+ * with a message limit of 64 messages. Message metadata (e.g. priority,
+ * timestamp) aren't in the mmap region, so they're lost on crash, but we can
+ * live with that.
+ */
+
+#if defined(HAVE_OPENAT) && defined(HAVE_UNLINKAT)
+#define CAN_DO_TLS 1
+#endif
+
+#define TLS_LOG_BUF_SIZE 8192
+#define TLS_LOG_MAXMSG 64
+
+struct zlog_tls {
+ char *mmbuf;
+ size_t bufpos;
+
+ size_t nmsgs;
+ struct zlog_msg msgs[TLS_LOG_MAXMSG];
+ struct zlog_msg *msgp[TLS_LOG_MAXMSG];
+};
+
+static inline void zlog_tls_free(void *arg);
+
+/* proper ELF TLS is a bit faster than pthread_[gs]etspecific, so if it's
+ * available we'll use it here
+ */
+
+#ifdef __OpenBSD__
+static pthread_key_t zlog_tls_key;
+
+static void zlog_tls_key_init(void) __attribute__((_CONSTRUCTOR(500)));
+static void zlog_tls_key_init(void)
+{
+ pthread_key_create(&zlog_tls_key, zlog_tls_free);
+}
+
+static void zlog_tls_key_fini(void) __attribute__((_DESTRUCTOR(500)));
+static void zlog_tls_key_fini(void)
+{
+ pthread_key_delete(zlog_tls_key);
+}
+
+static inline struct zlog_tls *zlog_tls_get(void)
+{
+ return pthread_getspecific(zlog_tls_key);
+}
+
+static inline void zlog_tls_set(struct zlog_tls *val)
+{
+ pthread_setspecific(zlog_tls_key, val);
+}
+#else
+# ifndef thread_local
+# define thread_local __thread
+# endif
+
+static thread_local struct zlog_tls *zlog_tls_var
+ __attribute__((tls_model("initial-exec")));
+
+static inline struct zlog_tls *zlog_tls_get(void)
+{
+ return zlog_tls_var;
+}
+
+static inline void zlog_tls_set(struct zlog_tls *val)
+{
+ zlog_tls_var = val;
+}
+#endif
+
+#ifdef CAN_DO_TLS
+static long zlog_gettid(void)
+{
+ long rv = -1;
+#ifdef HAVE_PTHREAD_GETTHREADID_NP
+ rv = pthread_getthreadid_np();
+#elif defined(linux)
+ rv = syscall(__NR_gettid);
+#elif defined(__NetBSD__)
+ rv = _lwp_self();
+#elif defined(__FreeBSD__)
+ thr_self(&rv);
+#elif defined(__DragonFly__)
+ rv = lwp_gettid();
+#elif defined(__OpenBSD__)
+ rv = getthrid();
+#elif defined(__sun)
+ rv = pthread_self();
+#elif defined(__APPLE__)
+ rv = mach_thread_self();
+ mach_port_deallocate(mach_task_self(), rv);
+#endif
+ return rv;
+}
+
+void zlog_tls_buffer_init(void)
+{
+ struct zlog_tls *zlog_tls;
+ char mmpath[MAXPATHLEN];
+ int mmfd;
+ size_t i;
+
+ zlog_tls = zlog_tls_get();
+
+ if (zlog_tls || zlog_tmpdirfd < 0)
+ return;
+
+ zlog_tls = XCALLOC(MTYPE_LOG_TLSBUF, sizeof(*zlog_tls));
+ for (i = 0; i < array_size(zlog_tls->msgp); i++)
+ zlog_tls->msgp[i] = &zlog_tls->msgs[i];
+
+ snprintfrr(mmpath, sizeof(mmpath), "logbuf.%ld", zlog_gettid());
+
+ mmfd = openat(zlog_tmpdirfd, mmpath,
+ O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0600);
+ if (mmfd < 0) {
+ zlog_err("failed to open thread log buffer \"%s\": %s",
+ mmpath, strerror(errno));
+ goto out_anon;
+ }
+ fchown(mmfd, zlog_uid, zlog_gid);
+
+#ifdef HAVE_POSIX_FALLOCATE
+ if (posix_fallocate(mmfd, 0, TLS_LOG_BUF_SIZE) < 0) {
+#else
+ if (ftruncate(mmfd, TLS_LOG_BUF_SIZE) < 0) {
+#endif
+ zlog_err("failed to allocate thread log buffer \"%s\": %s",
+ mmpath, strerror(errno));
+ goto out_anon_unlink;
+ }
+
+ zlog_tls->mmbuf = mmap(NULL, TLS_LOG_BUF_SIZE, PROT_READ | PROT_WRITE,
+ MAP_SHARED, mmfd, 0);
+ if (zlog_tls->mmbuf == MAP_FAILED) {
+ zlog_err("failed to mmap thread log buffer \"%s\": %s",
+ mmpath, strerror(errno));
+ goto out_anon_unlink;
+ }
+
+ close(mmfd);
+ zlog_tls_set(zlog_tls);
+ return;
+
+out_anon_unlink:
+ unlink(mmpath);
+ close(mmfd);
+out_anon:
+
+#ifndef MAP_ANONYMOUS
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+ zlog_tls->mmbuf = mmap(NULL, TLS_LOG_BUF_SIZE, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+
+ if (!zlog_tls->mmbuf) {
+ zlog_err("failed to anonymous-mmap thread log buffer: %s",
+ strerror(errno));
+ XFREE(MTYPE_LOG_TLSBUF, zlog_tls);
+ zlog_tls_set(NULL);
+ return;
+ }
+
+ zlog_tls_set(zlog_tls);
+}
+
+void zlog_tls_buffer_fini(void)
+{
+ char mmpath[MAXPATHLEN];
+
+ zlog_tls_buffer_flush();
+
+ zlog_tls_free(zlog_tls_get());
+ zlog_tls_set(NULL);
+
+ snprintfrr(mmpath, sizeof(mmpath), "logbuf.%ld", zlog_gettid());
+ if (unlinkat(zlog_tmpdirfd, mmpath, 0))
+ zlog_err("unlink logbuf: %s (%d)", strerror(errno), errno);
+}
+
+#else /* !CAN_DO_TLS */
+void zlog_tls_buffer_init(void)
+{
+}
+
+void zlog_tls_buffer_fini(void)
+{
+}
+#endif
+
+static inline void zlog_tls_free(void *arg)
+{
+ struct zlog_tls *zlog_tls = arg;
+
+ if (!zlog_tls)
+ return;
+
+ munmap(zlog_tls->mmbuf, TLS_LOG_BUF_SIZE);
+ XFREE(MTYPE_LOG_TLSBUF, zlog_tls);
+}
+
+void zlog_tls_buffer_flush(void)
+{
+ struct zlog_target *zt;
+ struct zlog_tls *zlog_tls = zlog_tls_get();
+
+ if (!zlog_tls)
+ return;
+ if (!zlog_tls->nmsgs)
+ return;
+
+ rcu_read_lock();
+ frr_each (zlog_targets, &zlog_targets, zt) {
+ if (!zt->logfn)
+ continue;
+
+ zt->logfn(zt, zlog_tls->msgp, zlog_tls->nmsgs);
+ }
+ rcu_read_unlock();
+
+ zlog_tls->bufpos = 0;
+ zlog_tls->nmsgs = 0;
+}
+
+
+static void vzlog_notls(int prio, const char *fmt, va_list ap)
+{
+ struct zlog_target *zt;
+ struct zlog_msg stackmsg = {
+ .prio = prio & LOG_PRIMASK,
+ .fmt = fmt,
+ }, *msg = &stackmsg;
+ char stackbuf[512];
+
+ clock_gettime(CLOCK_REALTIME, &msg->ts);
+ va_copy(msg->args, ap);
+ msg->stackbuf = stackbuf;
+ msg->stackbufsz = sizeof(stackbuf);
+
+ rcu_read_lock();
+ frr_each (zlog_targets, &zlog_targets, zt) {
+ if (prio > zt->prio_min)
+ continue;
+ if (!zt->logfn)
+ continue;
+
+ zt->logfn(zt, &msg, 1);
+ }
+ rcu_read_unlock();
+
+ va_end(msg->args);
+ if (msg->text && msg->text != stackbuf)
+ XFREE(MTYPE_LOG_MESSAGE, msg->text);
+}
+
+static void vzlog_tls(struct zlog_tls *zlog_tls, int prio,
+ const char *fmt, va_list ap)
+{
+ struct zlog_target *zt;
+ struct zlog_msg *msg;
+ char *buf;
+ bool ignoremsg = true;
+ bool immediate = false;
+
+ /* avoid further processing cost if no target wants this message */
+ rcu_read_lock();
+ frr_each (zlog_targets, &zlog_targets, zt) {
+ if (prio > zt->prio_min)
+ continue;
+ ignoremsg = false;
+ break;
+ }
+ rcu_read_unlock();
+
+ if (ignoremsg)
+ return;
+
+ msg = &zlog_tls->msgs[zlog_tls->nmsgs];
+ zlog_tls->nmsgs++;
+ if (zlog_tls->nmsgs == array_size(zlog_tls->msgs))
+ immediate = true;
+
+ memset(msg, 0, sizeof(*msg));
+ clock_gettime(CLOCK_REALTIME, &msg->ts);
+ va_copy(msg->args, ap);
+ msg->stackbuf = buf = zlog_tls->mmbuf + zlog_tls->bufpos;
+ msg->stackbufsz = TLS_LOG_BUF_SIZE - zlog_tls->bufpos - 1;
+ msg->fmt = fmt;
+ msg->prio = prio & LOG_PRIMASK;
+ if (msg->prio < LOG_INFO)
+ immediate = true;
+
+ if (!immediate) {
+ /* messages written later need to take the formatting cost
+ * immediately since we can't hold a reference on varargs
+ */
+ zlog_msg_text(msg, NULL);
+
+ if (msg->text != buf)
+ /* zlog_msg_text called malloc() on us :( */
+ immediate = true;
+ else {
+ zlog_tls->bufpos += msg->textlen + 1;
+ /* write a second \0 to mark current end position
+ * (in case of crash this signals end of unwritten log
+ * messages in mmap'd logbuf file)
+ */
+ zlog_tls->mmbuf[zlog_tls->bufpos] = '\0';
+
+ /* avoid malloc() for next message */
+ if (TLS_LOG_BUF_SIZE - zlog_tls->bufpos < 256)
+ immediate = true;
+ }
+ }
+
+ if (immediate)
+ zlog_tls_buffer_flush();
+
+ va_end(msg->args);
+ if (msg->text && msg->text != buf)
+ XFREE(MTYPE_LOG_MESSAGE, msg->text);
+}
+
+void vzlog(int prio, const char *fmt, va_list ap)
+{
+ struct zlog_tls *zlog_tls = zlog_tls_get();
+
+ if (zlog_tls)
+ vzlog_tls(zlog_tls, prio, fmt, ap);
+ else
+ vzlog_notls(prio, fmt, ap);
+}
+
+void zlog_sigsafe(const char *text, size_t len)
+{
+ struct zlog_target *zt;
+ const char *end = text + len, *nlpos;
+
+ while (text < end) {
+ nlpos = memchr(text, '\n', end - text);
+ if (!nlpos)
+ nlpos = end;
+
+ frr_each (zlog_targets, &zlog_targets, zt) {
+ if (LOG_CRIT > zt->prio_min)
+ continue;
+ if (!zt->logfn_sigsafe)
+ continue;
+
+ zt->logfn_sigsafe(zt, text, nlpos - text);
+ }
+
+ if (nlpos == end)
+ break;
+ text = nlpos + 1;
+ }
+}
+
+
+int zlog_msg_prio(struct zlog_msg *msg)
+{
+ return msg->prio;
+}
+
+const char *zlog_msg_text(struct zlog_msg *msg, size_t *textlen)
+{
+ if (!msg->text) {
+ va_list args;
+
+ va_copy(args, msg->args);
+ msg->text = vasnprintfrr(MTYPE_LOG_MESSAGE, msg->stackbuf,
+ msg->stackbufsz, msg->fmt, args);
+ msg->textlen = strlen(msg->text);
+ va_end(args);
+ }
+ if (textlen)
+ *textlen = msg->textlen;
+ return msg->text;
+}
+
+#define ZLOG_TS_FORMAT (ZLOG_TS_ISO8601 | ZLOG_TS_LEGACY)
+#define ZLOG_TS_FLAGS ~ZLOG_TS_PREC
+
+size_t zlog_msg_ts(struct zlog_msg *msg, char *out, size_t outsz,
+ uint32_t flags)
+{
+ size_t len1;
+
+ if (!(flags & ZLOG_TS_FORMAT))
+ return 0;
+
+ if (!(msg->ts_flags & ZLOG_TS_FORMAT) ||
+ ((msg->ts_flags ^ flags) & ZLOG_TS_UTC)) {
+ struct tm tm;
+
+ if (flags & ZLOG_TS_UTC)
+ gmtime_r(&msg->ts.tv_sec, &tm);
+ else
+ localtime_r(&msg->ts.tv_sec, &tm);
+
+ strftime(msg->ts_str, sizeof(msg->ts_str),
+ "%Y-%m-%dT%H:%M:%S", &tm);
+
+ if (flags & ZLOG_TS_UTC) {
+ msg->ts_zonetail[0] = 'Z';
+ msg->ts_zonetail[1] = '\0';
+ } else
+ snprintfrr(msg->ts_zonetail, sizeof(msg->ts_zonetail),
+ "%+03d:%02d",
+ (int)(tm.tm_gmtoff / 3600),
+ (int)(labs(tm.tm_gmtoff) / 60) % 60);
+
+ msg->ts_dot = msg->ts_str + strlen(msg->ts_str);
+ snprintfrr(msg->ts_dot,
+ msg->ts_str + sizeof(msg->ts_str) - msg->ts_dot,
+ ".%09lu", (unsigned long)msg->ts.tv_nsec);
+
+ msg->ts_flags = ZLOG_TS_ISO8601 | (flags & ZLOG_TS_UTC);
+ }
+
+ len1 = flags & ZLOG_TS_PREC;
+ len1 = (msg->ts_dot - msg->ts_str) + (len1 ? len1 + 1 : 0);
+
+ if (len1 > strlen(msg->ts_str))
+ len1 = strlen(msg->ts_str);
+
+ if (flags & ZLOG_TS_LEGACY) {
+ if (len1 + 1 > outsz)
+ return 0;
+
+ /* just swap out the formatting, faster than redoing it */
+ for (char *p = msg->ts_str; p < msg->ts_str + len1; p++) {
+ switch (*p) {
+ case '-':
+ *out++ = '/';
+ break;
+ case 'T':
+ *out++ = ' ';
+ break;
+ default:
+ *out++ = *p;
+ }
+ }
+ *out = '\0';
+ return len1;
+ } else {
+ size_t len2 = strlen(msg->ts_zonetail);
+
+ if (len1 + len2 + 1 > outsz)
+ return 0;
+ memcpy(out, msg->ts_str, len1);
+ memcpy(out + len1, msg->ts_zonetail, len2);
+ out[len1 + len2] = '\0';
+ return len1 + len2;
+ }
+}
+
+/* setup functions */
+
+struct zlog_target *zlog_target_clone(struct memtype *mt,
+ struct zlog_target *oldzt, size_t size)
+{
+ struct zlog_target *newzt;
+
+ newzt = XCALLOC(mt, size);
+ if (oldzt) {
+ newzt->prio_min = oldzt->prio_min;
+ newzt->logfn = oldzt->logfn;
+ newzt->logfn_sigsafe = oldzt->logfn_sigsafe;
+ }
+
+ return newzt;
+}
+
+struct zlog_target *zlog_target_replace(struct zlog_target *oldzt,
+ struct zlog_target *newzt)
+{
+ if (newzt)
+ zlog_targets_add_tail(&zlog_targets, newzt);
+ if (oldzt)
+ zlog_targets_del(&zlog_targets, oldzt);
+ return oldzt;
+}
+
+
+/* common init */
+
+#define TMPBASEDIR "/var/tmp/frr"
+
+static char zlog_tmpdir[MAXPATHLEN];
+
+void zlog_aux_init(const char *prefix, int prio_min)
+{
+ if (prefix)
+ strlcpy(zlog_prefix, prefix, sizeof(zlog_prefix));
+
+ hook_call(zlog_aux_init, prefix, prio_min);
+}
+
+void zlog_init(const char *progname, const char *protoname,
+ unsigned short instance, uid_t uid, gid_t gid)
+{
+ zlog_uid = uid;
+ zlog_gid = gid;
+
+ if (instance) {
+ snprintfrr(zlog_tmpdir, sizeof(zlog_tmpdir),
+ "/var/tmp/frr/%s-%d.%ld",
+ progname, instance, (long)getpid());
+
+ zlog_prefixsz = snprintfrr(zlog_prefix, sizeof(zlog_prefix),
+ "%s[%d]: ", protoname, instance);
+ } else {
+ snprintfrr(zlog_tmpdir, sizeof(zlog_tmpdir),
+ "/var/tmp/frr/%s.%ld",
+ progname, (long)getpid());
+
+ zlog_prefixsz = snprintfrr(zlog_prefix, sizeof(zlog_prefix),
+ "%s: ", protoname);
+ }
+
+ if (mkdir(TMPBASEDIR, 0700) != 0) {
+ if (errno != EEXIST) {
+ zlog_err("failed to mkdir \"%s\": %s",
+ TMPBASEDIR, strerror(errno));
+ goto out_warn;
+ }
+ }
+ chown(TMPBASEDIR, zlog_uid, zlog_gid);
+
+ if (mkdir(zlog_tmpdir, 0700) != 0) {
+ zlog_err("failed to mkdir \"%s\": %s",
+ zlog_tmpdir, strerror(errno));
+ goto out_warn;
+ }
+
+#ifdef O_PATH
+ zlog_tmpdirfd = open(zlog_tmpdir,
+ O_PATH | O_RDONLY | O_CLOEXEC);
+#else
+ zlog_tmpdirfd = open(zlog_tmpdir,
+ O_DIRECTORY | O_RDONLY | O_CLOEXEC);
+#endif
+ if (zlog_tmpdirfd < 0) {
+ zlog_err("failed to open \"%s\": %s",
+ zlog_tmpdir, strerror(errno));
+ goto out_warn;
+ }
+
+#ifdef AT_EMPTY_PATH
+ fchownat(zlog_tmpdirfd, "", zlog_uid, zlog_gid, AT_EMPTY_PATH);
+#else
+ chown(zlog_tmpdir, zlog_uid, zlog_gid);
+#endif
+
+ hook_call(zlog_init, progname, protoname, instance, uid, gid);
+ return;
+
+out_warn:
+ zlog_err("crashlog and per-thread log buffering unavailable!");
+ hook_call(zlog_init, progname, protoname, instance, uid, gid);
+}
+
+void zlog_fini(void)
+{
+ hook_call(zlog_fini);
+
+ if (zlog_tmpdirfd >= 0) {
+ close(zlog_tmpdirfd);
+ zlog_tmpdirfd = -1;
+
+ if (rmdir(zlog_tmpdir))
+ zlog_err("failed to rmdir \"%s\": %s",
+ zlog_tmpdir, strerror(errno));
+ }
+}
diff --git a/lib/zlog.h b/lib/zlog.h
new file mode 100644
index 0000000000..fd42ad50e4
--- /dev/null
+++ b/lib/zlog.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2015-19 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_ZLOG_H
+#define _FRR_ZLOG_H
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/uio.h>
+
+#include "atomlist.h"
+#include "frrcu.h"
+#include "memory.h"
+#include "hook.h"
+
+extern char zlog_prefix[];
+extern size_t zlog_prefixsz;
+extern int zlog_tmpdirfd;
+
+/* 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
+ * on context, it may still be the right thing to use fprintf though -- try to
+ * determine wether something is a log message or something else.
+ */
+
+extern void vzlog(int prio, const char *fmt, va_list ap);
+
+__attribute__ ((format (printf, 2, 3)))
+static inline void zlog(int prio, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vzlog(prio, fmt, ap);
+ 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__)
+
+extern void zlog_sigsafe(const char *text, size_t len);
+
+/* extra priority value to disable a target without deleting it */
+#define ZLOG_DISABLED (LOG_EMERG-1)
+
+/* zlog_msg encapsulates a particular logging call from somewhere in the code.
+ * The same struct is passed around to all zlog_targets.
+ *
+ * This is used to defer formatting the log message until it is actually
+ * requested by one of the targets. If none of the targets needs the message
+ * formatted, the formatting call is avoided entirely.
+ *
+ * This struct is opaque / private to the core zlog code. Logging targets
+ * should use zlog_msg_* functions to get text / timestamps / ... for a
+ * message.
+ */
+
+struct zlog_msg;
+
+extern int zlog_msg_prio(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);
+
+/* timestamp formatting control flags */
+
+/* sub-second digit count */
+#define ZLOG_TS_PREC 0xfU
+
+/* 8601: 0000-00-00T00:00:00Z (if used with ZLOG_TS_UTC)
+ * 0000-00-00T00:00:00+00:00 (otherwise)
+ * Legacy: 0000/00/00 00:00:00 (no TZ indicated!)
+ */
+#define ZLOG_TS_ISO8601 (1 << 8)
+#define ZLOG_TS_LEGACY (1 << 9)
+
+/* default is local time zone */
+#define ZLOG_TS_UTC (1 << 10)
+
+extern size_t zlog_msg_ts(struct zlog_msg *msg, char *out, size_t outsz,
+ uint32_t flags);
+
+/* This list & struct implements the actual logging targets. It is accessed
+ * lock-free from all threads, and thus MUST only be changed atomically, i.e.
+ * RCU.
+ *
+ * Since there's no atomic replace, the replacement action is an add followed
+ * by a delete. This means that during logging config changes, log messages
+ * may be duplicated in the log target that is being changed. The old entry
+ * being changed MUST also at the very least not crash or do other stupid
+ * things.
+ *
+ * This list and struct are NOT related to config. Logging config is kept
+ * separately, and results in creating appropriate zlog_target(s) to realize
+ * the config. Log targets may also be created from varying sources, e.g.
+ * command line options, or VTY commands ("log monitor").
+ *
+ * struct zlog_target is intended to be embedded into a larger structure that
+ * contains additional field for the specific logging target, e.g. an fd or
+ * additional options. It MUST be the first field in that larger struct.
+ */
+
+PREDECL_ATOMLIST(zlog_targets)
+struct zlog_target {
+ struct zlog_targets_item head;
+
+ int prio_min;
+
+ void (*logfn)(struct zlog_target *zt, struct zlog_msg *msg[],
+ size_t nmsgs);
+
+ /* for crash handlers, set to NULL if log target can't write crash logs
+ * without possibly deadlocking (AS-Safe)
+ *
+ * text is not \0 terminated & split up into lines (e.g. no \n)
+ */
+ void (*logfn_sigsafe)(struct zlog_target *zt, const char *text,
+ size_t len);
+
+ struct rcu_head rcu_head;
+};
+
+/* make a copy for RCUpdating. oldzt may be NULL to allocate a fresh one. */
+extern struct zlog_target *zlog_target_clone(struct memtype *mt,
+ struct zlog_target *oldzt,
+ size_t size);
+
+/* update the zlog_targets list; both oldzt and newzt may be NULL. You
+ * still need to zlog_target_free() the old target afterwards if it wasn't
+ * NULL.
+ *
+ * Returns oldzt so you can zlog_target_free(zlog_target_replace(old, new));
+ * (Some log targets may need extra cleanup inbetween, but remember the old
+ * target MUST remain functional until the end of the current RCU cycle.)
+ */
+extern struct zlog_target *zlog_target_replace(struct zlog_target *oldzt,
+ struct zlog_target *newzt);
+
+/* Mostly for symmetry for zlog_target_clone(), just rcu_free() internally. */
+#define zlog_target_free(mt, zt) \
+ rcu_free(mt, zt, rcu_head)
+
+extern void zlog_init(const char *progname, const char *protoname,
+ unsigned short instance, uid_t uid, gid_t gid);
+DECLARE_HOOK(zlog_init, (const char *progname, const char *protoname,
+ unsigned short instance, uid_t uid, gid_t gid),
+ (progname, protoname, instance, uid, gid))
+
+extern void zlog_fini(void);
+DECLARE_KOOH(zlog_fini, (), ())
+
+/* for tools & test programs, i.e. anything not a daemon.
+ * (no cleanup needed at exit)
+ */
+extern void zlog_aux_init(const char *prefix, int prio_min);
+DECLARE_HOOK(zlog_aux_init, (const char *prefix, int prio_min),
+ (prefix, prio_min))
+
+extern void zlog_startup_end(void);
+
+extern void zlog_tls_buffer_init(void);
+extern void zlog_tls_buffer_flush(void);
+extern void zlog_tls_buffer_fini(void);
+
+#endif /* _FRR_ZLOG_H */
diff --git a/lib/zlog_targets.c b/lib/zlog_targets.c
new file mode 100644
index 0000000000..b23ab073b4
--- /dev/null
+++ b/lib/zlog_targets.c
@@ -0,0 +1,574 @@
+/*
+ * Copyright (c) 2015-19 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.
+ */
+
+#include "zebra.h"
+
+#include <sys/un.h>
+#include <syslog.h>
+
+#include "memory.h"
+#include "frrcu.h"
+#include "frr_pthread.h"
+#include "printfrr.h"
+#include "zlog.h"
+#include "zlog_targets.h"
+
+/* these allocations are intentionally left active even when doing full exit
+ * cleanup, in order to keep the logging subsystem fully functional until the
+ * absolute end.
+ */
+
+DECLARE_MGROUP(LOG)
+DEFINE_MGROUP_ACTIVEATEXIT(LOG, "logging subsystem")
+
+DEFINE_MTYPE_STATIC(LOG, LOG_FD, "log file target")
+DEFINE_MTYPE_STATIC(LOG, LOG_FD_NAME, "log file name")
+DEFINE_MTYPE_STATIC(LOG, LOG_FD_ROTATE, "log file rotate helper")
+DEFINE_MTYPE_STATIC(LOG, LOG_SYSL, "syslog target")
+
+struct zlt_fd {
+ struct zlog_target zt;
+
+ atomic_uint_fast32_t fd;
+
+ char ts_subsec;
+ bool record_priority;
+
+ struct rcu_head_close head_close;
+};
+
+static const char * const prionames[] = {
+ [LOG_EMERG] = "emergencies: ",
+ [LOG_ALERT] = "alerts: ",
+ [LOG_CRIT] = "critical: ",
+ [LOG_ERR] = "errors: ",
+ [LOG_WARNING] = "warnings: ",
+ [LOG_NOTICE] = "notifications: ",
+ [LOG_INFO] = "informational: ",
+ [LOG_DEBUG] = "debugging: ",
+};
+
+void zlog_fd(struct zlog_target *zt, struct zlog_msg *msgs[], size_t nmsgs)
+{
+ struct zlt_fd *zte = container_of(zt, struct zlt_fd, zt);
+ int fd;
+ size_t i, textlen, iovpos = 0;
+ size_t niov = MIN(4 * nmsgs + 1, IOV_MAX);
+ struct iovec iov[niov];
+ /* "\nYYYY-MM-DD HH:MM:SS.NNNNNNNNN+ZZ:ZZ " = 37 chars */
+#define TS_LEN 40
+ char ts_buf[TS_LEN * nmsgs], *ts_pos = ts_buf;
+
+ fd = atomic_load_explicit(&zte->fd, memory_order_relaxed);
+
+ for (i = 0; i < nmsgs; i++) {
+ struct zlog_msg *msg = msgs[i];
+ int prio = zlog_msg_prio(msg);
+
+ if (prio > zt->prio_min)
+ continue;
+
+ iov[iovpos].iov_base = ts_pos;
+ if (iovpos > 0)
+ *ts_pos++ = '\n';
+ ts_pos += zlog_msg_ts(msg, ts_pos, sizeof(ts_buf) - 1
+ - (ts_pos - ts_buf),
+ ZLOG_TS_LEGACY | zte->ts_subsec);
+ *ts_pos++ = ' ';
+ iov[iovpos].iov_len = ts_pos - (char *)iov[iovpos].iov_base;
+
+ iovpos++;
+
+ if (zte->record_priority) {
+ iov[iovpos].iov_base = (char *)prionames[prio];
+ iov[iovpos].iov_len = strlen(iov[iovpos].iov_base);
+
+ iovpos++;
+ }
+
+ iov[iovpos].iov_base = zlog_prefix;
+ iov[iovpos].iov_len = zlog_prefixsz;
+
+ iovpos++;
+
+ iov[iovpos].iov_base = (char *)zlog_msg_text(msg, &textlen);
+ iov[iovpos].iov_len = textlen;
+
+ iovpos++;
+
+ if (ts_buf + sizeof(ts_buf) - ts_pos < TS_LEN
+ || i + 1 == nmsgs
+ || array_size(iov) - iovpos < 5) {
+ iov[iovpos].iov_base = (char *)"\n";
+ iov[iovpos].iov_len = 1;
+
+ iovpos++;
+
+ writev(fd, iov, iovpos);
+
+ iovpos = 0;
+ ts_pos = ts_buf;
+ }
+ }
+
+ assert(iovpos == 0);
+}
+
+static void zlog_fd_sigsafe(struct zlog_target *zt, const char *text,
+ size_t len)
+{
+ struct zlt_fd *zte = container_of(zt, struct zlt_fd, zt);
+ struct iovec iov[4];
+ int fd;
+
+ iov[0].iov_base = (char *)prionames[LOG_CRIT];
+ iov[0].iov_len = zte->record_priority ? strlen(iov[0].iov_base) : 0;
+
+ iov[1].iov_base = zlog_prefix;
+ iov[1].iov_len = zlog_prefixsz;
+
+ iov[2].iov_base = (char *)text;
+ iov[2].iov_len = len;
+
+ iov[3].iov_base = (char *)"\n";
+ iov[3].iov_len = 1;
+
+ fd = atomic_load_explicit(&zte->fd, memory_order_relaxed);
+
+ writev(fd, iov, array_size(iov));
+}
+
+/*
+ * (re-)configuration
+ */
+
+void zlog_file_init(struct zlog_cfg_file *zcf)
+{
+ memset(zcf, 0, sizeof(*zcf));
+ zcf->prio_min = ZLOG_DISABLED;
+ zcf->fd = -1;
+ pthread_mutex_init(&zcf->cfg_mtx, NULL);
+}
+
+static void zlog_file_target_free(struct zlt_fd *zlt)
+{
+ if (!zlt)
+ return;
+
+ rcu_close(&zlt->head_close, zlt->fd);
+ rcu_free(MTYPE_LOG_FD, zlt, zt.rcu_head);
+}
+
+void zlog_file_fini(struct zlog_cfg_file *zcf)
+{
+ if (zcf->active) {
+ struct zlt_fd *ztf;
+ struct zlog_target *zt;
+
+ zt = zlog_target_replace(&zcf->active->zt, NULL);
+ ztf = container_of(zt, struct zlt_fd, zt);
+ zlog_file_target_free(ztf);
+ }
+ XFREE(MTYPE_LOG_FD_NAME, zcf->filename);
+ pthread_mutex_destroy(&zcf->cfg_mtx);
+}
+
+static bool zlog_file_cycle(struct zlog_cfg_file *zcf)
+{
+ struct zlog_target *zt, *old;
+ struct zlt_fd *zlt = NULL;
+ int fd;
+ bool rv = true;
+
+ do {
+ if (zcf->prio_min == ZLOG_DISABLED)
+ break;
+
+ if (zcf->fd != -1)
+ fd = dup(zcf->fd);
+ else if (zcf->filename)
+ fd = open(zcf->filename,
+ O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC
+ | O_NOCTTY,
+ LOGFILE_MASK);
+ else
+ fd = -1;
+
+ if (fd < 0) {
+ rv = false;
+ break;
+ }
+
+ zt = zlog_target_clone(MTYPE_LOG_FD, &zcf->active->zt,
+ sizeof(*zlt));
+ zlt = container_of(zt, struct zlt_fd, zt);
+
+ zlt->fd = fd;
+ zlt->record_priority = zcf->record_priority;
+ zlt->ts_subsec = zcf->ts_subsec;
+
+ zlt->zt.prio_min = zcf->prio_min;
+ zlt->zt.logfn = zcf->zlog_wrap ? zcf->zlog_wrap : zlog_fd;
+ zlt->zt.logfn_sigsafe = zlog_fd_sigsafe;
+ } while (0);
+
+ old = zlog_target_replace(&zcf->active->zt, &zlt->zt);
+ zcf->active = zlt;
+
+ zlog_file_target_free(container_of(old, struct zlt_fd, zt));
+
+ return rv;
+}
+
+void zlog_file_set_other(struct zlog_cfg_file *zcf)
+{
+ frr_with_mutex(&zcf->cfg_mtx) {
+ zlog_file_cycle(zcf);
+ }
+}
+
+bool zlog_file_set_filename(struct zlog_cfg_file *zcf, const char *filename)
+{
+ frr_with_mutex(&zcf->cfg_mtx) {
+ XFREE(MTYPE_LOG_FD_NAME, zcf->filename);
+ zcf->filename = XSTRDUP(MTYPE_LOG_FD_NAME, filename);
+ zcf->fd = -1;
+
+ return zlog_file_cycle(zcf);
+ }
+ assert(0);
+}
+
+bool zlog_file_set_fd(struct zlog_cfg_file *zcf, int fd)
+{
+ frr_with_mutex(&zcf->cfg_mtx) {
+ if (zcf->fd == fd)
+ return true;
+
+ XFREE(MTYPE_LOG_FD_NAME, zcf->filename);
+ zcf->fd = fd;
+
+ return zlog_file_cycle(zcf);
+ }
+ assert(0);
+}
+
+struct rcu_close_rotate {
+ struct rcu_head_close head_close;
+ struct rcu_head head_self;
+};
+
+bool zlog_file_rotate(struct zlog_cfg_file *zcf)
+{
+ struct rcu_close_rotate *rcr;
+ int fd;
+
+ frr_with_mutex(&zcf->cfg_mtx) {
+ if (!zcf->active || !zcf->filename)
+ return true;
+
+ fd = open(zcf->filename,
+ O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC | O_NOCTTY,
+ LOGFILE_MASK);
+ if (fd < 0)
+ return false;
+
+ fd = atomic_exchange_explicit(&zcf->active->fd,
+ (uint_fast32_t)fd,
+ memory_order_relaxed);
+ }
+
+ rcr = XCALLOC(MTYPE_LOG_FD_ROTATE, sizeof(*rcr));
+ rcu_close(&rcr->head_close, fd);
+ rcu_free(MTYPE_LOG_FD_ROTATE, rcr, head_self);
+
+ return true;
+}
+
+/* fixed crash logging */
+
+static struct zlt_fd zlog_crashlog;
+
+static void zlog_crashlog_sigsafe(struct zlog_target *zt, const char *text,
+ size_t len)
+{
+ static int crashlog_fd = -1;
+
+ if (crashlog_fd == -1) {
+#ifdef HAVE_OPENAT
+ crashlog_fd = openat(zlog_tmpdirfd, "crashlog",
+ O_WRONLY | O_APPEND | O_CREAT,
+ LOGFILE_MASK);
+#endif
+ if (crashlog_fd < 0)
+ crashlog_fd = -2;
+ }
+
+ if (crashlog_fd == -2)
+ return;
+
+ zlog_crashlog.fd = crashlog_fd;
+ zlog_fd_sigsafe(&zlog_crashlog.zt, text, len);
+}
+
+/* this is used for assert failures (they don't need AS-Safe logging) */
+static void zlog_crashlog_plain(struct zlog_target *zt, struct zlog_msg *msgs[],
+ size_t nmsgs)
+{
+ size_t i, len;
+ const char *text;
+
+ for (i = 0; i < nmsgs; i++) {
+ if (zlog_msg_prio(msgs[i]) > zt->prio_min)
+ continue;
+
+ text = zlog_msg_text(msgs[i], &len);
+ zlog_crashlog_sigsafe(zt, text, len);
+ }
+}
+
+static void zlog_crashlog_init(void)
+{
+ zlog_crashlog.zt.prio_min = LOG_CRIT;
+ zlog_crashlog.zt.logfn = zlog_crashlog_plain;
+ zlog_crashlog.zt.logfn_sigsafe = zlog_crashlog_sigsafe;
+ zlog_crashlog.fd = -1;
+
+ zlog_target_replace(NULL, &zlog_crashlog.zt);
+}
+
+/* fixed logging for test/auxiliary programs */
+
+static struct zlt_fd zlog_aux_stdout;
+static bool zlog_is_aux;
+
+static int zlt_aux_init(const char *prefix, int prio_min)
+{
+ zlog_is_aux = true;
+
+ zlog_aux_stdout.zt.prio_min = prio_min;
+ zlog_aux_stdout.zt.logfn = zlog_fd;
+ zlog_aux_stdout.zt.logfn_sigsafe = zlog_fd_sigsafe;
+ zlog_aux_stdout.fd = STDOUT_FILENO;
+
+ zlog_target_replace(NULL, &zlog_aux_stdout.zt);
+ zlog_startup_end();
+ return 0;
+}
+
+static int zlt_init(const char *progname, const char *protoname,
+ unsigned short instance, uid_t uid, gid_t gid)
+{
+ openlog(progname, LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON);
+ return 0;
+}
+
+static int zlt_fini(void)
+{
+ closelog();
+ return 0;
+}
+
+/* fixed startup logging to stderr */
+
+static struct zlt_fd zlog_startup_stderr;
+
+__attribute__((_CONSTRUCTOR(450))) static void zlog_startup_init(void)
+{
+ zlog_startup_stderr.zt.prio_min = LOG_WARNING;
+ zlog_startup_stderr.zt.logfn = zlog_fd;
+ zlog_startup_stderr.zt.logfn_sigsafe = zlog_fd_sigsafe;
+ zlog_startup_stderr.fd = STDERR_FILENO;
+
+ zlog_target_replace(NULL, &zlog_startup_stderr.zt);
+
+ hook_register(zlog_aux_init, zlt_aux_init);
+ hook_register(zlog_init, zlt_init);
+ hook_register(zlog_fini, zlt_fini);
+}
+
+void zlog_startup_end(void)
+{
+ static bool startup_ended = false;
+
+ if (startup_ended)
+ return;
+ startup_ended = true;
+
+ zlog_target_replace(&zlog_startup_stderr.zt, NULL);
+
+ if (zlog_is_aux)
+ return;
+
+ /* until here, crashlogs go to stderr */
+ zlog_crashlog_init();
+}
+
+/* syslog */
+
+struct zlt_syslog {
+ struct zlog_target zt;
+
+ int syslog_facility;
+};
+
+static void zlog_syslog(struct zlog_target *zt, struct zlog_msg *msgs[],
+ size_t nmsgs)
+{
+ size_t i;
+ struct zlt_syslog *zte = container_of(zt, struct zlt_syslog, zt);
+
+ for (i = 0; i < nmsgs; i++) {
+ if (zlog_msg_prio(msgs[i]) > zt->prio_min)
+ continue;
+
+ syslog(zlog_msg_prio(msgs[i]) | zte->syslog_facility, "%s",
+ zlog_msg_text(msgs[i], NULL));
+ }
+}
+
+#ifndef _PATH_LOG
+#define _PATH_LOG "/dev/log"
+#endif
+
+static void zlog_syslog_sigsafe(struct zlog_target *zt, const char *text,
+ size_t len)
+{
+ static int syslog_fd = -1;
+
+ char hdr[192];
+ size_t hdrlen;
+ struct iovec iov[2];
+
+ if (syslog_fd == -1) {
+ syslog_fd = socket(AF_UNIX, SOCK_DGRAM, 0);
+ if (syslog_fd >= 0) {
+ struct sockaddr_un sa;
+ socklen_t salen = sizeof(sa);
+
+ sa.sun_family = AF_UNIX;
+ strlcpy(sa.sun_path, _PATH_LOG, sizeof(sa.sun_path));
+#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
+ salen = sa.sun_len = SUN_LEN(&sa);
+#endif
+ if (connect(syslog_fd, (struct sockaddr *)&sa, salen)) {
+ close(syslog_fd);
+ syslog_fd = -1;
+ }
+ }
+
+ /* /dev/log could be a fifo instead of a socket */
+ if (syslog_fd == -1) {
+ syslog_fd = open(_PATH_LOG, O_WRONLY | O_NOCTTY);
+ if (syslog_fd < 0)
+ /* give up ... */
+ syslog_fd = -2;
+ }
+ }
+
+ if (syslog_fd == -2)
+ return;
+
+ /* note zlog_prefix includes trailing ": ", need to cut off 2 chars */
+ hdrlen = snprintfrr(hdr, sizeof(hdr), "<%d>%.*s[%ld]: ", LOG_CRIT,
+ zlog_prefixsz > 2 ? (int)(zlog_prefixsz - 2) : 0,
+ zlog_prefix, (long)getpid());
+
+ iov[0].iov_base = hdr;
+ iov[0].iov_len = hdrlen;
+
+ iov[1].iov_base = (char *)text;
+ iov[1].iov_len = len;
+
+ writev(syslog_fd, iov, array_size(iov));
+}
+
+
+static pthread_mutex_t syslog_cfg_mutex = PTHREAD_MUTEX_INITIALIZER;
+static struct zlt_syslog *zlt_syslog;
+static int syslog_facility = LOG_DAEMON;
+static int syslog_prio_min = ZLOG_DISABLED;
+
+void zlog_syslog_set_facility(int facility)
+{
+ struct zlog_target *newztc;
+ struct zlt_syslog *newzt;
+
+ frr_with_mutex(&syslog_cfg_mutex) {
+ if (facility == syslog_facility)
+ return;
+ syslog_facility = facility;
+
+ if (syslog_prio_min == ZLOG_DISABLED)
+ return;
+
+ newztc = zlog_target_clone(MTYPE_LOG_SYSL, &zlt_syslog->zt,
+ sizeof(*newzt));
+ newzt = container_of(newztc, struct zlt_syslog, zt);
+ newzt->syslog_facility = syslog_facility;
+
+ zlog_target_free(MTYPE_LOG_SYSL,
+ zlog_target_replace(&zlt_syslog->zt,
+ &newzt->zt));
+
+ zlt_syslog = newzt;
+ }
+}
+
+int zlog_syslog_get_facility(void)
+{
+ frr_with_mutex(&syslog_cfg_mutex) {
+ return syslog_facility;
+ }
+ assert(0);
+}
+
+void zlog_syslog_set_prio_min(int prio_min)
+{
+ struct zlog_target *newztc;
+ struct zlt_syslog *newzt = NULL;
+
+ frr_with_mutex(&syslog_cfg_mutex) {
+ if (prio_min == syslog_prio_min)
+ return;
+ syslog_prio_min = prio_min;
+
+ if (syslog_prio_min != ZLOG_DISABLED) {
+ newztc = zlog_target_clone(MTYPE_LOG_SYSL,
+ &zlt_syslog->zt,
+ sizeof(*newzt));
+ newzt = container_of(newztc, struct zlt_syslog, zt);
+ newzt->zt.prio_min = prio_min;
+ newzt->zt.logfn = zlog_syslog;
+ newzt->zt.logfn_sigsafe = zlog_syslog_sigsafe;
+ newzt->syslog_facility = syslog_facility;
+ }
+
+ zlog_target_free(MTYPE_LOG_SYSL,
+ zlog_target_replace(&zlt_syslog->zt,
+ &newzt->zt));
+
+ zlt_syslog = newzt;
+ }
+}
+
+int zlog_syslog_get_prio_min(void)
+{
+ frr_with_mutex(&syslog_cfg_mutex) {
+ return syslog_prio_min;
+ }
+ assert(0);
+}
diff --git a/lib/zlog_targets.h b/lib/zlog_targets.h
new file mode 100644
index 0000000000..f95d349a57
--- /dev/null
+++ b/lib/zlog_targets.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2015-19 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_ZLOG_TARGETS_H
+#define _FRR_ZLOG_TARGETS_H
+
+#include <pthread.h>
+
+#include "zlog.h"
+
+/* multiple file log targets can be active */
+
+struct zlt_fd;
+
+struct zlog_cfg_file {
+ struct zlt_fd *active;
+
+ pthread_mutex_t cfg_mtx;
+
+ /* call zlog_file_set_other() to apply these */
+ int prio_min;
+ char ts_subsec;
+ bool record_priority;
+
+ /* call zlog_file_set_filename/fd() to change this */
+ char *filename;
+ int fd;
+
+ void (*zlog_wrap)(struct zlog_target *zt, struct zlog_msg *msgs[],
+ size_t nmsgs);
+};
+
+extern void zlog_file_init(struct zlog_cfg_file *zcf);
+extern void zlog_file_fini(struct zlog_cfg_file *zcf);
+
+extern void zlog_file_set_other(struct zlog_cfg_file *zcf);
+extern bool zlog_file_set_filename(struct zlog_cfg_file *zcf, const char *name);
+extern bool zlog_file_set_fd(struct zlog_cfg_file *zcf, int fd);
+extern bool zlog_file_rotate(struct zlog_cfg_file *zcf);
+
+extern void zlog_fd(struct zlog_target *zt, struct zlog_msg *msgs[],
+ size_t nmsgs);
+
+/* syslog is always limited to one target */
+
+extern void zlog_syslog_set_facility(int facility);
+extern int zlog_syslog_get_facility(void);
+
+/* use ZLOG_DISABLED to disable */
+extern void zlog_syslog_set_prio_min(int prio_min);
+extern int zlog_syslog_get_prio_min(void);
+
+#endif /* _FRR_ZLOG_TARGETS_H */