diff options
32 files changed, 505 insertions, 123 deletions
diff --git a/bgpd/bgp_mpath.c b/bgpd/bgp_mpath.c index ff5cfe05fb..37639f4bce 100644 --- a/bgpd/bgp_mpath.c +++ b/bgpd/bgp_mpath.c @@ -616,7 +616,8 @@ void bgp_path_info_mpath_update(struct bgp_dest *dest, all_paths_lb = false; if (debug) { bgp_path_info_path_with_addpath_rx_str( - cur_mpath, path_buf); + cur_mpath, path_buf, + sizeof(path_buf)); zlog_debug( "%pRN: %s is still multipath, cur count %d", bgp_dest_to_rnode(dest), @@ -626,7 +627,8 @@ void bgp_path_info_mpath_update(struct bgp_dest *dest, mpath_changed = 1; if (debug) { bgp_path_info_path_with_addpath_rx_str( - cur_mpath, path_buf); + cur_mpath, path_buf, + sizeof(path_buf)); zlog_debug( "%pRN: remove mpath %s nexthop %s, cur count %d", bgp_dest_to_rnode(dest), @@ -660,7 +662,7 @@ void bgp_path_info_mpath_update(struct bgp_dest *dest, mpath_changed = 1; if (debug) { bgp_path_info_path_with_addpath_rx_str( - cur_mpath, path_buf); + cur_mpath, path_buf, sizeof(path_buf)); zlog_debug( "%pRN: remove mpath %s nexthop %s, cur count %d", bgp_dest_to_rnode(dest), path_buf, @@ -710,7 +712,8 @@ void bgp_path_info_mpath_update(struct bgp_dest *dest, all_paths_lb = false; if (debug) { bgp_path_info_path_with_addpath_rx_str( - new_mpath, path_buf); + new_mpath, path_buf, + sizeof(path_buf)); zlog_debug( "%pRN: add mpath %s nexthop %s, cur count %d", bgp_dest_to_rnode(dest), diff --git a/bgpd/bgp_nb_config.c b/bgpd/bgp_nb_config.c index a3f58db88f..721ce5b5c6 100644 --- a/bgpd/bgp_nb_config.c +++ b/bgpd/bgp_nb_config.c @@ -69,7 +69,7 @@ int bgp_router_create(struct nb_cb_create_args *args) { const struct lyd_node *vrf_dnode; struct bgp *bgp; - struct vrf *vrf; + const char *vrf_name; const char *name = NULL; as_t as; enum bgp_instance_type inst_type; @@ -87,12 +87,12 @@ int bgp_router_create(struct nb_cb_create_args *args) case NB_EV_APPLY: vrf_dnode = yang_dnode_get_parent(args->dnode, "control-plane-protocol"); - vrf = nb_running_get_entry(vrf_dnode, NULL, true); + vrf_name = yang_dnode_get_string(vrf_dnode, "./vrf"); - if (strmatch(vrf->name, VRF_DEFAULT_NAME)) { + if (strmatch(vrf_name, VRF_DEFAULT_NAME)) { name = NULL; } else { - name = vrf->name; + name = vrf_name; inst_type = BGP_INSTANCE_TYPE_VRF; } diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 0f135985ed..18a0b3fb7d 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -525,13 +525,14 @@ static uint32_t bgp_med_value(struct attr *attr, struct bgp *bgp) } } -void bgp_path_info_path_with_addpath_rx_str(struct bgp_path_info *pi, char *buf) +void bgp_path_info_path_with_addpath_rx_str(struct bgp_path_info *pi, char *buf, + size_t buf_len) { if (pi->addpath_rx_id) - sprintf(buf, "path %s (addpath rxid %d)", pi->peer->host, - pi->addpath_rx_id); + snprintf(buf, buf_len, "path %s (addpath rxid %d)", + pi->peer->host, pi->addpath_rx_id); else - sprintf(buf, "path %s", pi->peer->host); + snprintf(buf, buf_len, "path %s", pi->peer->host); } /* Compare two bgp route entity. If 'new' is preferable over 'exist' return 1. @@ -582,7 +583,8 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (debug) - bgp_path_info_path_with_addpath_rx_str(new, new_buf); + bgp_path_info_path_with_addpath_rx_str(new, new_buf, + sizeof(new_buf)); if (exist == NULL) { *reason = bgp_path_selection_first; @@ -593,7 +595,8 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (debug) { - bgp_path_info_path_with_addpath_rx_str(exist, exist_buf); + bgp_path_info_path_with_addpath_rx_str(exist, exist_buf, + sizeof(exist_buf)); zlog_debug("%s: Comparing %s flags 0x%x with %s flags 0x%x", pfx_buf, new_buf, new->flags, exist_buf, exist->flags); @@ -621,10 +624,10 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, prefix2str( bgp_dest_get_prefix(new->net), pfx_buf, sizeof(*pfx_buf) * PREFIX2STR_BUFFER); - bgp_path_info_path_with_addpath_rx_str(new, - new_buf); bgp_path_info_path_with_addpath_rx_str( - exist, exist_buf); + new, new_buf, sizeof(new_buf)); + bgp_path_info_path_with_addpath_rx_str( + exist, exist_buf, sizeof(exist_buf)); } if (newattr->sticky && !existattr->sticky) { @@ -2348,7 +2351,7 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_dest *dest, if (debug) { bgp_path_info_path_with_addpath_rx_str( - new_select, path_buf); + new_select, path_buf, sizeof(path_buf)); zlog_debug( "%pBD: %s is the bestpath from AS %u", dest, path_buf, @@ -2422,8 +2425,8 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_dest *dest, */ if (debug) { if (new_select) - bgp_path_info_path_with_addpath_rx_str(new_select, - path_buf); + bgp_path_info_path_with_addpath_rx_str( + new_select, path_buf, sizeof(path_buf)); else snprintf(path_buf, sizeof(path_buf), "NONE"); zlog_debug( @@ -2438,7 +2441,7 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_dest *dest, if (debug) bgp_path_info_path_with_addpath_rx_str( - pi, path_buf); + pi, path_buf, sizeof(path_buf)); if (pi == new_select) { if (debug) @@ -6389,7 +6392,8 @@ DEFPY_YANG (bgp_network, bgp_network_cmd, int ret; ret = netmask_str2prefix_str(address_str, netmask_str, - addr_prefix_str); + addr_prefix_str, + sizeof(addr_prefix_str)); if (!ret) { vty_out(vty, "%% Inconsistent address and mask\n"); return CMD_WARNING_CONFIG_FAILED; @@ -7780,7 +7784,8 @@ DEFPY_YANG( char prefix_buf[PREFIX2STR_BUFFER]; if (addr_str) { - if (netmask_str2prefix_str(addr_str, mask_str, prefix_buf) + if (netmask_str2prefix_str(addr_str, mask_str, prefix_buf, + sizeof(prefix_buf)) == 0) { vty_out(vty, "%% Inconsistent address and mask\n"); return CMD_WARNING_CONFIG_FAILED; @@ -14398,7 +14403,7 @@ DEFUN (clear_ip_bgp_dampening_address_mask, char prefix_str[BUFSIZ]; ret = netmask_str2prefix_str(argv[idx_ipv4]->arg, argv[idx_ipv4_2]->arg, - prefix_str); + prefix_str, sizeof(prefix_str)); if (!ret) { vty_out(vty, "%% Inconsistent address and mask\n"); return CMD_WARNING; diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 1060d2e60d..766e5ade92 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -598,7 +598,7 @@ extern void bgp_path_info_set_flag(struct bgp_dest *dest, extern void bgp_path_info_unset_flag(struct bgp_dest *dest, struct bgp_path_info *path, uint32_t flag); extern void bgp_path_info_path_with_addpath_rx_str(struct bgp_path_info *pi, - char *buf); + char *buf, size_t buf_len); extern int bgp_nlri_parse_ip(struct peer *, struct attr *, struct bgp_nlri *); diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 3dc2cfbd5c..b7f3289ffc 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -4315,13 +4315,15 @@ DEFUN (match_community, int idx_comm_list = 2; int ret; char *argstr; + size_t argstr_len; if (argc == 4) { - argstr = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, - strlen(argv[idx_comm_list]->arg) - + strlen("exact-match") + 2); + argstr_len = strlen(argv[idx_comm_list]->arg) + + strlen("exact-match") + 2; + argstr = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, argstr_len); - sprintf(argstr, "%s exact-match", argv[idx_comm_list]->arg); + snprintf(argstr, argstr_len, "%s exact-match", + argv[idx_comm_list]->arg); } else argstr = argv[idx_comm_list]->arg; @@ -4362,13 +4364,15 @@ DEFUN (match_lcommunity, int idx_lcomm_list = 2; int ret; char *argstr; + size_t argstr_len; if (argc == 4) { - argstr = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, - strlen(argv[idx_lcomm_list]->arg) - + strlen("exact-match") + 2); + argstr_len = strlen(argv[idx_lcomm_list]->arg) + + strlen("exact-match") + 2; + argstr = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, argstr_len); - sprintf(argstr, "%s exact-match", argv[idx_lcomm_list]->arg); + snprintf(argstr, argstr_len, "%s exact-match", + argv[idx_lcomm_list]->arg); } else argstr = argv[idx_lcomm_list]->arg; @@ -5252,6 +5256,7 @@ DEFUN (set_aggregator_as, int ret; struct in_addr address; char *argstr; + size_t argstr_len; ret = inet_aton(argv[idx_ipv4]->arg, &address); if (ret == 0) { @@ -5259,11 +5264,12 @@ DEFUN (set_aggregator_as, return CMD_WARNING_CONFIG_FAILED; } - argstr = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, - strlen(argv[idx_number]->arg) - + strlen(argv[idx_ipv4]->arg) + 2); + argstr_len = + strlen(argv[idx_number]->arg) + strlen(argv[idx_ipv4]->arg) + 2; + argstr = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, argstr_len); - sprintf(argstr, "%s %s", argv[idx_number]->arg, argv[idx_ipv4]->arg); + snprintf(argstr, argstr_len, "%s %s", argv[idx_number]->arg, + argv[idx_ipv4]->arg); ret = generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), "aggregator as", argstr); @@ -5289,6 +5295,7 @@ DEFUN (no_set_aggregator_as, int ret; struct in_addr address; char *argstr; + size_t argstr_len; if (argc <= idx_asn) return generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), @@ -5300,11 +5307,11 @@ DEFUN (no_set_aggregator_as, return CMD_WARNING_CONFIG_FAILED; } - argstr = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, - strlen(argv[idx_asn]->arg) + strlen(argv[idx_ip]->arg) - + 2); + argstr_len = strlen(argv[idx_asn]->arg) + strlen(argv[idx_ip]->arg) + 2; + argstr = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, argstr_len); - sprintf(argstr, "%s %s", argv[idx_asn]->arg, argv[idx_ip]->arg); + snprintf(argstr, argstr_len, "%s %s", argv[idx_asn]->arg, + argv[idx_ip]->arg); ret = generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), "aggregator as", argstr); diff --git a/doc/developer/workflow.rst b/doc/developer/workflow.rst index 861d87b998..71e2b00448 100644 --- a/doc/developer/workflow.rst +++ b/doc/developer/workflow.rst @@ -563,7 +563,7 @@ In general, code submitted into FRR will be rejected if it uses unsafe programming practices. While there is no enforced overall ruleset, the following requirements have achieved consensus: -- ``strcpy``, ``strcat`` and ``sprintf`` are inacceptable without exception. +- ``strcpy``, ``strcat`` and ``sprintf`` are unacceptable without exception. Use ``strlcpy``, ``strlcat`` and ``snprintf`` instead. (Rationale: even if you know the operation cannot overflow the buffer, a future code change may inadvertedly introduce an overflow.) diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index 6d2303817b..a17d9a6ae2 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -691,8 +691,8 @@ static void lsp_set_time(struct isis_lsp *lsp) stream_putw_at(lsp->pdu, 10, lsp->hdr.rem_lifetime); } -void lspid_print(uint8_t *lsp_id, char *dest, char dynhost, char frag, - struct isis *isis) +void lspid_print(uint8_t *lsp_id, char *dest, size_t dest_len, char dynhost, + char frag, struct isis *isis) { struct isis_dynhn *dyn = NULL; char id[SYSID_STRLEN]; @@ -710,10 +710,10 @@ void lspid_print(uint8_t *lsp_id, char *dest, char dynhost, char frag, memcpy(id, sysid_print(lsp_id), 15); if (frag) - sprintf(dest, "%s.%02x-%02x", id, LSP_PSEUDO_ID(lsp_id), - LSP_FRAGMENT(lsp_id)); + snprintf(dest, dest_len, "%s.%02x-%02x", id, + LSP_PSEUDO_ID(lsp_id), LSP_FRAGMENT(lsp_id)); else - sprintf(dest, "%s.%02x", id, LSP_PSEUDO_ID(lsp_id)); + snprintf(dest, dest_len, "%s.%02x", id, LSP_PSEUDO_ID(lsp_id)); } /* Convert the lsp attribute bits to attribute string */ @@ -747,7 +747,7 @@ void lsp_print(struct isis_lsp *lsp, struct vty *vty, char dynhost, char age_out[8]; char b[200]; - lspid_print(lsp->hdr.lsp_id, LSPid, dynhost, 1, isis); + lspid_print(lsp->hdr.lsp_id, LSPid, sizeof(LSPid), dynhost, 1, isis); vty_out(vty, "%-21s%c ", LSPid, lsp->own_lsp ? '*' : ' '); vty_out(vty, "%5hu ", lsp->hdr.pdu_len); vty_out(vty, "0x%08x ", lsp->hdr.seqno); diff --git a/isisd/isis_lsp.h b/isisd/isis_lsp.h index 0783036e49..896d957607 100644 --- a/isisd/isis_lsp.h +++ b/isisd/isis_lsp.h @@ -116,8 +116,8 @@ void lsp_update(struct isis_lsp *lsp, struct isis_lsp_hdr *hdr, struct isis_tlvs *tlvs, struct stream *stream, struct isis_area *area, int level, bool confusion); void lsp_inc_seqno(struct isis_lsp *lsp, uint32_t seqno); -void lspid_print(uint8_t *lsp_id, char *dest, char dynhost, char frag, - struct isis *isis); +void lspid_print(uint8_t *lsp_id, char *dest, size_t dest_len, char dynhost, + char frag, struct isis *isis); void lsp_print(struct isis_lsp *lsp, struct vty *vty, char dynhost, struct isis *isis); void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, char dynhost, diff --git a/isisd/isis_misc.c b/isisd/isis_misc.c index 6e9cbaf98e..d3d081d376 100644 --- a/isisd/isis_misc.c +++ b/isisd/isis_misc.c @@ -59,27 +59,30 @@ char nlpidstring[30]; const char *isonet_print(const uint8_t *from, int len) { int i = 0; - char *pos = isonet; + char tbuf[4]; + isonet[0] = '\0'; if (!from) return "unknown"; while (i < len) { if (i & 1) { - sprintf(pos, "%02x", *(from + i)); - pos += 2; + snprintf(tbuf, sizeof(tbuf), "%02x", *(from + i)); + strlcat(isonet, tbuf, sizeof(isonet)); } else { if (i == (len - 1)) { /* No dot at the end of address */ - sprintf(pos, "%02x", *(from + i)); - pos += 2; + snprintf(tbuf, sizeof(tbuf), "%02x", + *(from + i)); + strlcat(isonet, tbuf, sizeof(isonet)); } else { - sprintf(pos, "%02x.", *(from + i)); - pos += 3; + snprintf(tbuf, sizeof(tbuf), "%02x.", + *(from + i)); + strlcat(isonet, tbuf, sizeof(isonet)); } } i++; } - *(pos) = '\0'; + return isonet; } @@ -202,17 +205,18 @@ const char *nlpid2str(uint8_t nlpid) char *nlpid2string(struct nlpids *nlpids) { - char *pos = nlpidstring; int i; + char tbuf[256]; + nlpidstring[0] = '\0'; for (i = 0; i < nlpids->count; i++) { - pos += sprintf(pos, "%s", nlpid2str(nlpids->nlpids[i])); + snprintf(tbuf, sizeof(tbuf), "%s", + nlpid2str(nlpids->nlpids[i])); + strlcat(nlpidstring, tbuf, sizeof(nlpidstring)); if (nlpids->count - i > 1) - pos += sprintf(pos, ", "); + strlcat(nlpidstring, ", ", sizeof(nlpidstring)); } - *(pos) = '\0'; - return nlpidstring; } @@ -359,34 +363,47 @@ const char *isis_format_id(const uint8_t *id, size_t len) const char *time2string(uint32_t time) { - char *pos = datestring; uint32_t rest; + char tbuf[32]; + datestring[0] = '\0'; if (time == 0) return "-"; - if (time / SECS_PER_YEAR) - pos += sprintf(pos, "%uY", time / SECS_PER_YEAR); + if (time / SECS_PER_YEAR) { + snprintf(tbuf, sizeof(tbuf), "%uY", time / SECS_PER_YEAR); + strlcat(datestring, tbuf, sizeof(datestring)); + } rest = time % SECS_PER_YEAR; - if (rest / SECS_PER_MONTH) - pos += sprintf(pos, "%uM", rest / SECS_PER_MONTH); + if (rest / SECS_PER_MONTH) { + snprintf(tbuf, sizeof(tbuf), "%uM", rest / SECS_PER_MONTH); + strlcat(datestring, tbuf, sizeof(datestring)); + } rest = rest % SECS_PER_MONTH; - if (rest / SECS_PER_WEEK) - pos += sprintf(pos, "%uw", rest / SECS_PER_WEEK); + if (rest / SECS_PER_WEEK) { + snprintf(tbuf, sizeof(tbuf), "%uw", rest / SECS_PER_WEEK); + strlcat(datestring, tbuf, sizeof(datestring)); + } rest = rest % SECS_PER_WEEK; - if (rest / SECS_PER_DAY) - pos += sprintf(pos, "%ud", rest / SECS_PER_DAY); + if (rest / SECS_PER_DAY) { + snprintf(tbuf, sizeof(tbuf), "%ud", rest / SECS_PER_DAY); + strlcat(datestring, tbuf, sizeof(datestring)); + } rest = rest % SECS_PER_DAY; - if (rest / SECS_PER_HOUR) - pos += sprintf(pos, "%uh", rest / SECS_PER_HOUR); + if (rest / SECS_PER_HOUR) { + snprintf(tbuf, sizeof(tbuf), "%uh", rest / SECS_PER_HOUR); + strlcat(datestring, tbuf, sizeof(datestring)); + } rest = rest % SECS_PER_HOUR; - if (rest / SECS_PER_MINUTE) - pos += sprintf(pos, "%um", rest / SECS_PER_MINUTE); + if (rest / SECS_PER_MINUTE) { + snprintf(tbuf, sizeof(tbuf), "%um", rest / SECS_PER_MINUTE); + strlcat(datestring, tbuf, sizeof(datestring)); + } rest = rest % SECS_PER_MINUTE; - if (rest) - pos += sprintf(pos, "%us", rest); - - *(pos) = 0; + if (rest) { + snprintf(tbuf, sizeof(tbuf), "%us", rest); + strlcat(datestring, tbuf, sizeof(datestring)); + } return datestring; } diff --git a/isisd/isis_vty_fabricd.c b/isisd/isis_vty_fabricd.c index d0a411a8db..6055984195 100644 --- a/isisd/isis_vty_fabricd.c +++ b/isisd/isis_vty_fabricd.c @@ -118,7 +118,7 @@ static void lsp_print_flooding(struct vty *vty, struct isis_lsp *lsp, char lspid[255]; char buf[MONOTIME_STRLEN]; - lspid_print(lsp->hdr.lsp_id, lspid, true, true, isis); + lspid_print(lsp->hdr.lsp_id, lspid, sizeof(lspid), true, true, isis); vty_out(vty, "Flooding information for %s\n", lspid); if (!lsp->flooding_neighbors[TX_LSP_NORMAL]) { diff --git a/lib/northbound.c b/lib/northbound.c index ecfa2c9d11..224951b22b 100644 --- a/lib/northbound.c +++ b/lib/northbound.c @@ -185,6 +185,25 @@ struct nb_node *nb_node_find(const char *xpath) return snode->priv; } +void nb_node_set_dependency_cbs(const char *dependency_xpath, + const char *dependant_xpath, + struct nb_dependency_callbacks *cbs) +{ + struct nb_node *dependency = nb_node_find(dependency_xpath); + struct nb_node *dependant = nb_node_find(dependant_xpath); + + if (!dependency || !dependant) + return; + + dependency->dep_cbs.get_dependant_xpath = cbs->get_dependant_xpath; + dependant->dep_cbs.get_dependency_xpath = cbs->get_dependency_xpath; +} + +bool nb_node_has_dependency(struct nb_node *node) +{ + return node->dep_cbs.get_dependency_xpath != NULL; +} + static int nb_node_validate_cb(const struct nb_node *nb_node, enum nb_operation operation, int callback_implemented, bool optional) @@ -532,8 +551,9 @@ int nb_candidate_edit(struct nb_config *candidate, const struct yang_data *previous, const struct yang_data *data) { - struct lyd_node *dnode; + struct lyd_node *dnode, *dep_dnode; char xpath_edit[XPATH_MAXLEN]; + char dep_xpath[XPATH_MAXLEN]; /* Use special notation for leaf-lists (RFC 6020, section 9.13.5). */ if (nb_node->snode->nodetype == LYS_LEAFLIST) @@ -549,9 +569,33 @@ int nb_candidate_edit(struct nb_config *candidate, dnode = lyd_new_path(candidate->dnode, ly_native_ctx, xpath_edit, (void *)data->value, 0, LYD_PATH_OPT_UPDATE); - if (!dnode && ly_errno) { - flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed", - __func__); + if (dnode) { + /* + * create dependency + * + * dnode returned by the lyd_new_path may be from a + * different schema, so we need to update the nb_node + */ + nb_node = dnode->schema->priv; + if (nb_node->dep_cbs.get_dependency_xpath) { + nb_node->dep_cbs.get_dependency_xpath( + dnode, dep_xpath); + + ly_errno = 0; + dep_dnode = lyd_new_path(candidate->dnode, + ly_native_ctx, + dep_xpath, NULL, 0, + LYD_PATH_OPT_UPDATE); + if (!dep_dnode && ly_errno) { + flog_warn(EC_LIB_LIBYANG, + "%s: lyd_new_path(%s) failed", + __func__, dep_xpath); + return NB_ERR; + } + } + } else if (ly_errno) { + flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path(%s) failed", + __func__, xpath_edit); return NB_ERR; } break; @@ -563,6 +607,14 @@ int nb_candidate_edit(struct nb_config *candidate, * whether to ignore it or not. */ return NB_ERR_NOT_FOUND; + /* destroy dependant */ + if (nb_node->dep_cbs.get_dependant_xpath) { + nb_node->dep_cbs.get_dependant_xpath(dnode, dep_xpath); + + dep_dnode = yang_dnode_get(candidate->dnode, dep_xpath); + if (dep_dnode) + lyd_free(dep_dnode); + } lyd_free(dnode); break; case NB_OP_MOVE: diff --git a/lib/northbound.h b/lib/northbound.h index 8dd6b4c337..3e1342f985 100644 --- a/lib/northbound.h +++ b/lib/northbound.h @@ -509,6 +509,11 @@ struct nb_callbacks { void (*cli_show_end)(struct vty *vty, struct lyd_node *dnode); }; +struct nb_dependency_callbacks { + void (*get_dependant_xpath)(const struct lyd_node *dnode, char *xpath); + void (*get_dependency_xpath)(const struct lyd_node *dnode, char *xpath); +}; + /* * Northbound-specific data that is allocated for each schema node of the native * YANG modules. @@ -523,6 +528,8 @@ struct nb_node { /* Priority - lower priorities are processed first. */ uint32_t priority; + struct nb_dependency_callbacks dep_cbs; + /* Callbacks implemented for this node. */ struct nb_callbacks cbs; @@ -722,6 +729,12 @@ void nb_nodes_delete(void); */ extern struct nb_node *nb_node_find(const char *xpath); +extern void nb_node_set_dependency_cbs(const char *dependency_xpath, + const char *dependant_xpath, + struct nb_dependency_callbacks *cbs); + +bool nb_node_has_dependency(struct nb_node *node); + /* * Create a new northbound configuration. * diff --git a/lib/prefix.c b/lib/prefix.c index 663a87afde..c98e0c1c72 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -1160,7 +1160,7 @@ in_addr_t ipv4_broadcast_addr(in_addr_t hostaddr, int masklen) ex.) "1.1.0.0" "255.255.0.0" => "1.1.0.0/16" ex.) "1.0.0.0" NULL => "1.0.0.0/8" */ int netmask_str2prefix_str(const char *net_str, const char *mask_str, - char *prefix_str) + char *prefix_str, size_t prefix_str_len) { struct in_addr network; struct in_addr mask; @@ -1193,7 +1193,7 @@ int netmask_str2prefix_str(const char *net_str, const char *mask_str, return 0; } - sprintf(prefix_str, "%s/%d", net_str, prefixlen); + snprintf(prefix_str, prefix_str_len, "%s/%d", net_str, prefixlen); return 1; } diff --git a/lib/prefix.h b/lib/prefix.h index b7fdc26369..b2f3b0592f 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -491,7 +491,7 @@ extern void masklen2ip(const int, struct in_addr *); * special treatment for /31 according to RFC3021 section 3.3 */ extern in_addr_t ipv4_broadcast_addr(in_addr_t hostaddr, int masklen); -extern int netmask_str2prefix_str(const char *, const char *, char *); +extern int netmask_str2prefix_str(const char *, const char *, char *, size_t); extern struct prefix_ipv6 *prefix_ipv6_new(void); extern void prefix_ipv6_free(struct prefix_ipv6 **p); diff --git a/lib/routing_nb.h b/lib/routing_nb.h index d1b59ea29e..ffba631a10 100644 --- a/lib/routing_nb.h +++ b/lib/routing_nb.h @@ -15,10 +15,17 @@ int routing_control_plane_protocols_control_plane_protocol_destroy( #define FRR_ROUTING_KEY_XPATH \ "/frr-routing:routing/control-plane-protocols/" \ "control-plane-protocol[type='%s'][name='%s'][vrf='%s']" + +#define FRR_ROUTING_KEY_XPATH_VRF \ + "/frr-routing:routing/control-plane-protocols/" \ + "control-plane-protocol[vrf='%s']" + /* * callbacks for routing to handle configuration events * based on the control plane protocol */ DECLARE_HOOK(routing_conf_event, (struct nb_cb_create_args *args), (args)) +void routing_control_plane_protocols_register_vrf_dependency(void); + #endif /* _FRR_ROUTING_NB_H_ */ diff --git a/lib/routing_nb_config.c b/lib/routing_nb_config.c index b789e8494e..17698d2b87 100644 --- a/lib/routing_nb_config.c +++ b/lib/routing_nb_config.c @@ -45,15 +45,21 @@ int routing_control_plane_protocols_control_plane_protocol_create( case NB_EV_ABORT: break; case NB_EV_APPLY: - vrfname = yang_dnode_get_string(args->dnode, "./vrf"); - vrf = vrf_lookup_by_name(vrfname); - vrf = vrf ? vrf : vrf_get(VRF_UNKNOWN, vrfname); - if (!vrf) { - flog_warn(EC_LIB_NB_CB_CONFIG_APPLY, - "vrf creation %s failed", vrfname); - return NB_ERR; + /* + * If the daemon relies on the VRF pointer stored in this + * dnode, then it should register the dependency between this + * module and the VRF module using + * routing_control_plane_protocols_register_vrf_dependency. + * If such dependency is not registered, then nothing is + * stored in the dnode. If the dependency is registered, + * find the vrf and store the pointer. + */ + if (nb_node_has_dependency(args->dnode->schema->priv)) { + vrfname = yang_dnode_get_string(args->dnode, "./vrf"); + vrf = vrf_lookup_by_name(vrfname); + assert(vrf); + nb_running_set_entry(args->dnode, vrf); } - nb_running_set_entry(args->dnode, vrf); break; }; @@ -63,12 +69,45 @@ int routing_control_plane_protocols_control_plane_protocol_create( int routing_control_plane_protocols_control_plane_protocol_destroy( struct nb_cb_destroy_args *args) { - struct vrf *vrf __attribute__((unused)); - if (args->event != NB_EV_APPLY) return NB_OK; - vrf = nb_running_unset_entry(args->dnode); + /* + * If dependency on VRF module is registered, then VRF + * pointer was stored and must be cleared. + */ + if (nb_node_has_dependency(args->dnode->schema->priv)) + nb_running_unset_entry(args->dnode); return NB_OK; } + +static void vrf_to_control_plane_protocol(const struct lyd_node *dnode, + char *xpath) +{ + const char *vrf; + + vrf = yang_dnode_get_string(dnode, "./name"); + + snprintf(xpath, XPATH_MAXLEN, FRR_ROUTING_KEY_XPATH_VRF, vrf); +} + +static void control_plane_protocol_to_vrf(const struct lyd_node *dnode, + char *xpath) +{ + const char *vrf; + + vrf = yang_dnode_get_string(dnode, "./vrf"); + + snprintf(xpath, XPATH_MAXLEN, FRR_VRF_KEY_XPATH, vrf); +} + +void routing_control_plane_protocols_register_vrf_dependency(void) +{ + struct nb_dependency_callbacks cbs; + + cbs.get_dependant_xpath = vrf_to_control_plane_protocol; + cbs.get_dependency_xpath = control_plane_protocol_to_vrf; + + nb_node_set_dependency_cbs(FRR_VRF_XPATH, FRR_ROUTING_XPATH, &cbs); +} @@ -686,8 +686,8 @@ int vrf_handler_create(struct vty *vty, const char *vrfname, } if (vty) { - snprintf(xpath_list, sizeof(xpath_list), - "/frr-vrf:lib/vrf[name='%s']", vrfname); + snprintf(xpath_list, sizeof(xpath_list), FRR_VRF_KEY_XPATH, + vrfname); nb_cli_enqueue_change(vty, xpath_list, NB_OP_CREATE, NULL); ret = nb_cli_apply_changes(vty, xpath_list); @@ -821,8 +821,7 @@ DEFUN_YANG (no_vrf, return CMD_WARNING_CONFIG_FAILED; } - snprintf(xpath_list, sizeof(xpath_list), "/frr-vrf:lib/vrf[name='%s']", - vrfname); + snprintf(xpath_list, sizeof(xpath_list), FRR_VRF_KEY_XPATH, vrfname); nb_cli_enqueue_change(vty, xpath_list, NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, xpath_list); @@ -52,6 +52,9 @@ enum { IFLA_VRF_UNSPEC, IFLA_VRF_TABLE, __IFLA_VRF_MAX }; #define VRF_ALL_CMD_HELP_STR "Specify the VRF\nAll VRFs\n" #define VRF_FULL_CMD_HELP_STR "Specify the VRF\nThe VRF name\nAll VRFs\n" +#define FRR_VRF_XPATH "/frr-vrf:lib/vrf" +#define FRR_VRF_KEY_XPATH "/frr-vrf:lib/vrf[name='%s']" + /* * Pass some OS specific data up through * to the daemons @@ -2427,9 +2427,9 @@ bool vty_read_config(struct nb_config *config, const char *config_file, __func__, errno); goto tmp_free_and_out; } - tmp = XMALLOC(MTYPE_TMP, - strlen(cwd) + strlen(config_file) + 2); - sprintf(tmp, "%s/%s", cwd, config_file); + size_t tmp_len = strlen(cwd) + strlen(config_file) + 2; + tmp = XMALLOC(MTYPE_TMP, tmp_len); + snprintf(tmp, tmp_len, "%s/%s", cwd, config_file); fullpath = tmp; } else fullpath = config_file; diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c index abcdb40547..f6a246500b 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -1292,6 +1292,7 @@ static char *ospf6_inter_area_prefix_lsa_get_prefix_str(struct ospf6_lsa *lsa, { struct ospf6_inter_prefix_lsa *prefix_lsa; struct in6_addr in6; + char tbuf[16]; if (lsa != NULL) { prefix_lsa = @@ -1301,8 +1302,9 @@ static char *ospf6_inter_area_prefix_lsa_get_prefix_str(struct ospf6_lsa *lsa, ospf6_prefix_in6_addr(&in6, prefix_lsa, &prefix_lsa->prefix); if (buf) { inet_ntop(AF_INET6, &in6, buf, buflen); - sprintf(&buf[strlen(buf)], "/%d", - prefix_lsa->prefix.prefix_length); + snprintf(tbuf, sizeof(tbuf), "/%d", + prefix_lsa->prefix.prefix_length); + strlcat(buf, tbuf, buflen); } } diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index 3449f48267..d0c93dd577 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -1865,6 +1865,7 @@ static char *ospf6_as_external_lsa_get_prefix_str(struct ospf6_lsa *lsa, struct ospf6_as_external_lsa *external; struct in6_addr in6; int prefix_length = 0; + char tbuf[16]; if (lsa) { external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END( @@ -1885,9 +1886,11 @@ static char *ospf6_as_external_lsa_get_prefix_str(struct ospf6_lsa *lsa, } if (buf) { inet_ntop(AF_INET6, &in6, buf, buflen); - if (prefix_length) - sprintf(&buf[strlen(buf)], "/%d", - prefix_length); + if (prefix_length) { + snprintf(tbuf, sizeof(tbuf), "/%d", + prefix_length); + strlcat(buf, tbuf, buflen); + } } } return (buf); diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index 5394ba9786..2cffc3a397 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -84,7 +84,7 @@ static char *ospf6_router_lsa_get_nbr_id(struct ospf6_lsa *lsa, char *buf, sizeof(buf1)); inet_ntop(AF_INET, &lsdesc->neighbor_router_id, buf2, sizeof(buf2)); - sprintf(buf, "%s/%s", buf2, buf1); + snprintf(buf, buflen, "%s/%s", buf2, buf1); return buf; } @@ -866,6 +866,7 @@ static char *ospf6_intra_prefix_lsa_get_prefix_str(struct ospf6_lsa *lsa, struct in6_addr in6; int prefixnum, cnt = 0; struct ospf6_prefix *prefix; + char tbuf[16]; if (lsa) { intra_prefix_lsa = @@ -898,8 +899,9 @@ static char *ospf6_intra_prefix_lsa_get_prefix_str(struct ospf6_lsa *lsa, OSPF6_PREFIX_SPACE( prefix->prefix_length)); inet_ntop(AF_INET6, &in6, buf, buflen); - sprintf(&buf[strlen(buf)], "/%d", - prefix->prefix_length); + snprintf(tbuf, sizeof(tbuf), "/%d", + prefix->prefix_length); + strlcat(buf, tbuf, buflen); return (buf); } } diff --git a/pimd/pim_main.c b/pimd/pim_main.c index 9c11cc47d5..5a09e7a8ee 100644 --- a/pimd/pim_main.c +++ b/pimd/pim_main.c @@ -145,6 +145,8 @@ int main(int argc, char **argv, char **envp) hook_register(routing_conf_event, routing_control_plane_protocols_name_validate); + routing_control_plane_protocols_register_vrf_dependency(); + frr_config_fork(); #ifdef PIM_DEBUG_BYDEFAULT diff --git a/staticd/static_main.c b/staticd/static_main.c index ac8f8ff029..560814771d 100644 --- a/staticd/static_main.c +++ b/staticd/static_main.c @@ -164,6 +164,8 @@ int main(int argc, char **argv, char **envp) hook_register(routing_conf_event, routing_control_plane_protocols_name_validate); + routing_control_plane_protocols_register_vrf_dependency(); + snprintf(backup_config_file, sizeof(backup_config_file), "%s/zebra.conf", frr_sysconfdir); staticd_di.backup_config_file = backup_config_file; diff --git a/tests/topotests/bgp-aggregator-zero/__init__.py b/tests/topotests/bgp-aggregator-zero/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp-aggregator-zero/__init__.py diff --git a/tests/topotests/bgp-aggregator-zero/exabgp.env b/tests/topotests/bgp-aggregator-zero/exabgp.env new file mode 100644 index 0000000000..28e642360a --- /dev/null +++ b/tests/topotests/bgp-aggregator-zero/exabgp.env @@ -0,0 +1,53 @@ +[exabgp.api] +encoder = text +highres = false +respawn = false +socket = '' + +[exabgp.bgp] +openwait = 60 + +[exabgp.cache] +attributes = true +nexthops = true + +[exabgp.daemon] +daemonize = true +pid = '/var/run/exabgp/exabgp.pid' +user = 'exabgp' +##daemonize = false + +[exabgp.log] +all = false +configuration = true +daemon = true +destination = '/var/log/exabgp.log' +enable = true +level = INFO +message = false +network = true +packets = false +parser = false +processes = true +reactor = true +rib = false +routes = false +short = false +timers = false + +[exabgp.pdb] +enable = false + +[exabgp.profile] +enable = false +file = '' + +[exabgp.reactor] +speed = 1.0 + +[exabgp.tcp] +acl = false +bind = '' +delay = 0 +once = false +port = 179 diff --git a/tests/topotests/bgp-aggregator-zero/peer1/exabgp.cfg b/tests/topotests/bgp-aggregator-zero/peer1/exabgp.cfg new file mode 100644 index 0000000000..b3f25272d2 --- /dev/null +++ b/tests/topotests/bgp-aggregator-zero/peer1/exabgp.cfg @@ -0,0 +1,18 @@ +neighbor 10.0.0.1 { + router-id 10.0.0.2; + local-address 10.0.0.2; + local-as 65001; + peer-as 65534; + + static { + route 192.168.100.101/32 { + aggregator (0:10.0.0.2); + next-hop 10.0.0.2; + } + + route 192.168.100.102/32 { + aggregator (65001:10.0.0.2); + next-hop 10.0.0.2; + } + } +} diff --git a/tests/topotests/bgp-aggregator-zero/r1/bgpd.conf b/tests/topotests/bgp-aggregator-zero/r1/bgpd.conf new file mode 100644 index 0000000000..002a5c78c0 --- /dev/null +++ b/tests/topotests/bgp-aggregator-zero/r1/bgpd.conf @@ -0,0 +1,6 @@ +! +router bgp 65534 + no bgp ebgp-requires-policy + neighbor 10.0.0.2 remote-as external + neighbor 10.0.0.2 timers 3 10 +! diff --git a/tests/topotests/bgp-aggregator-zero/r1/zebra.conf b/tests/topotests/bgp-aggregator-zero/r1/zebra.conf new file mode 100644 index 0000000000..22a26ac610 --- /dev/null +++ b/tests/topotests/bgp-aggregator-zero/r1/zebra.conf @@ -0,0 +1,6 @@ +! +interface r1-eth0 + ip address 10.0.0.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp-aggregator-zero/test_bgp_aggregator_zero.py b/tests/topotests/bgp-aggregator-zero/test_bgp_aggregator_zero.py new file mode 100644 index 0000000000..0db47da3f2 --- /dev/null +++ b/tests/topotests/bgp-aggregator-zero/test_bgp_aggregator_zero.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2021 by +# Donatas Abraitis <donatas.abraitis@gmail.com> +# +# 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 if BGP UPDATE with AGGREGATOR AS attribute with value zero (0) +is continued to be processed, but AGGREGATOR attribute is discarded. +""" + +import os +import sys +import json +import time +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from mininet.topo import Topo + + +class BgpAggregatorAsnZero(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + r1 = tgen.add_router("r1") + peer1 = tgen.add_exabgp_peer( + "peer1", ip="10.0.0.2", defaultRoute="via 10.0.0.1" + ) + + switch = tgen.add_switch("s1") + switch.add_link(r1) + switch.add_link(peer1) + + +def setup_module(mod): + tgen = Topogen(BgpAggregatorAsnZero, mod.__name__) + tgen.start_topology() + + router = tgen.gears["r1"] + router.load_config(TopoRouter.RD_ZEBRA, os.path.join(CWD, "r1/zebra.conf")) + router.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "r1/bgpd.conf")) + router.start() + + peer = tgen.gears["peer1"] + peer.start(os.path.join(CWD, "peer1"), os.path.join(CWD, "exabgp.env")) + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_aggregator_zero(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_converge(): + output = json.loads( + tgen.gears["r1"].vtysh_cmd("show ip bgp neighbor 10.0.0.2 json") + ) + expected = { + "10.0.0.2": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}}, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert result is None, 'Failed bgp convergence in "{}"'.format(tgen.gears["r1"]) + + def _bgp_has_correct_aggregator_route_with_asn_0(): + output = json.loads( + tgen.gears["r1"].vtysh_cmd("show ip bgp 192.168.100.101/32 json") + ) + + if "aggregatorAs" in output["paths"][0].keys(): + return False + else: + return True + + assert ( + _bgp_has_correct_aggregator_route_with_asn_0() is True + ), 'Aggregator AS attribute with ASN 0 found in "{}"'.format(tgen.gears["r1"]) + + def _bgp_has_correct_aggregator_route_with_good_asn(): + output = json.loads( + tgen.gears["r1"].vtysh_cmd("show ip bgp 192.168.100.102/32 json") + ) + expected = {"paths": [{"aggregatorAs": 65001, "aggregatorId": "10.0.0.2"}]} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_has_correct_aggregator_route_with_good_asn) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert result is None, 'Aggregator AS attribute not found in "{}"'.format( + tgen.gears["r1"] + ) + + +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/zebra/irdp_interface.c b/zebra/irdp_interface.c index 2ab5fd3a4c..5352c6214d 100644 --- a/zebra/irdp_interface.c +++ b/zebra/irdp_interface.c @@ -93,10 +93,10 @@ static int irdp_if_delete(struct interface *ifp) return 0; } -static const char *inet_2a(uint32_t a, char *b) +static const char *inet_2a(uint32_t a, char *b, size_t b_len) { - sprintf(b, "%u.%u.%u.%u", (a)&0xFF, (a >> 8) & 0xFF, (a >> 16) & 0xFF, - (a >> 24) & 0xFF); + snprintf(b, b_len, "%u.%u.%u.%u", (a)&0xFF, (a >> 8) & 0xFF, + (a >> 16) & 0xFF, (a >> 24) & 0xFF); return b; } @@ -140,7 +140,8 @@ static int if_group(struct interface *ifp, int sock, uint32_t group, flog_err_sys(EC_LIB_SOCKET, "IRDP: %s can't setsockopt %s: %s", add_leave == IP_ADD_MEMBERSHIP ? "join group" : "leave group", - inet_2a(group, b1), safe_strerror(errno)); + inet_2a(group, b1, sizeof(b1)), + safe_strerror(errno)); return ret; } @@ -162,7 +163,8 @@ static int if_add_group(struct interface *ifp) if (irdp->flags & IF_DEBUG_MISC) zlog_debug("IRDP: Adding group %s for %s", - inet_2a(htonl(INADDR_ALLRTRS_GROUP), b1), ifp->name); + inet_2a(htonl(INADDR_ALLRTRS_GROUP), b1, sizeof(b1)), + ifp->name); return 0; } @@ -183,7 +185,8 @@ static int if_drop_group(struct interface *ifp) if (irdp->flags & IF_DEBUG_MISC) zlog_debug("IRDP: Leaving group %s for %s", - inet_2a(htonl(INADDR_ALLRTRS_GROUP), b1), ifp->name); + inet_2a(htonl(INADDR_ALLRTRS_GROUP), b1, sizeof(b1)), + ifp->name); return 0; } @@ -383,7 +386,8 @@ int irdp_config_write(struct vty *vty, struct interface *ifp) for (ALL_LIST_ELEMENTS_RO(irdp->AdvPrefList, node, adv)) vty_out(vty, " ip irdp address %s preference %d\n", - inet_2a(adv->ip.s_addr, b1), adv->pref); + inet_2a(adv->ip.s_addr, b1, sizeof(b1)), + adv->pref); vty_out(vty, " ip irdp holdtime %d\n", irdp->Lifetime); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 602805be3c..46a751ce69 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1132,8 +1132,8 @@ static int build_label_stack(struct mpls_label_stack *nh_label, if (IS_ZEBRA_DEBUG_KERNEL) { if (!num_labels) - sprintf(label_buf, "label %u", - nh_label->label[i]); + snprintf(label_buf, label_buf_size, "label %u", + nh_label->label[i]); else { snprintf(label_buf1, sizeof(label_buf1), "/%u", nh_label->label[i]); |
