diff options
108 files changed, 4565 insertions, 882 deletions
diff --git a/babeld/babel_main.c b/babeld/babel_main.c index eaff97a495..a3f2b4e7d8 100644 --- a/babeld/babel_main.c +++ b/babeld/babel_main.c @@ -68,7 +68,7 @@ const unsigned char ones[16] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; -static const char *state_file = DAEMON_VTY_DIR "/babel-state"; +static char state_file[1024]; unsigned char protocol_group[16]; /* babel's link-local multicast address */ int protocol_port; /* babel's port */ @@ -187,6 +187,9 @@ main(int argc, char **argv) } } + snprintf(state_file, sizeof(state_file), "%s/%s", + frr_vtydir, "babel-state"); + /* create the threads handler */ master = frr_init (); diff --git a/bfdd/bfdd.c b/bfdd/bfdd.c index 218f0883c5..06e01abcfa 100644 --- a/bfdd/bfdd.c +++ b/bfdd/bfdd.c @@ -20,6 +20,8 @@ #include <zebra.h> +#include "filter.h" + #include "bfd.h" #include "lib/version.h" @@ -158,7 +160,8 @@ static void bg_init(void) int main(int argc, char *argv[]) { - const char *ctl_path = BFDD_CONTROL_SOCKET; + char ctl_path[512]; + bool ctlsockused = false; int opt; /* Initialize system sockets. */ @@ -168,6 +171,8 @@ int main(int argc, char *argv[]) frr_opt_add("", longopts, " --bfdctl Specify bfdd control socket\n"); + snprintf(ctl_path, sizeof(ctl_path), BFDD_CONTROL_SOCKET, + "", ""); while (true) { opt = frr_getopt(argc, argv, NULL); if (opt == EOF) @@ -175,7 +180,8 @@ int main(int argc, char *argv[]) switch (opt) { case OPTION_CTLSOCK: - ctl_path = optarg; + strlcpy(ctl_path, optarg, sizeof(ctl_path)); + ctlsockused = true; break; default: @@ -184,6 +190,10 @@ int main(int argc, char *argv[]) } } + if (bfdd_di.pathspace && !ctlsockused) + snprintf(ctl_path, sizeof(ctl_path), BFDD_CONTROL_SOCKET, + "/", bfdd_di.pathspace); + #if 0 /* TODO add support for JSON configuration files. */ parse_config(conf); #endif @@ -202,6 +212,8 @@ int main(int argc, char *argv[]) bfd_vrf_init(); + access_list_init(); + /* Initialize zebra connection. */ bfdd_zclient_init(&bglobal.bfdd_privs); diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 31243c899d..4de73f1244 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -9542,12 +9542,14 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, vty_out(vty, "Paths: (%d available", count); if (best) { vty_out(vty, ", best #%d", best); - if (safi == SAFI_UNICAST) - vty_out(vty, ", table %s", - (bgp->inst_type - == BGP_INSTANCE_TYPE_DEFAULT) - ? VRF_DEFAULT_NAME - : bgp->name); + if (safi == SAFI_UNICAST) { + if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) + vty_out(vty, ", table %s", + VRF_DEFAULT_NAME); + else + vty_out(vty, ", vrf %s", + bgp->name); + } } else vty_out(vty, ", no best path"); @@ -12373,30 +12375,9 @@ void bgp_config_write_network(struct vty *vty, struct bgp *bgp, afi_t afi, p = &rn->p; - /* "network" configuration display. */ - if (bgp_option_check(BGP_OPT_CONFIG_CISCO) && afi == AFI_IP) { - uint32_t destination; - struct in_addr netmask; - - destination = ntohl(p->u.prefix4.s_addr); - masklen2ip(p->prefixlen, &netmask); - vty_out(vty, " network %s", - inet_ntop(p->family, &p->u.prefix, buf, - SU_ADDRSTRLEN)); - - if ((IN_CLASSC(destination) && p->prefixlen == 24) - || (IN_CLASSB(destination) && p->prefixlen == 16) - || (IN_CLASSA(destination) && p->prefixlen == 8) - || p->u.prefix4.s_addr == 0) { - /* Natural mask is not display. */ - } else - vty_out(vty, " mask %s", inet_ntoa(netmask)); - } else { - vty_out(vty, " network %s/%d", - inet_ntop(p->family, &p->u.prefix, buf, - SU_ADDRSTRLEN), - p->prefixlen); - } + vty_out(vty, " network %s/%d", + inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen); if (bgp_static->label_index != BGP_INVALID_LABEL_INDEX) vty_out(vty, " label-index %u", @@ -12420,20 +12401,9 @@ void bgp_config_write_network(struct vty *vty, struct bgp *bgp, afi_t afi, p = &rn->p; - if (bgp_option_check(BGP_OPT_CONFIG_CISCO) && afi == AFI_IP) { - struct in_addr netmask; - - masklen2ip(p->prefixlen, &netmask); - vty_out(vty, " aggregate-address %s %s", - inet_ntop(p->family, &p->u.prefix, buf, - SU_ADDRSTRLEN), - inet_ntoa(netmask)); - } else { - vty_out(vty, " aggregate-address %s/%d", - inet_ntop(p->family, &p->u.prefix, buf, - SU_ADDRSTRLEN), - p->prefixlen); - } + vty_out(vty, " aggregate-address %s/%d", + inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen); if (bgp_aggregate->as_set) vty_out(vty, " as-set"); diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index c8386e6cbe..28a763ed5e 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -3016,7 +3016,7 @@ static int bgp_route_match_add(struct vty *vty, const char *command, int retval = CMD_SUCCESS; int ret; - ret = route_map_add_match(index, command, arg); + ret = route_map_add_match(index, command, arg, type); switch (ret) { case RMAP_RULE_MISSING: vty_out(vty, "%% BGP Can't find rule.\n"); diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c index aa09026b78..0637723990 100644 --- a/bgpd/bgp_rpki.c +++ b/bgpd/bgp_rpki.c @@ -1403,7 +1403,8 @@ DEFUN (match_rpki, VTY_DECLVAR_CONTEXT(route_map_index, index); int ret; - ret = route_map_add_match(index, "rpki", argv[2]->arg); + ret = route_map_add_match(index, "rpki", argv[2]->arg, + RMAP_EVENT_MATCH_ADDED); if (ret) { switch (ret) { case RMAP_RULE_MISSING: diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 6451c8d8ed..7dc3f02b0d 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -815,41 +815,6 @@ static void bgp_clear_star_soft_out(struct vty *vty, const char *name) #include "bgpd/bgp_vty_clippy.c" #endif -/* BGP global configuration. */ -#if (CONFDATE > 20190601) -CPP_NOTICE("bgpd: time to remove deprecated bgp multiple-instance") -CPP_NOTICE("This includes BGP_OPT_MULTIPLE_INSTANCE") -#endif -DEFUN_HIDDEN (bgp_multiple_instance_func, - bgp_multiple_instance_cmd, - "bgp multiple-instance", - BGP_STR - "Enable bgp multiple instance\n") -{ - bgp_option_set(BGP_OPT_MULTIPLE_INSTANCE); - return CMD_SUCCESS; -} - -DEFUN_HIDDEN (no_bgp_multiple_instance, - no_bgp_multiple_instance_cmd, - "no bgp multiple-instance", - NO_STR - BGP_STR - "BGP multiple instance\n") -{ - int ret; - - vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n"); - vty_out(vty, "if you are using this please let the developers know\n"); - zlog_info("Deprecated option: `bgp multiple-instance` being used"); - ret = bgp_option_unset(BGP_OPT_MULTIPLE_INSTANCE); - if (ret < 0) { - vty_out(vty, "%% There are more than two BGP instances\n"); - return CMD_WARNING_CONFIG_FAILED; - } - return CMD_SUCCESS; -} - DEFUN_HIDDEN (bgp_local_mac, bgp_local_mac_cmd, "bgp local-mac vni " CMD_VNI_RANGE " mac WORD seq (0-4294967295)", @@ -931,44 +896,6 @@ DEFUN_HIDDEN (no_bgp_local_mac, return CMD_SUCCESS; } -#if (CONFDATE > 20190601) -CPP_NOTICE("bgpd: time to remove deprecated cli bgp config-type cisco") -CPP_NOTICE("This includes BGP_OPT_CISCO_CONFIG") -#endif -DEFUN_HIDDEN (bgp_config_type, - bgp_config_type_cmd, - "bgp config-type <cisco|zebra>", - BGP_STR - "Configuration type\n" - "cisco\n" - "zebra\n") -{ - int idx = 0; - if (argv_find(argv, argc, "cisco", &idx)) { - vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n"); - vty_out(vty, "if you are using this please let the developers know!\n"); - zlog_info("Deprecated option: `bgp config-type cisco` being used"); - bgp_option_set(BGP_OPT_CONFIG_CISCO); - } else - bgp_option_unset(BGP_OPT_CONFIG_CISCO); - - return CMD_SUCCESS; -} - -DEFUN_HIDDEN (no_bgp_config_type, - no_bgp_config_type_cmd, - "no bgp config-type [<cisco|zebra>]", - NO_STR - BGP_STR - "Display configuration type\n" - "cisco\n" - "zebra\n") -{ - bgp_option_unset(BGP_OPT_CONFIG_CISCO); - return CMD_SUCCESS; -} - - DEFUN (no_synchronization, no_synchronization_cmd, "no synchronization", @@ -7481,11 +7408,6 @@ DEFUN (show_bgp_views, struct listnode *node; struct bgp *bgp; - if (!bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) { - vty_out(vty, "BGP Multiple Instance is not enabled\n"); - return CMD_WARNING; - } - vty_out(vty, "Defined BGP views:\n"); for (ALL_LIST_ELEMENTS_RO(inst, node, bgp)) { /* Skip VRFs. */ @@ -7516,11 +7438,6 @@ DEFUN (show_bgp_vrfs, json_object *json_vrfs = NULL; int count = 0; - if (!bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) { - vty_out(vty, "BGP Multiple Instance is not enabled\n"); - return CMD_WARNING; - } - if (uj) { json = json_object_new_object(); json_vrfs = json_object_new_object(); @@ -12896,14 +12813,6 @@ void bgp_vty_init(void) install_default(BGP_EVPN_NODE); install_default(BGP_EVPN_VNI_NODE); - /* "bgp multiple-instance" commands. */ - install_element(CONFIG_NODE, &bgp_multiple_instance_cmd); - install_element(CONFIG_NODE, &no_bgp_multiple_instance_cmd); - - /* "bgp config-type" commands. */ - install_element(CONFIG_NODE, &bgp_config_type_cmd); - install_element(CONFIG_NODE, &no_bgp_config_type_cmd); - /* "bgp local-mac" hidden commands. */ install_element(CONFIG_NODE, &bgp_local_mac_cmd); install_element(CONFIG_NODE, &no_bgp_local_mac_cmd); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 2e648af1bb..02eda7a430 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -171,8 +171,6 @@ int bgp_option_set(int flag) { switch (flag) { case BGP_OPT_NO_FIB: - case BGP_OPT_MULTIPLE_INSTANCE: - case BGP_OPT_CONFIG_CISCO: case BGP_OPT_NO_LISTEN: case BGP_OPT_NO_ZEBRA: SET_FLAG(bm->options, flag); @@ -186,13 +184,9 @@ int bgp_option_set(int flag) int bgp_option_unset(int flag) { switch (flag) { - case BGP_OPT_MULTIPLE_INSTANCE: - if (listcount(bm->bgp) > 1) - return BGP_ERR_MULTIPLE_INSTANCE_USED; /* Fall through. */ case BGP_OPT_NO_ZEBRA: case BGP_OPT_NO_FIB: - case BGP_OPT_CONFIG_CISCO: UNSET_FLAG(bm->options, flag); break; default: @@ -1205,21 +1199,18 @@ struct peer *peer_new(struct bgp *bgp) /* Set default flags. */ FOREACH_AFI_SAFI (afi, safi) { - if (!bgp_option_check(BGP_OPT_CONFIG_CISCO)) { - SET_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_SEND_COMMUNITY); - SET_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_SEND_EXT_COMMUNITY); - SET_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_SEND_LARGE_COMMUNITY); - - SET_FLAG(peer->af_flags_invert[afi][safi], - PEER_FLAG_SEND_COMMUNITY); - SET_FLAG(peer->af_flags_invert[afi][safi], - PEER_FLAG_SEND_EXT_COMMUNITY); - SET_FLAG(peer->af_flags_invert[afi][safi], - PEER_FLAG_SEND_LARGE_COMMUNITY); - } + SET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY); + SET_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_SEND_EXT_COMMUNITY); + SET_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_SEND_LARGE_COMMUNITY); + + SET_FLAG(peer->af_flags_invert[afi][safi], + PEER_FLAG_SEND_COMMUNITY); + SET_FLAG(peer->af_flags_invert[afi][safi], + PEER_FLAG_SEND_EXT_COMMUNITY); + SET_FLAG(peer->af_flags_invert[afi][safi], + PEER_FLAG_SEND_LARGE_COMMUNITY); peer->addpath_type[afi][safi] = BGP_ADDPATH_NONE; } @@ -3174,40 +3165,21 @@ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name, struct vrf *vrf = NULL; /* Multiple instance check. */ - if (bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) { - if (name) - bgp = bgp_lookup_by_name(name); - else - bgp = bgp_get_default(); - - /* Already exists. */ - if (bgp) { - if (bgp->as != *as) { - *as = bgp->as; - return BGP_ERR_INSTANCE_MISMATCH; - } - if (bgp->inst_type != inst_type) - return BGP_ERR_INSTANCE_MISMATCH; - *bgp_val = bgp; - return 0; - } - } else { - /* BGP instance name can not be specified for single instance. - */ - if (name) - return BGP_ERR_MULTIPLE_INSTANCE_NOT_SET; - - /* Get default BGP structure if exists. */ + if (name) + bgp = bgp_lookup_by_name(name); + else bgp = bgp_get_default(); - if (bgp) { - if (bgp->as != *as) { - *as = bgp->as; - return BGP_ERR_AS_MISMATCH; - } - *bgp_val = bgp; - return 0; + /* Already exists. */ + if (bgp) { + if (bgp->as != *as) { + *as = bgp->as; + return BGP_ERR_INSTANCE_MISMATCH; } + if (bgp->inst_type != inst_type) + return BGP_ERR_INSTANCE_MISMATCH; + *bgp_val = bgp; + return 0; } bgp = bgp_create(as, name, inst_type); @@ -7319,45 +7291,19 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, flag_slcomm = peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_SEND_LARGE_COMMUNITY); - if (!bgp_option_check(BGP_OPT_CONFIG_CISCO)) { - if (flag_scomm && flag_secomm && flag_slcomm) { - vty_out(vty, " no neighbor %s send-community all\n", - addr); - } else { - if (flag_scomm) - vty_out(vty, - " no neighbor %s send-community\n", - addr); - if (flag_secomm) - vty_out(vty, - " no neighbor %s send-community extended\n", - addr); - - if (flag_slcomm) - vty_out(vty, - " no neighbor %s send-community large\n", - addr); - } + if (flag_scomm && flag_secomm && flag_slcomm) { + vty_out(vty, " no neighbor %s send-community all\n", addr); } else { - if (flag_scomm && flag_secomm && flag_slcomm) { - vty_out(vty, " neighbor %s send-community all\n", + if (flag_scomm) + vty_out(vty, " no neighbor %s send-community\n", addr); + if (flag_secomm) + vty_out(vty, + " no neighbor %s send-community extended\n", addr); - } else if (flag_scomm && flag_secomm) { - vty_out(vty, " neighbor %s send-community both\n", + + if (flag_slcomm) + vty_out(vty, " no neighbor %s send-community large\n", addr); - } else { - if (flag_scomm) - vty_out(vty, " neighbor %s send-community\n", - addr); - if (flag_secomm) - vty_out(vty, - " neighbor %s send-community extended\n", - addr); - if (flag_slcomm) - vty_out(vty, - " neighbor %s send-community large\n", - addr); - } } /* Default information */ @@ -7568,18 +7514,6 @@ int bgp_config_write(struct vty *vty) struct listnode *node, *nnode; struct listnode *mnode, *mnnode; - /* BGP Multiple instance. */ - if (!bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) { - vty_out(vty, "no bgp multiple-instance\n"); - write++; - } - - /* BGP Config type. */ - if (bgp_option_check(BGP_OPT_CONFIG_CISCO)) { - vty_out(vty, "bgp config-type cisco\n"); - write++; - } - if (bm->rmap_update_timer != RMAP_DEFAULT_UPDATE_TIMER) vty_out(vty, "bgp route-map delay-timer %u\n", bm->rmap_update_timer); @@ -7597,21 +7531,12 @@ int bgp_config_write(struct vty *vty) /* Router bgp ASN */ vty_out(vty, "router bgp %u", bgp->as); - if (bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) { - if (bgp->name) - vty_out(vty, " %s %s", - (bgp->inst_type - == BGP_INSTANCE_TYPE_VIEW) - ? "view" - : "vrf", - bgp->name); - } + if (bgp->name) + vty_out(vty, " %s %s", + (bgp->inst_type == BGP_INSTANCE_TYPE_VIEW) + ? "view" : "vrf", bgp->name); vty_out(vty, "\n"); - /* No Synchronization */ - if (bgp_option_check(BGP_OPT_CONFIG_CISCO)) - vty_out(vty, " no synchronization\n"); - /* BGP fast-external-failover. */ if (CHECK_FLAG(bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER)) vty_out(vty, " no bgp fast-external-failover\n"); @@ -7825,10 +7750,6 @@ int bgp_config_write(struct vty *vty) if (bgp->autoshutdown) vty_out(vty, " bgp default shutdown\n"); - /* No auto-summary */ - if (bgp_option_check(BGP_OPT_CONFIG_CISCO)) - vty_out(vty, " no auto-summary\n"); - /* IPv4 unicast configuration. */ bgp_config_write_family(vty, bgp, AFI_IP, SAFI_UNICAST); @@ -7904,9 +7825,6 @@ void bgp_master_init(struct thread_master *master) bf_init(bm->rd_idspace, UINT16_MAX); bf_assign_zero_index(bm->rd_idspace); - /* Enable multiple instances by default. */ - bgp_option_set(BGP_OPT_MULTIPLE_INSTANCE); - /* mpls label dynamic allocation pool */ bgp_lp_init(bm->master, &bm->labelpool); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index c600d9f3f3..518f1f6714 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -134,10 +134,8 @@ struct bgp_master { /* Various BGP global configuration. */ uint8_t options; #define BGP_OPT_NO_FIB (1 << 0) -#define BGP_OPT_MULTIPLE_INSTANCE (1 << 1) -#define BGP_OPT_CONFIG_CISCO (1 << 2) -#define BGP_OPT_NO_LISTEN (1 << 3) -#define BGP_OPT_NO_ZEBRA (1 << 4) +#define BGP_OPT_NO_LISTEN (1 << 1) +#define BGP_OPT_NO_ZEBRA (1 << 2) uint64_t updgrp_idspace; uint64_t subgrp_idspace; diff --git a/configure.ac b/configure.ac index c228ff0c91..e94cb541ec 100755 --- a/configure.ac +++ b/configure.ac @@ -2202,10 +2202,10 @@ fi AC_MSG_RESULT([${frr_statedir}]) AC_SUBST([frr_statedir]) -AC_DEFINE_UNQUOTED([LDPD_SOCKET], ["$frr_statedir/ldpd.sock"], [ldpd control socket]) -AC_DEFINE_UNQUOTED([ZEBRA_SERV_PATH], ["$frr_statedir/zserv.api"], [zebra api socket]) -AC_DEFINE_UNQUOTED([BFDD_CONTROL_SOCKET], ["$frr_statedir/bfdd.sock"], [bfdd control socket]) -AC_DEFINE_UNQUOTED([DAEMON_VTY_DIR], ["$frr_statedir"], [daemon vty directory]) +AC_DEFINE_UNQUOTED([LDPD_SOCKET], ["$frr_statedir%s%s/ldpd.sock"], [ldpd control socket]) +AC_DEFINE_UNQUOTED([ZEBRA_SERV_PATH], ["$frr_statedir%s%s/zserv.api"], [zebra api socket]) +AC_DEFINE_UNQUOTED([BFDD_CONTROL_SOCKET], ["$frr_statedir%s%s/bfdd.sock"], [bfdd control socket]) +AC_DEFINE_UNQUOTED([DAEMON_VTY_DIR], ["$frr_statedir%s%s"], [daemon vty directory]) AC_DEFINE_UNQUOTED([DAEMON_DB_DIR], ["$frr_statedir"], [daemon database directory]) dnl autoconf does this, but it does it too late... diff --git a/doc/developer/building-frr-for-centos7.rst b/doc/developer/building-frr-for-centos7.rst index ea3c44478c..67f71bc3a5 100644 --- a/doc/developer/building-frr-for-centos7.rst +++ b/doc/developer/building-frr-for-centos7.rst @@ -70,7 +70,8 @@ an example.) --disable-ldpd \ --enable-fpm \ --with-pkg-git-version \ - --with-pkg-extra-version=-MyOwnFRRVersion + --with-pkg-extra-version=-MyOwnFRRVersion \ + SPHINXBUILD=/usr/bin/sphinx-build make make check sudo make install diff --git a/doc/developer/logging.rst b/doc/developer/logging.rst index 1dc1885158..e393fe6fba 100644 --- a/doc/developer/logging.rst +++ b/doc/developer/logging.rst @@ -6,6 +6,114 @@ to log, what level to log it at, and when to log it. Here is a list of recommendations for these decisions. +printfrr() +---------- + +``printfrr()`` is FRR's modified version of ``printf()``, designed to make +life easier when printing nontrivial datastructures. The following variants +are available: + +.. c:function:: ssize_t snprintfrr(char *buf, size_t len, const char *fmt, ...) +.. c:function:: ssize_t vsnprintfrr(char *buf, size_t len, const char *fmt, va_list) + + These correspond to ``snprintf``/``vsnprintf``. If you pass NULL for buf + or 0 for len, no output is written but the return value is still calculated. + + The return value is always the full length of the output, unconstrained by + `len`. It does **not** include the terminating ``\0`` character. A + malformed format string can result in a ``-1`` return value. + +.. c:function:: ssize_t csnprintfrr(char *buf, size_t len, const char *fmt, ...) +.. c:function:: ssize_t vcsnprintfrr(char *buf, size_t len, const char *fmt, va_list) + + Same as above, but the ``c`` stands for "continue" or "concatenate". The + output is appended to the string instead of overwriting it. + +.. c:function:: char *asprintfrr(struct memtype *mt, const char *fmt, ...) +.. c:function:: char *vasprintfrr(struct memtype *mt, const char *fmt, va_list) + + These functions allocate a dynamic buffer (using MTYPE `mt`) and print to + that. If the format string is malformed, they return a copy of the format + string, so the return value is always non-NULL and always dynamically + allocated with `mt`. + +.. c:function:: char *asnprintfrr(struct memtype *mt, char *buf, size_t len, const char *fmt, ...) +.. c:function:: char *vasnprintfrr(struct memtype *mt, char *buf, size_t len, const char *fmt, va_list) + + This variant tries to use the static buffer provided, but falls back to + dynamic allocation if it is insufficient. + + The return value can be either `buf` or a newly allocated string using + `mt`. You MUST free it like this:: + + char *ret = asnprintfrr(MTYPE_FOO, buf, sizeof(buf), ...); + if (ret != buf) + XFREE(MTYPE_FOO, ret); + +Extensions +^^^^^^^^^^ + +``printfrr()`` format strings can be extended with suffixes after `%p` or +`%d`. The following extended format specifiers are available: + ++-----------+--------------------------+----------------------------------------------+ +| Specifier | Argument | Output | ++===========+==========================+==============================================+ +| ``%Lu`` | ``uint64_t`` | ``12345`` | ++-----------+--------------------------+----------------------------------------------+ +| ``%Ld`` | ``int64_t`` | ``-12345`` | ++-----------+--------------------------+----------------------------------------------+ +| ``%pI4`` | ``struct in_addr *`` | ``1.2.3.4`` | +| | | | +| | ``in_addr_t *`` | | ++-----------+--------------------------+----------------------------------------------+ +| ``%pI6`` | ``struct in6_addr *`` | ``fe80::1234`` | ++-----------+--------------------------+----------------------------------------------+ +| ``%pFX`` | ``struct prefix *`` | ``fe80::1234/64`` | ++-----------+--------------------------+----------------------------------------------+ +| ``%pSG4`` | ``struct prefix_sg *`` | ``(*,1.2.3.4)`` | ++-----------+--------------------------+----------------------------------------------+ +| ``%pRN`` | ``struct route_node *`` | ``192.168.1.0/24`` (dst-only node) | +| | | | +| | | ``2001:db8::/32 from fe80::/64`` (SADR node) | ++-----------+--------------------------+----------------------------------------------+ +| ``%pNHv`` | ``struct nexthop *`` | ``1.2.3.4, via eth0`` | ++-----------+--------------------------+----------------------------------------------+ +| ``%pNHs`` | ``struct nexthop *`` | ``1.2.3.4 if 15`` | ++-----------+--------------------------+----------------------------------------------+ + +Printf features like field lengths can be used normally with these extensions, +e.g. ``%-15pI4`` works correctly. + +The extension specifier after ``%p`` or ``%d`` is always an uppercase letter; +by means of established pattern uppercase letters and numbers form the type +identifier which may be followed by lowercase flags. + +You can grep the FRR source for ``printfrr_ext_autoreg`` to see all extended +printers and what exactly they do. More printers are likely to be added as +needed/useful, so the list above may become outdated. + +``%Ld`` is not an "extension" for printfrr; it's wired directly into the main +printf logic. + +.. note:: + + The ``zlog_*``/``flog_*`` and ``vty_out`` functions all use printfrr + internally, so these extensions are available there. However, they are + **not** available when calling ``snprintf`` directly. You need to call + ``snprintfrr`` instead. + +AS-Safety +^^^^^^^^^ + +``printfrr()`` are AS-Safe under the following conditions: + +* the ``[v]as[n]printfrr`` variants are not AS-Safe (allocating memory) +* floating point specifiers are not AS-Safe (system printf is used for these) +* the positional ``%1$d`` syntax should not be used (8 arguments are supported + while AS-Safe) +* extensions are only AS-Safe if their printer is AS-Safe + Errors and warnings ------------------- diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst index 09f12ec436..e12bc37256 100644 --- a/doc/developer/topotests.rst +++ b/doc/developer/topotests.rst @@ -145,30 +145,23 @@ the following env variable can be set:: export TOPOTESTS_CHECK_STDERR=Yes -(The value doesn't matter at this time. The check is if the env variable exists -or not) There is no pass/fail on this reporting. The Output will be reported to -the console:: - - export TOPOTESTS_CHECK_MEMLEAK="/home/mydir/memleak_" - -This will enable the check and output to console and the writing of the -information to files with the given prefix (followed by testname), ie -:file:`/home/mydir/memcheck_test_bgp_multiview_topo1.txt` in case of a memory -leak. +(The value doesn't matter at this time. The check is whether the env +variable exists or not.) There is no pass/fail on this reporting; the +Output will be reported to the console. Collect Memory Leak Information ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -FRR processes have the capabilities to report remaining memory allocations upon -exit. To enable the reporting of the memory, define an environment variable +FRR processes can report unfreed memory allocations upon exit. To +enable the reporting of memory leaks, define an environment variable ``TOPOTESTS_CHECK_MEMLEAK`` with the file prefix, i.e.:: export TOPOTESTS_CHECK_MEMLEAK="/home/mydir/memleak_" -This will enable the check and output to console and the writing of the -information to files with the given prefix (followed by testname), ie -:file:`/home/mydir/memcheck_test_bgp_multiview_topo1.txt` in case of a memory -leak. +This will enable the check and output to console and the writing of +the information to files with the given prefix (followed by testname), +ie :file:`/home/mydir/memcheck_test_bgp_multiview_topo1.txt` in case +of a memory leak. Running Topotests with AddressSanitizer ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/doc/developer/workflow.rst b/doc/developer/workflow.rst index b3b3a47cb0..07c43ac2de 100644 --- a/doc/developer/workflow.rst +++ b/doc/developer/workflow.rst @@ -73,14 +73,20 @@ Releases FRR employs a ``<MAJOR>.<MINOR>.<BUGFIX>`` versioning scheme. ``MAJOR`` - Significant new features or multiple minor features. The addition of a new - routing protocol or daemon would fall under this class. + Significant new features or multiple minor features. This should mostly + cover any kind of disruptive change that is visible or "risky" to operators. + New features or protocols do not necessarily trigger this. (This was changed + for FRR 7.x after feedback from users that the pace of major version number + increments was too high.) ``MINOR`` - Small features, e.g. options for automatic BGP shutdown. + General incremental development releases, excluding "major" changes + mentioned above. Not necessarily fully backwards compatible, as smaller + (but still visible) changes or deprecated feature removals may still happen. + However, there shouldn't be any huge "surprises" between minor releases. ``BUGFIX`` - Fixes for actual bugs and/or security issues. + Fixes for actual bugs and/or security issues. Fully compatible. We will pull a new development branch for the next release every 4 months. The current schedule is Feb/June/October 1. The decision for a ``MAJOR/MINOR`` @@ -362,6 +368,19 @@ Documentation should be written in reStructuredText. Sphinx extensions may be utilized but pure ReST is preferred where possible. See :ref:`documentation`. +Use of C++ +---------- + +While C++ is not accepted for core components of FRR, extensions, modules or +other distinct components may want to use C++ and include FRR header files. +There is no requirement on contributors to work to retain C++ compatibility, +but fixes for C++ compatibility are welcome. + +This implies that the burden of work to keep C++ compatibility is placed with +the people who need it, and they may provide it at their leisure to the extent +it is useful to them. So, if only a subset of header files, or even parts of +a header file are made available to C++, this is perfectly fine. + Code Reviews ============ @@ -750,7 +769,8 @@ developer will use this convention to allow control of their debugs. Static Analysis and Sanitizers ------------------------------ -Clang/LLVM comes with a variety of tools that can be used to help find bugs in FRR. +Clang/LLVM and GCC come with a variety of tools that can be used to help find +bugs in FRR. clang-analyze This is a static analyzer that scans the source code looking for patterns @@ -794,11 +814,31 @@ All of the above tools are available in the Clang/LLVM toolchain since 3.4. AddressSanitizer and ThreadSanitizer are available in recent versions of GCC, but are no longer actively maintained. MemorySanitizer is not available in GCC. +.. note:: + + The different Sanitizers are mostly incompatible with each other. Please + refer to GCC/LLVM documentation for details. + Additionally, the FRR codebase is regularly scanned with Coverity. Unfortunately Coverity does not have the ability to handle scanning pull requests, but after code is merged it will send an email notifying project members with Coverity access of newly introduced defects. +Executing non-installed dynamic binaries +---------------------------------------- + +Since FRR uses the GNU autotools build system, it inherits its shortcomings. +To execute a binary directly from the build tree under a wrapper like +`valgrind`, `gdb` or `strace`, use:: + + ./libtool --mode=execute valgrind [--valgrind-opts] zebra/zebra [--zebra-opts] + +While replacing valgrind/zebra as needed. The `libtool` script is found in +the root of the build directory after `./configure` has completed. Its purpose +is to correctly set up `LD_LIBRARY_PATH` so that libraries from the build tree +are used. (On some systems, `libtool` is also available from PATH, but this is +not always the case.) + CLI changes ----------- diff --git a/doc/user/basic.rst b/doc/user/basic.rst index 3df60169f7..3d3a75d4b1 100644 --- a/doc/user/basic.rst +++ b/doc/user/basic.rst @@ -180,13 +180,14 @@ Basic Config Commands In this example, the precision is set to provide timestamps with millisecond accuracy. -.. index:: log commands -.. clicmd:: log commands +.. index:: [no] log commands +.. clicmd:: [no] log commands This command enables the logging of all commands typed by a user to all enabled log destinations. The note that logging includes full command lines, - including passwords. Once set, command logging can only be turned off by - restarting the daemon. + including passwords. If the daemon startup option `--command-log-always` + is used to start the daemon then this command is turned on by default + and cannot be turned off and the [no] form of the command is dissallowed. .. index:: service password-encryption .. clicmd:: service password-encryption @@ -478,10 +479,23 @@ These options apply to all |PACKAGE_NAME| daemons. Set the user and group to run as. +.. option:: -N <namespace> + + Set the namespace that the daemon will run in. A "/<namespace>" will + be added to all files that use the statedir. If you have "/var/run/frr" + as the default statedir then it will become "/var/run/frr/<namespace>". + .. option:: -v, --version Print program version. +.. option:: --command-log-always + + Cause the daemon to always log commands entered to the specified log file. + This also makes the `no log commands` command dissallowed. Enabling this + is suggested if you have need to track what the operator is doing on + this router. + .. option:: --log <stdout|syslog|file:/path/to/log/file> When initializing the daemon, setup the log to go to either stdout, diff --git a/doc/user/bfd.rst b/doc/user/bfd.rst index 33bc77049e..32ca5707d2 100644 --- a/doc/user/bfd.rst +++ b/doc/user/bfd.rst @@ -47,6 +47,9 @@ may also be specified (:ref:`common-invocation-options`). #define BFDD_CONTROL_SOCKET "|INSTALL_PREFIX_STATE|/bfdd.sock" + This option overrides the location addition that the -N option provides + to the bfdd.sock + .. _bfd-commands: diff --git a/doc/user/ldpd.rst b/doc/user/ldpd.rst index 85d280343d..977195d6a7 100644 --- a/doc/user/ldpd.rst +++ b/doc/user/ldpd.rst @@ -24,6 +24,12 @@ Running Ldpd The *ldpd* daemon can be invoked with any of the common options (:ref:`common-invocation-options`). +..option:: --ctl_socket + + This option allows you to override the path to the ldpd.sock file + used to control this daemon. If specified this option overrides + the -N option path addition. + The *zebra* daemon must be running before *ldpd* is invoked. Configuration of *ldpd* is done in its configuration file diff --git a/doc/user/pim.rst b/doc/user/pim.rst index d05127059b..e8c74f7b87 100644 --- a/doc/user/pim.rst +++ b/doc/user/pim.rst @@ -232,6 +232,19 @@ is in a vrf, enter the interface command with the vrf keyword at the end. or IGMP report is received on this interface and the Group is denied by the prefix-list, PIM will ignore the join or report. +.. index:: ip igmp last-member-query-count (1-7) +.. clicmd:: ip igmp last-member-query-count (1-7) + + Set the IGMP last member query count. The default value is 2. 'no' form of + this command is used to to configure back to the default value. + +.. index:: ip igmp last-member-query-interval (1-255) +.. clicmd:: ip igmp last-member-query-interval (1-255) + + Set the IGMP last member query interval in deciseconds. The default value is + 10 deciseconds. 'no' form of this command is used to to configure back to the + default value. + .. _pim-multicast-rib-insertion: PIM Multicast RIB insertion: @@ -534,6 +547,13 @@ Clear commands reset various variables. Reset multicast routes. +.. index:: clear ip mroute [vrf NAME] count +.. clicmd:: clear ip mroute [vrf NAME] count + + When this command is issued, reset the counts of data shown for + packet count, byte count and wrong interface to 0 and start count + up from this spot. + .. index:: clear ip pim interfaces .. clicmd:: clear ip pim interfaces diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index 40d8949297..eefc5802a2 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -58,6 +58,12 @@ Besides the common invocation options (:ref:`common-invocation-options`), the .. seealso:: :ref:`zebra-vrf` +.. option:: -z <path_to_socket>, --socket <path_to_socket> + + If this option is supplied on the cli, the path to the zebra + control socket(zapi), is used. This option overrides a -N <namespace> + option if handed to it on the cli. + .. option:: --v6-rr-semantics The linux kernel is receiving the ability to use the same route diff --git a/eigrpd/eigrp_routemap.c b/eigrpd/eigrp_routemap.c index ee8d5f7582..0a00497d5a 100644 --- a/eigrpd/eigrp_routemap.c +++ b/eigrpd/eigrp_routemap.c @@ -136,7 +136,7 @@ static int eigrp_route_match_add(struct vty *vty, struct route_map_index *index, const char *command, const char *arg) { int ret; - ret = route_map_add_match(index, command, arg); + ret = route_map_add_match(index, command, arg, type); switch (ret) { case RMAP_RULE_MISSING: vty_out(vty, "%% Can't find rule.\n"); diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c index 6b8e74f07b..6edfeb4c97 100644 --- a/isisd/isis_tlvs.c +++ b/isisd/isis_tlvs.c @@ -3271,13 +3271,18 @@ void isis_tlvs_add_ipv6_addresses(struct isis_tlvs *tlvs, { struct listnode *node; struct prefix_ipv6 *ip_addr; + unsigned int addr_count = 0; for (ALL_LIST_ELEMENTS_RO(addresses, node, ip_addr)) { + if (addr_count >= 15) + break; + struct isis_ipv6_address *a = XCALLOC(MTYPE_ISIS_TLV, sizeof(*a)); a->addr = ip_addr->prefix; append_item(&tlvs->ipv6_address, (struct isis_item *)a); + addr_count++; } } diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c index 771d3b7459..5aaa2ec325 100644 --- a/ldpd/ldpd.c +++ b/ldpd/ldpd.c @@ -116,7 +116,7 @@ struct zebra_privs_t ldpd_privs = }; /* CTL Socket path */ -char ctl_sock_path[MAXPATHLEN] = LDPD_SOCKET; +char ctl_sock_path[MAXPATHLEN]; /* LDPd options. */ #define OPTION_CTLSOCK 1001 @@ -219,6 +219,10 @@ main(int argc, char *argv[]) int pipe_parent2lde[2], pipe_parent2lde_sync[2]; char *ctl_sock_name; struct thread *thread = NULL; + bool ctl_sock_used = false; + + snprintf(ctl_sock_path, sizeof(ctl_sock_path), LDPD_SOCKET, + "", ""); ldpd_process = PROC_MAIN; log_procname = log_procnames[ldpd_process]; @@ -244,6 +248,7 @@ main(int argc, char *argv[]) case 0: break; case OPTION_CTLSOCK: + ctl_sock_used = true; ctl_sock_name = strrchr(LDPD_SOCKET, '/'); if (ctl_sock_name) /* skip '/' */ @@ -277,6 +282,10 @@ main(int argc, char *argv[]) } } + if (ldpd_di.pathspace && !ctl_sock_used) + snprintf(ctl_sock_path, sizeof(ctl_sock_path), LDPD_SOCKET, + "/", ldpd_di.pathspace); + strlcpy(init.user, ldpd_privs.user, sizeof(init.user)); strlcpy(init.group, ldpd_privs.group, sizeof(init.group)); strlcpy(init.ctl_sock_path, ctl_sock_path, sizeof(init.ctl_sock_path)); diff --git a/lib/command.c b/lib/command.c index 29f41a712c..e5e0623163 100644 --- a/lib/command.c +++ b/lib/command.c @@ -1962,7 +1962,15 @@ DEFUN (config_hostname, struct cmd_token *word = argv[1]; if (!isalnum((int)word->arg[0])) { - vty_out(vty, "Please specify string starting with alphabet\n"); + vty_out(vty, + "Please specify string starting with alphabet or number\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* With reference to RFC 1123 Section 2.1 */ + if (strlen(word->arg) > HOSTNAME_LEN) { + vty_out(vty, "Hostname length should be less than %d chars\n", + HOSTNAME_LEN); return CMD_WARNING_CONFIG_FAILED; } @@ -2803,9 +2811,10 @@ void cmd_init(int terminal) /* Each node's basic commands. */ install_element(VIEW_NODE, &show_version_cmd); install_element(ENABLE_NODE, &show_startup_config_cmd); - install_element(ENABLE_NODE, &debug_memstats_cmd); if (terminal) { + install_element(ENABLE_NODE, &debug_memstats_cmd); + install_element(VIEW_NODE, &config_list_cmd); install_element(VIEW_NODE, &config_exit_cmd); install_element(VIEW_NODE, &config_quit_cmd); @@ -2839,9 +2848,10 @@ void cmd_init(int terminal) install_element(CONFIG_NODE, &domainname_cmd); install_element(CONFIG_NODE, &no_domainname_cmd); install_element(CONFIG_NODE, &frr_version_defaults_cmd); - install_element(CONFIG_NODE, &debug_memstats_cmd); if (terminal > 0) { + install_element(CONFIG_NODE, &debug_memstats_cmd); + install_element(CONFIG_NODE, &password_cmd); install_element(CONFIG_NODE, &no_password_cmd); install_element(CONFIG_NODE, &enable_password_cmd); diff --git a/lib/command.h b/lib/command.h index d96ec97e67..d6c41e0824 100644 --- a/lib/command.h +++ b/lib/command.h @@ -37,6 +37,17 @@ extern "C" { DECLARE_MTYPE(HOST) DECLARE_MTYPE(COMPLETION) +/* + * From RFC 1123 (Requirements for Internet Hosts), Section 2.1 on hostnames: + * One aspect of host name syntax is hereby changed: the restriction on + * the first character is relaxed to allow either a letter or a digit. + * Host software MUST support this more liberal syntax. + * + * Host software MUST handle host names of up to 63 characters and + * SHOULD handle host names of up to 255 characters. + */ +#define HOSTNAME_LEN 255 + /* Host configuration variable */ struct host { /* Host name of this router. */ diff --git a/lib/compiler.h b/lib/compiler.h index c2e57db7f8..7509428220 100644 --- a/lib/compiler.h +++ b/lib/compiler.h @@ -50,6 +50,16 @@ extern "C" { #endif #endif +#if !defined(__GNUC__) +#error module code needs GCC visibility extensions +#elif __GNUC__ < 4 +#error module code needs GCC visibility extensions +#else +# define DSO_PUBLIC __attribute__ ((visibility ("default"))) +# define DSO_SELF __attribute__ ((visibility ("protected"))) +# define DSO_LOCAL __attribute__ ((visibility ("hidden"))) +#endif + #ifdef __sun /* Solaris doesn't do constructor priorities due to linker restrictions */ #undef _CONSTRUCTOR diff --git a/lib/grammar_sandbox_main.c b/lib/grammar_sandbox_main.c index 38494fb007..6d28a667b3 100644 --- a/lib/grammar_sandbox_main.c +++ b/lib/grammar_sandbox_main.c @@ -56,7 +56,7 @@ int main(int argc, char **argv) host.name = strdup("test"); host.domainname = strdup("testdomainname"); - vty_init(master); + vty_init(master, true); memory_init(); yang_init(); nb_init(master, NULL, 0); diff --git a/lib/libfrr.c b/lib/libfrr.c index 15de96feee..ed784fc73a 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -46,7 +46,7 @@ DEFINE_KOOH(frr_early_fini, (), ()) DEFINE_KOOH(frr_fini, (), ()) const char frr_sysconfdir[] = SYSCONFDIR; -const char frr_vtydir[] = DAEMON_VTY_DIR; +char frr_vtydir[256]; #ifdef HAVE_SQLITE3 const char frr_dbdir[] = DAEMON_DB_DIR; #endif @@ -57,11 +57,11 @@ char frr_protonameinst[256] = "NONE"; char config_default[512]; char frr_zclientpath[256]; -static char pidfile_default[512]; +static char pidfile_default[1024]; #ifdef HAVE_SQLITE3 static char dbfile_default[512]; #endif -static char vtypath_default[256]; +static char vtypath_default[512]; bool debug_memstats_at_exit = false; static bool nodetach_term, nodetach_daemon; @@ -94,6 +94,7 @@ static void opt_extend(const struct optspec *os) #define OPTION_LOGLEVEL 1004 #define OPTION_TCLI 1005 #define OPTION_DB_FILE 1006 +#define OPTION_LOGGING 1007 static const struct option lo_always[] = { {"help", no_argument, NULL, 'h'}, @@ -105,6 +106,7 @@ static const struct option lo_always[] = { {"log", required_argument, NULL, OPTION_LOG}, {"log-level", required_argument, NULL, OPTION_LOGLEVEL}, {"tcli", no_argument, NULL, OPTION_TCLI}, + {"command-log-always", no_argument, NULL, OPTION_LOGGING}, {NULL}}; static const struct optspec os_always = { "hvdM:", @@ -177,7 +179,7 @@ bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len, memset(sa, 0, sizeof(*sa)); if (!path) - path = ZEBRA_SERV_PATH; + path = frr_zclientpath; if (!strncmp(path, ZAPI_TCP_PATHNAME, strlen(ZAPI_TCP_PATHNAME))) { /* note: this functionality is disabled at bottom */ @@ -283,6 +285,11 @@ bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len, static struct frr_daemon_info *di = NULL; +void frr_init_vtydir(void) +{ + snprintf(frr_vtydir, sizeof(frr_vtydir), DAEMON_VTY_DIR, "", ""); +} + void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv) { di = daemon; @@ -305,10 +312,13 @@ void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv) if (di->flags & FRR_DETACH_LATER) nodetach_daemon = true; + frr_init_vtydir(); snprintf(config_default, sizeof(config_default), "%s/%s.conf", frr_sysconfdir, di->name); snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s.pid", frr_vtydir, di->name); + snprintf(frr_zclientpath, sizeof(frr_zclientpath), + ZEBRA_SERV_PATH, "", ""); #ifdef HAVE_SQLITE3 snprintf(dbfile_default, sizeof(dbfile_default), "%s/%s.db", frr_dbdir, di->name); @@ -317,8 +327,6 @@ void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv) strlcpy(frr_protoname, di->logname, sizeof(frr_protoname)); strlcpy(frr_protonameinst, di->logname, sizeof(frr_protonameinst)); - strlcpy(frr_zclientpath, ZEBRA_SERV_PATH, sizeof(frr_zclientpath)); - di->cli_mode = FRR_CLI_CLASSIC; } @@ -398,6 +406,10 @@ static int frr_opt(int opt) errors++; break; } + if (di->zpathspace) + fprintf(stderr, + "-N option overriden by -z for zebra named socket path\n"); + if (strchr(optarg, '/') || strchr(optarg, '.')) { fprintf(stderr, "slashes or dots are not permitted in the --pathspace option.\n"); @@ -405,6 +417,14 @@ static int frr_opt(int opt) break; } di->pathspace = optarg; + + if (!di->zpathspace) + snprintf(frr_zclientpath, sizeof(frr_zclientpath), + ZEBRA_SERV_PATH, "/", di->pathspace); + snprintf(frr_vtydir, sizeof(frr_vtydir), DAEMON_VTY_DIR, "/", + di->pathspace); + snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s.pid", + frr_vtydir, di->name); break; #ifdef HAVE_SQLITE3 case OPTION_DB_FILE: @@ -424,6 +444,10 @@ static int frr_opt(int opt) di->terminal = 1; break; case 'z': + di->zpathspace = true; + if (di->pathspace) + fprintf(stderr, + "-z option overrides -N option for zebra named socket path\n"); if (di->flags & FRR_NO_ZCLIENT) return 1; strlcpy(frr_zclientpath, optarg, sizeof(frr_zclientpath)); @@ -496,6 +520,9 @@ static int frr_opt(int opt) case OPTION_LOGLEVEL: di->early_loglevel = optarg; break; + case OPTION_LOGGING: + di->log_always = true; + break; default: return 1; } @@ -590,8 +617,8 @@ struct thread_master *frr_init(void) snprintf(config_default, sizeof(config_default), "%s%s%s%s.conf", frr_sysconfdir, p_pathspace, di->name, p_instance); - snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s%s%s.pid", - frr_vtydir, p_pathspace, di->name, p_instance); + snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s%s.pid", + frr_vtydir, di->name, p_instance); #ifdef HAVE_SQLITE3 snprintf(dbfile_default, sizeof(dbfile_default), "%s/%s%s%s.db", frr_dbdir, p_pathspace, di->name, p_instance); @@ -648,7 +675,7 @@ struct thread_master *frr_init(void) else cmd_init(1); - vty_init(master); + vty_init(master, di->log_always); memory_init(); log_ref_init(); @@ -875,9 +902,7 @@ static void frr_vty_serv(void) const char *dir; char defvtydir[256]; - snprintf(defvtydir, sizeof(defvtydir), "%s%s%s", frr_vtydir, - di->pathspace ? "/" : "", - di->pathspace ? di->pathspace : ""); + snprintf(defvtydir, sizeof(defvtydir), "%s", frr_vtydir); dir = di->vty_sock_path ? di->vty_sock_path : defvtydir; diff --git a/lib/libfrr.h b/lib/libfrr.h index 891e2c1282..e2b3db74a3 100644 --- a/lib/libfrr.h +++ b/lib/libfrr.h @@ -81,7 +81,10 @@ struct frr_daemon_info { #endif const char *vty_path; const char *module_path; + const char *pathspace; + bool zpathspace; + const char *early_logging; const char *early_loglevel; @@ -97,6 +100,8 @@ struct frr_daemon_info { const struct frr_yang_module_info **yang_modules; size_t n_yang_modules; + + bool log_always; }; /* execname is the daemon's executable (and pidfile and configfile) name, @@ -118,6 +123,7 @@ struct frr_daemon_info { .version = FRR_VERSION, ) \ /* end */ +extern void frr_init_vtydir(void); extern void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv); extern void frr_opt_add(const char *optstr, const struct option *longopts, const char *helpstr); @@ -148,7 +154,7 @@ extern void frr_fini(void); extern char config_default[512]; extern char frr_zclientpath[256]; extern const char frr_sysconfdir[]; -extern const char frr_vtydir[]; +extern char frr_vtydir[256]; extern const char frr_moduledir[]; extern char frr_protoname[]; @@ -30,6 +30,7 @@ #include "command.h" #include "lib_errors.h" #include "lib/hook.h" +#include "printfrr.h" #ifndef SUNOS_5 #include <sys/un.h> @@ -191,19 +192,13 @@ static void time_print(FILE *fp, struct timestamp_control *ctl) static void vzlog_file(struct zlog *zl, struct timestamp_control *tsctl, const char *proto_str, int record_priority, int priority, - FILE *fp, const char *format, va_list args) + FILE *fp, const char *msg) { - va_list ac; - time_print(fp, tsctl); if (record_priority) fprintf(fp, "%s: ", zlog_priority[priority]); - fprintf(fp, "%s", proto_str); - va_copy(ac, args); - vfprintf(fp, format, ac); - va_end(ac); - fprintf(fp, "\n"); + fprintf(fp, "%s%s\n", proto_str, msg); fflush(fp); } @@ -217,33 +212,26 @@ void vzlog(int priority, const char *format, va_list args) struct timestamp_control tsctl; tsctl.already_rendered = 0; struct zlog *zl = zlog_default; + char buf[256], *msg; /* call external hook */ hook_call(zebra_ext_log, priority, format, args); + msg = vasnprintfrr(MTYPE_TMP, buf, sizeof(buf), format, args); + /* When zlog_default is also NULL, use stderr for logging. */ if (zl == NULL) { tsctl.precision = 0; time_print(stderr, &tsctl); - fprintf(stderr, "%s: ", "unknown"); - vfprintf(stderr, format, args); - fprintf(stderr, "\n"); + fprintf(stderr, "%s: %s\n", "unknown", msg); fflush(stderr); - - /* In this case we return at here. */ - errno = original_errno; - pthread_mutex_unlock(&loglock); - return; + goto out; } tsctl.precision = zl->timestamp_precision; /* Syslog output */ - if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG]) { - va_list ac; - va_copy(ac, args); - vsyslog(priority | zlog_default->facility, format, ac); - va_end(ac); - } + if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG]) + syslog(priority | zlog_default->facility, "%s", msg); if (zl->instance) sprintf(proto_str, "%s[%d]: ", zl->protoname, zl->instance); @@ -253,7 +241,7 @@ void vzlog(int priority, const char *format, va_list args) /* File output. */ if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp) vzlog_file(zl, &tsctl, proto_str, zl->record_priority, priority, - zl->fp, format, args); + zl->fp, msg); /* fixed-config logging to stderr while we're stating up & haven't * daemonized / reached mainloop yet @@ -261,17 +249,19 @@ void vzlog(int priority, const char *format, va_list args) * 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, format, - args); + 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, format, args); + stdout, msg); /* Terminal monitor. */ if (priority <= zl->maxlvl[ZLOG_DEST_MONITOR]) vty_log((zl->record_priority ? zlog_priority[priority] : NULL), - proto_str, format, &tsctl, args); + proto_str, msg, &tsctl); +out: + if (msg != buf) + XFREE(MTYPE_TMP, msg); errno = original_errno; pthread_mutex_unlock(&loglock); } @@ -305,47 +295,11 @@ int vzlog_test(int priority) return ret; } -static char *str_append(char *dst, int len, const char *src) -{ - while ((len-- > 0) && *src) - *dst++ = *src++; - return dst; -} - -static char *num_append(char *s, int len, unsigned long x) -{ - char buf[30]; - char *t; - - if (!x) - return str_append(s, len, "0"); - *(t = &buf[sizeof(buf) - 1]) = '\0'; - while (x && (t > buf)) { - *--t = '0' + (x % 10); - x /= 10; - } - return str_append(s, len, t); -} - -#if defined(SA_SIGINFO) \ - || defined(HAVE_PRINTSTACK) \ - || defined(HAVE_GLIBC_BACKTRACE) -static char *hex_append(char *s, int len, unsigned long x) -{ - char buf[30]; - char *t; - - if (!x) - return str_append(s, len, "0"); - *(t = &buf[sizeof(buf) - 1]) = '\0'; - while (x && (t > buf)) { - unsigned int cc = (x % 16); - *--t = ((cc < 10) ? ('0' + cc) : ('a' + cc - 10)); - x /= 16; - } - return str_append(s, len, t); -} -#endif +/* + * 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) @@ -354,7 +308,6 @@ static int syslog_connect(void) return -1; #else int fd; - char *s; struct sockaddr_un addr; if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) @@ -365,10 +318,8 @@ static int syslog_connect(void) #else #define SYSLOG_SOCKET_PATH "/dev/log" #endif - s = str_append(addr.sun_path, sizeof(addr.sun_path), - SYSLOG_SOCKET_PATH); + strlcpy(addr.sun_path, SYSLOG_SOCKET_PATH, sizeof(addr.sun_path)); #undef SYSLOG_SOCKET_PATH - *s = '\0'; if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { close(fd); return -1; @@ -381,168 +332,109 @@ 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]; - char *s; + struct fbuf fb = { .buf = buf, .pos = buf, .len = sizeof(buf) }; if ((syslog_fd < 0) && ((syslog_fd = syslog_connect()) < 0)) return; -#define LOC s,buf+sizeof(buf)-s - s = buf; - s = str_append(LOC, "<"); - s = num_append(LOC, priority); - s = str_append(LOC, ">"); /* forget about the timestamp, too difficult in a signal handler */ - s = str_append(LOC, zlog_default->ident); - if (zlog_default->syslog_options & LOG_PID) { - s = str_append(LOC, "["); - s = num_append(LOC, getpid()); - s = str_append(LOC, "]"); - } - s = str_append(LOC, ": "); - s = str_append(LOC, msg); - write_wrapper(syslog_fd, buf, s - buf); -#undef LOC + 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) { -#define CRASHLOG_PREFIX "/var/tmp/quagga." -#define CRASHLOG_SUFFIX "crashlog" - if (zlog_default && zlog_default->ident) { - /* Avoid strlen since it is not async-signal-safe. */ - const char *p; - size_t ilen; - - for (p = zlog_default->ident, ilen = 0; *p; p++) - ilen++; - { - char buf[sizeof(CRASHLOG_PREFIX) + ilen - + sizeof(CRASHLOG_SUFFIX) + 3]; - char *s = buf; -#define LOC s,buf+sizeof(buf)-s - s = str_append(LOC, CRASHLOG_PREFIX); - s = str_append(LOC, zlog_default->ident); - s = str_append(LOC, "."); - s = str_append(LOC, CRASHLOG_SUFFIX); -#undef LOC - *s = '\0'; - return open(buf, O_WRONLY | O_CREAT | O_EXCL, - LOGFILE_MASK); - } - } - return open(CRASHLOG_PREFIX CRASHLOG_SUFFIX, - O_WRONLY | O_CREAT | O_EXCL, LOGFILE_MASK); -#undef CRASHLOG_SUFFIX -#undef CRASHLOG_PREFIX -} - -/* Note: the goal here is to use only async-signal-safe functions. */ -void zlog_signal(int signo, const char *action -#ifdef SA_SIGINFO - , - siginfo_t *siginfo, void *program_counter -#endif - ) -{ - time_t now; - char buf[sizeof("DEFAULT: Received signal S at T (si_addr 0xP, PC 0xP); aborting...") - + 100]; - char *s = buf; - char *msgstart = buf; -#define LOC s,buf+sizeof(buf)-s + char crashlog_buf[PATH_MAX]; + const char *crashlog_default = "/var/tmp/frr.crashlog", *crashlog; - time(&now); - if (zlog_default) { - s = str_append(LOC, zlog_default->protoname); - *s++ = ':'; - *s++ = ' '; - msgstart = s; - } - s = str_append(LOC, "Received signal "); - s = num_append(LOC, signo); - s = str_append(LOC, " at "); - s = num_append(LOC, now); -#ifdef SA_SIGINFO - s = str_append(LOC, " (si_addr 0x"); - s = hex_append(LOC, (unsigned long)(siginfo->si_addr)); - if (program_counter) { - s = str_append(LOC, ", PC 0x"); - s = hex_append(LOC, (unsigned long)program_counter); + 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; } - s = str_append(LOC, "); "); -#else /* SA_SIGINFO */ - s = str_append(LOC, "; "); -#endif /* SA_SIGINFO */ - s = str_append(LOC, action); - if (s < buf + sizeof(buf)) - *s++ = '\n'; + return open(crashlog, O_WRONLY | O_CREAT | O_EXCL, LOGFILE_MASK); +} /* N.B. implicit priority is most severe */ #define PRI LOG_CRIT -#define DUMP(FD) write_wrapper(FD, buf, s-buf); +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)) - DUMP(logfile_fd) + write(logfile_fd, fb->buf, fb->pos - fb->buf); if (!zlog_default) - DUMP(STDERR_FILENO) + write(STDERR_FILENO, fb->buf, fb->pos - fb->buf); else { if (PRI <= zlog_default->maxlvl[ZLOG_DEST_STDOUT]) - DUMP(STDOUT_FILENO) + write(STDOUT_FILENO, fb->buf, fb->pos - fb->buf); /* Remove trailing '\n' for monitor and syslog */ - *--s = '\0'; + fb->pos--; if (PRI <= zlog_default->maxlvl[ZLOG_DEST_MONITOR]) - vty_log_fixed(buf, s - buf); + vty_log_fixed(fb->buf, fb->pos - fb->buf); if (PRI <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG]) syslog_sigsafe(PRI | zlog_default->facility, msgstart, - s - msgstart); + fb->pos - msgstart); } -#undef DUMP +} - zlog_backtrace_sigsafe(PRI, +/* 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) +{ #ifdef SA_SIGINFO - program_counter -#else - NULL + siginfo_t *siginfo = siginfo_v; #endif - ); + 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); +#ifdef SA_SIGINFO + if (program_counter) + bprintfrr(&fb, " (si_addr 0x%tx, PC 0x%tx)", + (ptrdiff_t)siginfo->si_addr, + (ptrdiff_t)program_counter); + else + bprintfrr(&fb, " (si_addr 0x%tx)", + (ptrdiff_t)siginfo->si_addr); +#endif /* SA_SIGINFO */ + bprintfrr(&fb, "; %s\n", action); + + crash_write(&fb, msgstart); + + zlog_backtrace_sigsafe(PRI, program_counter); + + fb.pos = buf; - s = buf; struct thread *tc; tc = pthread_getspecific(thread_current); - if (!tc) - s = str_append(LOC, "no thread information available\n"); - else { - s = str_append(LOC, "in thread "); - s = str_append(LOC, tc->funcname); - s = str_append(LOC, " scheduled from "); - s = str_append(LOC, tc->schedfrom); - s = str_append(LOC, ":"); - s = num_append(LOC, tc->schedfrom_line); - s = str_append(LOC, "\n"); - } -#define DUMP(FD) write_wrapper(FD, buf, s-buf); - /* If no file logging configured, try to write to fallback log file. */ - if (logfile_fd >= 0) - DUMP(logfile_fd) - if (!zlog_default) - DUMP(STDERR_FILENO) - else { - if (PRI <= zlog_default->maxlvl[ZLOG_DEST_STDOUT]) - DUMP(STDOUT_FILENO) - /* Remove trailing '\n' for monitor and syslog */ - *--s = '\0'; - if (PRI <= zlog_default->maxlvl[ZLOG_DEST_MONITOR]) - vty_log_fixed(buf, s - buf); - if (PRI <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG]) - syslog_sigsafe(PRI | zlog_default->facility, msgstart, - s - msgstart); - } -#undef DUMP + if (!tc) + bprintfrr(&fb, "no thread information available\n"); + else + bprintfrr(&fb, "in thread %s scheduled from %s:%d\n", + tc->funcname, tc->schedfrom, tc->schedfrom_line); -#undef PRI -#undef LOC + crash_write(&fb, NULL); } /* Log a backtrace using only async-signal-safe functions. @@ -550,7 +442,8 @@ void zlog_signal(int signo, const char *action void zlog_backtrace_sigsafe(int priority, void *program_counter) { #ifdef HAVE_LIBUNWIND - char buf[100]; + char buf[256]; + struct fbuf fb = { .buf = buf, .len = sizeof(buf) }; unw_cursor_t cursor; unw_context_t uc; unw_word_t ip, off, sp; @@ -564,28 +457,27 @@ void zlog_backtrace_sigsafe(int priority, void *program_counter) unw_get_reg(&cursor, UNW_REG_IP, &ip); unw_get_reg(&cursor, UNW_REG_SP, &sp); - if (unw_is_signal_frame(&cursor)) - dprintf(2, " ---- signal ----\n"); - - if (!unw_get_proc_name(&cursor, buf, sizeof(buf), &off)) { - snprintf(name, sizeof(name), "%s+%#lx", - buf, (long)off); - } - dprintf(2, "%-30s %16lx %16lx", name, (long)ip, (long)sp); - if (dladdr((void *)ip, &dlinfo)) { - dprintf(2, " %s (mapped at %p)", - dlinfo.dli_fname, dlinfo.dli_fbase); - } - dprintf(2, "\n"); + if (!unw_get_proc_name(&cursor, buf, sizeof(buf), &off)) + snprintfrr(name, sizeof(name), "%s+%#lx", + buf, (long)off); + fb.pos = buf; + if (unw_is_signal_frame(&cursor)) + bprintfrr(&fb, " ---- signal ----\n"); + bprintfrr(&fb, "%-30s %16lx %16lx", name, (long)ip, (long)sp); + if (dladdr((void *)ip, &dlinfo)) + bprintfrr(&fb, " %s (mapped at %p)", + dlinfo.dli_fname, dlinfo.dli_fbase); + bprintfrr(&fb, "\n"); + crash_write(&fb, NULL); } #elif defined(HAVE_GLIBC_BACKTRACE) || defined(HAVE_PRINTSTACK) static const char pclabel[] = "Program counter: "; void *array[64]; int size; - char buf[100]; - char *s, **bt = NULL; -#define LOC s,buf+sizeof(buf)-s + 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)); @@ -598,7 +490,7 @@ void zlog_backtrace_sigsafe(int priority, void *program_counter) write_wrapper(FD, pclabel, sizeof(pclabel) - 1); \ backtrace_symbols_fd(&program_counter, 1, FD); \ } \ - write_wrapper(FD, buf, s - buf); \ + write_wrapper(FD, fb.buf, fb.pos - fb.buf); \ backtrace_symbols_fd(array, size, FD); \ } #elif defined(HAVE_PRINTSTACK) @@ -608,15 +500,12 @@ void zlog_backtrace_sigsafe(int priority, void *program_counter) { \ if (program_counter) \ write_wrapper((FD), pclabel, sizeof(pclabel) - 1); \ - write_wrapper((FD), buf, s - buf); \ + write_wrapper((FD), fb.buf, fb.pos - fb.buf); \ printstack((FD)); \ } #endif /* HAVE_GLIBC_BACKTRACE, HAVE_PRINTSTACK */ - s = buf; - s = str_append(LOC, "Backtrace for "); - s = num_append(LOC, size); - s = str_append(LOC, " stack frames:\n"); + bprintfrr(&fb, "Backtrace for %d stack frames:\n", size); if ((logfile_fd >= 0) || ((logfile_fd = open_crashlog()) >= 0)) DUMP(logfile_fd) @@ -626,12 +515,12 @@ void zlog_backtrace_sigsafe(int priority, void *program_counter) if (priority <= zlog_default->maxlvl[ZLOG_DEST_STDOUT]) DUMP(STDOUT_FILENO) /* Remove trailing '\n' for monitor and syslog */ - *--s = '\0'; + fb.pos--; if (priority <= zlog_default->maxlvl[ZLOG_DEST_MONITOR]) - vty_log_fixed(buf, s - buf); + vty_log_fixed(fb.buf, fb.pos - fb.buf); if (priority <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG]) - syslog_sigsafe(priority | zlog_default->facility, buf, - s - buf); + syslog_sigsafe(priority | zlog_default->facility, + fb.buf, fb.pos - fb.buf); { int i; #ifdef HAVE_GLIBC_BACKTRACE @@ -639,34 +528,26 @@ void zlog_backtrace_sigsafe(int priority, void *program_counter) #endif /* Just print the function addresses. */ for (i = 0; i < size; i++) { - s = buf; + fb.pos = buf; if (bt) - s = str_append(LOC, bt[i]); - else { - s = str_append(LOC, "[bt "); - s = num_append(LOC, i); - s = str_append(LOC, "] 0x"); - s = hex_append( - LOC, (unsigned long)(array[i])); - } - *s = '\0'; + 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(buf, s - buf); + vty_log_fixed(fb.buf, fb.pos - fb.buf); if (priority <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG]) - syslog_sigsafe( - priority - | zlog_default - ->facility, - buf, s - buf); + syslog_sigsafe(priority + | zlog_default->facility, + fb.buf, fb.pos - fb.buf); } if (bt) free(bt); } } #undef DUMP -#undef LOC #endif /* HAVE_STRACK_TRACE */ } @@ -128,12 +128,8 @@ const char *lookup_msg(const struct message *mz, int kz, const char *nf); extern const char *safe_strerror(int errnum); /* To be called when a fatal signal is caught. */ -extern void zlog_signal(int signo, const char *action -#ifdef SA_SIGINFO - , - siginfo_t *siginfo, void *program_counter -#endif - ); +extern void zlog_signal(int signo, const char *action, void *siginfo, + void *program_counter); /* Log a backtrace. */ extern void zlog_backtrace(int priority); diff --git a/lib/module.h b/lib/module.h index c5f96db85b..79cf52d75a 100644 --- a/lib/module.h +++ b/lib/module.h @@ -24,16 +24,6 @@ extern "C" { #endif -#if !defined(__GNUC__) -#error module code needs GCC visibility extensions -#elif __GNUC__ < 4 -#error module code needs GCC visibility extensions -#else -# define DSO_PUBLIC __attribute__ ((visibility ("default"))) -# define DSO_SELF __attribute__ ((visibility ("protected"))) -# define DSO_LOCAL __attribute__ ((visibility ("hidden"))) -#endif - struct frrmod_runtime; struct frrmod_info { diff --git a/lib/nexthop.c b/lib/nexthop.c index 57a2f1daaa..4cea14955a 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -32,6 +32,7 @@ #include "nexthop.h" #include "mpls.h" #include "jhash.h" +#include "printfrr.h" DEFINE_MTYPE_STATIC(LIB, NEXTHOP, "Nexthop") DEFINE_MTYPE_STATIC(LIB, NH_LABEL, "Nexthop label") @@ -423,3 +424,87 @@ uint32_t nexthop_hash(const struct nexthop *nexthop) } return key; } + +/* + * nexthop printing variants: + * %pNHvv + * via 1.2.3.4 + * via 1.2.3.4, eth0 + * is directly connected, eth0 + * unreachable (blackhole) + * %pNHv + * 1.2.3.4 + * 1.2.3.4, via eth0 + * directly connected, eth0 + * unreachable (blackhole) + * %pNHs + * nexthop2str() + */ +printfrr_ext_autoreg_p("NH", printfrr_nh) +static ssize_t printfrr_nh(char *buf, size_t bsz, const char *fmt, + int prec, const void *ptr) +{ + const struct nexthop *nexthop = ptr; + struct fbuf fb = { .buf = buf, .pos = buf, .len = bsz - 1 }; + bool do_ifi = false; + const char *s, *v_is = "", *v_via = "", *v_viaif = "via "; + ssize_t ret = 3; + + switch (fmt[2]) { + case 'v': + if (fmt[3] == 'v') { + v_is = "is "; + v_via = "via "; + v_viaif = ""; + ret++; + } + + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + bprintfrr(&fb, "%s%pI4", v_via, &nexthop->gate.ipv4); + do_ifi = true; + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + bprintfrr(&fb, "%s%pI6", v_via, &nexthop->gate.ipv6); + do_ifi = true; + break; + case NEXTHOP_TYPE_IFINDEX: + bprintfrr(&fb, "%sdirectly connected, %s", v_is, + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); + break; + case NEXTHOP_TYPE_BLACKHOLE: + switch (nexthop->bh_type) { + case BLACKHOLE_REJECT: + s = " (ICMP unreachable)"; + break; + case BLACKHOLE_ADMINPROHIB: + s = " (ICMP admin-prohibited)"; + break; + case BLACKHOLE_NULL: + s = " (blackhole)"; + break; + default: + s = ""; + break; + } + bprintfrr(&fb, "unreachable%s", s); + break; + default: + break; + } + if (do_ifi && nexthop->ifindex) + bprintfrr(&fb, ", %s%s", v_viaif, ifindex2ifname( + nexthop->ifindex, + nexthop->vrf_id)); + + *fb.pos = '\0'; + return ret; + case 's': + nexthop2str(nexthop, buf, bsz); + return 3; + } + return 0; +} diff --git a/lib/northbound.c b/lib/northbound.c index dbf90c58d4..8a5cd0ef14 100644 --- a/lib/northbound.c +++ b/lib/northbound.c @@ -777,7 +777,7 @@ static void nb_log_callback(const enum nb_event event, zlog_debug( "northbound callback: event [%s] op [%s] xpath [%s] value [%s]", nb_event_name(event), nb_operation_name(operation), xpath, - value); + value ? value : "(NULL)"); } /* diff --git a/lib/prefix.c b/lib/prefix.c index 42d202ddbc..134d9cf908 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -28,6 +28,7 @@ #include "log.h" #include "jhash.h" #include "lib_errors.h" +#include "printfrr.h" DEFINE_MTYPE_STATIC(LIB, PREFIX, "Prefix") DEFINE_MTYPE_STATIC(LIB, PREFIX_FLOWSPEC, "Prefix Flowspec") @@ -1626,3 +1627,48 @@ char *esi_to_str(const esi_t *esi, char *buf, int size) esi->val[9]); return ptr; } + +printfrr_ext_autoreg_p("I4", printfrr_i4) +static ssize_t printfrr_i4(char *buf, size_t bsz, const char *fmt, + int prec, const void *ptr) +{ + inet_ntop(AF_INET, ptr, buf, bsz); + return 2; +} + +printfrr_ext_autoreg_p("I6", printfrr_i6) +static ssize_t printfrr_i6(char *buf, size_t bsz, const char *fmt, + int prec, const void *ptr) +{ + inet_ntop(AF_INET6, ptr, buf, bsz); + return 2; +} + +printfrr_ext_autoreg_p("FX", printfrr_pfx) +static ssize_t printfrr_pfx(char *buf, size_t bsz, const char *fmt, + int prec, const void *ptr) +{ + prefix2str(ptr, buf, bsz); + return 2; +} + +printfrr_ext_autoreg_p("SG4", printfrr_psg) +static ssize_t printfrr_psg(char *buf, size_t bsz, const char *fmt, + int prec, const void *ptr) +{ + const struct prefix_sg *sg = ptr; + struct fbuf fb = { .buf = buf, .pos = buf, .len = bsz - 1 }; + + if (sg->src.s_addr == INADDR_ANY) + bprintfrr(&fb, "(*,"); + else + bprintfrr(&fb, "(%pI4,", &sg->src); + + if (sg->grp.s_addr == INADDR_ANY) + bprintfrr(&fb, "*)"); + else + bprintfrr(&fb, "%pI4)", &sg->grp); + + fb.pos[0] = '\0'; + return 3; +} diff --git a/lib/printf/README b/lib/printf/README new file mode 100644 index 0000000000..ac283642d7 --- /dev/null +++ b/lib/printf/README @@ -0,0 +1,9 @@ +This is the printf implementation from FreeBSD. It was imported on 2019-05-12, +from SVN revision 347514 (but the code hadn't been touched for 2 years before +that.) + +Please don't reindent or otherwise mangle the files to make importing fixes +easy (not that there are significant changes likely to happen...) + +The changes to this code are published under FreeBSD's license as listed in the +file headers. If you change license, please make that as obvious as possible. diff --git a/lib/printf/glue.c b/lib/printf/glue.c new file mode 100644 index 0000000000..1b760dc2d3 --- /dev/null +++ b/lib/printf/glue.c @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2019 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> +#include <string.h> +#include <wchar.h> + +#include "printfrr.h" +#include "printflocal.h" + +ssize_t bprintfrr(struct fbuf *out, const char *fmt, ...) +{ + ssize_t ret; + va_list ap; + + va_start(ap, fmt); + ret = vbprintfrr(out, fmt, ap); + va_end(ap); + return ret; +} + +ssize_t vsnprintfrr(char *out, size_t outsz, const char *fmt, va_list ap) +{ + struct fbuf fbb = { .buf = out, .pos = out, .len = outsz - 1, }; + struct fbuf *fb = (out && outsz) ? &fbb : NULL; + ssize_t ret; + + ret = vbprintfrr(fb, fmt, ap); + if (fb) + fb->pos[0] = '\0'; + return ret; +} + +ssize_t snprintfrr(char *out, size_t outsz, const char *fmt, ...) +{ + struct fbuf fbb = { .buf = out, .pos = out, .len = outsz - 1, }; + struct fbuf *fb = (out && outsz) ? &fbb : NULL; + ssize_t ret; + va_list ap; + + va_start(ap, fmt); + ret = vbprintfrr(fb, fmt, ap); + va_end(ap); + if (fb) + fb->pos[0] = '\0'; + return ret; +} + +ssize_t vcsnprintfrr(char *out, size_t outsz, const char *fmt, va_list ap) +{ + if (!out || !outsz) + return vbprintfrr(NULL, fmt, ap); + + struct fbuf fbb = { .buf = out, .pos = out, .len = outsz - 1, }; + ssize_t ret; + size_t pos; + + pos = strnlen(out, outsz); + fbb.pos += pos; + + ret = vbprintfrr(&fbb, fmt, ap); + fbb.pos[0] = '\0'; + return ret >= 0 ? ret + (ssize_t)pos : ret; +} + +ssize_t csnprintfrr(char *out, size_t outsz, const char *fmt, ...) +{ + ssize_t ret; + va_list ap; + + va_start(ap, fmt); + ret = vcsnprintfrr(out, outsz, fmt, ap); + va_end(ap); + return ret; +} + +char *vasnprintfrr(struct memtype *mt, char *out, size_t outsz, const char *fmt, + va_list ap) +{ + struct fbuf fb = { .buf = out, .pos = out, .len = outsz - 1, }; + ssize_t len; + va_list ap2; + char *ret = out; + + va_copy(ap2, ap); + len = vbprintfrr(&fb, fmt, ap); + if (len < 0) + /* error = malformed format string => try something useful */ + return qstrdup(mt, fmt); + + if ((size_t)len >= outsz - 1) { + ret = qmalloc(mt, len + 1); + fb.buf = fb.pos = ret; + fb.len = len; + + vbprintfrr(&fb, fmt, ap2); + } + ret[len] = '\0'; + return ret; +} + +char *asnprintfrr(struct memtype *mt, char *out, size_t outsz, const char *fmt, + ...) +{ + va_list ap; + char *ret; + + va_start(ap, fmt); + ret = vasnprintfrr(mt, out, outsz, fmt, ap); + va_end(ap); + return ret; +} + +char *vasprintfrr(struct memtype *mt, const char *fmt, va_list ap) +{ + char buf[256]; + char *ret; + + ret = vasnprintfrr(mt, buf, sizeof(buf), fmt, ap); + + if (ret == buf) + ret = qstrdup(mt, ret); + return ret; +} + +char *asprintfrr(struct memtype *mt, const char *fmt, ...) +{ + char buf[256]; + va_list ap; + char *ret; + + va_start(ap, fmt); + ret = vasnprintfrr(mt, buf, sizeof(buf), fmt, ap); + va_end(ap); + + if (ret == buf) + ret = qstrdup(mt, ret); + return ret; +} + +/* Q: WTF? + * A: since printf should be reasonably fast (think debugging logs), the idea + * here is to keep things close by each other in a cacheline. That's why + * ext_quick just has the first 2 characters of an extension, and we do a + * nice linear continuous sweep. Only if we find something, we go do more + * expensive things. + * + * Q: doesn't this need a mutex/lock? + * A: theoretically, yes, but that's quite expensive and I rather elide that + * necessity by putting down some usage rules. Just call this at startup + * while singlethreaded and all is fine. Ideally, just use constructors + * (and make sure dlopen() doesn't mess things up...) + */ +#define MAXEXT 64 + +struct ext_quick { + char fmt[2]; +}; + +static uint8_t ext_offsets[26] __attribute__((aligned(32))); +static struct ext_quick entries[MAXEXT] __attribute__((aligned(64))); +static const struct printfrr_ext *exts[MAXEXT] __attribute__((aligned(64))); + +void printfrr_ext_reg(const struct printfrr_ext *ext) +{ + uint8_t o; + ptrdiff_t i; + + if (!printfrr_ext_char(ext->match[0])) + return; + + o = ext->match[0] - 'A'; + for (i = ext_offsets[o]; + i < MAXEXT && entries[i].fmt[0] && + memcmp(entries[i].fmt, ext->match, 2) < 0; + i++) + ; + if (i == MAXEXT) + return; + for (o++; o <= 'Z' - 'A'; o++) + ext_offsets[o]++; + + memmove(entries + i + 1, entries + i, + (MAXEXT - i - 1) * sizeof(entries[0])); + memmove(exts + i + 1, exts + i, + (MAXEXT - i - 1) * sizeof(exts[0])); + + memcpy(entries[i].fmt, ext->match, 2); + exts[i] = ext; +} + +ssize_t printfrr_extp(char *buf, size_t sz, const char *fmt, int prec, + const void *ptr) +{ + const struct printfrr_ext *ext; + size_t i; + + for (i = ext_offsets[fmt[0] - 'A']; i < MAXEXT; i++) { + if (!entries[i].fmt[0] || entries[i].fmt[0] > fmt[0]) + return 0; + if (entries[i].fmt[1] && entries[i].fmt[1] != fmt[1]) + continue; + ext = exts[i]; + if (!ext->print_ptr) + continue; + if (strncmp(ext->match, fmt, strlen(ext->match))) + continue; + return ext->print_ptr(buf, sz, fmt, prec, ptr); + } + return 0; +} + +ssize_t printfrr_exti(char *buf, size_t sz, const char *fmt, int prec, + uintmax_t num) +{ + const struct printfrr_ext *ext; + size_t i; + + for (i = ext_offsets[fmt[0] - 'A']; i < MAXEXT; i++) { + if (!entries[i].fmt[0] || entries[i].fmt[0] > fmt[0]) + return 0; + if (entries[i].fmt[1] && entries[i].fmt[1] != fmt[1]) + continue; + ext = exts[i]; + if (!ext->print_int) + continue; + if (strncmp(ext->match, fmt, strlen(ext->match))) + continue; + return ext->print_int(buf, sz, fmt, prec, num); + } + return 0; +} diff --git a/lib/printf/printf-pos.c b/lib/printf/printf-pos.c new file mode 100644 index 0000000000..399573e6c1 --- /dev/null +++ b/lib/printf/printf-pos.c @@ -0,0 +1,786 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_SYS_CDEFS_H +#include <sys/cdefs.h> +#endif + +/* + * This is the code responsible for handling positional arguments + * (%m$ and %m$.n$) for vfprintf() and vfwprintf(). + */ + +#include <sys/types.h> + +#include <limits.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> + +#include "printflocal.h" + +#ifdef NL_ARGMAX +#define MAX_POSARG NL_ARGMAX +#else +#define MAX_POSARG 65536 +#endif + +/* + * Type ids for argument type table. + */ +enum typeid { + T_UNUSED, TP_SHORT, T_INT, T_U_INT, TP_INT, + T_LONG, T_U_LONG, TP_LONG, T_LLONG, T_U_LLONG, TP_LLONG, + T_PTRDIFFT, TP_PTRDIFFT, T_SSIZET, T_SIZET, TP_SSIZET, + T_INT64T, T_UINT64T, T_INTMAXT, T_UINTMAXT, TP_INTMAXT, TP_VOID, + TP_CHAR, TP_SCHAR, T_DOUBLE, T_LONG_DOUBLE, T_WINT, TP_WCHAR +}; + +/* An expandable array of types. */ +struct typetable { + enum typeid *table; /* table of types */ + enum typeid stattable[STATIC_ARG_TBL_SIZE]; + u_int tablesize; /* current size of type table */ + u_int tablemax; /* largest used index in table */ + u_int nextarg; /* 1-based argument index */ +}; + +static int __grow_type_table(struct typetable *); +static void build_arg_table (struct typetable *, va_list, union arg **); + +/* + * Initialize a struct typetable. + */ +static inline void +inittypes(struct typetable *types) +{ + u_int n; + + types->table = types->stattable; + types->tablesize = STATIC_ARG_TBL_SIZE; + types->tablemax = 0; + types->nextarg = 1; + for (n = 0; n < STATIC_ARG_TBL_SIZE; n++) + types->table[n] = T_UNUSED; +} + +/* + * struct typetable destructor. + */ +static inline void +freetypes(struct typetable *types) +{ + + if (types->table != types->stattable) + free (types->table); +} + +/* + * Ensure that there is space to add a new argument type to the type table. + * Expand the table if necessary. Returns 0 on success. + */ +static inline int +_ensurespace(struct typetable *types) +{ + + if (types->nextarg >= types->tablesize) { + if (__grow_type_table(types)) + return (-1); + } + if (types->nextarg > types->tablemax) + types->tablemax = types->nextarg; + return (0); +} + +/* + * Add an argument type to the table, expanding if necessary. + * Returns 0 on success. + */ +static inline int +addtype(struct typetable *types, enum typeid type) +{ + + if (_ensurespace(types)) + return (-1); + types->table[types->nextarg++] = type; + return (0); +} + +static inline int +addsarg(struct typetable *types, int flags) +{ + + if (_ensurespace(types)) + return (-1); + if (flags & LONGDBL) + types->table[types->nextarg++] = T_INT64T; + else if (flags & INTMAXT) + types->table[types->nextarg++] = T_INTMAXT; + else if (flags & SIZET) + types->table[types->nextarg++] = T_SSIZET; + else if (flags & PTRDIFFT) + types->table[types->nextarg++] = T_PTRDIFFT; + else if (flags & LLONGINT) + types->table[types->nextarg++] = T_LLONG; + else if (flags & LONGINT) + types->table[types->nextarg++] = T_LONG; + else + types->table[types->nextarg++] = T_INT; + return (0); +} + +static inline int +adduarg(struct typetable *types, int flags) +{ + + if (_ensurespace(types)) + return (-1); + if (flags & LONGDBL) + types->table[types->nextarg++] = T_UINT64T; + else if (flags & INTMAXT) + types->table[types->nextarg++] = T_UINTMAXT; + else if (flags & SIZET) + types->table[types->nextarg++] = T_SIZET; + else if (flags & PTRDIFFT) + types->table[types->nextarg++] = T_SIZET; + else if (flags & LLONGINT) + types->table[types->nextarg++] = T_U_LLONG; + else if (flags & LONGINT) + types->table[types->nextarg++] = T_U_LONG; + else + types->table[types->nextarg++] = T_U_INT; + return (0); +} + +/* + * Add * arguments to the type array. + */ +static inline int +addaster(struct typetable *types, char **fmtp) +{ + char *cp; + u_int n2; + + n2 = 0; + cp = *fmtp; + while (is_digit(*cp)) { + n2 = 10 * n2 + to_digit(*cp); + cp++; + } + if (*cp == '$') { + u_int hold = types->nextarg; + types->nextarg = n2; + if (addtype(types, T_INT)) + return (-1); + types->nextarg = hold; + *fmtp = ++cp; + } else { + if (addtype(types, T_INT)) + return (-1); + } + return (0); +} + +#ifdef WCHAR_SUPPORT +static inline int +addwaster(struct typetable *types, wchar_t **fmtp) +{ + wchar_t *cp; + u_int n2; + + n2 = 0; + cp = *fmtp; + while (is_digit(*cp)) { + n2 = 10 * n2 + to_digit(*cp); + cp++; + } + if (*cp == '$') { + u_int hold = types->nextarg; + types->nextarg = n2; + if (addtype(types, T_INT)) + return (-1); + types->nextarg = hold; + *fmtp = ++cp; + } else { + if (addtype(types, T_INT)) + return (-1); + } + return (0); +} +#endif /* WCHAR_SUPPORT */ + +/* + * Find all arguments when a positional parameter is encountered. Returns a + * table, indexed by argument number, of pointers to each arguments. The + * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries. + * It will be replaces with a malloc-ed one if it overflows. + * Returns 0 on success. On failure, returns nonzero and sets errno. + */ +int +_frr_find_arguments (const char *fmt0, va_list ap, union arg **argtable) +{ + char *fmt; /* format string */ + int ch; /* character from fmt */ + u_int n; /* handy integer (short term usage) */ + int error; + int flags; /* flags as above */ + struct typetable types; /* table of types */ + + fmt = (char *)fmt0; + inittypes(&types); + error = 0; + + /* + * Scan the format for conversions (`%' character). + */ + for (;;) { + while ((ch = *fmt) != '\0' && ch != '%') + fmt++; + if (ch == '\0') + goto done; + fmt++; /* skip over '%' */ + + flags = 0; + +rflag: ch = *fmt++; +reswitch: switch (ch) { + case ' ': + case '#': + goto rflag; + case '*': + if ((error = addaster(&types, &fmt))) + goto error; + goto rflag; + case '-': + case '+': + case '\'': + goto rflag; + case '.': + if ((ch = *fmt++) == '*') { + if ((error = addaster(&types, &fmt))) + goto error; + goto rflag; + } + while (is_digit(ch)) { + ch = *fmt++; + } + goto reswitch; + case '0': + goto rflag; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + n = 0; + do { + n = 10 * n + to_digit(ch); + /* Detect overflow */ + if (n > MAX_POSARG) { + error = -1; + goto error; + } + ch = *fmt++; + } while (is_digit(ch)); + if (ch == '$') { + types.nextarg = n; + goto rflag; + } + goto reswitch; + case 'L': + flags |= LONGDBL; + goto rflag; + case 'h': + if (flags & SHORTINT) { + flags &= ~SHORTINT; + flags |= CHARINT; + } else + flags |= SHORTINT; + goto rflag; + case 'j': + flags |= INTMAXT; + goto rflag; + case 'l': + if (flags & LONGINT) { + flags &= ~LONGINT; + flags |= LLONGINT; + } else + flags |= LONGINT; + goto rflag; + case 'q': + flags |= LLONGINT; /* not necessarily */ + goto rflag; + case 't': + flags |= PTRDIFFT; + goto rflag; + case 'z': + flags |= SIZET; + goto rflag; + case 'C': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'c': + error = addtype(&types, + (flags & LONGINT) ? T_WINT : T_INT); + if (error) + goto error; + break; + case 'D': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'd': + case 'i': + if ((error = addsarg(&types, flags))) + goto error; + break; +#ifndef NO_FLOATING_POINT + case 'a': + case 'A': + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + error = addtype(&types, + (flags & LONGDBL) ? T_LONG_DOUBLE : T_DOUBLE); + if (error) + goto error; + break; +#endif /* !NO_FLOATING_POINT */ + case 'n': + if (flags & INTMAXT) + error = addtype(&types, TP_INTMAXT); + else if (flags & PTRDIFFT) + error = addtype(&types, TP_PTRDIFFT); + else if (flags & SIZET) + error = addtype(&types, TP_SSIZET); + else if (flags & LLONGINT) + error = addtype(&types, TP_LLONG); + else if (flags & LONGINT) + error = addtype(&types, TP_LONG); + else if (flags & SHORTINT) + error = addtype(&types, TP_SHORT); + else if (flags & CHARINT) + error = addtype(&types, TP_SCHAR); + else + error = addtype(&types, TP_INT); + if (error) + goto error; + continue; /* no output */ + case 'O': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'o': + if ((error = adduarg(&types, flags))) + goto error; + break; + case 'p': + if ((error = addtype(&types, TP_VOID))) + goto error; + break; + case 'S': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 's': + error = addtype(&types, + (flags & LONGINT) ? TP_WCHAR : TP_CHAR); + if (error) + goto error; + break; + case 'U': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'u': + case 'X': + case 'x': + if ((error = adduarg(&types, flags))) + goto error; + break; + default: /* "%?" prints ?, unless ? is NUL */ + if (ch == '\0') + goto done; + break; + } + } +done: + build_arg_table(&types, ap, argtable); +error: + freetypes(&types); + return (error || *argtable == NULL); +} + +#ifdef WCHAR_SUPPORT +/* wchar version of __find_arguments. */ +int +_frr_find_warguments (const wchar_t *fmt0, va_list ap, union arg **argtable) +{ + wchar_t *fmt; /* format string */ + wchar_t ch; /* character from fmt */ + u_int n; /* handy integer (short term usage) */ + int error; + int flags; /* flags as above */ + struct typetable types; /* table of types */ + + fmt = (wchar_t *)fmt0; + inittypes(&types); + error = 0; + + /* + * Scan the format for conversions (`%' character). + */ + for (;;) { + while ((ch = *fmt) != '\0' && ch != '%') + fmt++; + if (ch == '\0') + goto done; + fmt++; /* skip over '%' */ + + flags = 0; + +rflag: ch = *fmt++; +reswitch: switch (ch) { + case ' ': + case '#': + goto rflag; + case '*': + if ((error = addwaster(&types, &fmt))) + goto error; + goto rflag; + case '-': + case '+': + case '\'': + goto rflag; + case '.': + if ((ch = *fmt++) == '*') { + if ((error = addwaster(&types, &fmt))) + goto error; + goto rflag; + } + while (is_digit(ch)) { + ch = *fmt++; + } + goto reswitch; + case '0': + goto rflag; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + n = 0; + do { + n = 10 * n + to_digit(ch); + /* Detect overflow */ + if (n > MAX_POSARG) { + error = -1; + goto error; + } + ch = *fmt++; + } while (is_digit(ch)); + if (ch == '$') { + types.nextarg = n; + goto rflag; + } + goto reswitch; + case 'L': + flags |= LONGDBL; + goto rflag; + case 'h': + if (flags & SHORTINT) { + flags &= ~SHORTINT; + flags |= CHARINT; + } else + flags |= SHORTINT; + goto rflag; + case 'j': + flags |= INTMAXT; + goto rflag; + case 'l': + if (flags & LONGINT) { + flags &= ~LONGINT; + flags |= LLONGINT; + } else + flags |= LONGINT; + goto rflag; + case 'q': + flags |= LLONGINT; /* not necessarily */ + goto rflag; + case 't': + flags |= PTRDIFFT; + goto rflag; + case 'z': + flags |= SIZET; + goto rflag; + case 'C': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'c': + error = addtype(&types, + (flags & LONGINT) ? T_WINT : T_INT); + if (error) + goto error; + break; + case 'D': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'd': + case 'i': + if ((error = addsarg(&types, flags))) + goto error; + break; +#ifndef NO_FLOATING_POINT + case 'a': + case 'A': + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + error = addtype(&types, + (flags & LONGDBL) ? T_LONG_DOUBLE : T_DOUBLE); + if (error) + goto error; + break; +#endif /* !NO_FLOATING_POINT */ + case 'n': + if (flags & INTMAXT) + error = addtype(&types, TP_INTMAXT); + else if (flags & PTRDIFFT) + error = addtype(&types, TP_PTRDIFFT); + else if (flags & SIZET) + error = addtype(&types, TP_SSIZET); + else if (flags & LLONGINT) + error = addtype(&types, TP_LLONG); + else if (flags & LONGINT) + error = addtype(&types, TP_LONG); + else if (flags & SHORTINT) + error = addtype(&types, TP_SHORT); + else if (flags & CHARINT) + error = addtype(&types, TP_SCHAR); + else + error = addtype(&types, TP_INT); + if (error) + goto error; + continue; /* no output */ + case 'O': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'o': + if ((error = adduarg(&types, flags))) + goto error; + break; + case 'p': + if ((error = addtype(&types, TP_VOID))) + goto error; + break; + case 'S': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 's': + error = addtype(&types, + (flags & LONGINT) ? TP_WCHAR : TP_CHAR); + if (error) + goto error; + break; + case 'U': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'u': + case 'X': + case 'x': + if ((error = adduarg(&types, flags))) + goto error; + break; + default: /* "%?" prints ?, unless ? is NUL */ + if (ch == '\0') + goto done; + break; + } + } +done: + build_arg_table(&types, ap, argtable); +error: + freetypes(&types); + return (error || *argtable == NULL); +} +#endif /* WCHAR_SUPPORT */ + +/* + * Increase the size of the type table. Returns 0 on success. + */ +static int +__grow_type_table(struct typetable *types) +{ + enum typeid *const oldtable = types->table; + const int oldsize = types->tablesize; + enum typeid *newtable; + u_int n, newsize; + + /* Detect overflow */ + if (types->nextarg > MAX_POSARG) + return (-1); + + newsize = oldsize * 2; + if (newsize < types->nextarg + 1) + newsize = types->nextarg + 1; + if (oldsize == STATIC_ARG_TBL_SIZE) { + if ((newtable = malloc(newsize * sizeof(enum typeid))) == NULL) + return (-1); + bcopy(oldtable, newtable, oldsize * sizeof(enum typeid)); + } else { + newtable = realloc(oldtable, newsize * sizeof(enum typeid)); + if (newtable == NULL) + return (-1); + } + for (n = oldsize; n < newsize; n++) + newtable[n] = T_UNUSED; + + types->table = newtable; + types->tablesize = newsize; + + return (0); +} + +/* + * Build the argument table from the completed type table. + * On malloc failure, *argtable is set to NULL. + */ +static void +build_arg_table(struct typetable *types, va_list ap, union arg **argtable) +{ + u_int n; + + if (types->tablemax >= STATIC_ARG_TBL_SIZE) { + *argtable = (union arg *) + malloc (sizeof (union arg) * (types->tablemax + 1)); + if (*argtable == NULL) + return; + } + + (*argtable) [0].intarg = 0; + for (n = 1; n <= types->tablemax; n++) { + switch (types->table[n]) { + case T_UNUSED: /* whoops! */ + (*argtable) [n].intarg = va_arg (ap, int); + break; + case TP_SCHAR: + (*argtable) [n].pschararg = va_arg (ap, signed char *); + break; + case TP_SHORT: + (*argtable) [n].pshortarg = va_arg (ap, short *); + break; + case T_INT: + (*argtable) [n].intarg = va_arg (ap, int); + break; + case T_U_INT: + (*argtable) [n].uintarg = va_arg (ap, unsigned int); + break; + case TP_INT: + (*argtable) [n].pintarg = va_arg (ap, int *); + break; + case T_LONG: + (*argtable) [n].longarg = va_arg (ap, long); + break; + case T_U_LONG: + (*argtable) [n].ulongarg = va_arg (ap, unsigned long); + break; + case TP_LONG: + (*argtable) [n].plongarg = va_arg (ap, long *); + break; + case T_LLONG: + (*argtable) [n].longlongarg = va_arg (ap, long long); + break; + case T_U_LLONG: + (*argtable) [n].ulonglongarg = va_arg (ap, unsigned long long); + break; + case TP_LLONG: + (*argtable) [n].plonglongarg = va_arg (ap, long long *); + break; + case T_PTRDIFFT: + (*argtable) [n].ptrdiffarg = va_arg (ap, ptrdiff_t); + break; + case TP_PTRDIFFT: + (*argtable) [n].pptrdiffarg = va_arg (ap, ptrdiff_t *); + break; + case T_SIZET: + (*argtable) [n].sizearg = va_arg (ap, size_t); + break; + case T_SSIZET: + (*argtable) [n].sizearg = va_arg (ap, ssize_t); + break; + case TP_SSIZET: + (*argtable) [n].pssizearg = va_arg (ap, ssize_t *); + break; + case T_INTMAXT: + (*argtable) [n].intmaxarg = va_arg (ap, intmax_t); + break; + case T_UINTMAXT: + (*argtable) [n].uintmaxarg = va_arg (ap, uintmax_t); + break; + case TP_INTMAXT: + (*argtable) [n].pintmaxarg = va_arg (ap, intmax_t *); + break; + case T_INT64T: + (*argtable) [n].intmaxarg = va_arg (ap, int64_t); + break; + case T_UINT64T: + (*argtable) [n].uintmaxarg = va_arg (ap, uint64_t); + break; + case T_DOUBLE: +#ifndef NO_FLOATING_POINT + (*argtable) [n].doublearg = va_arg (ap, double); +#endif + break; + case T_LONG_DOUBLE: +#ifndef NO_FLOATING_POINT + (*argtable) [n].longdoublearg = va_arg (ap, long double); +#endif + break; + case TP_CHAR: + (*argtable) [n].pchararg = va_arg (ap, char *); + break; + case TP_VOID: + (*argtable) [n].pvoidarg = va_arg (ap, void *); + break; + case T_WINT: + (*argtable) [n].wintarg = va_arg (ap, wint_t); + break; + case TP_WCHAR: + (*argtable) [n].pwchararg = va_arg (ap, wchar_t *); + break; + } + } +} diff --git a/lib/printf/printfcommon.h b/lib/printf/printfcommon.h new file mode 100644 index 0000000000..5c45520b4c --- /dev/null +++ b/lib/printf/printfcommon.h @@ -0,0 +1,244 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Copyright (c) 2011 The FreeBSD Foundation + * All rights reserved. + * Portions of this software were developed by David Chisnall + * under sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * This file defines common routines used by both printf and wprintf. + * You must define CHAR to either char or wchar_t prior to including this. + */ + + +static CHAR *__ujtoa(uintmax_t, CHAR *, int, int, const char *); +static CHAR *__ultoa(u_long, CHAR *, int, int, const char *); + +#define NIOV 8 +struct io_state { + struct fbuf *cb; + size_t avail; +}; + +static inline void +io_init(struct io_state *iop, struct fbuf *cb) +{ + iop->cb = cb; + iop->avail = cb ? cb->len - (cb->pos - cb->buf) : 0; +} + +/* + * WARNING: The buffer passed to io_print() is not copied immediately; it must + * remain valid until io_flush() is called. + */ +static inline int +io_print(struct io_state *iop, const CHAR * __restrict ptr, size_t len) +{ + size_t copylen = len; + + if (!iop->cb) + return 0; + if (iop->avail < copylen) + copylen = iop->avail; + + memcpy(iop->cb->pos, ptr, copylen); + iop->avail -= copylen; + iop->cb->pos += copylen; + return 0; +} + +/* + * Choose PADSIZE to trade efficiency vs. size. If larger printf + * fields occur frequently, increase PADSIZE and make the initialisers + * below longer. + */ +#define PADSIZE 16 /* pad chunk size */ +static const CHAR blanks[PADSIZE] = +{' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; +static const CHAR zeroes[PADSIZE] = +{'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; + +/* + * Pad with blanks or zeroes. 'with' should point to either the blanks array + * or the zeroes array. + */ +static inline int +io_pad(struct io_state *iop, int howmany, const CHAR * __restrict with) +{ + int n; + + while (howmany > 0) { + n = (howmany >= PADSIZE) ? PADSIZE : howmany; + if (io_print(iop, with, n)) + return (-1); + howmany -= n; + } + return (0); +} + +/* + * Print exactly len characters of the string spanning p to ep, truncating + * or padding with 'with' as necessary. + */ +static inline int +io_printandpad(struct io_state *iop, const CHAR *p, const CHAR *ep, + int len, const CHAR * __restrict with) +{ + int p_len; + + p_len = ep - p; + if (p_len > len) + p_len = len; + if (p_len > 0) { + if (io_print(iop, p, p_len)) + return (-1); + } else { + p_len = 0; + } + return (io_pad(iop, len - p_len, with)); +} + +/* + * Convert an unsigned long to ASCII for printf purposes, returning + * a pointer to the first character of the string representation. + * Octal numbers can be forced to have a leading zero; hex numbers + * use the given digits. + */ +static CHAR * +__ultoa(u_long val, CHAR *endp, int base, int octzero, const char *xdigs) +{ + CHAR *cp = endp; + long sval; + + /* + * Handle the three cases separately, in the hope of getting + * better/faster code. + */ + switch (base) { + case 10: + if (val < 10) { /* many numbers are 1 digit */ + *--cp = to_char(val); + return (cp); + } + /* + * On many machines, unsigned arithmetic is harder than + * signed arithmetic, so we do at most one unsigned mod and + * divide; this is sufficient to reduce the range of + * the incoming value to where signed arithmetic works. + */ + if (val > LONG_MAX) { + *--cp = to_char(val % 10); + sval = val / 10; + } else + sval = val; + do { + *--cp = to_char(sval % 10); + sval /= 10; + } while (sval != 0); + break; + + case 8: + do { + *--cp = to_char(val & 7); + val >>= 3; + } while (val); + if (octzero && *cp != '0') + *--cp = '0'; + break; + + case 16: + do { + *--cp = xdigs[val & 15]; + val >>= 4; + } while (val); + break; + + default: /* oops */ + abort(); + } + return (cp); +} + +/* Identical to __ultoa, but for intmax_t. */ +static CHAR * +__ujtoa(uintmax_t val, CHAR *endp, int base, int octzero, const char *xdigs) +{ + CHAR *cp = endp; + intmax_t sval; + + /* quick test for small values; __ultoa is typically much faster */ + /* (perhaps instead we should run until small, then call __ultoa?) */ + if (val <= ULONG_MAX) + return (__ultoa((u_long)val, endp, base, octzero, xdigs)); + switch (base) { + case 10: + if (val < 10) { + *--cp = to_char(val % 10); + return (cp); + } + if (val > INTMAX_MAX) { + *--cp = to_char(val % 10); + sval = val / 10; + } else + sval = val; + do { + *--cp = to_char(sval % 10); + sval /= 10; + } while (sval != 0); + break; + + case 8: + do { + *--cp = to_char(val & 7); + val >>= 3; + } while (val); + if (octzero && *cp != '0') + *--cp = '0'; + break; + + case 16: + do { + *--cp = xdigs[val & 15]; + val >>= 4; + } while (val); + break; + + default: + abort(); + } + return (cp); +} diff --git a/lib/printf/printflocal.h b/lib/printf/printflocal.h new file mode 100644 index 0000000000..335e09872e --- /dev/null +++ b/lib/printf/printflocal.h @@ -0,0 +1,105 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "compiler.h" +#include "printfrr.h" + +/* + * Flags used during conversion. + */ +#define ALT 0x001 /* alternate form */ +#define LADJUST 0x004 /* left adjustment */ +#define LONGDBL 0x008 /* long double */ +#define LONGINT 0x010 /* long integer */ +#define LLONGINT 0x020 /* long long integer */ +#define SHORTINT 0x040 /* short integer */ +#define ZEROPAD 0x080 /* zero (as opposed to blank) pad */ +#define FPT 0x100 /* Floating point number */ +#define GROUPING 0x200 /* use grouping ("'" flag) */ + /* C99 additional size modifiers: */ +#define SIZET 0x400 /* size_t */ +#define PTRDIFFT 0x800 /* ptrdiff_t */ +#define INTMAXT 0x1000 /* intmax_t */ +#define CHARINT 0x2000 /* print char using int format */ + +/* + * Macros for converting digits to letters and vice versa + */ +#define to_digit(c) ((c) - '0') +#define is_digit(c) ((unsigned)to_digit(c) <= 9) +#define to_char(n) ((n) + '0') + +/* Size of the static argument table. */ +#define STATIC_ARG_TBL_SIZE 8 + +union arg { + int intarg; + u_int uintarg; + long longarg; + u_long ulongarg; + long long longlongarg; + unsigned long long ulonglongarg; + ptrdiff_t ptrdiffarg; + size_t sizearg; + intmax_t intmaxarg; + uintmax_t uintmaxarg; + void *pvoidarg; + char *pchararg; + signed char *pschararg; + short *pshortarg; + int *pintarg; + long *plongarg; + long long *plonglongarg; + ptrdiff_t *pptrdiffarg; + ssize_t *pssizearg; + intmax_t *pintmaxarg; +#ifndef NO_FLOATING_POINT + double doublearg; + long double longdoublearg; +#endif + wint_t wintarg; + wchar_t *pwchararg; +}; + +/* Handle positional parameters. */ +int _frr_find_arguments(const char *, va_list, union arg **) DSO_LOCAL; +#ifdef WCHAR_SUPPORT +int _frr_find_warguments(const wchar_t *, va_list, union arg **) DSO_LOCAL; +#endif + +/* returns number of bytes consumed for extended specifier */ +ssize_t printfrr_extp(char *, size_t, const char *, int, const void *) DSO_LOCAL; +ssize_t printfrr_exti(char *, size_t, const char *, int, uintmax_t) DSO_LOCAL; diff --git a/lib/printf/vfprintf.c b/lib/printf/vfprintf.c new file mode 100644 index 0000000000..07df6dd8fe --- /dev/null +++ b/lib/printf/vfprintf.c @@ -0,0 +1,730 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Copyright (c) 2011 The FreeBSD Foundation + * All rights reserved. + * Portions of this software were developed by David Chisnall + * under sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_SYS_CDEFS_H +#include <sys/cdefs.h> +#endif + +/* + * Actual printf innards. + * + * This code is large and complicated... + */ + +#include <sys/types.h> +#include <sys/uio.h> + +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> + +#include <stdarg.h> + +#include "printflocal.h" + +#define CHAR char +#include "printfcommon.h" + +#ifdef WCHAR_SUPPORT +/* + * Convert a wide character string argument for the %ls format to a multibyte + * string representation. If not -1, prec specifies the maximum number of + * bytes to output, and also means that we can't assume that the wide char. + * string ends is null-terminated. + */ +static char * +__wcsconv(wchar_t *wcsarg, int prec) +{ + static const mbstate_t initial; + mbstate_t mbs; + char buf[MB_LEN_MAX]; + wchar_t *p; + char *convbuf; + size_t clen, nbytes; + + /* Allocate space for the maximum number of bytes we could output. */ + if (prec < 0) { + p = wcsarg; + mbs = initial; + nbytes = wcsrtombs(NULL, (const wchar_t **)&p, 0, &mbs); + if (nbytes == (size_t)-1) + return (NULL); + } else { + /* + * Optimisation: if the output precision is small enough, + * just allocate enough memory for the maximum instead of + * scanning the string. + */ + if (prec < 128) + nbytes = prec; + else { + nbytes = 0; + p = wcsarg; + mbs = initial; + for (;;) { + clen = wcrtomb(buf, *p++, &mbs); + if (clen == 0 || clen == (size_t)-1 || + nbytes + clen > (size_t)prec) + break; + nbytes += clen; + } + } + } + if ((convbuf = malloc(nbytes + 1)) == NULL) + return (NULL); + + /* Fill the output buffer. */ + p = wcsarg; + mbs = initial; + if ((nbytes = wcsrtombs(convbuf, (const wchar_t **)&p, + nbytes, &mbs)) == (size_t)-1) { + free(convbuf); + return (NULL); + } + convbuf[nbytes] = '\0'; + return (convbuf); +} +#endif /* WCHAR_SUPPORT */ + +/* + * The size of the buffer we use as scratch space for integer + * conversions, among other things. We need enough space to + * write a uintmax_t in octal (plus one byte). + */ +#if UINTMAX_MAX <= UINT64_MAX +#define BUF 64 +#else +#error "BUF must be large enough to format a uintmax_t" +#endif + +/* + * Non-MT-safe version + */ +ssize_t +vbprintfrr(struct fbuf *cb, const char *fmt0, va_list ap) +{ + const char *fmt; /* format string */ + int ch; /* character from fmt */ + int n, n2; /* handy integer (short term usage) */ + const char *cp; /* handy char pointer (short term usage) */ + int flags; /* flags as above */ + int ret; /* return value accumulator */ + int width; /* width from format (%8d), or 0 */ + int prec; /* precision from format; <0 for N/A */ + int saved_errno; + char sign; /* sign prefix (' ', '+', '-', or \0) */ + + u_long ulval = 0; /* integer arguments %[diouxX] */ + uintmax_t ujval = 0; /* %j, %ll, %q, %t, %z integers */ + void *ptrval; /* %p */ + int base; /* base for [diouxX] conversion */ + int dprec; /* a copy of prec if [diouxX], 0 otherwise */ + int realsz; /* field size expanded by dprec, sign, etc */ + int size; /* size of converted field or string */ + int prsize; /* max size of printed field */ + const char *xdigs; /* digits for %[xX] conversion */ + struct io_state io; /* I/O buffering state */ + char buf[BUF]; /* buffer with space for digits of uintmax_t */ + char ox[2]; /* space for 0x; ox[1] is either x, X, or \0 */ + union arg *argtable; /* args, built due to positional arg */ + union arg statargtable [STATIC_ARG_TBL_SIZE]; + int nextarg; /* 1-based argument index */ + va_list orgap; /* original argument pointer */ + char *convbuf; /* wide to multibyte conversion result */ + + static const char xdigs_lower[16] = "0123456789abcdef"; + static const char xdigs_upper[16] = "0123456789ABCDEF"; + + /* BEWARE, these `goto error' on error. */ +#define PRINT(ptr, len) { \ + if (io_print(&io, (ptr), (len))) \ + goto error; \ +} +#define PAD(howmany, with) { \ + if (io_pad(&io, (howmany), (with))) \ + goto error; \ +} +#define PRINTANDPAD(p, ep, len, with) { \ + if (io_printandpad(&io, (p), (ep), (len), (with))) \ + goto error; \ +} +#define FLUSH() do { } while (0) + + /* + * Get the argument indexed by nextarg. If the argument table is + * built, use it to get the argument. If its not, get the next + * argument (and arguments must be gotten sequentially). + */ +#define GETARG(type) \ + ((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : \ + (nextarg++, va_arg(ap, type))) + + /* + * To extend shorts properly, we need both signed and unsigned + * argument extraction methods. + */ +#define SARG() \ + (flags&LONGINT ? GETARG(long) : \ + flags&SHORTINT ? (long)(short)GETARG(int) : \ + flags&CHARINT ? (long)(signed char)GETARG(int) : \ + (long)GETARG(int)) +#define UARG() \ + (flags&LONGINT ? GETARG(u_long) : \ + flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \ + flags&CHARINT ? (u_long)(u_char)GETARG(int) : \ + (u_long)GETARG(u_int)) +#define INTMAX_SIZE (INTMAXT|SIZET|PTRDIFFT|LLONGINT|LONGDBL) +#define SJARG() \ + (flags&LONGDBL ? GETARG(int64_t) : \ + flags&INTMAXT ? GETARG(intmax_t) : \ + flags&SIZET ? (intmax_t)GETARG(ssize_t) : \ + flags&PTRDIFFT ? (intmax_t)GETARG(ptrdiff_t) : \ + (intmax_t)GETARG(long long)) +#define UJARG() \ + (flags&LONGDBL ? GETARG(uint64_t) : \ + flags&INTMAXT ? GETARG(uintmax_t) : \ + flags&SIZET ? (uintmax_t)GETARG(size_t) : \ + flags&PTRDIFFT ? (uintmax_t)GETARG(ptrdiff_t) : \ + (uintmax_t)GETARG(unsigned long long)) + + /* + * Get * arguments, including the form *nn$. Preserve the nextarg + * that the argument can be gotten once the type is determined. + */ +#define GETASTER(val) \ + n2 = 0; \ + cp = fmt; \ + while (is_digit(*cp)) { \ + n2 = 10 * n2 + to_digit(*cp); \ + cp++; \ + } \ + if (*cp == '$') { \ + int hold = nextarg; \ + if (argtable == NULL) { \ + argtable = statargtable; \ + if (_frr_find_arguments (fmt0, orgap, &argtable)) { \ + ret = EOF; \ + goto error; \ + } \ + } \ + nextarg = n2; \ + val = GETARG (int); \ + nextarg = hold; \ + fmt = ++cp; \ + } else { \ + val = GETARG (int); \ + } + + xdigs = xdigs_lower; + saved_errno = errno; + convbuf = NULL; + fmt = (char *)fmt0; + argtable = NULL; + nextarg = 1; + va_copy(orgap, ap); + io_init(&io, cb); + ret = 0; + + /* + * Scan the format for conversions (`%' character). + */ + for (;;) { + for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) + /* void */; + if ((n = fmt - cp) != 0) { + if ((unsigned)ret + n > INT_MAX) { + ret = EOF; + errno = EOVERFLOW; + goto error; + } + PRINT(cp, n); + ret += n; + } + if (ch == '\0') + goto done; + fmt++; /* skip over '%' */ + + flags = 0; + dprec = 0; + width = 0; + prec = -1; + sign = '\0'; + ox[1] = '\0'; + +rflag: ch = *fmt++; +reswitch: switch (ch) { + case ' ': + /*- + * ``If the space and + flags both appear, the space + * flag will be ignored.'' + * -- ANSI X3J11 + */ + if (!sign) + sign = ' '; + goto rflag; + case '#': + flags |= ALT; + goto rflag; + case '*': + /*- + * ``A negative field width argument is taken as a + * - flag followed by a positive field width.'' + * -- ANSI X3J11 + * They don't exclude field widths read from args. + */ + GETASTER (width); + if (width >= 0) + goto rflag; + width = -width; + /* FALLTHROUGH */ + case '-': + flags |= LADJUST; + goto rflag; + case '+': + sign = '+'; + goto rflag; + case '\'': + flags |= GROUPING; + goto rflag; + case '.': + if ((ch = *fmt++) == '*') { + GETASTER (prec); + goto rflag; + } + prec = 0; + while (is_digit(ch)) { + prec = 10 * prec + to_digit(ch); + ch = *fmt++; + } + goto reswitch; + case '0': + /*- + * ``Note that 0 is taken as a flag, not as the + * beginning of a field width.'' + * -- ANSI X3J11 + */ + flags |= ZEROPAD; + goto rflag; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + n = 0; + do { + n = 10 * n + to_digit(ch); + ch = *fmt++; + } while (is_digit(ch)); + if (ch == '$') { + nextarg = n; + if (argtable == NULL) { + argtable = statargtable; + if (_frr_find_arguments (fmt0, orgap, + &argtable)) { + ret = EOF; + goto error; + } + } + goto rflag; + } + width = n; + goto reswitch; + case 'L': + flags |= LONGDBL; + goto rflag; + case 'h': + if (flags & SHORTINT) { + flags &= ~SHORTINT; + flags |= CHARINT; + } else + flags |= SHORTINT; + goto rflag; + case 'j': + flags |= INTMAXT; + goto rflag; + case 'l': + if (flags & LONGINT) { + flags &= ~LONGINT; + flags |= LLONGINT; + } else + flags |= LONGINT; + goto rflag; + case 'q': + flags |= LLONGINT; /* not necessarily */ + goto rflag; + case 't': + flags |= PTRDIFFT; + goto rflag; + case 'z': + flags |= SIZET; + goto rflag; + case 'C': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'c': +#ifdef WCHAR_SUPPORT + if (flags & LONGINT) { + static const mbstate_t initial; + mbstate_t mbs; + size_t mbseqlen; + + mbs = initial; + mbseqlen = wcrtomb(cp = buf, + (wchar_t)GETARG(wint_t), &mbs); + if (mbseqlen == (size_t)-1) { + goto error; + } + size = (int)mbseqlen; + } else +#endif /* WCHAR_SUPPORT */ + { + buf[0] = GETARG(int); + cp = buf; + size = 1; + } + sign = '\0'; + break; + case 'D': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'd': + case 'i': + if (flags & INTMAX_SIZE) + ujval = SJARG(); + else + ulval = SARG(); + + if (printfrr_ext_char(fmt[0])) { + n2 = printfrr_exti(buf, sizeof(buf), fmt, prec, + (flags & INTMAX_SIZE) ? ujval + : (uintmax_t)ulval); + if (n2 > 0) { + fmt += n2; + cp = buf; + size = strlen(cp); + sign = '\0'; + break; + } + } + if (flags & INTMAX_SIZE) { + if ((intmax_t)ujval < 0) { + ujval = -ujval; + sign = '-'; + } + } else { + if ((long)ulval < 0) { + ulval = -ulval; + sign = '-'; + } + } + base = 10; + goto number; +#ifndef NO_FLOATING_POINT + case 'a': + case 'A': + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + if (flags & LONGDBL) { + long double arg = GETARG(long double); + char fmt[6] = "%.*L"; + fmt[4] = ch; + fmt[5] = '\0'; + + snprintf(buf, sizeof(buf), fmt, prec, arg); + } else { + double arg = GETARG(double); + char fmt[5] = "%.*"; + fmt[3] = ch; + fmt[4] = '\0'; + + snprintf(buf, sizeof(buf), fmt, prec, arg); + } + cp = buf; + /* for proper padding */ + if (*cp == '-') { + cp++; + sign = '-'; + } + /* "inf" */ + if (!is_digit(*cp) && *cp != '.') + flags &= ~ZEROPAD; + size = strlen(buf); + break; +#endif + case 'm': + cp = strerror(saved_errno); + size = (prec >= 0) ? strnlen(cp, prec) : strlen(cp); + sign = '\0'; + break; + case 'n': + /* + * Assignment-like behavior is specified if the + * value overflows or is otherwise unrepresentable. + * C99 says to use `signed char' for %hhn conversions. + */ + if (flags & LLONGINT) + *GETARG(long long *) = ret; + else if (flags & SIZET) + *GETARG(ssize_t *) = (ssize_t)ret; + else if (flags & PTRDIFFT) + *GETARG(ptrdiff_t *) = ret; + else if (flags & INTMAXT) + *GETARG(intmax_t *) = ret; + else if (flags & LONGINT) + *GETARG(long *) = ret; + else if (flags & SHORTINT) + *GETARG(short *) = ret; + else if (flags & CHARINT) + *GETARG(signed char *) = ret; + else + *GETARG(int *) = ret; + continue; /* no output */ + case 'O': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'o': + if (flags & INTMAX_SIZE) + ujval = UJARG(); + else + ulval = UARG(); + base = 8; + goto nosign; + case 'p': + /*- + * ``The argument shall be a pointer to void. The + * value of the pointer is converted to a sequence + * of printable characters, in an implementation- + * defined manner.'' + * -- ANSI X3J11 + */ + ptrval = GETARG(void *); + if (printfrr_ext_char(fmt[0]) && + (n2 = printfrr_extp(buf, sizeof(buf), + fmt, prec, ptrval)) > 0) { + fmt += n2; + cp = buf; + size = strlen(cp); + sign = '\0'; + break; + } + ujval = (uintmax_t)(uintptr_t)ptrval; + base = 16; + xdigs = xdigs_lower; + flags = flags | INTMAXT; + ox[1] = 'x'; + goto nosign; + case 'S': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 's': +#ifdef WCHAR_SUPPORT + if (flags & LONGINT) { + wchar_t *wcp; + + if (convbuf != NULL) + free(convbuf); + if ((wcp = GETARG(wchar_t *)) == NULL) + cp = "(null)"; + else { + convbuf = __wcsconv(wcp, prec); + if (convbuf == NULL) { + goto error; + } + cp = convbuf; + } + } else +#endif + if ((cp = GETARG(char *)) == NULL) + cp = "(null)"; + size = (prec >= 0) ? strnlen(cp, prec) : strlen(cp); + sign = '\0'; + break; + case 'U': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'u': + if (flags & INTMAX_SIZE) + ujval = UJARG(); + else + ulval = UARG(); + base = 10; + goto nosign; + case 'X': + xdigs = xdigs_upper; + goto hex; + case 'x': + xdigs = xdigs_lower; +hex: + if (flags & INTMAX_SIZE) + ujval = UJARG(); + else + ulval = UARG(); + base = 16; + /* leading 0x/X only if non-zero */ + if (flags & ALT && + (flags & INTMAX_SIZE ? ujval != 0 : ulval != 0)) + ox[1] = ch; + + flags &= ~GROUPING; + /* unsigned conversions */ +nosign: sign = '\0'; + /*- + * ``... diouXx conversions ... if a precision is + * specified, the 0 flag will be ignored.'' + * -- ANSI X3J11 + */ +number: if ((dprec = prec) >= 0) + flags &= ~ZEROPAD; + + /*- + * ``The result of converting a zero value with an + * explicit precision of zero is no characters.'' + * -- ANSI X3J11 + * + * ``The C Standard is clear enough as is. The call + * printf("%#.0o", 0) should print 0.'' + * -- Defect Report #151 + */ + cp = buf + BUF; + if (flags & INTMAX_SIZE) { + if (ujval != 0 || prec != 0 || + (flags & ALT && base == 8)) + cp = __ujtoa(ujval, buf + BUF, base, + flags & ALT, xdigs); + } else { + if (ulval != 0 || prec != 0 || + (flags & ALT && base == 8)) + cp = __ultoa(ulval, buf + BUF, base, + flags & ALT, xdigs); + } + size = buf + BUF - cp; + if (size > BUF) /* should never happen */ + abort(); + break; + default: /* "%?" prints ?, unless ? is NUL */ + if (ch == '\0') + goto done; + /* pretend it was %c with argument ch */ + buf[0] = ch; + cp = buf; + size = 1; + sign = '\0'; + break; + } + + /* + * All reasonable formats wind up here. At this point, `cp' + * points to a string which (if not flags&LADJUST) should be + * padded out to `width' places. If flags&ZEROPAD, it should + * first be prefixed by any sign or other prefix; otherwise, + * it should be blank padded before the prefix is emitted. + * After any left-hand padding and prefixing, emit zeroes + * required by a decimal [diouxX] precision, then print the + * string proper, then emit zeroes required by any leftover + * floating precision; finally, if LADJUST, pad with blanks. + * + * Compute actual size, so we know how much to pad. + * size excludes decimal prec; realsz includes it. + */ + realsz = dprec > size ? dprec : size; + if (sign) + realsz++; + if (ox[1]) + realsz += 2; + + prsize = width > realsz ? width : realsz; + if ((unsigned)ret + prsize > INT_MAX) { + ret = EOF; + errno = EOVERFLOW; + goto error; + } + + /* right-adjusting blank padding */ + if ((flags & (LADJUST|ZEROPAD)) == 0) + PAD(width - realsz, blanks); + + /* prefix */ + if (sign) + PRINT(&sign, 1); + + if (ox[1]) { /* ox[1] is either x, X, or \0 */ + ox[0] = '0'; + PRINT(ox, 2); + } + + /* right-adjusting zero padding */ + if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) + PAD(width - realsz, zeroes); + + /* the string or number proper */ + /* leading zeroes from decimal precision */ + PAD(dprec - size, zeroes); + PRINT(cp, size); + /* left-adjusting padding (always blank) */ + if (flags & LADJUST) + PAD(width - realsz, blanks); + + /* finally, adjust ret */ + ret += prsize; + + FLUSH(); /* copy out the I/O vectors */ + } +done: + FLUSH(); +error: + va_end(orgap); + if (convbuf != NULL) + free(convbuf); + if ((argtable != NULL) && (argtable != statargtable)) + free (argtable); + return (ret); + /* NOTREACHED */ +} + diff --git a/lib/printfrr.h b/lib/printfrr.h new file mode 100644 index 0000000000..95dace7021 --- /dev/null +++ b/lib/printfrr.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2019 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_PRINTFRR_H +#define _FRR_PRINTFRR_H + +#include <stddef.h> +#include <stdarg.h> +#include <stdint.h> + +#include "compiler.h" +#include "memory.h" + +struct fbuf { + char *buf; + char *pos; + size_t len; +}; + +#define at(a, b) \ + __attribute__((format(printf, a, b))) +#define atn(a, b) \ + at(a, b) __attribute__((nonnull(1) _RET_NONNULL)) +#define atm(a, b) \ + atn(a, b) __attribute__((malloc)) + +/* return value is length needed for the full string (excluding \0) in all + * cases. The functions write as much as they can, but continue regardless, + * so the return value is independent of buffer length. Both bprintfrr and + * snprintf also accept NULL as output buffer. + */ + +/* bprintfrr does NOT null terminate! use sparingly (only provided since it's + * the most direct interface) - useful for incrementally building long text + * (call bprintfrr repeatedly with the same buffer) + */ +ssize_t vbprintfrr(struct fbuf *out, const char *fmt, va_list) at(2, 0); +ssize_t bprintfrr(struct fbuf *out, const char *fmt, ...) at(2, 3); + +/* these do null terminate like their snprintf cousins */ +ssize_t vsnprintfrr(char *out, size_t sz, const char *fmt, va_list) at(3, 0); +ssize_t snprintfrr(char *out, size_t sz, const char *fmt, ...) at(3, 4); + +/* c = continue / concatenate (append at the end of the string) + * return value is would-be string length (regardless of buffer length), + * i.e. includes already written chars */ +ssize_t vcsnprintfrr(char *out, size_t sz, const char *fmt, va_list) at(3, 0); +ssize_t csnprintfrr(char *out, size_t sz, const char *fmt, ...) at(3, 4); + +/* memory allocations don't fail in FRR, so you always get something here. + * (in case of error, returns a strdup of the format string) */ +char *vasprintfrr(struct memtype *mt, const char *fmt, va_list) atm(2, 0); +char *asprintfrr(struct memtype *mt, const char *fmt, ...) atm(2, 3); + +/* try to use provided buffer (presumably from stack), allocate if it's too + * short. Must call XFREE(mt, return value) if return value != out. + */ +char *vasnprintfrr(struct memtype *mt, char *out, size_t sz, + const char *fmt, va_list) atn(4, 0); +char *asnprintfrr(struct memtype *mt, char *out, size_t sz, + const char *fmt, ...) atn(4, 5); + +#undef at +#undef atm + +/* extension specs must start with a capital letter (this is a restriction + * for both performance's and human understanding's sake.) + * + * Note that the entire thing mostly works because a letter directly following + * a %p print specifier is extremely unlikely to occur (why would you want to + * print "0x12345678HELLO"?) Normally, you'd expect spacing or punctuation + * after a placeholder. That also means that neither of those works well for + * extension purposes, e.g. "%p{foo}" is reasonable to see actually used. + * + * TODO: would be nice to support a "%pF%dF" specifier that consumes 2 + * arguments, e.g. to pass an integer + a list of known values... can be + * done, but a bit tricky. + */ +#define printfrr_ext_char(ch) ((ch) >= 'A' && (ch) <= 'Z') + +struct printfrr_ext { + /* embedded string to minimize cache line pollution */ + char match[8]; + + /* both can be given, if not the code continues searching + * (you can do %pX and %dX in 2 different entries) + * + * return value: number of bytes consumed from the format string, so + * you can consume extra flags (e.g. register for "%pX", consume + * "%pXfoo" or "%pXbar" for flags.) Convention is to make those flags + * lowercase letters or numbers. + * + * bsz is a compile-time constant in printf; it's gonna be relatively + * small. This isn't designed to print Shakespeare from a pointer. + * + * prec is the precision specifier (the 999 in "%.999p") -1 means + * none given (value in the format string cannot be negative) + */ + ssize_t (*print_ptr)(char *buf, size_t bsz, const char *fmt, int prec, + const void *); + ssize_t (*print_int)(char *buf, size_t bsz, const char *fmt, int prec, + uintmax_t); +}; + +/* no locking - must be called when single threaded (e.g. at startup.) + * this restriction hopefully won't be a huge bother considering normal usage + * scenarios... + */ +void printfrr_ext_reg(const struct printfrr_ext *); + +#define printfrr_ext_autoreg_p(matchs, print_fn) \ + static ssize_t print_fn(char *, size_t, const char *, int, \ + const void *); \ + static struct printfrr_ext _printext_##print_fn = { \ + .match = matchs, \ + .print_ptr = print_fn, \ + }; \ + static void _printreg_##print_fn(void) __attribute__((constructor)); \ + static void _printreg_##print_fn(void) { \ + printfrr_ext_reg(&_printext_##print_fn); \ + } \ + /* end */ + +#define printfrr_ext_autoreg_i(matchs, print_fn) \ + static ssize_t print_fn(char *, size_t, const char *, int, uintmax_t); \ + static struct printfrr_ext _printext_##print_fn = { \ + .match = matchs, \ + .print_int = print_fn, \ + }; \ + static void _printreg_##print_fn(void) __attribute__((constructor)); \ + static void _printreg_##print_fn(void) { \ + printfrr_ext_reg(&_printext_##print_fn); \ + } \ + /* end */ + +#endif diff --git a/lib/routemap.c b/lib/routemap.c index 3542994e65..9336154b1a 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -39,6 +39,7 @@ DEFINE_MTYPE(LIB, ROUTE_MAP_RULE, "Route map rule") DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP_RULE_STR, "Route map rule str") DEFINE_MTYPE(LIB, ROUTE_MAP_COMPILED, "Route map compiled") DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP_DEP, "Route map dependency") +DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP_DEP_DATA, "Route map dependency data") DEFINE_QOBJ_TYPE(route_map_index) DEFINE_QOBJ_TYPE(route_map) @@ -475,7 +476,7 @@ int generic_match_add(struct vty *vty, struct route_map_index *index, { int ret; - ret = route_map_add_match(index, command, arg); + ret = route_map_add_match(index, command, arg, type); switch (ret) { case RMAP_COMPILE_SUCCESS: if (type != RMAP_EVENT_MATCH_ADDED) { @@ -670,6 +671,16 @@ struct route_map_dep { struct hash *this_hash; /* ptr to the hash structure this is part of */ }; +struct route_map_dep_data { + /* Route-map name. + */ + char *rname; + /* Count of number of sequences of this + * route-map that depend on the same entity. + */ + uint16_t refcnt; +}; + /* Hashes maintaining dependency between various sublists used by route maps */ struct hash *route_map_dep_hash[ROUTE_MAP_DEP_MAX]; @@ -1281,14 +1292,65 @@ const char *route_map_get_match_arg(struct route_map_index *index, return (NULL); } +static route_map_event_t get_route_map_delete_event(route_map_event_t type) +{ + switch (type) { + case RMAP_EVENT_CALL_ADDED: + return RMAP_EVENT_CALL_DELETED; + case RMAP_EVENT_PLIST_ADDED: + return RMAP_EVENT_PLIST_DELETED; + case RMAP_EVENT_CLIST_ADDED: + return RMAP_EVENT_CLIST_DELETED; + case RMAP_EVENT_ECLIST_ADDED: + return RMAP_EVENT_ECLIST_DELETED; + case RMAP_EVENT_LLIST_ADDED: + return RMAP_EVENT_LLIST_DELETED; + case RMAP_EVENT_ASLIST_ADDED: + return RMAP_EVENT_ASLIST_DELETED; + case RMAP_EVENT_FILTER_ADDED: + return RMAP_EVENT_FILTER_DELETED; + case RMAP_EVENT_SET_ADDED: + case RMAP_EVENT_SET_DELETED: + case RMAP_EVENT_SET_REPLACED: + case RMAP_EVENT_MATCH_ADDED: + case RMAP_EVENT_MATCH_DELETED: + case RMAP_EVENT_MATCH_REPLACED: + case RMAP_EVENT_INDEX_ADDED: + case RMAP_EVENT_INDEX_DELETED: + case RMAP_EVENT_CALL_DELETED: + case RMAP_EVENT_PLIST_DELETED: + case RMAP_EVENT_CLIST_DELETED: + case RMAP_EVENT_ECLIST_DELETED: + case RMAP_EVENT_LLIST_DELETED: + case RMAP_EVENT_ASLIST_DELETED: + case RMAP_EVENT_FILTER_DELETED: + /* This function returns the appropriate 'deleted' event type + * for every 'added' event type passed to this function. + * This is done only for named entities used in the + * route-map match commands. + * This function is not to be invoked for any of the other event + * types. + */ + assert(0); + } + + assert(0); + /* + * Return to make c happy but if we get here something has gone + * terribly terribly wrong, so yes this return makes no sense. + */ + return RMAP_EVENT_CALL_ADDED; +} + /* Add match statement to route map. */ int route_map_add_match(struct route_map_index *index, const char *match_name, - const char *match_arg) + const char *match_arg, route_map_event_t type) { struct route_map_rule *rule; struct route_map_rule *next; struct route_map_rule_cmd *cmd; void *compile; + int8_t delete_rmap_event_type = 0; /* First lookup rule for add match statement. */ cmd = route_map_lookup_match(match_name); @@ -1314,7 +1376,20 @@ int route_map_add_match(struct route_map_index *index, const char *match_name, if (strcmp(match_arg, rule->rule_str) == 0) { if (cmd->func_free) (*cmd->func_free)(compile); - return RMAP_COMPILE_SUCCESS; + + return RMAP_DUPLICATE_RULE; + } + + /* Remove the dependency of the route-map on the rule + * that is being replaced. + */ + if (type >= RMAP_EVENT_CALL_ADDED) { + delete_rmap_event_type = + get_route_map_delete_event(type); + route_map_upd8_dependency( + delete_rmap_event_type, + rule->rule_str, + index->map->name); } route_map_rule_delete(&index->match_list, rule); @@ -1644,7 +1719,9 @@ void route_map_event_hook(void (*func)(const char *name)) /* Routines for route map dependency lists and dependency processing */ static bool route_map_rmap_hash_cmp(const void *p1, const void *p2) { - return (strcmp((const char *)p1, (const char *)p2) == 0); + return strcmp(((const struct route_map_dep_data *)p1)->rname, + ((const struct route_map_dep_data *)p2)->rname) + == 0; } static bool route_map_dep_hash_cmp(const void *p1, const void *p2) @@ -1657,14 +1734,17 @@ static bool route_map_dep_hash_cmp(const void *p1, const void *p2) static void route_map_clear_reference(struct hash_bucket *bucket, void *arg) { - struct route_map_dep *dep = (struct route_map_dep *)bucket->data; - char *rmap_name; + struct route_map_dep *dep = bucket->data; + struct route_map_dep_data *dep_data = NULL, tmp_dep_data; if (arg) { - rmap_name = - (char *)hash_release(dep->dep_rmap_hash, (void *)arg); - if (rmap_name) { - XFREE(MTYPE_ROUTE_MAP_NAME, rmap_name); + memset(&tmp_dep_data, 0, sizeof(struct route_map_dep_data)); + tmp_dep_data.rname = arg; + dep_data = hash_release(dep->dep_rmap_hash, + &tmp_dep_data); + if (dep_data) { + XFREE(MTYPE_ROUTE_MAP_NAME, dep_data->rname); + XFREE(MTYPE_ROUTE_MAP_DEP_DATA, dep_data); } if (!dep->dep_rmap_hash->count) { dep = hash_release(dep->this_hash, @@ -1686,6 +1766,13 @@ static void route_map_clear_all_references(char *rmap_name) } } +static unsigned int route_map_dep_data_hash_make_key(const void *p) +{ + const struct route_map_dep_data *dep_data = p; + + return string_hash_make(dep_data->rname); +} + static void *route_map_dep_hash_alloc(void *p) { char *dep_name = (char *)p; @@ -1694,16 +1781,22 @@ static void *route_map_dep_hash_alloc(void *p) dep_entry = XCALLOC(MTYPE_ROUTE_MAP_DEP, sizeof(struct route_map_dep)); dep_entry->dep_name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, dep_name); dep_entry->dep_rmap_hash = - hash_create_size(8, route_map_dep_hash_make_key, + hash_create_size(8, route_map_dep_data_hash_make_key, route_map_rmap_hash_cmp, "Route Map Dep Hash"); dep_entry->this_hash = NULL; - return ((void *)dep_entry); + return dep_entry; } static void *route_map_name_hash_alloc(void *p) { - return ((void *)XSTRDUP(MTYPE_ROUTE_MAP_NAME, (const char *)p)); + struct route_map_dep_data *dep_data = NULL, *tmp_dep_data = NULL; + + dep_data = XCALLOC(MTYPE_ROUTE_MAP_DEP_DATA, + sizeof(struct route_map_dep_data)); + tmp_dep_data = p; + dep_data->rname = XSTRDUP(MTYPE_ROUTE_MAP_NAME, tmp_dep_data->rname); + return dep_data; } static unsigned int route_map_dep_hash_make_key(const void *p) @@ -1713,8 +1806,9 @@ static unsigned int route_map_dep_hash_make_key(const void *p) static void route_map_print_dependency(struct hash_bucket *bucket, void *data) { - char *rmap_name = (char *)bucket->data; - char *dep_name = (char *)data; + struct route_map_dep_data *dep_data = bucket->data; + char *rmap_name = dep_data->rname; + char *dep_name = data; zlog_debug("%s: Dependency for %s: %s", __FUNCTION__, dep_name, rmap_name); @@ -1724,9 +1818,10 @@ static int route_map_dep_update(struct hash *dephash, const char *dep_name, const char *rmap_name, route_map_event_t type) { struct route_map_dep *dep = NULL; - char *ret_map_name; char *dname, *rname; int ret = 0; + struct route_map_dep_data *dep_data = NULL, *ret_dep_data = NULL; + struct route_map_dep_data tmp_dep_data; dname = XSTRDUP(MTYPE_ROUTE_MAP_NAME, dep_name); rname = XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap_name); @@ -1752,7 +1847,14 @@ static int route_map_dep_update(struct hash *dephash, const char *dep_name, if (!dep->this_hash) dep->this_hash = dephash; - hash_get(dep->dep_rmap_hash, rname, route_map_name_hash_alloc); + memset(&tmp_dep_data, 0, sizeof(struct route_map_dep_data)); + tmp_dep_data.rname = rname; + dep_data = hash_lookup(dep->dep_rmap_hash, &tmp_dep_data); + if (!dep_data) + dep_data = hash_get(dep->dep_rmap_hash, &tmp_dep_data, + route_map_name_hash_alloc); + + dep_data->refcnt++; break; case RMAP_EVENT_PLIST_DELETED: case RMAP_EVENT_CLIST_DELETED: @@ -1769,8 +1871,20 @@ static int route_map_dep_update(struct hash *dephash, const char *dep_name, goto out; } - ret_map_name = (char *)hash_release(dep->dep_rmap_hash, rname); - XFREE(MTYPE_ROUTE_MAP_NAME, ret_map_name); + memset(&tmp_dep_data, 0, sizeof(struct route_map_dep_data)); + tmp_dep_data.rname = rname; + dep_data = hash_lookup(dep->dep_rmap_hash, &tmp_dep_data); + dep_data->refcnt--; + + if (!dep_data->refcnt) { + ret_dep_data = hash_release(dep->dep_rmap_hash, + &tmp_dep_data); + if (ret_dep_data) { + XFREE(MTYPE_ROUTE_MAP_NAME, + ret_dep_data->rname); + XFREE(MTYPE_ROUTE_MAP_DEP_DATA, ret_dep_data); + } + } if (!dep->dep_rmap_hash->count) { dep = hash_release(dephash, dname); @@ -1858,7 +1972,11 @@ static struct hash *route_map_get_dep_hash(route_map_event_t event) static void route_map_process_dependency(struct hash_bucket *bucket, void *data) { - char *rmap_name = (char *)bucket->data; + struct route_map_dep_data *dep_data = NULL; + char *rmap_name = NULL; + + dep_data = bucket->data; + rmap_name = dep_data->rname; if (rmap_debug) zlog_debug("%s: Notifying %s of dependency", diff --git a/lib/routemap.h b/lib/routemap.h index 9969936a6b..3781d227df 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -104,13 +104,18 @@ struct route_map_rule_cmd { }; /* Route map apply error. */ -enum { RMAP_COMPILE_SUCCESS, +enum { + RMAP_COMPILE_SUCCESS, - /* Route map rule is missing. */ - RMAP_RULE_MISSING, + /* Route map rule is missing. */ + RMAP_RULE_MISSING, - /* Route map rule can't compile */ - RMAP_COMPILE_ERROR }; + /* Route map rule can't compile */ + RMAP_COMPILE_ERROR, + + /* Route map rule is duplicate */ + RMAP_DUPLICATE_RULE +}; /* Route map rule list. */ struct route_map_rule_list { @@ -192,7 +197,8 @@ extern void route_map_finish(void); /* Add match statement to route map. */ extern int route_map_add_match(struct route_map_index *index, - const char *match_name, const char *match_arg); + const char *match_name, const char *match_arg, + route_map_event_t type); /* Delete specified route match rule. */ extern int route_map_delete_match(struct route_map_index *index, diff --git a/lib/sigevent.c b/lib/sigevent.c index f00ff4921e..d02b074223 100644 --- a/lib/sigevent.c +++ b/lib/sigevent.c @@ -209,12 +209,14 @@ exit_handler(int signo #endif ) { - zlog_signal(signo, "exiting..." -#ifdef SA_SIGINFO - , - siginfo, program_counter(context) +#ifndef SA_SIGINFO + void *siginfo = NULL; + void *pc = NULL; +#else + void *pc = program_counter(context); #endif - ); + + zlog_signal(signo, "exiting...", siginfo, pc); _exit(128 + signo); } @@ -226,6 +228,13 @@ core_handler(int signo #endif ) { +#ifndef SA_SIGINFO + void *siginfo = NULL; + void *pc = NULL; +#else + void *pc = program_counter(context); +#endif + /* make sure we don't hang in here. default for SIGALRM is terminate. * - if we're in backtrace for more than a second, abort. */ struct sigaction sa_default = {.sa_handler = SIG_DFL}; @@ -238,12 +247,8 @@ core_handler(int signo alarm(1); - zlog_signal(signo, "aborting..." -#ifdef SA_SIGINFO - , - siginfo, program_counter(context) -#endif - ); + zlog_signal(signo, "aborting...", siginfo, pc); + /* dump memory stats on core */ log_memstats(stderr, "core_handler"); abort(); diff --git a/lib/srcdest_table.c b/lib/srcdest_table.c index 80004b41ac..ee87d73077 100644 --- a/lib/srcdest_table.c +++ b/lib/srcdest_table.c @@ -28,6 +28,7 @@ #include "memory.h" #include "prefix.h" #include "table.h" +#include "printfrr.h" DEFINE_MTYPE_STATIC(LIB, ROUTE_SRC_NODE, "Route source node") @@ -264,7 +265,8 @@ struct route_node *srcdest_rnode_lookup(struct route_table *table, return srn; } -void srcdest_rnode_prefixes(struct route_node *rn, const struct prefix **p, +void srcdest_rnode_prefixes(const struct route_node *rn, + const struct prefix **p, const struct prefix **src_p) { if (rnode_is_srcnode(rn)) { @@ -296,10 +298,22 @@ const char *srcdest2str(const struct prefix *dst_p, return str; } -const char *srcdest_rnode2str(struct route_node *rn, char *str, int size) +const char *srcdest_rnode2str(const struct route_node *rn, char *str, int size) { const struct prefix *dst_p, *src_p; srcdest_rnode_prefixes(rn, &dst_p, &src_p); return srcdest2str(dst_p, (const struct prefix_ipv6 *)src_p, str, size); } + +printfrr_ext_autoreg_p("RN", printfrr_rn) +static ssize_t printfrr_rn(char *buf, size_t bsz, const char *fmt, + int prec, const void *ptr) +{ + const struct route_node *rn = ptr; + const struct prefix *dst_p, *src_p; + + srcdest_rnode_prefixes(rn, &dst_p, &src_p); + srcdest2str(dst_p, (const struct prefix_ipv6 *)src_p, buf, bsz); + return 2; +} diff --git a/lib/srcdest_table.h b/lib/srcdest_table.h index 8845931de7..90418944c7 100644 --- a/lib/srcdest_table.h +++ b/lib/srcdest_table.h @@ -65,22 +65,22 @@ extern struct route_node *srcdest_rnode_get(struct route_table *table, extern struct route_node *srcdest_rnode_lookup(struct route_table *table, union prefixconstptr dst_pu, const struct prefix_ipv6 *src_p); -extern void srcdest_rnode_prefixes(struct route_node *rn, +extern void srcdest_rnode_prefixes(const struct route_node *rn, const struct prefix **p, const struct prefix **src_p); extern const char *srcdest2str(const struct prefix *dst_p, const struct prefix_ipv6 *src_p, char *str, int size); -extern const char *srcdest_rnode2str(struct route_node *rn, char *str, +extern const char *srcdest_rnode2str(const struct route_node *rn, char *str, int size); extern struct route_node *srcdest_route_next(struct route_node *rn); -static inline int rnode_is_dstnode(struct route_node *rn) +static inline int rnode_is_dstnode(const struct route_node *rn) { return rn->table->delegate == &_srcdest_dstnode_delegate; } -static inline int rnode_is_srcnode(struct route_node *rn) +static inline int rnode_is_srcnode(const struct route_node *rn) { return rn->table->delegate == &_srcdest_srcnode_delegate; } diff --git a/lib/subdir.am b/lib/subdir.am index 4897f5e8e5..50ff1feecc 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -94,6 +94,9 @@ lib_libfrr_la_SOURCES = \ lib/yang_wrappers.c \ lib/zclient.c \ lib/logicalrouter.c \ + lib/printf/printf-pos.c \ + lib/printf/vfprintf.c \ + lib/printf/glue.c \ # end nodist_lib_libfrr_la_SOURCES = \ @@ -131,6 +134,8 @@ lib/nexthop_group_clippy.c: $(CLIPPY_DEPS) lib/nexthop_group.lo: lib/nexthop_group_clippy.c lib/northbound_cli_clippy.c: $(CLIPPY_DEPS) lib/northbound_cli.lo: lib/northbound_cli_clippy.c +lib/vty_clippy.c: $(CLIPPY_DEPS) +lib/vty.lo: lib/vty_clippy.c pkginclude_HEADERS += \ lib/agg_table.h \ @@ -190,6 +195,7 @@ pkginclude_HEADERS += \ lib/plist.h \ lib/pqueue.h \ lib/prefix.h \ + lib/printfrr.h \ lib/privs.h \ lib/ptm_lib.h \ lib/pw.h \ @@ -242,6 +248,8 @@ noinst_HEADERS += \ lib/clippy.h \ lib/log_int.h \ lib/plist_int.h \ + lib/printf/printfcommon.h \ + lib/printf/printflocal.h \ #end # General note about module and module helper library (libfrrsnmp, libfrrzmq) @@ -42,10 +42,15 @@ #include "frrstr.h" #include "lib_errors.h" #include "northbound_cli.h" +#include "printfrr.h" #include <arpa/telnet.h> #include <termios.h> +#ifndef VTYSH_EXTRACT_PL +#include "lib/vty_clippy.c" +#endif + DEFINE_MTYPE_STATIC(LIB, VTY, "VTY") DEFINE_MTYPE_STATIC(LIB, VTY_OUT_BUF, "VTY output buffer") DEFINE_MTYPE_STATIC(LIB, VTY_HIST, "VTY history") @@ -92,7 +97,8 @@ static int no_password_check = 0; /* Integrated configuration file path */ static char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG; -static int do_log_commands = 0; +static bool do_log_commands; +static bool do_log_commands_perm; void vty_frame(struct vty *vty, const char *format, ...) { @@ -143,10 +149,9 @@ bool vty_set_include(struct vty *vty, const char *regexp) int vty_out(struct vty *vty, const char *format, ...) { va_list args; - int len = 0; - int size = 1024; + ssize_t len; char buf[1024]; - char *p = NULL; + char *p = buf; char *filtered; if (vty->frame_pos) { @@ -154,35 +159,11 @@ int vty_out(struct vty *vty, const char *format, ...) vty_out(vty, "%s", vty->frame); } - /* Try to write to initial buffer. */ va_start(args, format); - len = vsnprintf(buf, sizeof(buf), format, args); + p = vasnprintfrr(MTYPE_VTY_OUT_BUF, buf, sizeof(buf), format, args); va_end(args); - /* Initial buffer is not enough. */ - if (len < 0 || len >= size) { - while (1) { - if (len > -1) - size = len + 1; - else - size = size * 2; - - p = XREALLOC(MTYPE_VTY_OUT_BUF, p, size); - if (!p) - return -1; - - va_start(args, format); - len = vsnprintf(p, size, format, args); - va_end(args); - - if (len > -1 && len < size) - break; - } - } - - /* When initial buffer is enough to store all output. */ - if (!p) - p = buf; + len = strlen(p); /* filter buffer */ if (vty->filter) { @@ -269,8 +250,8 @@ done: } static int vty_log_out(struct vty *vty, const char *level, - const char *proto_str, const char *format, - struct timestamp_control *ctl, va_list va) + const char *proto_str, const char *msg, + struct timestamp_control *ctl) { int ret; int len; @@ -295,7 +276,7 @@ static int vty_log_out(struct vty *vty, const char *level, if ((ret < 0) || ((size_t)(len += ret) >= sizeof(buf))) return -1; - if (((ret = vsnprintf(buf + len, sizeof(buf) - len, format, va)) < 0) + if (((ret = snprintf(buf + len, sizeof(buf) - len, "%s", msg)) < 0) || ((size_t)((len += ret) + 2) > sizeof(buf))) return -1; @@ -2547,8 +2528,8 @@ tmp_free_and_out: } /* Small utility function which output log to the VTY. */ -void vty_log(const char *level, const char *proto_str, const char *format, - struct timestamp_control *ctl, va_list va) +void vty_log(const char *level, const char *proto_str, const char *msg, + struct timestamp_control *ctl) { unsigned int i; struct vty *vty; @@ -2558,13 +2539,8 @@ void vty_log(const char *level, const char *proto_str, const char *format, for (i = 0; i < vector_active(vtyvec); i++) if ((vty = vector_slot(vtyvec, i)) != NULL) - if (vty->monitor) { - va_list ac; - va_copy(ac, va); - vty_log_out(vty, level, proto_str, format, ctl, - ac); - va_end(ac); - } + if (vty->monitor) + vty_log_out(vty, level, proto_str, msg, ctl); } /* Async-signal-safe version of vty_log for fixed strings. */ @@ -2975,13 +2951,24 @@ DEFUN_NOSH (show_history, } /* vty login. */ -DEFUN (log_commands, +DEFPY (log_commands, log_commands_cmd, - "log commands", + "[no] log commands", + NO_STR "Logging control\n" - "Log all commands (can't be unset without restart)\n") + "Log all commands\n") { - do_log_commands = 1; + if (no) { + if (do_log_commands_perm) { + vty_out(vty, + "Daemon started with permanent logging turned on for commands, ignoring\n"); + return CMD_WARNING; + } + + do_log_commands = false; + } else + do_log_commands = true; + return CMD_SUCCESS; } @@ -3101,7 +3088,7 @@ void vty_init_vtysh(void) } /* Install vty's own commands like `who' command. */ -void vty_init(struct thread_master *master_thread) +void vty_init(struct thread_master *master_thread, bool do_command_logging) { /* For further configuration read, preserve current directory. */ vty_save_cwd(); @@ -3125,6 +3112,12 @@ void vty_init(struct thread_master *master_thread) install_element(CONFIG_NODE, &no_service_advanced_vty_cmd); install_element(CONFIG_NODE, &show_history_cmd); install_element(CONFIG_NODE, &log_commands_cmd); + + if (do_command_logging) { + do_log_commands = true; + do_log_commands_perm = true; + } + install_element(ENABLE_NODE, &terminal_monitor_cmd); install_element(ENABLE_NODE, &terminal_no_monitor_cmd); install_element(ENABLE_NODE, &no_terminal_monitor_cmd); @@ -290,7 +290,7 @@ struct vty_arg { #endif /* Prototypes. */ -extern void vty_init(struct thread_master *); +extern void vty_init(struct thread_master *, bool do_command_logging); extern void vty_init_vtysh(void); extern void vty_terminate(void); extern void vty_reset(void); @@ -313,8 +313,8 @@ extern void vty_time_print(struct vty *, int); extern void vty_serv_sock(const char *, unsigned short, const char *); extern void vty_close(struct vty *); extern char *vty_get_cwd(void); -extern void vty_log(const char *level, const char *proto, const char *fmt, - struct timestamp_control *, va_list); +extern void vty_log(const char *level, const char *proto, const char *msg, + struct timestamp_control *); extern int vty_config_enter(struct vty *vty, bool private_config, bool exclusive); extern void vty_config_exit(struct vty *); diff --git a/lib/zclient.c b/lib/zclient.c index 0972590ca6..e9b4f5a58b 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -62,10 +62,13 @@ struct zclient *zclient_new(struct thread_master *master, struct zclient_options *opt) { struct zclient *zclient; + size_t stream_size = + MAX(ZEBRA_MAX_PACKET_SIZ, sizeof(struct zapi_route)); + zclient = XCALLOC(MTYPE_ZCLIENT, sizeof(struct zclient)); - zclient->ibuf = stream_new(ZEBRA_MAX_PACKET_SIZ); - zclient->obuf = stream_new(ZEBRA_MAX_PACKET_SIZ); + zclient->ibuf = stream_new(stream_size); + zclient->obuf = stream_new(stream_size); zclient->wb = buffer_new(0); zclient->master = master; diff --git a/lib/zclient.h b/lib/zclient.h index 09f0acad84..c61c8d4226 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -39,7 +39,7 @@ #include "mlag.h" /* For input/output buffer to zebra. */ -#define ZEBRA_MAX_PACKET_SIZ 16384 +#define ZEBRA_MAX_PACKET_SIZ 16384U /* Zebra header size. */ #define ZEBRA_HEADER_SIZE 10 diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index e6e9c2d0c8..7788191454 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -658,6 +658,7 @@ static void igmp_show_interfaces_single(struct pim_instance *pim, long oqpi_msec; /* Other Querier Present Interval */ long qri_msec; time_t now; + int lmqc; json_object *json = NULL; json_object *json_row = NULL; @@ -702,8 +703,8 @@ static void igmp_show_interfaces_single(struct pim_instance *pim, pim_ifp->igmp_query_max_response_time_dsec); lmqt_msec = PIM_IGMP_LMQT_MSEC( - pim_ifp->igmp_query_max_response_time_dsec, - igmp->querier_robustness_variable); + pim_ifp->igmp_specific_query_max_response_time_dsec, + pim_ifp->igmp_last_member_query_count); ohpi_msec = PIM_IGMP_OHPI_DSEC( @@ -719,6 +720,7 @@ static void igmp_show_interfaces_single(struct pim_instance *pim, pim_ifp->pim_sock_fd); else mloop = 0; + lmqc = pim_ifp->igmp_last_member_query_count; if (uj) { json_row = json_object_new_object(); @@ -744,6 +746,9 @@ static void igmp_show_interfaces_single(struct pim_instance *pim, "timerGroupMembershipIntervalMsec", gmi_msec); json_object_int_add(json_row, + "lastMemberQueryCount", + lmqc); + json_object_int_add(json_row, "timerLastMemberQueryMsec", lmqt_msec); json_object_int_add( @@ -810,6 +815,9 @@ static void igmp_show_interfaces_single(struct pim_instance *pim, "Group Membership Interval : %lis\n", gmi_msec / 1000); vty_out(vty, + "Last Member Query Count : %d\n", + lmqc); + vty_out(vty, "Last Member Query Time : %lis\n", lmqt_msec / 1000); vty_out(vty, @@ -3074,7 +3082,7 @@ static void pim_show_group_rp_mappings_info(struct pim_instance *pim, json_object *json_row = NULL; if (pim->global_scope.current_bsr.s_addr == INADDR_ANY) - strncpy(bsr_str, "0.0.0.0", sizeof(bsr_str)); + strlcpy(bsr_str, "0.0.0.0", sizeof(bsr_str)); else pim_inet4_dump("<bsr?>", pim->global_scope.current_bsr, bsr_str, @@ -3642,7 +3650,7 @@ static void pim_show_bsr(struct pim_instance *pim, vty_out(vty, "PIMv2 Bootstrap information\n"); if (pim->global_scope.current_bsr.s_addr == INADDR_ANY) { - strncpy(bsr_str, "0.0.0.0", sizeof(bsr_str)); + strlcpy(bsr_str, "0.0.0.0", sizeof(bsr_str)); pim_time_uptime(uptime, sizeof(uptime), pim->global_scope.current_bsr_first_ts); pim_time_uptime(last_bsm_seen, sizeof(last_bsm_seen), @@ -3661,16 +3669,16 @@ static void pim_show_bsr(struct pim_instance *pim, switch (pim->global_scope.state) { case NO_INFO: - strncpy(bsr_state, "NO_INFO", sizeof(bsr_state)); + strlcpy(bsr_state, "NO_INFO", sizeof(bsr_state)); break; case ACCEPT_ANY: - strncpy(bsr_state, "ACCEPT_ANY", sizeof(bsr_state)); + strlcpy(bsr_state, "ACCEPT_ANY", sizeof(bsr_state)); break; case ACCEPT_PREFERRED: - strncpy(bsr_state, "ACCEPT_PREFERRED", sizeof(bsr_state)); + strlcpy(bsr_state, "ACCEPT_PREFERRED", sizeof(bsr_state)); break; default: - strncpy(bsr_state, "", sizeof(bsr_state)); + strlcpy(bsr_state, "", sizeof(bsr_state)); } if (uj) { @@ -3798,44 +3806,51 @@ DEFUN (clear_ip_pim_statistics, return CMD_SUCCESS; } -static void mroute_add_all(struct pim_instance *pim) +static void clear_mroute(struct pim_instance *pim) { - struct listnode *node; - struct channel_oil *c_oil; + struct pim_upstream *up; + struct interface *ifp; - for (ALL_LIST_ELEMENTS_RO(pim->channel_oil_list, node, c_oil)) { - if (pim_mroute_add(c_oil, __PRETTY_FUNCTION__)) { - /* just log warning */ - char source_str[INET_ADDRSTRLEN]; - char group_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, - source_str, sizeof(source_str)); - pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, - group_str, sizeof(group_str)); - zlog_warn("%s %s: (S,G)=(%s,%s) failure writing MFC", - __FILE__, __PRETTY_FUNCTION__, source_str, - group_str); + /* scan interfaces */ + FOR_ALL_INTERFACES (pim->vrf, ifp) { + struct pim_interface *pim_ifp = ifp->info; + struct listnode *sock_node; + struct igmp_sock *igmp; + struct pim_ifchannel *ch; + + if (!pim_ifp) + continue; + + /* deleting all ifchannels */ + while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) { + ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb); + + pim_ifchannel_delete(ch); } - } -} -static void mroute_del_all(struct pim_instance *pim) -{ - struct listnode *node; - struct channel_oil *c_oil; + /* clean up all igmp groups */ + /* scan igmp sockets */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, + igmp)) { + + struct igmp_group *grp; + + if (igmp->igmp_group_list) { + while (igmp->igmp_group_list->count) { + grp = listnode_head( + igmp->igmp_group_list); + igmp_group_delete(grp); + } + } - for (ALL_LIST_ELEMENTS_RO(pim->channel_oil_list, node, c_oil)) { - if (pim_mroute_del(c_oil, __PRETTY_FUNCTION__)) { - /* just log warning */ - char source_str[INET_ADDRSTRLEN]; - char group_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, - source_str, sizeof(source_str)); - pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, - group_str, sizeof(group_str)); - zlog_warn("%s %s: (S,G)=(%s,%s) failure clearing MFC", - __FILE__, __PRETTY_FUNCTION__, source_str, - group_str); + } + } + + /* clean up all upstreams*/ + if (pim->upstream_list) { + while (pim->upstream_list->count) { + up = listnode_head(pim->upstream_list); + pim_upstream_del(pim, up, __PRETTY_FUNCTION__); } } } @@ -3854,8 +3869,7 @@ DEFUN (clear_ip_mroute, if (!vrf) return CMD_WARNING; - mroute_del_all(vrf->info); - mroute_add_all(vrf->info); + clear_mroute(vrf->info); return CMD_SUCCESS; } @@ -5602,11 +5616,54 @@ DEFUN (show_ip_mroute_vrf_all, return CMD_SUCCESS; } +DEFUN (clear_ip_mroute_count, + clear_ip_mroute_count_cmd, + "clear ip mroute [vrf NAME] count", + CLEAR_STR + IP_STR + MROUTE_STR + VRF_CMD_HELP_STR + "Route and packet count data\n") +{ + int idx = 2; + struct listnode *node; + struct channel_oil *c_oil; + struct static_route *sr; + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct pim_instance *pim; + + if (!vrf) + return CMD_WARNING; + + pim = vrf->info; + for (ALL_LIST_ELEMENTS_RO(pim->channel_oil_list, node, c_oil)) { + if (!c_oil->installed) + continue; + + pim_mroute_update_counters(c_oil); + c_oil->cc.origpktcnt = c_oil->cc.pktcnt; + c_oil->cc.origbytecnt = c_oil->cc.bytecnt; + c_oil->cc.origwrong_if = c_oil->cc.wrong_if; + } + + for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, sr)) { + if (!sr->c_oil.installed) + continue; + + pim_mroute_update_counters(&sr->c_oil); + + sr->c_oil.cc.origpktcnt = sr->c_oil.cc.pktcnt; + sr->c_oil.cc.origbytecnt = sr->c_oil.cc.bytecnt; + sr->c_oil.cc.origwrong_if = sr->c_oil.cc.wrong_if; + } + return CMD_SUCCESS; +} + static void show_mroute_count(struct pim_instance *pim, struct vty *vty) { struct listnode *node; struct channel_oil *c_oil; - struct static_route *s_route; + struct static_route *sr; vty_out(vty, "\n"); @@ -5630,28 +5687,30 @@ static void show_mroute_count(struct pim_instance *pim, struct vty *vty) vty_out(vty, "%-15s %-15s %-8llu %-7ld %-10ld %-7ld\n", source_str, group_str, c_oil->cc.lastused / 100, - c_oil->cc.pktcnt, c_oil->cc.bytecnt, - c_oil->cc.wrong_if); + c_oil->cc.pktcnt - c_oil->cc.origpktcnt, + c_oil->cc.bytecnt - c_oil->cc.origbytecnt, + c_oil->cc.wrong_if - c_oil->cc.origwrong_if); } - for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, s_route)) { + for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, sr)) { char group_str[INET_ADDRSTRLEN]; char source_str[INET_ADDRSTRLEN]; - if (!s_route->c_oil.installed) + if (!sr->c_oil.installed) continue; - pim_mroute_update_counters(&s_route->c_oil); + pim_mroute_update_counters(&sr->c_oil); - pim_inet4_dump("<group?>", s_route->c_oil.oil.mfcc_mcastgrp, + pim_inet4_dump("<group?>", sr->c_oil.oil.mfcc_mcastgrp, group_str, sizeof(group_str)); - pim_inet4_dump("<source?>", s_route->c_oil.oil.mfcc_origin, + pim_inet4_dump("<source?>", sr->c_oil.oil.mfcc_origin, source_str, sizeof(source_str)); vty_out(vty, "%-15s %-15s %-8llu %-7ld %-10ld %-7ld\n", - source_str, group_str, s_route->c_oil.cc.lastused, - s_route->c_oil.cc.pktcnt, s_route->c_oil.cc.bytecnt, - s_route->c_oil.cc.wrong_if); + source_str, group_str, sr->c_oil.cc.lastused, + sr->c_oil.cc.pktcnt - sr->c_oil.cc.origpktcnt, + sr->c_oil.cc.bytecnt - sr->c_oil.cc.origbytecnt, + sr->c_oil.cc.wrong_if - sr->c_oil.cc.origwrong_if); } } @@ -7161,6 +7220,106 @@ DEFUN_HIDDEN (interface_no_ip_igmp_query_max_response_time_dsec, return CMD_SUCCESS; } +#define IGMP_LAST_MEMBER_QUERY_COUNT_MIN (1) +#define IGMP_LAST_MEMBER_QUERY_COUNT_MAX (7) + +DEFUN (interface_ip_igmp_last_member_query_count, + interface_ip_igmp_last_member_query_count_cmd, + "ip igmp last-member-query-count (1-7)", + IP_STR + IFACE_IGMP_STR + IFACE_IGMP_LAST_MEMBER_QUERY_COUNT_STR + "Last member query count\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct pim_interface *pim_ifp = ifp->info; + int last_member_query_count; + int ret; + + if (!pim_ifp) { + ret = pim_cmd_igmp_start(vty, ifp); + if (ret != CMD_SUCCESS) + return ret; + pim_ifp = ifp->info; + } + + last_member_query_count = atoi(argv[3]->arg); + + pim_ifp->igmp_last_member_query_count = last_member_query_count; + + return CMD_SUCCESS; +} + +DEFUN (interface_no_ip_igmp_last_member_query_count, + interface_no_ip_igmp_last_member_query_count_cmd, + "no ip igmp last-member-query-count", + NO_STR + IP_STR + IFACE_IGMP_STR + IFACE_IGMP_LAST_MEMBER_QUERY_COUNT_STR) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct pim_interface *pim_ifp = ifp->info; + + if (!pim_ifp) + return CMD_SUCCESS; + + pim_ifp->igmp_last_member_query_count = + IGMP_DEFAULT_ROBUSTNESS_VARIABLE; + + return CMD_SUCCESS; +} + +#define IGMP_LAST_MEMBER_QUERY_INTERVAL_MIN (1) +#define IGMP_LAST_MEMBER_QUERY_INTERVAL_MAX (255) + +DEFUN (interface_ip_igmp_last_member_query_interval, + interface_ip_igmp_last_member_query_interval_cmd, + "ip igmp last-member-query-interval (1-255)", + IP_STR + IFACE_IGMP_STR + IFACE_IGMP_LAST_MEMBER_QUERY_INTERVAL_STR + "Last member query interval in deciseconds\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct pim_interface *pim_ifp = ifp->info; + int last_member_query_interval; + int ret; + + if (!pim_ifp) { + ret = pim_cmd_igmp_start(vty, ifp); + if (ret != CMD_SUCCESS) + return ret; + pim_ifp = ifp->info; + } + + last_member_query_interval = atoi(argv[3]->arg); + pim_ifp->igmp_specific_query_max_response_time_dsec + = last_member_query_interval; + + return CMD_SUCCESS; +} + +DEFUN (interface_no_ip_igmp_last_member_query_interval, + interface_no_ip_igmp_last_member_query_interval_cmd, + "no ip igmp last-member-query-interval", + NO_STR + IP_STR + IFACE_IGMP_STR + IFACE_IGMP_LAST_MEMBER_QUERY_INTERVAL_STR) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct pim_interface *pim_ifp = ifp->info; + + if (!pim_ifp) + return CMD_SUCCESS; + + pim_ifp->igmp_specific_query_max_response_time_dsec = + IGMP_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC; + + return CMD_SUCCESS; +} + DEFUN (interface_ip_pim_drprio, interface_ip_pim_drprio_cmd, "ip pim drpriority (1-4294967295)", @@ -10116,6 +10275,14 @@ void pim_cmd_init(void) &interface_ip_igmp_query_max_response_time_dsec_cmd); install_element(INTERFACE_NODE, &interface_no_ip_igmp_query_max_response_time_dsec_cmd); + install_element(INTERFACE_NODE, + &interface_ip_igmp_last_member_query_count_cmd); + install_element(INTERFACE_NODE, + &interface_no_ip_igmp_last_member_query_count_cmd); + install_element(INTERFACE_NODE, + &interface_ip_igmp_last_member_query_interval_cmd); + install_element(INTERFACE_NODE, + &interface_no_ip_igmp_last_member_query_interval_cmd); install_element(INTERFACE_NODE, &interface_ip_pim_activeactive_cmd); install_element(INTERFACE_NODE, &interface_ip_pim_ssm_cmd); install_element(INTERFACE_NODE, &interface_no_ip_pim_ssm_cmd); @@ -10187,6 +10354,7 @@ void pim_cmd_init(void) install_element(VIEW_NODE, &show_ip_pim_bsm_db_cmd); install_element(VIEW_NODE, &show_ip_pim_statistics_cmd); + install_element(ENABLE_NODE, &clear_ip_mroute_count_cmd); install_element(ENABLE_NODE, &clear_ip_interfaces_cmd); install_element(ENABLE_NODE, &clear_ip_igmp_interfaces_cmd); install_element(ENABLE_NODE, &clear_ip_mroute_cmd); diff --git a/pimd/pim_cmd.h b/pimd/pim_cmd.h index 558f28231b..f5bb316a7a 100644 --- a/pimd/pim_cmd.h +++ b/pimd/pim_cmd.h @@ -35,6 +35,8 @@ #define IFACE_IGMP_QUERY_INTERVAL_STR "IGMP host query interval\n" #define IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_STR "IGMP max query response value (seconds)\n" #define IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR "IGMP max query response value (deciseconds)\n" +#define IFACE_IGMP_LAST_MEMBER_QUERY_INTERVAL_STR "IGMP last member query interval\n" +#define IFACE_IGMP_LAST_MEMBER_QUERY_COUNT_STR "IGMP last member query count\n" #define DEBUG_IGMP_STR "IGMP protocol activity\n" #define DEBUG_IGMP_EVENTS_STR "IGMP protocol events\n" #define DEBUG_IGMP_PACKETS_STR "IGMP protocol packets\n" diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index 08be38c138..7b8f045697 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -131,6 +131,8 @@ struct pim_interface *pim_if_new(struct interface *ifp, bool igmp, bool pim, IGMP_QUERY_MAX_RESPONSE_TIME_DSEC; pim_ifp->igmp_specific_query_max_response_time_dsec = IGMP_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC; + pim_ifp->igmp_last_member_query_count = + IGMP_DEFAULT_ROBUSTNESS_VARIABLE; /* BSM config on interface: TRUE by default */ pim_ifp->bsm_enable = true; diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h index ab138589bd..1c11e85705 100644 --- a/pimd/pim_iface.h +++ b/pimd/pim_iface.h @@ -88,8 +88,14 @@ struct pim_interface { int igmp_query_max_response_time_dsec; /* IGMPv3 Max Response Time in dsecs for general queries */ int igmp_specific_query_max_response_time_dsec; /* IGMPv3 Max Response - Time in dsecs for - specific queries */ + Time in dsecs called + as last member query + interval, defines the + maximum response time + advertised in IGMP + group-specific + queries */ + int igmp_last_member_query_count; /* IGMP last member query count */ struct list *igmp_socket_list; /* list of struct igmp_sock */ struct list *igmp_join_list; /* list of struct igmp_join */ diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index 213ca48bb5..4ae6f69d3e 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -741,7 +741,7 @@ static void igmp_group_free(struct igmp_group *group) XFREE(MTYPE_PIM_IGMP_GROUP, group); } -static void igmp_group_delete(struct igmp_group *group) +void igmp_group_delete(struct igmp_group *group) { struct listnode *src_node; struct listnode *src_nextnode; diff --git a/pimd/pim_igmp.h b/pimd/pim_igmp.h index c8b880ddd7..7db568dcfe 100644 --- a/pimd/pim_igmp.h +++ b/pimd/pim_igmp.h @@ -197,4 +197,5 @@ void igmp_send_query(int igmp_version, struct igmp_group *group, int fd, int query_max_response_time_dsec, uint8_t s_flag, uint8_t querier_robustness_variable, uint16_t querier_query_interval); +void igmp_group_delete(struct igmp_group *group); #endif /* PIM_IGMP_H */ diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c index b845f54f06..bc0460fa03 100644 --- a/pimd/pim_igmpv3.c +++ b/pimd/pim_igmpv3.c @@ -997,7 +997,7 @@ static void group_retransmit_group(struct igmp_group *group) char query_buf[query_buf_size]; - lmqc = igmp->querier_robustness_variable; + lmqc = pim_ifp->igmp_last_member_query_count; lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; lmqt_msec = lmqc * lmqi_msec; @@ -1076,7 +1076,7 @@ static int group_retransmit_sources(struct igmp_group *group, igmp = group->group_igmp_sock; pim_ifp = igmp->interface->info; - lmqc = igmp->querier_robustness_variable; + lmqc = pim_ifp->igmp_last_member_query_count; lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; lmqt_msec = lmqc * lmqi_msec; @@ -1314,9 +1314,13 @@ static long igmp_source_timer_remain_msec(struct igmp_source *source) */ static void group_query_send(struct igmp_group *group) { + struct pim_interface *pim_ifp; + struct igmp_sock *igmp; long lmqc; /* Last Member Query Count */ - lmqc = group->group_igmp_sock->querier_robustness_variable; + igmp = group->group_igmp_sock; + pim_ifp = igmp->interface->info; + lmqc = pim_ifp->igmp_last_member_query_count; /* lower group timer to lmqt */ igmp_group_timer_lower_to_lmqt(group); @@ -1351,7 +1355,7 @@ static void source_query_send_by_flag(struct igmp_group *group, igmp = group->group_igmp_sock; pim_ifp = igmp->interface->info; - lmqc = igmp->querier_robustness_variable; + lmqc = pim_ifp->igmp_last_member_query_count; lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; lmqt_msec = lmqc * lmqi_msec; @@ -1509,7 +1513,7 @@ void igmp_group_timer_lower_to_lmqt(struct igmp_group *group) ifname = ifp->name; lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec; - lmqc = igmp->querier_robustness_variable; + lmqc = pim_ifp->igmp_last_member_query_count; lmqt_msec = PIM_IGMP_LMQT_MSEC( lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */ @@ -1546,7 +1550,7 @@ void igmp_source_timer_lower_to_lmqt(struct igmp_source *source) ifname = ifp->name; lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec; - lmqc = igmp->querier_robustness_variable; + lmqc = pim_ifp->igmp_last_member_query_count; lmqt_msec = PIM_IGMP_LMQT_MSEC( lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */ diff --git a/pimd/pim_oil.h b/pimd/pim_oil.h index c5106d01cb..57930e3418 100644 --- a/pimd/pim_oil.h +++ b/pimd/pim_oil.h @@ -53,10 +53,13 @@ struct channel_counts { unsigned long long lastused; + unsigned long origpktcnt; unsigned long pktcnt; unsigned long oldpktcnt; + unsigned long origbytecnt; unsigned long bytecnt; unsigned long oldbytecnt; + unsigned long origwrong_if; unsigned long wrong_if; unsigned long oldwrong_if; }; diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c index 8d40f85132..468cd56ee5 100644 --- a/pimd/pim_vty.c +++ b/pimd/pim_vty.c @@ -350,6 +350,24 @@ int pim_interface_config_write(struct vty *vty) ++writes; } + /* IF ip igmp last-member_query-count */ + if (pim_ifp->igmp_last_member_query_count + != IGMP_DEFAULT_ROBUSTNESS_VARIABLE) { + vty_out(vty, + " ip igmp last-member-query-count %d\n", + pim_ifp->igmp_last_member_query_count); + ++writes; + } + + /* IF ip igmp last-member_query-interval */ + if (pim_ifp->igmp_specific_query_max_response_time_dsec + != IGMP_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC) { + vty_out(vty, + " ip igmp last-member-query-interval %d\n", + pim_ifp->igmp_specific_query_max_response_time_dsec); + ++writes; + } + /* IF ip igmp join */ if (pim_ifp->igmp_join_list) { struct listnode *node; diff --git a/tests/.gitignore b/tests/.gitignore index 380172487d..7177165e4a 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -30,6 +30,7 @@ /lib/test_idalloc /lib/test_memory /lib/test_nexthop_iter +/lib/test_printfrr /lib/test_privs /lib/test_ringbuf /lib/test_segv diff --git a/tests/helpers/c/main.c b/tests/helpers/c/main.c index 11db2dabc3..b1dcfcf707 100644 --- a/tests/helpers/c/main.c +++ b/tests/helpers/c/main.c @@ -153,7 +153,7 @@ int main(int argc, char **argv) /* Library inits. */ cmd_init(1); - vty_init(master); + vty_init(master, false); memory_init(); yang_init(); nb_init(master, NULL, 0); diff --git a/tests/lib/cli/common_cli.c b/tests/lib/cli/common_cli.c index 393b588745..2071ae08cd 100644 --- a/tests/lib/cli/common_cli.c +++ b/tests/lib/cli/common_cli.c @@ -82,7 +82,7 @@ int main(int argc, char **argv) cmd_hostname_set("test"); cmd_domainname_set("test.domain"); - vty_init(master); + vty_init(master, false); memory_init(); yang_init(); nb_init(master, NULL, 0); diff --git a/tests/lib/northbound/test_oper_data.c b/tests/lib/northbound/test_oper_data.c index 7cd622854e..3180f9f9f3 100644 --- a/tests/lib/northbound/test_oper_data.c +++ b/tests/lib/northbound/test_oper_data.c @@ -411,7 +411,7 @@ int main(int argc, char **argv) /* Library inits. */ cmd_init(1); cmd_hostname_set("test"); - vty_init(master); + vty_init(master, false); memory_init(); yang_init(); nb_init(master, modules, array_size(modules)); diff --git a/tests/lib/test_printfrr.c b/tests/lib/test_printfrr.c new file mode 100644 index 0000000000..c8ef150b07 --- /dev/null +++ b/tests/lib/test_printfrr.c @@ -0,0 +1,148 @@ +/* + * printfrr() unit test + * Copyright (C) 2019 David Lamparter + * + * 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 <math.h> + +#include "lib/printfrr.h" +#include "lib/memory.h" +#include "lib/prefix.h" + +static int errors; + +static void printcmp(const char *fmt, ...) +{ + va_list ap; + char buf[256], bufrr[256], *p; + int cmp; + memset(bufrr, 0xcc, sizeof(bufrr)); + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + va_start(ap, fmt); + vsnprintfrr(bufrr, sizeof(bufrr), fmt, ap); + va_end(ap); + + cmp = strcmp(buf, bufrr); + + /* OS dependent "+nan" vs. "nan" */ + if (cmp && (p = strstr(bufrr, "+nan"))) { + p[0] = ' '; + if (!strcmp(buf, bufrr)) + cmp = 0; + p[0] = '+'; + } + printf("fmt: \"%s\"\nsys: \"%s\"\nfrr: \"%s\"\n%s\n\n", + fmt, buf, bufrr, cmp ? "ERROR" : "ok"); + + if (cmp) + errors++; +} + +static void printchk(const char *ref, const char *fmt, ...) +{ + va_list ap; + char bufrr[256]; + memset(bufrr, 0xcc, sizeof(bufrr)); + + va_start(ap, fmt); + vsnprintfrr(bufrr, sizeof(bufrr), fmt, ap); + va_end(ap); + + printf("fmt: \"%s\"\nref: \"%s\"\nfrr: \"%s\"\n%s\n\n", + fmt, ref, bufrr, strcmp(ref, bufrr) ? "ERROR" : "ok"); + if (strcmp(ref, bufrr)) + errors++; +} + +int main(int argc, char **argv) +{ + size_t i; + float flts[] = { + 123.456789, + 23.456789e-30, + 3.456789e+30, + INFINITY, + NAN, + }; + uint64_t ui64 = 0xfeed1278cafef00d; + struct in_addr ip; + char *p; + char buf[256]; + + printcmp("%d %u %d %u", 123, 123, -456, -456); + printcmp("%lld %llu %lld %llu", 123LL, 123LL, -456LL, -456LL); + + printcmp("%-20s,%20s,%.20s", "test", "test", "test"); + printcmp("%-3s,%3s,%.3s", "test", "test", "test"); + printcmp("%-6.3s,%6.3s,%6.3s", "test", "test", "test"); + printcmp("%*s,%*s,%.*s", -3, "test", 3, "test", 3, "test"); + + for (i = 0; i < array_size(flts); i++) { + printcmp("%-6.3e,%6.3e,%+06.3e", flts[i], flts[i], flts[i]); + printcmp("%-6.3f,%6.3f,%+06.3f", flts[i], flts[i], flts[i]); + printcmp("%-6.3g,%6.3g,%+06.3g", flts[i], flts[i], flts[i]); + printcmp("%-6.3a,%6.3a,%+06.3a", flts[i], flts[i], flts[i]); + } + + printchk("-77385308584349683 18369358765125201933 feed1278cafef00d", + "%Ld %Lu %Lx", ui64, ui64, ui64); + + inet_aton("192.168.1.2", &ip); + printchk("192.168.1.2", "%pI4", &ip); + printchk(" 192.168.1.2", "%20pI4", &ip); + + printcmp("%p", &ip); + + snprintfrr(buf, sizeof(buf), "test%s", "#1"); + csnprintfrr(buf, sizeof(buf), "test%s", "#2"); + assert(strcmp(buf, "test#1test#2") == 0); + + p = asnprintfrr(MTYPE_TMP, buf, sizeof(buf), "test%s", "#3"); + assert(p == buf); + assert(strcmp(buf, "test#3") == 0); + + p = asnprintfrr(MTYPE_TMP, buf, 4, "test%s", "#4"); + assert(p != buf); + assert(strcmp(p, "test#4") == 0); + XFREE(MTYPE_TMP, p); + + p = asprintfrr(MTYPE_TMP, "test%s", "#5"); + assert(strcmp(p, "test#5") == 0); + XFREE(MTYPE_TMP, p); + + struct prefix_sg sg; + sg.src.s_addr = INADDR_ANY; + sg.grp.s_addr = INADDR_ANY; + printchk("(*,*)", "%pSG4", &sg); + + inet_aton("192.168.1.2", &sg.src); + printchk("(192.168.1.2,*)", "%pSG4", &sg); + + inet_aton("224.1.2.3", &sg.grp); + printchk("(192.168.1.2,224.1.2.3)", "%pSG4", &sg); + + sg.src.s_addr = INADDR_ANY; + printchk("(*,224.1.2.3)", "%pSG4", &sg); + + return !!errors; +} diff --git a/tests/lib/test_printfrr.py b/tests/lib/test_printfrr.py new file mode 100644 index 0000000000..4fe238618e --- /dev/null +++ b/tests/lib/test_printfrr.py @@ -0,0 +1,6 @@ +import frrtest + +class TestPrintfrr(frrtest.TestMultiOut): + program = './test_printfrr' + +TestPrintfrr.exit_cleanly() diff --git a/tests/subdir.am b/tests/subdir.am index ec5fea705e..bbab8cd86a 100644 --- a/tests/subdir.am +++ b/tests/subdir.am @@ -56,6 +56,7 @@ check_PROGRAMS = \ tests/lib/test_idalloc \ tests/lib/test_memory \ tests/lib/test_nexthop_iter \ + tests/lib/test_printfrr \ tests/lib/test_privs \ tests/lib/test_ringbuf \ tests/lib/test_srcdest_table \ @@ -232,6 +233,10 @@ tests_lib_test_nexthop_iter_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_nexthop_iter_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_nexthop_iter_LDADD = $(ALL_TESTS_LDADD) tests_lib_test_nexthop_iter_SOURCES = tests/lib/test_nexthop_iter.c tests/helpers/c/prng.c +tests_lib_test_printfrr_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_printfrr_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_printfrr_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_printfrr_SOURCES = tests/lib/test_printfrr.c tests_lib_test_privs_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_privs_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_privs_LDADD = $(ALL_TESTS_LDADD) @@ -319,6 +324,7 @@ EXTRA_DIST += \ tests/lib/northbound/test_oper_data.refout \ tests/lib/test_atomlist.py \ tests/lib/test_nexthop_iter.py \ + tests/lib/test_printfrr.py \ tests/lib/test_ringbuf.py \ tests/lib/test_srcdest_table.py \ tests/lib/test_stream.py \ diff --git a/tests/topotests/bgp_ipv6_rtadv/__init__.py b/tests/topotests/bgp_ipv6_rtadv/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/__init__.py diff --git a/tests/topotests/bgp_ipv6_rtadv/r1/bgpd.conf b/tests/topotests/bgp_ipv6_rtadv/r1/bgpd.conf new file mode 100644 index 0000000000..1623b4578b --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r1/bgpd.conf @@ -0,0 +1,13 @@ +router bgp 101 + bgp router-id 10.254.254.1 + neighbor r2g peer-group + neighbor r2g remote-as external + neighbor r2g bfd + neighbor r1-eth0 interface peer-group r2g + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + neighbor r2g activate + exit-address-family +! diff --git a/tests/topotests/bgp_ipv6_rtadv/r1/ipv4_routes.json b/tests/topotests/bgp_ipv6_rtadv/r1/ipv4_routes.json new file mode 100644 index 0000000000..8718e49778 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r1/ipv4_routes.json @@ -0,0 +1,44 @@ +{ + "10.254.254.2/32": [ + { + "distance": 20, + "protocol": "bgp", + "internalFlags": 8, + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "10.254.254.2/32", + "nexthops": [ + { + "interfaceName": "r1-eth0", + "interfaceIndex": 2, + "fib": true, + "flags": 3, + "active": true, + "afi": "ipv6" + } + ] + } + ], + "10.254.254.1/32": [ + { + "distance": 0, + "protocol": "connected", + "internalFlags": 8, + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "10.254.254.1/32", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "lo", + "interfaceIndex": 1, + "fib": true, + "flags": 3, + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_ipv6_rtadv/r1/ipv6_routes.json b/tests/topotests/bgp_ipv6_rtadv/r1/ipv6_routes.json new file mode 100644 index 0000000000..d0378b5649 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r1/ipv6_routes.json @@ -0,0 +1,39 @@ +{ + "2001:db8:1::/64": [ + { + "distance": 20, + "protocol": "bgp", + "internalFlags": 0, + "metric": 0, + "prefix": "2001:db8:1::/64", + "nexthops": [ + { + "interfaceName": "r1-eth0", + "interfaceIndex": 2, + "flags": 1, + "active": true, + "afi": "ipv6" + } + ] + }, + { + "distance": 0, + "protocol": "connected", + "internalFlags": 8, + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:1::/64", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "r1-eth0", + "interfaceIndex": 2, + "fib": true, + "flags": 3, + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_ipv6_rtadv/r1/zebra.conf b/tests/topotests/bgp_ipv6_rtadv/r1/zebra.conf new file mode 100644 index 0000000000..f95c3b07a7 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r1/zebra.conf @@ -0,0 +1,9 @@ +debug zebra packet recv +debug zebra packet send +log stdout +interface lo + ip address 10.254.254.1/32 +! +interface r1-eth0 + ipv6 address 2001:db8:1::1/64 +! diff --git a/tests/topotests/bgp_ipv6_rtadv/r2/bgpd.conf b/tests/topotests/bgp_ipv6_rtadv/r2/bgpd.conf new file mode 100644 index 0000000000..bf42d21812 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r2/bgpd.conf @@ -0,0 +1,16 @@ +router bgp 102 + bgp router-id 10.254.254.2 + neighbor r2g peer-group + neighbor r2g remote-as external + neighbor r2g bfd + neighbor r2-eth0 interface peer-group r2g + ! + address-family ipv4 unicast + redistribute connected + exit-address-family + ! + address-family ipv6 unicast + redistribute connected + neighbor r2g activate + exit-address-family +! diff --git a/tests/topotests/bgp_ipv6_rtadv/r2/ipv4_routes.json b/tests/topotests/bgp_ipv6_rtadv/r2/ipv4_routes.json new file mode 100644 index 0000000000..fe26937e80 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r2/ipv4_routes.json @@ -0,0 +1,44 @@ +{ + "10.254.254.2/32": [ + { + "distance": 0, + "protocol": "connected", + "internalFlags": 8, + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "10.254.254.2/32", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "lo", + "interfaceIndex": 1, + "fib": true, + "flags": 3, + "active": true + } + ] + } + ], + "10.254.254.1/32": [ + { + "distance": 20, + "protocol": "bgp", + "internalFlags": 8, + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "10.254.254.1/32", + "nexthops": [ + { + "interfaceName": "r2-eth0", + "interfaceIndex": 2, + "fib": true, + "flags": 3, + "active": true, + "afi": "ipv6" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_ipv6_rtadv/r2/ipv6_routes.json b/tests/topotests/bgp_ipv6_rtadv/r2/ipv6_routes.json new file mode 100644 index 0000000000..d5ad1a2c5b --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r2/ipv6_routes.json @@ -0,0 +1,23 @@ +{ + "2001:db8:1::/64": [ + { + "distance": 0, + "protocol": "connected", + "internalFlags": 8, + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:1::/64", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "r2-eth0", + "interfaceIndex": 2, + "fib": true, + "flags": 3, + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_ipv6_rtadv/r2/zebra.conf b/tests/topotests/bgp_ipv6_rtadv/r2/zebra.conf new file mode 100644 index 0000000000..0131a11be0 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r2/zebra.conf @@ -0,0 +1,9 @@ +ip forwarding +ipv6 forwarding +! +interface lo + ip address 10.254.254.2/32 +! +interface r2-eth0 + ipv6 address 2001:db8:1::2/64 +! diff --git a/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.dot b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.dot new file mode 100644 index 0000000000..da67c29a09 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.dot @@ -0,0 +1,44 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="bfd-topo2"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw1 [ + shape=oval, + label="sw1\n2001:db8:1::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- sw1 [label="eth0"]; + r2 -- sw1 [label="eth0"]; + +} diff --git a/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py new file mode 100644 index 0000000000..6cf223af42 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python + +# +# test_bgp_ipv6_rtadv.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by 6WIND +# +# Permission to use, copy, modify, and/or 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 NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF 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. +# + +""" + test_bgp_ipv6_rtadv.py: Test the FRR/Quagga BGP daemon with BGP IPv6 interface + with route advertisements on a separate netns. +""" + +import os +import sys +import json +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class BGPIPV6RTADVTopo(Topo): + "Test topology builder" + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Create 2 routers. + tgen.add_router('r1') + tgen.add_router('r2') + + switch = tgen.add_switch('s1') + switch.add_link(tgen.gears['r1']) + switch.add_link(tgen.gears['r2']) + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(BGPIPV6RTADVTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, '{}/bgpd.conf'.format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + tgen.stop_topology() + + +def test_protocols_convergence(): + """ + Assert that all protocols have converged + statuses as they depend on it. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Check IPv4 routing tables. + logger.info("Checking IPv4 routes for convergence") + for router in tgen.routers().values(): + json_file = '{}/{}/ipv4_routes.json'.format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info('skipping file {}'.format(json_file)) + continue + + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + router, 'show ip route json'.format(router.name), expected) + _, result = topotest.run_and_expect(test_func, None, count=160, + wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + # Check IPv6 routing tables. + logger.info("Checking IPv6 routes for convergence") + for router in tgen.routers().values(): + json_file = '{}/{}/ipv6_routes.json'.format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info('skipping file {}'.format(json_file)) + continue + + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + router, 'show ipv6 route json'.format(router.name), expected) + _, result = topotest.run_and_expect(test_func, None, count=160, + wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip('Memory leak test/report is disabled') + + tgen.report_memory_leaks() + + +if __name__ == '__main__': + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/__init__.py b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/__init__.py diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/bgpd.conf b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/bgpd.conf new file mode 100644 index 0000000000..3c974c767f --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/bgpd.conf @@ -0,0 +1,13 @@ +router bgp 101 vrf r1-cust1 + bgp router-id 10.254.254.1 + neighbor r2g peer-group + neighbor r2g remote-as external + neighbor r2g bfd + neighbor r1-eth0 interface peer-group r2g + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + neighbor r2g activate + exit-address-family +! diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv4_routes.json b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv4_routes.json new file mode 100644 index 0000000000..e32c84b7d5 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv4_routes.json @@ -0,0 +1,50 @@ +{ + "10.254.254.2/32": [ + { + "prefix": "10.254.254.2/32", + "protocol": "bgp", + "vrfId":3, + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "internalStatus": 34, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceIndex": 2, + "interfaceName": "r1-eth0", + "active": true + } + ] + } + ], + "10.254.254.1/32": [ + { + "prefix": "10.254.254.1/32", + "protocol": "connected", + "vrfId":3, + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "internalStatus": 32, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceIndex": 4, + "interfaceName": "loop1", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv6_routes.json b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv6_routes.json new file mode 100644 index 0000000000..88e8c5cd83 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv6_routes.json @@ -0,0 +1,44 @@ +{ + "2001:db8:1::/64": [ + { + "prefix": "2001:db8:1::/64", + "protocol": "bgp", + "vrfId":3, + "distance": 20, + "metric": 0, + "internalStatus": 2, + "internalFlags": 0, + "nexthops": [ + { + "flags": 1, + "afi": "ipv6", + "interfaceIndex": 2, + "interfaceName": "r1-eth0", + "active": true + } + ] + }, + { + "prefix": "2001:db8:1::/64", + "protocol": "connected", + "vrfId":3, + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "internalStatus": 32, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceIndex": 2, + "interfaceName": "r1-eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/zebra.conf b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/zebra.conf new file mode 100644 index 0000000000..f19c497208 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/zebra.conf @@ -0,0 +1,9 @@ +debug zebra packet recv +debug zebra packet send +log stdout +interface loop1 vrf r1-cust1 + ip address 10.254.254.1/32 +! +interface r1-eth0 vrf r1-cust1 + ipv6 address 2001:db8:1::1/64 +! diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/bgpd.conf b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/bgpd.conf new file mode 100644 index 0000000000..39362abd46 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/bgpd.conf @@ -0,0 +1,16 @@ +router bgp 102 vrf r2-cust1 + bgp router-id 10.254.254.2 + neighbor r2g peer-group + neighbor r2g remote-as external + neighbor r2g bfd + neighbor r2-eth0 interface peer-group r2g + ! + address-family ipv4 unicast + redistribute connected + exit-address-family + ! + address-family ipv6 unicast + redistribute connected + neighbor r2g activate + exit-address-family +! diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv4_routes.json b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv4_routes.json new file mode 100644 index 0000000000..9d7c0e6e4f --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv4_routes.json @@ -0,0 +1,50 @@ +{ + "10.254.254.2/32": [ + { + "prefix": "10.254.254.2/32", + "protocol": "connected", + "vrfId":3, + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "internalStatus": 32, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceIndex": 4, + "interfaceName": "loop1", + "active": true + } + ] + } + ], + "10.254.254.1/32": [ + { + "prefix": "10.254.254.1/32", + "protocol": "bgp", + "vrfId":3, + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "internalStatus": 34, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceIndex": 2, + "interfaceName": "r2-eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv6_routes.json b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv6_routes.json new file mode 100644 index 0000000000..230fe38748 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv6_routes.json @@ -0,0 +1,26 @@ +{ + "2001:db8:1::/64": [ + { + "prefix": "2001:db8:1::/64", + "protocol": "connected", + "vrfId":3, + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "internalStatus": 32, + "internalFlags": 8, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceIndex": 2, + "interfaceName": "r2-eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/zebra.conf b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/zebra.conf new file mode 100644 index 0000000000..c3795ab959 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/zebra.conf @@ -0,0 +1,9 @@ +ip forwarding +ipv6 forwarding +! +interface loop1 vrf r2-cust1 + ip address 10.254.254.2/32 +! +interface r2-eth0 vrf r2-cust1 + ipv6 address 2001:db8:1::2/64 +! diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.dot b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.dot new file mode 100644 index 0000000000..da67c29a09 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.dot @@ -0,0 +1,44 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="bfd-topo2"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw1 [ + shape=oval, + label="sw1\n2001:db8:1::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- sw1 [label="eth0"]; + r2 -- sw1 [label="eth0"]; + +} diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py new file mode 100644 index 0000000000..6b4df78c69 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python + +# +# test_bgp_ipv6_rtadv.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by 6WIND +# +# Permission to use, copy, modify, and/or 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 NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF 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. +# + +""" + test_bgp_ipv6_rtadv.py: Test the FRR/Quagga BGP daemon with BGP IPv6 interface + with route advertisements on a separate netns. +""" + +import os +import sys +import json +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class BGPIPV6RTADVVRFTopo(Topo): + "Test topology builder" + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Create 2 routers. + tgen.add_router('r1') + tgen.add_router('r2') + + switch = tgen.add_switch('s1') + switch.add_link(tgen.gears['r1']) + switch.add_link(tgen.gears['r2']) + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(BGPIPV6RTADVVRFTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + logger.info('Testing with VRF Lite support') + + cmds = ['ip link add {0}-cust1 type vrf table 1001', + 'ip link add loop1 type dummy', + 'ip link set loop1 master {0}-cust1', + 'ip link set {0}-eth0 master {0}-cust1'] + + for rname, router in router_list.iteritems(): + for cmd in cmds: + output = tgen.net[rname].cmd(cmd.format(rname)) + + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, '{}/bgpd.conf'.format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + tgen.stop_topology() + + +def test_protocols_convergence(): + """ + Assert that all protocols have converged + statuses as they depend on it. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Check IPv4 routing tables. + logger.info("Checking IPv4 routes for convergence") + + for router in tgen.routers().values(): + json_file = '{}/{}/ipv4_routes.json'.format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info('skipping file {}'.format(json_file)) + continue + + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + router, 'show ip route vrf {}-cust1 json'.format(router.name), expected) + _, result = topotest.run_and_expect(test_func, None, count=160, + wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + # Check IPv6 routing tables. + logger.info("Checking IPv6 routes for convergence") + for router in tgen.routers().values(): + json_file = '{}/{}/ipv6_routes.json'.format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info('skipping file {}'.format(json_file)) + continue + + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + router, 'show ipv6 route vrf {}-cust1 json'.format(router.name), expected) + _, result = topotest.run_and_expect(test_func, None, count=160, + wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip('Memory leak test/report is disabled') + + tgen.report_memory_leaks() + + +if __name__ == '__main__': + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tools/frr.in b/tools/frr.in index d871afa42b..50f7ecaa9f 100755 --- a/tools/frr.in +++ b/tools/frr.in @@ -561,30 +561,7 @@ case "$1" in stop_prio 0 $dmn fi - if [ -z "$dmn" -o "$dmn" = "zebra" ]; then - echo "Removing all routes made by FRR." - # Specific values for each proto can be found - # in /etc/iproute2/rt_protos as well as FRR - # specific ones in /etc/iproute2/rt_protos.d - # Additionally if a new protocol is added - # we need to add it here as well as - # in rt_netlink.h( follow the directions! ) - ip route flush proto 4 - ip route flush proto 11 - ip route flush proto 42 - ip route flush proto 186 - ip route flush proto 187 - ip route flush proto 188 - ip route flush proto 189 - ip route flush proto 190 - ip route flush proto 191 - ip route flush proto 192 - ip route flush proto 193 - ip route flush proto 194 - ip route flush proto 195 - ip route flush proto 196 - ip route flush proto 197 - else + if [ [ -n "$dmn" ] && [ "$dmn" != "zebra" ] ]; then [ -n "$dmn" ] && eval "${dmn/-/_}=0" start_watchfrr fi diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 51d5e42915..baf77d1cb7 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -2553,6 +2553,15 @@ DEFUNSH(VTYSH_ALL, vtysh_log_timestamp_precision, return CMD_SUCCESS; } +DEFUNSH(VTYSH_ALL, vtysh_debug_memstats, + vtysh_debug_memstats_cmd, "[no] debug memstats-at-exit", + NO_STR + "Debug\n" + "Print memory statistics at exit\n") +{ + return CMD_SUCCESS; +} + DEFUNSH(VTYSH_ALL, no_vtysh_log_timestamp_precision, no_vtysh_log_timestamp_precision_cmd, "no log timestamp precision", NO_STR @@ -3429,7 +3438,7 @@ void vtysh_readline_init(void) char *vtysh_prompt(void) { - static char buf[100]; + static char buf[512]; snprintf(buf, sizeof buf, cmd_prompt(vty->node), cmd_hostname_get()); return buf; @@ -3849,6 +3858,8 @@ void vtysh_init_vty(void) install_element(VIEW_NODE, &vtysh_show_debugging_hashtable_cmd); install_element(ENABLE_NODE, &vtysh_debug_all_cmd); install_element(CONFIG_NODE, &vtysh_debug_all_cmd); + install_element(ENABLE_NODE, &vtysh_debug_memstats_cmd); + install_element(CONFIG_NODE, &vtysh_debug_memstats_cmd); /* misc lib show commands */ install_element(VIEW_NODE, &vtysh_show_memory_cmd); diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index cf94ab643a..9c2de0f62b 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -521,10 +521,10 @@ int vtysh_read_config(const char *config_default_dir) */ void vtysh_config_write(void) { - char line[81]; + char line[512]; if (cmd_hostname_get()) { - sprintf(line, "hostname %s", cmd_hostname_get()); + snprintf(line, sizeof(line), "hostname %s", cmd_hostname_get()); vtysh_config_parse_line(NULL, line); } diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c index 2e4510a45a..9667460189 100644 --- a/vtysh/vtysh_main.c +++ b/vtysh/vtysh_main.c @@ -332,6 +332,8 @@ int main(int argc, char **argv, char **env) progname = ((p = strrchr(argv[0], '/')) ? ++p : argv[0]); strlcpy(sysconfdir, frr_sysconfdir, sizeof(sysconfdir)); + + frr_init_vtydir(); strlcpy(vtydir, frr_vtydir, sizeof(vtydir)); /* Option handling. */ diff --git a/watchfrr/watchfrr.c b/watchfrr/watchfrr.c index 34f8dabdf1..1cc7722f4f 100644 --- a/watchfrr/watchfrr.c +++ b/watchfrr/watchfrr.c @@ -648,6 +648,7 @@ static void daemon_send_ready(int exitcode) { FILE *fp; static int sent = 0; + char started[1024]; if (sent) return; @@ -669,7 +670,9 @@ static void daemon_send_ready(int exitcode) frr_detach(); - fp = fopen(DAEMON_VTY_DIR "/watchfrr.started", "w"); + snprintf(started, sizeof(started), "%s%s", frr_vtydir, + "watchfrr.started"); + fp = fopen(started, "w"); if (fp) fclose(fp); #if defined HAVE_SYSTEMD diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index fe37a33358..387a3531bd 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -340,8 +340,7 @@ static void netlink_write_incoming(const char *buf, const unsigned int size, char fname[MAXPATHLEN]; FILE *f; - snprintf(fname, MAXPATHLEN, "%s/%s_%u", DAEMON_VTY_DIR, "netlink", - counter); + snprintf(fname, MAXPATHLEN, "%s/%s_%u", frr_vtydir, "netlink", counter); frr_elevate_privs(&zserv_privs) { f = fopen(fname, "w"); } diff --git a/zebra/rib.h b/zebra/rib.h index a54e164d98..292f6bc600 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -340,19 +340,6 @@ extern void route_entry_copy_nexthops(struct route_entry *re, extern void _route_entry_dump(const char *func, union prefixconstptr pp, union prefixconstptr src_pp, const struct route_entry *re); -/* RPF lookup behaviour */ -enum multicast_mode { - MCAST_NO_CONFIG = 0, /* MIX_MRIB_FIRST, but no show in config write */ - MCAST_MRIB_ONLY, /* MRIB only */ - MCAST_URIB_ONLY, /* URIB only */ - MCAST_MIX_MRIB_FIRST, /* MRIB, if nothing at all then URIB */ - MCAST_MIX_DISTANCE, /* MRIB & URIB, lower distance wins */ - MCAST_MIX_PFXLEN, /* MRIB & URIB, longer prefix wins */ - /* on equal value, MRIB wins for last 2 */ -}; - -extern void multicast_mode_ipv4_set(enum multicast_mode mode); -extern enum multicast_mode multicast_mode_ipv4_get(void); extern void rib_lookup_and_dump(struct prefix_ipv4 *p, vrf_id_t vrf_id); extern void rib_lookup_and_pushup(struct prefix_ipv4 *p, vrf_id_t vrf_id); diff --git a/zebra/rtadv.c b/zebra/rtadv.c index 5088e2e8e1..e181b495b8 100644 --- a/zebra/rtadv.c +++ b/zebra/rtadv.c @@ -42,7 +42,6 @@ #include "zebra/debug.h" #include "zebra/rib.h" #include "zebra/zapi_msg.h" -#include "zebra/zebra_ns.h" #include "zebra/zebra_vrf.h" #include "zebra/zebra_errors.h" #include "zebra/zebra_router.h" @@ -81,18 +80,25 @@ enum rtadv_event { RTADV_READ }; -static void rtadv_event(struct zebra_ns *, enum rtadv_event, int); +static void rtadv_event(struct zebra_vrf *, enum rtadv_event, int); static int if_join_all_router(int, struct interface *); static int if_leave_all_router(int, struct interface *); -static int rtadv_increment_received(struct zebra_ns *zns, ifindex_t *ifindex) +static int rtadv_get_socket(struct zebra_vrf *zvrf) +{ + if (zvrf->rtadv.sock > 0) + return zvrf->rtadv.sock; + return zrouter.rtadv_sock; +} + +static int rtadv_increment_received(struct zebra_vrf *zvrf, ifindex_t *ifindex) { int ret = -1; struct interface *iface; struct zebra_if *zif; - iface = if_lookup_by_index_per_ns(zns, *ifindex); + iface = if_lookup_by_index(*ifindex, zvrf->vrf->vrf_id); if (iface && iface->info) { zif = iface->info; zif->ra_rcvd++; @@ -101,7 +107,7 @@ static int rtadv_increment_received(struct zebra_ns *zns, ifindex_t *ifindex) return ret; } -static int rtadv_recv_packet(struct zebra_ns *zns, int sock, uint8_t *buf, +static int rtadv_recv_packet(struct zebra_vrf *zvrf, int sock, uint8_t *buf, int buflen, struct sockaddr_in6 *from, ifindex_t *ifindex, int *hoplimit) { @@ -149,7 +155,7 @@ static int rtadv_recv_packet(struct zebra_ns *zns, int sock, uint8_t *buf, } } - rtadv_increment_received(zns, ifindex); + rtadv_increment_received(zvrf, ifindex); return ret; } @@ -461,19 +467,19 @@ no_more_opts: static int rtadv_timer(struct thread *thread) { - struct zebra_ns *zns = THREAD_ARG(thread); + struct zebra_vrf *zvrf = THREAD_ARG(thread); struct vrf *vrf; struct interface *ifp; struct zebra_if *zif; int period; - zrouter.rtadv.ra_timer = NULL; - if (zrouter.rtadv.adv_msec_if_count == 0) { + zvrf->rtadv.ra_timer = NULL; + if (zvrf->rtadv.adv_msec_if_count == 0) { period = 1000; /* 1 s */ - rtadv_event(zns, RTADV_TIMER, 1 /* 1 s */); + rtadv_event(zvrf, RTADV_TIMER, 1 /* 1 s */); } else { period = 10; /* 10 ms */ - rtadv_event(zns, RTADV_TIMER_MSEC, 10 /* 10 ms */); + rtadv_event(zvrf, RTADV_TIMER_MSEC, 10 /* 10 ms */); } RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) @@ -500,7 +506,7 @@ static int rtadv_timer(struct thread *thread) "Fast RA Rexmit on interface %s", ifp->name); - rtadv_send_packet(zrouter.rtadv.sock, + rtadv_send_packet(rtadv_get_socket(zvrf), ifp); } else { zif->rtadv.AdvIntervalTimer -= period; @@ -514,8 +520,8 @@ static int rtadv_timer(struct thread *thread) zif->rtadv .MaxRtrAdvInterval; rtadv_send_packet( - zrouter.rtadv.sock, - ifp); + rtadv_get_socket(zvrf), + ifp); } } } @@ -527,10 +533,9 @@ static int rtadv_timer(struct thread *thread) static void rtadv_process_solicit(struct interface *ifp) { struct zebra_vrf *zvrf = vrf_info_lookup(ifp->vrf_id); - struct zebra_ns *zns = zvrf->zns; - assert(zns); - rtadv_send_packet(zrouter.rtadv.sock, ifp); + assert(zvrf); + rtadv_send_packet(rtadv_get_socket(zvrf), ifp); } /* @@ -652,7 +657,7 @@ static void rtadv_process_advert(uint8_t *msg, unsigned int len, static void rtadv_process_packet(uint8_t *buf, unsigned int len, ifindex_t ifindex, int hoplimit, struct sockaddr_in6 *from, - struct zebra_ns *zns) + struct zebra_vrf *zvrf) { struct icmp6_hdr *icmph; struct interface *ifp; @@ -662,7 +667,7 @@ static void rtadv_process_packet(uint8_t *buf, unsigned int len, inet_ntop(AF_INET6, &from->sin6_addr, addr_str, INET6_ADDRSTRLEN); /* Interface search. */ - ifp = if_lookup_by_index_per_ns(zns, ifindex); + ifp = if_lookup_by_index(ifindex, zvrf->vrf->vrf_id); if (ifp == NULL) { flog_warn(EC_ZEBRA_UNKNOWN_INTERFACE, "RA/RS received on unknown IF %u from %s", ifindex, @@ -724,15 +729,15 @@ static int rtadv_read(struct thread *thread) struct sockaddr_in6 from; ifindex_t ifindex = 0; int hoplimit = -1; - struct zebra_ns *zns = THREAD_ARG(thread); + struct zebra_vrf *zvrf = THREAD_ARG(thread); sock = THREAD_FD(thread); - zrouter.rtadv.ra_read = NULL; + zvrf->rtadv.ra_read = NULL; /* Register myself. */ - rtadv_event(zns, RTADV_READ, sock); + rtadv_event(zvrf, RTADV_READ, sock); - len = rtadv_recv_packet(zns, sock, buf, sizeof(buf), &from, &ifindex, + len = rtadv_recv_packet(zvrf, sock, buf, sizeof(buf), &from, &ifindex, &hoplimit); if (len < 0) { @@ -742,7 +747,7 @@ static int rtadv_read(struct thread *thread) return len; } - rtadv_process_packet(buf, (unsigned)len, ifindex, hoplimit, &from, zns); + rtadv_process_packet(buf, (unsigned)len, ifindex, hoplimit, &from, zvrf); return 0; } @@ -875,29 +880,27 @@ static void ipv6_nd_suppress_ra_set(struct interface *ifp, { struct zebra_if *zif; struct zebra_vrf *zvrf; - struct zebra_ns *zns; zif = ifp->info; zvrf = vrf_info_lookup(ifp->vrf_id); - zns = zvrf->zns; if (status == RA_SUPPRESS) { /* RA is currently enabled */ if (zif->rtadv.AdvSendAdvertisements) { zif->rtadv.AdvSendAdvertisements = 0; zif->rtadv.AdvIntervalTimer = 0; - zrouter.rtadv.adv_if_count--; + zvrf->rtadv.adv_if_count--; - if_leave_all_router(zrouter.rtadv.sock, ifp); + if_leave_all_router(rtadv_get_socket(zvrf), ifp); - if (zrouter.rtadv.adv_if_count == 0) - rtadv_event(zns, RTADV_STOP, 0); + if (zvrf->rtadv.adv_if_count == 0) + rtadv_event(zvrf, RTADV_STOP, 0); } } else { if (!zif->rtadv.AdvSendAdvertisements) { zif->rtadv.AdvSendAdvertisements = 1; zif->rtadv.AdvIntervalTimer = 0; - zrouter.rtadv.adv_if_count++; + zvrf->rtadv.adv_if_count++; if (zif->rtadv.MaxRtrAdvInterval >= 1000) { /* Enable Fast RA only when RA interval is in @@ -907,11 +910,11 @@ static void ipv6_nd_suppress_ra_set(struct interface *ifp, RTADV_NUM_FAST_REXMITS; } - if_join_all_router(zrouter.rtadv.sock, ifp); + if_join_all_router(rtadv_get_socket(zvrf), ifp); - if (zrouter.rtadv.adv_if_count == 1) - rtadv_event(zns, RTADV_START, - zrouter.rtadv.sock); + if (zvrf->rtadv.adv_if_count == 1) + rtadv_event(zvrf, RTADV_START, + rtadv_get_socket(zvrf)); } } } @@ -944,7 +947,7 @@ static void zebra_interface_radv_set(ZAPI_HANDLER_ARGS, int enable) zebra_route_string(client->proto), ra_interval); /* Locate interface and check VRF match. */ - ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), ifindex); + ifp = if_lookup_by_index(ifindex, zvrf->vrf->vrf_id); if (!ifp) { flog_warn(EC_ZEBRA_UNKNOWN_INTERFACE, "%u: IF %u RA %s client %s - interface unknown", @@ -1051,6 +1054,9 @@ DEFUN (ipv6_nd_ra_interval_msec, VTY_DECLVAR_CONTEXT(interface, ifp); unsigned interval; struct zebra_if *zif = ifp->info; + struct zebra_vrf *zvrf; + + zvrf = vrf_info_lookup(ifp->vrf_id); interval = strtoul(argv[idx_number]->arg, NULL, 10); if ((zif->rtadv.AdvDefaultLifetime != -1 @@ -1061,10 +1067,10 @@ DEFUN (ipv6_nd_ra_interval_msec, } if (zif->rtadv.MaxRtrAdvInterval % 1000) - zrouter.rtadv.adv_msec_if_count--; + zvrf->rtadv.adv_msec_if_count--; if (interval % 1000) - zrouter.rtadv.adv_msec_if_count++; + zvrf->rtadv.adv_msec_if_count++; SET_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED); zif->rtadv.MaxRtrAdvInterval = interval; @@ -1086,6 +1092,9 @@ DEFUN (ipv6_nd_ra_interval, VTY_DECLVAR_CONTEXT(interface, ifp); unsigned interval; struct zebra_if *zif = ifp->info; + struct zebra_vrf *zvrf; + + zvrf = vrf_info_lookup(ifp->vrf_id); interval = strtoul(argv[idx_number]->arg, NULL, 10); if ((zif->rtadv.AdvDefaultLifetime != -1 @@ -1096,7 +1105,7 @@ DEFUN (ipv6_nd_ra_interval, } if (zif->rtadv.MaxRtrAdvInterval % 1000) - zrouter.rtadv.adv_msec_if_count--; + zvrf->rtadv.adv_msec_if_count--; /* convert to milliseconds */ interval = interval * 1000; @@ -1122,9 +1131,12 @@ DEFUN (no_ipv6_nd_ra_interval, { VTY_DECLVAR_CONTEXT(interface, ifp); struct zebra_if *zif = ifp->info; + struct zebra_vrf *zvrf = NULL; + + zvrf = vrf_info_lookup(ifp->vrf_id); if (zif->rtadv.MaxRtrAdvInterval % 1000) - zrouter.rtadv.adv_msec_if_count--; + zvrf->rtadv.adv_msec_if_count--; UNSET_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED); @@ -2094,15 +2106,15 @@ static int rtadv_config_write(struct vty *vty, struct interface *ifp) } -static void rtadv_event(struct zebra_ns *zns, enum rtadv_event event, int val) +static void rtadv_event(struct zebra_vrf *zvrf, enum rtadv_event event, int val) { - struct rtadv *rtadv = &zrouter.rtadv; + struct rtadv *rtadv = &zvrf->rtadv; switch (event) { case RTADV_START: - thread_add_read(zrouter.master, rtadv_read, zns, val, + thread_add_read(zrouter.master, rtadv_read, zvrf, val, &rtadv->ra_read); - thread_add_event(zrouter.master, rtadv_timer, zns, 0, + thread_add_event(zrouter.master, rtadv_timer, zvrf, 0, &rtadv->ra_timer); break; case RTADV_STOP: @@ -2116,15 +2128,15 @@ static void rtadv_event(struct zebra_ns *zns, enum rtadv_event event, int val) } break; case RTADV_TIMER: - thread_add_timer(zrouter.master, rtadv_timer, zns, val, + thread_add_timer(zrouter.master, rtadv_timer, zvrf, val, &rtadv->ra_timer); break; case RTADV_TIMER_MSEC: - thread_add_timer_msec(zrouter.master, rtadv_timer, zns, val, + thread_add_timer_msec(zrouter.master, rtadv_timer, zvrf, val, &rtadv->ra_timer); break; case RTADV_READ: - thread_add_read(zrouter.master, rtadv_read, zns, val, + thread_add_read(zrouter.master, rtadv_read, zvrf, val, &rtadv->ra_read); break; default: @@ -2133,21 +2145,30 @@ static void rtadv_event(struct zebra_ns *zns, enum rtadv_event event, int val) return; } -void rtadv_init(struct zebra_ns *zns) +void rtadv_init(struct zebra_vrf *zvrf) { - zrouter.rtadv.sock = rtadv_make_socket(zns->ns_id); + if (vrf_is_backend_netns()) { + zvrf->rtadv.sock = rtadv_make_socket(zvrf->zns->ns_id); + zrouter.rtadv_sock = -1; + } else if (!zrouter.rtadv_sock) { + zvrf->rtadv.sock = -1; + if (!zrouter.rtadv_sock) + zrouter.rtadv_sock = rtadv_make_socket(zvrf->zns->ns_id); + } } -void rtadv_terminate(struct zebra_ns *zns) +void rtadv_terminate(struct zebra_vrf *zvrf) { - rtadv_event(zns, RTADV_STOP, 0); - if (zrouter.rtadv.sock >= 0) { - close(zrouter.rtadv.sock); - zrouter.rtadv.sock = -1; + rtadv_event(zvrf, RTADV_STOP, 0); + if (zvrf->rtadv.sock >= 0) { + close(zvrf->rtadv.sock); + zvrf->rtadv.sock = -1; + } else if (zrouter.rtadv_sock >= 0) { + close(zrouter.rtadv_sock); + zrouter.rtadv_sock = -1; } - - zrouter.rtadv.adv_if_count = 0; - zrouter.rtadv.adv_msec_if_count = 0; + zvrf->rtadv.adv_if_count = 0; + zvrf->rtadv.adv_msec_if_count = 0; } void rtadv_cmd_init(void) @@ -2243,11 +2264,11 @@ static int if_leave_all_router(int sock, struct interface *ifp) } #else -void rtadv_init(struct zebra_ns *zns) +void rtadv_init(struct zebra_vrf *zvrf) { /* Empty.*/; } -void rtadv_terminate(struct zebra_ns *zns) +void rtadv_terminate(struct zebra_vrf *zvrf) { /* Empty.*/; } diff --git a/zebra/rtadv.h b/zebra/rtadv.h index 53c497fc09..d692ef2417 100644 --- a/zebra/rtadv.h +++ b/zebra/rtadv.h @@ -135,8 +135,8 @@ typedef enum { RA_SUPPRESS, } ipv6_nd_suppress_ra_status; -extern void rtadv_init(struct zebra_ns *); -extern void rtadv_terminate(struct zebra_ns *); +extern void rtadv_init(struct zebra_vrf *zvrf); +extern void rtadv_terminate(struct zebra_vrf *zvrf); extern void rtadv_cmd_init(void); extern void zebra_interface_radv_disable(ZAPI_HANDLER_ARGS); extern void zebra_interface_radv_enable(ZAPI_HANDLER_ARGS); diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 94bfa34b38..61200806ba 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -524,6 +524,8 @@ int zsend_redistribute_route(int cmd, struct zserv *client, struct nexthop *nexthop; int count = 0; afi_t afi; + size_t stream_size = + MAX(ZEBRA_MAX_PACKET_SIZ, sizeof(struct zapi_route)); memset(&api, 0, sizeof(api)); api.vrf_id = re->vrf_id; @@ -605,7 +607,7 @@ int zsend_redistribute_route(int cmd, struct zserv *client, SET_FLAG(api.message, ZAPI_MESSAGE_MTU); api.mtu = re->mtu; - struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ); + struct stream *s = stream_new(stream_size); /* Encode route and send. */ if (zapi_route_encode(cmd, s, &api) < 0) { @@ -2524,7 +2526,7 @@ static void zserv_write_incoming(struct stream *orig, uint16_t command) copy = stream_dup(orig); stream_set_getp(copy, 0); - snprintf(fname, MAXPATHLEN, "%s/%u", DAEMON_VTY_DIR, command); + snprintf(fname, MAXPATHLEN, "%s/%u", frr_vtydir, command); frr_elevate_privs(&zserv_privs) { fd = open(fname, O_CREAT | O_WRONLY | O_EXCL, 0644); diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 0c743d8678..db4f9d0015 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -27,7 +27,6 @@ #include "lib/prefix.h" #include "lib/memory.h" -#include "rtadv.h" #include "zebra_ns.h" #include "zebra_vrf.h" #include "zebra_memory.h" @@ -122,10 +121,6 @@ int zebra_ns_enable(ns_id_t ns_id, void **info) zns->ns_id = ns_id; -#if defined(HAVE_RTADV) - rtadv_init(zns); -#endif - kernel_init(zns); interface_list(zns); route_read(zns); @@ -142,9 +137,6 @@ int zebra_ns_enable(ns_id_t ns_id, void **info) static int zebra_ns_disable_internal(struct zebra_ns *zns, bool complete) { route_table_finish(zns->if_table); -#if defined(HAVE_RTADV) - rtadv_terminate(zns); -#endif kernel_terminate(zns, complete); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 83eb5f4223..cc614abac5 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -108,10 +108,6 @@ static const struct { /* no entry/default: 150 */ }; -/* RPF lookup behaviour */ -static enum multicast_mode ipv4_multicast_mode = MCAST_NO_CONFIG; - - static void __attribute__((format(printf, 5, 6))) _rnode_zlog(const char *_func, vrf_id_t vrf_id, struct route_node *rn, int priority, const char *msgfmt, ...) @@ -404,7 +400,7 @@ struct route_entry *rib_match_ipv4_multicast(vrf_id_t vrf_id, struct route_node *m_rn = NULL, *u_rn = NULL; union g_addr gaddr = {.ipv4 = addr}; - switch (ipv4_multicast_mode) { + switch (zrouter.ipv4_multicast_mode) { case MCAST_MRIB_ONLY: return rib_match(AFI_IP, SAFI_MULTICAST, vrf_id, &gaddr, rn_out); @@ -456,19 +452,6 @@ struct route_entry *rib_match_ipv4_multicast(vrf_id_t vrf_id, return re; } -void multicast_mode_ipv4_set(enum multicast_mode mode) -{ - if (IS_ZEBRA_DEBUG_RIB) - zlog_debug("%s: multicast lookup mode set (%d)", __func__, - mode); - ipv4_multicast_mode = mode; -} - -enum multicast_mode multicast_mode_ipv4_get(void) -{ - return ipv4_multicast_mode; -} - struct route_entry *rib_lookup_ipv4(struct prefix_ipv4 *p, vrf_id_t vrf_id) { struct route_table *table; diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 2917d0e7a8..a63d015716 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -1132,7 +1132,7 @@ static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type, static void print_nh(struct nexthop *nexthop, struct vty *vty) { char buf[BUFSIZ]; - struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + struct zebra_ns *zns = zebra_ns_lookup(nexthop->vrf_id); switch (nexthop->type) { case NEXTHOP_TYPE_IPV4: diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c index f48bf3b033..dbfe695a00 100644 --- a/zebra/zebra_routemap.c +++ b/zebra/zebra_routemap.c @@ -67,7 +67,7 @@ static int zebra_route_match_add(struct vty *vty, const char *command, int ret; int retval = CMD_SUCCESS; - ret = route_map_add_match(index, command, arg); + ret = route_map_add_match(index, command, arg, type); switch (ret) { case RMAP_RULE_MISSING: vty_out(vty, "%% Zebra Can't find rule.\n"); diff --git a/zebra/zebra_router.c b/zebra/zebra_router.c index 610d51d3ea..4352d688a1 100644 --- a/zebra/zebra_router.c +++ b/zebra/zebra_router.c @@ -29,9 +29,12 @@ #include "zebra_pbr.h" #include "zebra_vxlan.h" #include "zebra_mlag.h" +#include "zebra_nhg.h" +#include "debug.h" struct zebra_router zrouter = { .multipath_num = MULTIPATH_NUM, + .ipv4_multicast_mode = MCAST_NO_CONFIG, }; static inline int @@ -187,6 +190,19 @@ uint32_t zebra_router_get_next_sequence(void) memory_order_relaxed); } +void multicast_mode_ipv4_set(enum multicast_mode mode) +{ + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug("%s: multicast lookup mode set (%d)", __func__, + mode); + zrouter.ipv4_multicast_mode = mode; +} + +enum multicast_mode multicast_mode_ipv4_get(void) +{ + return zrouter.ipv4_multicast_mode; +} + void zebra_router_terminate(void) { struct zebra_router_table *zrt, *tmp; diff --git a/zebra/zebra_router.h b/zebra/zebra_router.h index 6c9f3a0f28..e50f8a1186 100644 --- a/zebra/zebra_router.h +++ b/zebra/zebra_router.h @@ -50,6 +50,17 @@ RB_HEAD(zebra_router_table_head, zebra_router_table); RB_PROTOTYPE(zebra_router_table_head, zebra_router_table, zebra_router_table_entry, zebra_router_table_entry_compare) +/* RPF lookup behaviour */ +enum multicast_mode { + MCAST_NO_CONFIG = 0, /* MIX_MRIB_FIRST, but no show in config write */ + MCAST_MRIB_ONLY, /* MRIB only */ + MCAST_URIB_ONLY, /* URIB only */ + MCAST_MIX_MRIB_FIRST, /* MRIB, if nothing at all then URIB */ + MCAST_MIX_DISTANCE, /* MRIB & URIB, lower distance wins */ + MCAST_MIX_PFXLEN, /* MRIB & URIB, longer prefix wins */ + /* on equal value, MRIB wins for last 2 */ +}; + struct zebra_mlag_info { /* Role this zebra router is playing */ enum mlag_role role; @@ -82,9 +93,8 @@ struct zebra_router { struct hash *iptable_hash; -#if defined(HAVE_RTADV) - struct rtadv rtadv; -#endif /* HAVE_RTADV */ + /* used if vrf backend is not network namespace */ + int rtadv_sock; /* A sequence number used for tracking routes */ _Atomic uint32_t sequence_num; @@ -113,6 +123,9 @@ struct zebra_router { uint32_t multipath_num; + /* RPF Lookup behavior */ + enum multicast_mode ipv4_multicast_mode; + /* * Time for when we sweep the rib from old routes */ @@ -153,6 +166,10 @@ static inline struct zebra_vrf *zebra_vrf_get_evpn(void) : zebra_vrf_lookup_by_id(VRF_DEFAULT); } +extern void multicast_mode_ipv4_set(enum multicast_mode mode); + +extern enum multicast_mode multicast_mode_ipv4_get(void); + #ifdef __cplusplus } #endif diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 6343054943..fdf0cbc693 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -29,6 +29,7 @@ #include "vty.h" #include "zebra/zebra_router.h" +#include "zebra/rtadv.h" #include "zebra/debug.h" #include "zebra/zapi_msg.h" #include "zebra/rib.h" @@ -119,6 +120,10 @@ static int zebra_vrf_enable(struct vrf *vrf) zvrf->zns = zebra_ns_lookup((ns_id_t)vrf->vrf_id); else zvrf->zns = zebra_ns_lookup(NS_DEFAULT); +#if defined(HAVE_RTADV) + rtadv_init(zvrf); +#endif + /* Inform clients that the VRF is now active. This is an * add for the clients. */ @@ -161,6 +166,10 @@ static int zebra_vrf_disable(struct vrf *vrf) /* Stop any VxLAN-EVPN processing. */ zebra_vxlan_vrf_disable(zvrf); +#if defined(HAVE_RTADV) + rtadv_terminate(zvrf); +#endif + /* Inform clients that the VRF is now inactive. This is a * delete for the clients. */ diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index febaf3c844..972fe381cc 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -169,6 +169,10 @@ struct zebra_vrf { uint64_t lsp_removals_queued; uint64_t lsp_installs; uint64_t lsp_removals; + +#if defined(HAVE_RTADV) + struct rtadv rtadv; +#endif /* HAVE_RTADV */ }; #define PROTO_RM_NAME(zvrf, afi, rtype) zvrf->proto_rm[afi][rtype].name #define NHT_RM_NAME(zvrf, afi, rtype) zvrf->nht_rm[afi][rtype].name diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index f2f8a2a059..077c1ff8f0 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -839,9 +839,9 @@ static void zvni_print_neigh_hash(struct hash_bucket *bucket, void *ctxt) return; if (json_vni == NULL) { - vty_out(vty, "%*s %-6s %-8s %-17s\n", + vty_out(vty, "%*s %-6s %-8s %-17s %u/%u\n", -wctx->addr_width, buf2, "local", - state_str, buf1); + state_str, buf1, n->loc_seq, n->rem_seq); } else { json_object_string_add(json_row, "type", "local"); json_object_string_add(json_row, "state", state_str); @@ -875,9 +875,9 @@ static void zvni_print_neigh_hash(struct hash_bucket *bucket, void *ctxt) "%*s %-6s %-8s %-17s %-21s\n", -wctx->addr_width, "Neighbor", "Type", "State", "MAC", "Remote VTEP"); - vty_out(vty, "%*s %-6s %-8s %-17s %-21s\n", + vty_out(vty, "%*s %-6s %-8s %-17s %-21s %u/%u\n", -wctx->addr_width, buf2, "remote", state_str, - buf1, inet_ntoa(n->r_vtep_ip)); + buf1, inet_ntoa(n->r_vtep_ip), n->loc_seq, n->rem_seq); } else { json_object_string_add(json_row, "type", "remote"); json_object_string_add(json_row, "state", state_str); @@ -987,9 +987,9 @@ static void zvni_print_neigh_hash_all_vni(struct hash_bucket *bucket, hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx); if (json == NULL) { - vty_out(vty, "%*s %-6s %-8s %-17s %-21s\n", + vty_out(vty, "%*s %-6s %-8s %-17s %-21s %s\n", -wctx.addr_width, "IP", "Type", - "State", "MAC", "Remote VTEP"); + "State", "MAC", "Remote VTEP", "Seq #'s"); } if (print_dup) hash_iterate(zvni->neigh_table, zvni_print_dad_neigh_hash, diff --git a/zebra/zserv.c b/zebra/zserv.c index fbb5af875d..49fb302ba8 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -682,6 +682,8 @@ static int zserv_handle_client_fail(struct thread *thread) static struct zserv *zserv_client_create(int sock) { struct zserv *client; + size_t stream_size = + MAX(ZEBRA_MAX_PACKET_SIZ, sizeof(struct zapi_route)); int i; afi_t afi; @@ -691,8 +693,8 @@ static struct zserv *zserv_client_create(int sock) client->sock = sock; client->ibuf_fifo = stream_fifo_new(); client->obuf_fifo = stream_fifo_new(); - client->ibuf_work = stream_new(ZEBRA_MAX_PACKET_SIZ); - client->obuf_work = stream_new(ZEBRA_MAX_PACKET_SIZ); + client->ibuf_work = stream_new(stream_size); + client->obuf_work = stream_new(stream_size); pthread_mutex_init(&client->ibuf_mtx, NULL); pthread_mutex_init(&client->obuf_mtx, NULL); client->wb = buffer_new(0); |
