diff options
126 files changed, 10141 insertions, 1961 deletions
diff --git a/bfdd/config.c b/bfdd/config.c index b4bd0daf15..4ae7bfdc08 100644 --- a/bfdd/config.c +++ b/bfdd/config.c @@ -243,17 +243,17 @@ static int parse_peer_config(struct json_object *jo, struct bfd_peer_cfg *bpc) } else if (strcmp(key, "receive-interval") == 0) { bpc->bpc_recvinterval = json_object_get_int64(jo_val); bpc->bpc_has_recvinterval = true; - log_debug(" receive-interval: %llu", + log_debug(" receive-interval: %" PRIu64, bpc->bpc_recvinterval); } else if (strcmp(key, "transmit-interval") == 0) { bpc->bpc_txinterval = json_object_get_int64(jo_val); bpc->bpc_has_txinterval = true; - log_debug(" transmit-interval: %llu", + log_debug(" transmit-interval: %" PRIu64, bpc->bpc_txinterval); } else if (strcmp(key, "echo-interval") == 0) { bpc->bpc_echointerval = json_object_get_int64(jo_val); bpc->bpc_has_echointerval = true; - log_debug(" echo-interval: %llu", + log_debug(" echo-interval: %" PRIu64, bpc->bpc_echointerval); } else if (strcmp(key, "create-only") == 0) { bpc->bpc_createonly = json_object_get_boolean(jo_val); diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 0bdd079e0b..221386e38d 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -898,14 +898,11 @@ struct attr *bgp_attr_default_set(struct attr *attr, uint8_t origin) } /* Create the attributes for an aggregate */ -struct attr *bgp_attr_aggregate_intern(struct bgp *bgp, uint8_t origin, - struct aspath *aspath, - struct community *community, - struct ecommunity *ecommunity, - struct lcommunity *lcommunity, - struct bgp_aggregate *aggregate, - uint8_t atomic_aggregate, - struct prefix *p) +struct attr *bgp_attr_aggregate_intern( + struct bgp *bgp, uint8_t origin, struct aspath *aspath, + struct community *community, struct ecommunity *ecommunity, + struct lcommunity *lcommunity, struct bgp_aggregate *aggregate, + uint8_t atomic_aggregate, const struct prefix *p) { struct attr attr; struct attr *new; @@ -1655,20 +1652,21 @@ static int bgp_attr_aggregator(struct bgp_attr_parser_args *args) else aggregator_as = stream_getw(peer->curr); + attr->aggregator_as = aggregator_as; + attr->aggregator_addr.s_addr = stream_get_ipv4(peer->curr); + + /* Set atomic aggregate flag. */ + attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AGGREGATOR); + /* Codification of AS 0 Processing */ if (aggregator_as == BGP_AS_ZERO) { flog_err(EC_BGP_ATTR_LEN, - "AGGREGATOR attribute is BGP_AS_ZERO(0)"); + "AGGREGATOR AS number is 0 for aspath: %s", + aspath_print(attr->aspath)); return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_MAL_AS_PATH, args->total); } - attr->aggregator_as = aggregator_as; - attr->aggregator_addr.s_addr = stream_get_ipv4(peer->curr); - - /* Set atomic aggregate flag. */ - attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AGGREGATOR); - return BGP_ATTR_PARSE_PROCEED; } @@ -1690,20 +1688,21 @@ bgp_attr_as4_aggregator(struct bgp_attr_parser_args *args, 0); } - /* Codification of AS 0 Processing */ aggregator_as = stream_getl(peer->curr); + *as4_aggregator_as = aggregator_as; + as4_aggregator_addr->s_addr = stream_get_ipv4(peer->curr); + + attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AS4_AGGREGATOR); + + /* Codification of AS 0 Processing */ if (aggregator_as == BGP_AS_ZERO) { flog_err(EC_BGP_ATTR_LEN, - "AS4_AGGREGATOR attribute is BGP_AS_ZERO(0)"); + "AS4_AGGREGATOR AS number is 0 for aspath: %s", + aspath_print(attr->aspath)); return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_MAL_AS_PATH, 0); } - *as4_aggregator_as = aggregator_as; - as4_aggregator_addr->s_addr = stream_get_ipv4(peer->curr); - - attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AS4_AGGREGATOR); - return BGP_ATTR_PARSE_PROCEED; } @@ -3416,10 +3415,10 @@ size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer, afi_t afi, } void bgp_packet_mpattr_prefix(struct stream *s, afi_t afi, safi_t safi, - struct prefix *p, struct prefix_rd *prd, - mpls_label_t *label, uint32_t num_labels, - int addpath_encode, uint32_t addpath_tx_id, - struct attr *attr) + const struct prefix *p, + const struct prefix_rd *prd, mpls_label_t *label, + uint32_t num_labels, int addpath_encode, + uint32_t addpath_tx_id, struct attr *attr) { if (safi == SAFI_MPLS_VPN) { if (addpath_encode) @@ -3445,7 +3444,8 @@ void bgp_packet_mpattr_prefix(struct stream *s, afi_t afi, safi_t safi, stream_put_prefix_addpath(s, p, addpath_encode, addpath_tx_id); } -size_t bgp_packet_mpattr_prefix_size(afi_t afi, safi_t safi, struct prefix *p) +size_t bgp_packet_mpattr_prefix_size(afi_t afi, safi_t safi, + const struct prefix *p) { int size = PSIZE(p->prefixlen); if (safi == SAFI_MPLS_VPN) @@ -4079,8 +4079,9 @@ size_t bgp_packet_mpunreach_start(struct stream *s, afi_t afi, safi_t safi) return attrlen_pnt; } -void bgp_packet_mpunreach_prefix(struct stream *s, struct prefix *p, afi_t afi, - safi_t safi, struct prefix_rd *prd, +void bgp_packet_mpunreach_prefix(struct stream *s, const struct prefix *p, + afi_t afi, safi_t safi, + const struct prefix_rd *prd, mpls_label_t *label, uint32_t num_labels, int addpath_encode, uint32_t addpath_tx_id, struct attr *attr) diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 04b3a20d6b..98a9a620f7 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -310,14 +310,11 @@ extern void bgp_attr_unintern_sub(struct attr *); extern void bgp_attr_unintern(struct attr **); extern void bgp_attr_flush(struct attr *); extern struct attr *bgp_attr_default_set(struct attr *attr, uint8_t); -extern struct attr *bgp_attr_aggregate_intern(struct bgp *bgp, uint8_t origin, - struct aspath *aspath, - struct community *community, - struct ecommunity *ecommunity, - struct lcommunity *lcommunity, - struct bgp_aggregate *aggregate, - uint8_t atomic_aggregate, - struct prefix *p); +extern struct attr *bgp_attr_aggregate_intern( + struct bgp *bgp, uint8_t origin, struct aspath *aspath, + struct community *community, struct ecommunity *ecommunity, + struct lcommunity *lcommunity, struct bgp_aggregate *aggregate, + uint8_t atomic_aggregate, const struct prefix *p); extern bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *, struct stream *, struct attr *, struct bpacket_attr_vec_arr *vecarr, @@ -372,20 +369,21 @@ extern size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer, struct bpacket_attr_vec_arr *vecarr, struct attr *attr); extern void bgp_packet_mpattr_prefix(struct stream *s, afi_t afi, safi_t safi, - struct prefix *p, struct prefix_rd *prd, + const struct prefix *p, + const struct prefix_rd *prd, mpls_label_t *label, uint32_t num_labels, int addpath_encode, uint32_t addpath_tx_id, struct attr *); extern size_t bgp_packet_mpattr_prefix_size(afi_t afi, safi_t safi, - struct prefix *p); + const struct prefix *p); extern void bgp_packet_mpattr_end(struct stream *s, size_t sizep); extern size_t bgp_packet_mpunreach_start(struct stream *s, afi_t afi, safi_t safi); -extern void bgp_packet_mpunreach_prefix(struct stream *s, struct prefix *p, - afi_t afi, safi_t safi, - struct prefix_rd *prd, mpls_label_t *, - uint32_t, int, uint32_t, struct attr *); +extern void bgp_packet_mpunreach_prefix( + struct stream *s, const struct prefix *p, afi_t afi, safi_t safi, + const struct prefix_rd *prd, mpls_label_t *label, uint32_t num_labels, + int addpath_encode, uint32_t addpath_tx_id, struct attr *attr); extern void bgp_packet_mpunreach_end(struct stream *s, size_t attrlen_pnt); extern bgp_attr_parse_ret_t bgp_attr_nexthop_valid(struct peer *peer, diff --git a/bgpd/bgp_bmp.c b/bgpd/bgp_bmp.c index 65a032abd5..a6fc4ebd03 100644 --- a/bgpd/bgp_bmp.c +++ b/bgpd/bgp_bmp.c @@ -765,8 +765,8 @@ static void bmp_eor(struct bmp *bmp, afi_t afi, safi_t safi, uint8_t flags) stream_free(s); } -static struct stream *bmp_update(struct prefix *p, struct peer *peer, - struct attr *attr, afi_t afi, safi_t safi) +static struct stream *bmp_update(const struct prefix *p, struct peer *peer, + struct attr *attr, afi_t afi, safi_t safi) { struct bpacket_attr_vec_arr vecarr; struct stream *s; @@ -813,7 +813,8 @@ static struct stream *bmp_update(struct prefix *p, struct peer *peer, return s; } -static struct stream *bmp_withdraw(struct prefix *p, afi_t afi, safi_t safi) +static struct stream *bmp_withdraw(const struct prefix *p, afi_t afi, + safi_t safi) { struct stream *s; size_t attrlen_pos = 0, mp_start, mplen_pos; @@ -853,7 +854,7 @@ static struct stream *bmp_withdraw(struct prefix *p, afi_t afi, safi_t safi) } static void bmp_monitor(struct bmp *bmp, struct peer *peer, uint8_t flags, - struct prefix *p, struct attr *attr, afi_t afi, + const struct prefix *p, struct attr *attr, afi_t afi, safi_t safi, time_t uptime) { struct stream *hdr, *msg; @@ -940,7 +941,7 @@ afibreak: return true; } bmp->syncpeerid = 0; - prefix_copy(&bmp->syncpos, &bn->p); + prefix_copy(&bmp->syncpos, bgp_node_get_prefix(bn)); } if (bmp->targets->afimon[afi][safi] & BMP_MON_POSTPOLICY) { @@ -988,12 +989,14 @@ afibreak: bmp->syncpeerid = adjin->peer->qobj_node.nid; } + const struct prefix *bn_p = bgp_node_get_prefix(bn); + if (bpi) - bmp_monitor(bmp, bpi->peer, BMP_PEER_FLAG_L, &bn->p, bpi->attr, + bmp_monitor(bmp, bpi->peer, BMP_PEER_FLAG_L, bn_p, bpi->attr, afi, safi, bpi->uptime); if (adjin) - bmp_monitor(bmp, adjin->peer, 0, &bn->p, adjin->attr, - afi, safi, adjin->uptime); + bmp_monitor(bmp, adjin->peer, 0, bn_p, adjin->attr, afi, safi, + adjin->uptime); return true; } @@ -1130,16 +1133,13 @@ static void bmp_process_one(struct bmp_targets *bt, struct bgp *bgp, struct bmp *bmp; struct bmp_queue_entry *bqe, bqeref; size_t refcount; - char buf[256]; - - prefix2str(&bn->p, buf, sizeof(buf)); refcount = bmp_session_count(&bt->sessions); if (refcount == 0) return; memset(&bqeref, 0, sizeof(bqeref)); - prefix_copy(&bqeref.p, &bn->p); + prefix_copy(&bqeref.p, bgp_node_get_prefix(bn)); bqeref.peerid = peer->qobj_node.nid; bqeref.afi = afi; bqeref.safi = safi; diff --git a/bgpd/bgp_damp.c b/bgpd/bgp_damp.c index 792f3cea70..538610f6d7 100644 --- a/bgpd/bgp_damp.c +++ b/bgpd/bgp_damp.c @@ -155,9 +155,9 @@ static int bgp_reuse_timer(struct thread *t) if (bdi->lastrecord == BGP_RECORD_UPDATE) { bgp_path_info_unset_flag(bdi->rn, bdi->path, BGP_PATH_HISTORY); - bgp_aggregate_increment(bgp, &bdi->rn->p, - bdi->path, bdi->afi, - bdi->safi); + bgp_aggregate_increment( + bgp, bgp_node_get_prefix(bdi->rn), + bdi->path, bdi->afi, bdi->safi); bgp_process(bgp, bdi->rn, bdi->afi, bdi->safi); } diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index ee5f75a079..5104e23515 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -2532,7 +2532,7 @@ bool bgp_debug_bestpath(struct bgp_node *rn) { if (BGP_DEBUG(bestpath, BESTPATH)) { if (bgp_debug_per_prefix( - &rn->p, term_bgp_debug_bestpath, + bgp_node_get_prefix(rn), term_bgp_debug_bestpath, BGP_DEBUG_BESTPATH, bgp_debug_bestpath_prefixes)) return true; } @@ -2553,7 +2553,7 @@ bool bgp_debug_zebra(const struct prefix *p) } const char *bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi, - struct prefix_rd *prd, + const struct prefix_rd *prd, union prefixconstptr pu, mpls_label_t *label, uint32_t num_labels, int addpath_valid, uint32_t addpath_id, diff --git a/bgpd/bgp_debug.h b/bgpd/bgp_debug.h index 4ce1e1dcef..7352b7917a 100644 --- a/bgpd/bgp_debug.h +++ b/bgpd/bgp_debug.h @@ -171,10 +171,11 @@ extern bool bgp_debug_update(struct peer *peer, const struct prefix *p, extern bool bgp_debug_bestpath(struct bgp_node *rn); extern bool bgp_debug_zebra(const struct prefix *p); -extern const char *bgp_debug_rdpfxpath2str(afi_t, safi_t, struct prefix_rd *, - union prefixconstptr, mpls_label_t *, - uint32_t, int, uint32_t, char *, - int); +extern const char * +bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi, const struct prefix_rd *prd, + union prefixconstptr pu, mpls_label_t *label, + uint32_t num_labels, int addpath_valid, + uint32_t addpath_id, char *str, int size); const char *bgp_notify_admin_message(char *buf, size_t bufsz, uint8_t *data, size_t datalen); diff --git a/bgpd/bgp_dump.c b/bgpd/bgp_dump.c index c448b9894a..cd1722ccca 100644 --- a/bgpd/bgp_dump.c +++ b/bgpd/bgp_dump.c @@ -307,6 +307,7 @@ bgp_dump_route_node_record(int afi, struct bgp_node *rn, struct stream *obuf; size_t sizep; size_t endp; + const struct prefix *p = bgp_node_get_prefix(rn); obuf = bgp_dump_obuf; stream_reset(obuf); @@ -325,19 +326,19 @@ bgp_dump_route_node_record(int afi, struct bgp_node *rn, stream_putl(obuf, seq); /* Prefix length */ - stream_putc(obuf, rn->p.prefixlen); + stream_putc(obuf, p->prefixlen); /* Prefix */ if (afi == AFI_IP) { /* We'll dump only the useful bits (those not 0), but have to * align on 8 bits */ - stream_write(obuf, (uint8_t *)&rn->p.u.prefix4, - (rn->p.prefixlen + 7) / 8); + stream_write(obuf, (uint8_t *)&p->u.prefix4, + (p->prefixlen + 7) / 8); } else if (afi == AFI_IP6) { /* We'll dump only the useful bits (those not 0), but have to * align on 8 bits */ - stream_write(obuf, (uint8_t *)&rn->p.u.prefix6, - (rn->p.prefixlen + 7) / 8); + stream_write(obuf, (uint8_t *)&p->u.prefix6, + (p->prefixlen + 7) / 8); } /* Save where we are now, so we can overwride the entry count later */ @@ -361,7 +362,7 @@ bgp_dump_route_node_record(int afi, struct bgp_node *rn, /* Dump attribute. */ /* Skip prefix & AFI/SAFI for MP_NLRI */ - bgp_dump_routes_attr(obuf, path->attr, &rn->p); + bgp_dump_routes_attr(obuf, path->attr, p); cur_endp = stream_get_endp(obuf); if (cur_endp > BGP_MAX_PACKET_SIZE + BGP_DUMP_MSG_HEADER diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 5202f6801e..a77a1e912e 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -1005,7 +1005,7 @@ static void evpn_delete_old_local_route(struct bgp *bgp, struct bgpevpn *vpn, * L3VPN routes. */ global_rn = bgp_afi_node_lookup(bgp->rib[afi][safi], afi, safi, - (struct prefix *)&rn->p, &vpn->prd); + bgp_node_get_prefix(rn), &vpn->prd); if (global_rn) { /* Delete route entry in the global EVPN table. */ delete_evpn_route_entry(bgp, afi, safi, global_rn, &pi); @@ -1128,7 +1128,7 @@ static int evpn_es_route_select_install(struct bgp *bgp, && !bgp_addpath_is_addpath_used(&bgp->tx_addpath, afi, safi)) { if (bgp_zebra_has_route_changed(rn, old_select)) { ret = evpn_es_install_vtep(bgp, es, - (struct prefix_evpn *)&rn->p, + (const struct prefix_evpn *)bgp_node_get_prefix(rn), old_select->attr->nexthop); } UNSET_FLAG(old_select->flags, BGP_PATH_MULTIPATH_CHG); @@ -1157,13 +1157,13 @@ static int evpn_es_route_select_install(struct bgp *bgp, if (new_select && new_select->type == ZEBRA_ROUTE_BGP && new_select->sub_type == BGP_ROUTE_IMPORTED) { ret = evpn_es_install_vtep(bgp, es, - (const struct prefix_evpn *)&rn->p, + (const struct prefix_evpn *)bgp_node_get_prefix(rn), new_select->attr->nexthop); } else { if (old_select && old_select->type == ZEBRA_ROUTE_BGP && old_select->sub_type == BGP_ROUTE_IMPORTED) ret = evpn_es_uninstall_vtep( - bgp, es, (struct prefix_evpn *)&rn->p, + bgp, es, (struct prefix_evpn *)bgp_node_get_prefix(rn), old_select->attr->nexthop); } @@ -1208,7 +1208,7 @@ static int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn, && !bgp_addpath_is_addpath_used(&bgp->tx_addpath, afi, safi)) { if (bgp_zebra_has_route_changed(rn, old_select)) ret = evpn_zebra_install( - bgp, vpn, (struct prefix_evpn *)&rn->p, + bgp, vpn, (const struct prefix_evpn *)bgp_node_get_prefix(rn), old_select); UNSET_FLAG(old_select->flags, BGP_PATH_MULTIPATH_CHG); bgp_zebra_clear_route_change_flags(rn); @@ -1234,8 +1234,9 @@ static int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn, if (new_select && new_select->type == ZEBRA_ROUTE_BGP && new_select->sub_type == BGP_ROUTE_IMPORTED) { - ret = evpn_zebra_install(bgp, vpn, (struct prefix_evpn *)&rn->p, - new_select); + ret = evpn_zebra_install( + bgp, vpn, (struct prefix_evpn *)bgp_node_get_prefix(rn), + new_select); /* If an old best existed and it was a "local" route, the only * reason @@ -1251,9 +1252,11 @@ static int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn, } else { if (old_select && old_select->type == ZEBRA_ROUTE_BGP && old_select->sub_type == BGP_ROUTE_IMPORTED) - ret = evpn_zebra_uninstall(bgp, vpn, - (struct prefix_evpn *)&rn->p, - old_select->attr->nexthop); + ret = evpn_zebra_uninstall( + bgp, vpn, + (const struct prefix_evpn *)bgp_node_get_prefix( + rn), + old_select->attr->nexthop); } /* Clear any route change flags. */ @@ -1330,11 +1333,11 @@ static int update_evpn_type4_route_entry(struct bgp *bgp, struct evpnes *es, struct bgp_path_info *local_pi = NULL; /* local route entry if any */ struct bgp_path_info *remote_pi = NULL; /* remote route entry if any */ struct attr *attr_new = NULL; - struct prefix_evpn *evp = NULL; + const struct prefix_evpn *evp = NULL; *ri = NULL; *route_changed = 1; - evp = (struct prefix_evpn *)&rn->p; + evp = (const struct prefix_evpn *)bgp_node_get_prefix(rn); /* locate the local and remote entries if any */ for (tmp_pi = bgp_node_get_bgp_path_info(rn); tmp_pi; @@ -1662,10 +1665,10 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, uint32_t num_labels = 1; int route_change = 1; uint8_t sticky = 0; - struct prefix_evpn *evp; + const struct prefix_evpn *evp; *pi = NULL; - evp = (struct prefix_evpn *)&rn->p; + evp = (const struct prefix_evpn *)bgp_node_get_prefix(rn); memset(&label, 0, sizeof(label)); /* See if this is an update of an existing route, or a new add. */ @@ -1797,8 +1800,8 @@ static void evpn_zebra_reinstall_best_route(struct bgp *bgp, if (curr_select && curr_select->type == ZEBRA_ROUTE_BGP && curr_select->sub_type == BGP_ROUTE_IMPORTED) evpn_zebra_install(bgp, vpn, - (struct prefix_evpn *)&rn->p, - curr_select); + (const struct prefix_evpn *)bgp_node_get_prefix(rn), + curr_select); } /* @@ -1820,13 +1823,10 @@ static void evpn_cleanup_local_non_best_route(struct bgp *bgp, struct bgp_node *rn, struct bgp_path_info *local_pi) { - char buf[PREFIX_STRLEN]; - /* local path was not picked as the winner; kick it out */ - if (bgp_debug_zebra(NULL)) { - zlog_debug("evicting local evpn prefix %s as remote won", - prefix2str(&rn->p, buf, sizeof(buf))); - } + if (bgp_debug_zebra(NULL)) + zlog_debug("evicting local evpn prefix %pRN as remote won", rn); + evpn_delete_old_local_route(bgp, vpn, rn, local_pi); bgp_path_info_reap(rn, local_pi); @@ -2145,7 +2145,7 @@ static int update_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn) */ for (rn = bgp_table_top(vpn->route_table); rn; rn = bgp_route_next(rn)) { - struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + const struct prefix_evpn *evp = (const struct prefix_evpn *)bgp_node_get_prefix(rn); struct bgp_node *rd_rn; struct bgp_path_info *global_pi; @@ -2278,7 +2278,7 @@ static int delete_global_type2_routes(struct bgp *bgp, struct bgpevpn *vpn) if (rdrn && bgp_node_has_bgp_path_info_data(rdrn)) { table = bgp_node_get_bgp_table_info(rdrn); for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { - struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + const struct prefix_evpn *evp = (const struct prefix_evpn *)bgp_node_get_prefix(rn); if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE) continue; @@ -2319,7 +2319,7 @@ static int delete_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn) /* Next, walk this VNI's route table and delete local type-2 routes. */ for (rn = bgp_table_top(vpn->route_table); rn; rn = bgp_route_next(rn)) { - struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + const struct prefix_evpn *evp = (const struct prefix_evpn *)bgp_node_get_prefix(rn); if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE) continue; @@ -2709,7 +2709,8 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, /* as it is an importation, change nexthop */ bgp_path_info_set_flag(rn, pi, BGP_PATH_ANNC_NH_SELF); - bgp_aggregate_increment(bgp_vrf, &rn->p, pi, afi, safi); + bgp_aggregate_increment(bgp_vrf, bgp_node_get_prefix(rn), pi, afi, + safi); /* Perform route selection and update zebra, if required. */ bgp_process(bgp_vrf, rn, afi, safi); @@ -2876,7 +2877,8 @@ static int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, /* Process for route leaking. */ vpn_leak_from_vrf_withdraw(bgp_get_default(), bgp_vrf, pi); - bgp_aggregate_decrement(bgp_vrf, &rn->p, pi, afi, safi); + bgp_aggregate_decrement(bgp_vrf, bgp_node_get_prefix(rn), pi, afi, + safi); /* Mark entry for deletion */ bgp_path_info_delete(rn, pi); @@ -3107,7 +3109,9 @@ static int install_uninstall_routes_for_es(struct bgp *bgp, continue; for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { - struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + const struct prefix_evpn *evp = + (const struct prefix_evpn *)bgp_node_get_prefix( + rn); for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) { @@ -3214,7 +3218,7 @@ static int install_uninstall_routes_for_vrf(struct bgp *bgp_vrf, int install) continue; for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { - struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + const struct prefix_evpn *evp = (const struct prefix_evpn *)bgp_node_get_prefix(rn); /* if not mac-ip route skip this route */ if (!(evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE @@ -3302,7 +3306,9 @@ static int install_uninstall_routes_for_vni(struct bgp *bgp, continue; for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { - struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + const struct prefix_evpn *evp = + (const struct prefix_evpn *)bgp_node_get_prefix( + rn); if (evp->prefix.route_type != rtype) continue; @@ -3774,7 +3780,8 @@ static int update_advertise_vni_routes(struct bgp *bgp, struct bgpevpn *vpn) */ for (rn = bgp_table_top(vpn->route_table); rn; rn = bgp_route_next(rn)) { - struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + const struct prefix_evpn *evp = + (const struct prefix_evpn *)bgp_node_get_prefix(rn); /* Identify MAC-IP local routes. */ if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE) @@ -4297,13 +4304,14 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi, return ret; } -static void evpn_mpattr_encode_type5(struct stream *s, struct prefix *p, - struct prefix_rd *prd, mpls_label_t *label, - uint32_t num_labels, struct attr *attr) +static void evpn_mpattr_encode_type5(struct stream *s, const struct prefix *p, + const struct prefix_rd *prd, + mpls_label_t *label, uint32_t num_labels, + struct attr *attr) { int len; char temp[16]; - struct evpn_addr *p_evpn_p; + const struct evpn_addr *p_evpn_p; memset(&temp, 0, 16); if (p->family != AF_EVPN) @@ -4499,8 +4507,9 @@ void bgp_evpn_withdraw_type5_routes(struct bgp *bgp_vrf, afi_t afi, safi_t safi) for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) { if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED) && is_route_injectable_into_evpn(pi)) { - bgp_evpn_withdraw_type5_route(bgp_vrf, &rn->p, - afi, safi); + bgp_evpn_withdraw_type5_route( + bgp_vrf, bgp_node_get_prefix(rn), afi, + safi); break; } } @@ -4592,18 +4601,21 @@ void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf, afi_t afi, ret = route_map_apply( bgp_vrf->adv_cmd_rmap[afi][safi] .map, - &rn->p, RMAP_BGP, &tmp_pi); + bgp_node_get_prefix(rn), + RMAP_BGP, &tmp_pi); if (ret == RMAP_DENYMATCH) { bgp_attr_flush(&tmp_attr); continue; } bgp_evpn_advertise_type5_route( - bgp_vrf, &rn->p, &tmp_attr, - afi, safi); + bgp_vrf, + bgp_node_get_prefix(rn), + &tmp_attr, afi, safi); } else bgp_evpn_advertise_type5_route( - bgp_vrf, &rn->p, pi->attr, - afi, safi); + bgp_vrf, + bgp_node_get_prefix(rn), + pi->attr, afi, safi); break; } } @@ -5030,8 +5042,8 @@ char *bgp_evpn_route2str(const struct prefix_evpn *p, char *buf, int len) /* * Encode EVPN prefix in Update (MP_REACH) */ -void bgp_evpn_encode_prefix(struct stream *s, struct prefix *p, - struct prefix_rd *prd, mpls_label_t *label, +void bgp_evpn_encode_prefix(struct stream *s, const struct prefix *p, + const struct prefix_rd *prd, mpls_label_t *label, uint32_t num_labels, struct attr *attr, int addpath_encode, uint32_t addpath_tx_id) { @@ -5601,25 +5613,23 @@ int bgp_filter_evpn_routes_upon_martian_nh_change(struct bgp *bgp) if (bgp_nexthop_self(bgp, afi, pi->type, pi->sub_type, pi->attr, rn)) { + const struct prefix *p = + bgp_node_get_prefix(rn); - char attr_str[BUFSIZ] = {0}; - char pbuf[PREFIX_STRLEN]; + if (bgp_debug_update(pi->peer, p, NULL, + 1)) { + char attr_str[BUFSIZ] = {0}; - bgp_dump_attr(pi->attr, attr_str, - BUFSIZ); + bgp_dump_attr(pi->attr, + attr_str, BUFSIZ); - if (bgp_debug_update(pi->peer, &rn->p, - NULL, 1)) zlog_debug( - "%u: prefix %s with attr %s - DENIED due to martian or self nexthop", - bgp->vrf_id, - prefix2str( - &rn->p, pbuf, - sizeof(pbuf)), + "%u: prefix %pRN with attr %s - DENIED due to martian or self nexthop", + bgp->vrf_id, rn, attr_str); - + } bgp_evpn_unimport_route(bgp, afi, safi, - &rn->p, pi); + p, pi); bgp_rib_remove(rn, pi, pi->peer, afi, safi); @@ -6264,7 +6274,7 @@ void bgp_evpn_vrf_delete(struct bgp *bgp_vrf) /* * Get the prefixlen of the ip prefix carried within the type5 evpn route. */ -int bgp_evpn_get_type5_prefixlen(struct prefix *pfx) +int bgp_evpn_get_type5_prefixlen(const struct prefix *pfx) { struct prefix_evpn *evp = (struct prefix_evpn *)pfx; diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index 219321fc68..a48a707b94 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -157,10 +157,11 @@ extern char *bgp_evpn_label2str(mpls_label_t *label, uint32_t num_labels, extern char *bgp_evpn_route2str(const struct prefix_evpn *p, char *buf, int len); extern void bgp_evpn_route2json(const struct prefix_evpn *p, json_object *json); -extern void bgp_evpn_encode_prefix(struct stream *s, struct prefix *p, - struct prefix_rd *prd, mpls_label_t *label, - uint32_t num_labels, struct attr *attr, - int addpath_encode, uint32_t addpath_tx_id); +extern void bgp_evpn_encode_prefix(struct stream *s, const struct prefix *p, + const struct prefix_rd *prd, + mpls_label_t *label, uint32_t num_labels, + struct attr *attr, int addpath_encode, + uint32_t addpath_tx_id); extern int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, struct bgp_nlri *packet, int withdraw); extern int bgp_evpn_import_route(struct bgp *bgp, afi_t afi, safi_t safi, @@ -195,7 +196,7 @@ extern void bgp_evpn_flood_control_change(struct bgp *bgp); extern void bgp_evpn_cleanup_on_disable(struct bgp *bgp); extern void bgp_evpn_cleanup(struct bgp *bgp); extern void bgp_evpn_init(struct bgp *bgp); -extern int bgp_evpn_get_type5_prefixlen(struct prefix *pfx); +extern int bgp_evpn_get_type5_prefixlen(const struct prefix *pfx); extern bool bgp_evpn_is_prefix_nht_supported(const struct prefix *pfx); extern void update_advertise_vrf_routes(struct bgp *bgp_vrf); diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 70bd7b2193..fddb00b6e2 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -281,9 +281,10 @@ static void bgp_evpn_show_route_rd_header(struct vty *vty, uint16_t type; struct rd_as rd_as; struct rd_ip rd_ip; - uint8_t *pnt; + const uint8_t *pnt; + const struct prefix *p = bgp_node_get_prefix(rd_rn); - pnt = rd_rn->p.u.val; + pnt = p->u.val; /* Decode RD type. */ type = decode_rd_type(pnt); @@ -647,8 +648,9 @@ static void show_esi_routes(struct bgp *bgp, char prefix_str[BUFSIZ]; json_object *json_paths = NULL; json_object *json_prefix = NULL; + const struct prefix *p = bgp_node_get_prefix(rn); - bgp_evpn_route2str((struct prefix_evpn *)&rn->p, prefix_str, + bgp_evpn_route2str((struct prefix_evpn *)p, prefix_str, sizeof(prefix_str)); if (json) @@ -678,7 +680,7 @@ static void show_esi_routes(struct bgp *bgp, if (json) json_path = json_object_new_array(); - route_vty_out(vty, &rn->p, pi, 0, SAFI_EVPN, json_path); + route_vty_out(vty, p, pi, 0, SAFI_EVPN, json_path); if (json) json_object_array_add(json_paths, json_path); @@ -692,7 +694,7 @@ static void show_esi_routes(struct bgp *bgp, json_object_string_add(json_prefix, "prefix", prefix_str); json_object_int_add(json_prefix, "prefixLen", - rn->p.prefixlen); + p->prefixlen); json_object_object_add(json_prefix, "paths", json_paths); json_object_object_add(json, prefix_str, @@ -735,13 +737,15 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, tbl_ver = table->version; for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { - struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + const struct prefix_evpn *evp = + (const struct prefix_evpn *)bgp_node_get_prefix(rn); int add_prefix_to_json = 0; char prefix_str[BUFSIZ]; json_object *json_paths = NULL; json_object *json_prefix = NULL; + const struct prefix *p = bgp_node_get_prefix(rn); - bgp_evpn_route2str((struct prefix_evpn *)&rn->p, prefix_str, + bgp_evpn_route2str((const struct prefix_evpn *)p, prefix_str, sizeof(prefix_str)); if (type && evp->prefix.route_type != type) @@ -784,7 +788,7 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, AFI_L2VPN, SAFI_EVPN, json_path); else - route_vty_out(vty, &rn->p, pi, 0, SAFI_EVPN, + route_vty_out(vty, p, pi, 0, SAFI_EVPN, json_path); if (json) @@ -799,7 +803,7 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, json_object_string_add(json_prefix, "prefix", prefix_str); json_object_int_add(json_prefix, "prefixLen", - rn->p.prefixlen); + p->prefixlen); json_object_object_add(json_prefix, "paths", json_paths); json_object_object_add(json, prefix_str, @@ -1188,8 +1192,9 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd, rn = bgp_route_next(rn)) { uint64_t tbl_ver; json_object *json_nroute = NULL; + const struct prefix *p = bgp_node_get_prefix(rn); - if (prd && memcmp(rn->p.u.val, prd->val, 8) != 0) + if (prd && memcmp(p->u.val, prd->val, 8) != 0) continue; table = bgp_node_get_bgp_table_info(rn); @@ -1290,16 +1295,18 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd, json_array = json_object_new_array(); if (option == SHOW_DISPLAY_TAGS) - route_vty_out_tag(vty, &rm->p, pi, - no_display, SAFI_EVPN, - json_array); + route_vty_out_tag( + vty, bgp_node_get_prefix(rm), + pi, no_display, SAFI_EVPN, + json_array); else if (option == SHOW_DISPLAY_OVERLAY) - route_vty_out_overlay(vty, &rm->p, pi, - no_display, - json_array); + route_vty_out_overlay( + vty, bgp_node_get_prefix(rm), + pi, no_display, json_array); else - route_vty_out(vty, &rm->p, pi, - no_display, SAFI_EVPN, + route_vty_out(vty, + bgp_node_get_prefix(rm), + pi, no_display, SAFI_EVPN, json_array); no_display = 1; } @@ -1308,15 +1315,19 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd, output_count++; if (use_json && json_array) { + const struct prefix *p = + bgp_node_get_prefix(rm); + json_prefix_info = json_object_new_object(); - json_object_string_add(json_prefix_info, - "prefix", bgp_evpn_route2str( - (struct prefix_evpn *)&rm->p, buf, - BUFSIZ)); + json_object_string_add( + json_prefix_info, "prefix", + bgp_evpn_route2str( + (struct prefix_evpn *)p, buf, + BUFSIZ)); json_object_int_add(json_prefix_info, - "prefixLen", rm->p.prefixlen); + "prefixLen", p->prefixlen); json_object_object_add(json_prefix_info, "paths", json_array); @@ -2580,13 +2591,14 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, /* Display all prefixes with this RD. */ for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { - struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + const struct prefix_evpn *evp = + (const struct prefix_evpn *)bgp_node_get_prefix(rn); json_object *json_prefix = NULL; json_object *json_paths = NULL; char prefix_str[BUFSIZ]; int add_prefix_to_json = 0; - bgp_evpn_route2str((struct prefix_evpn *)&rn->p, prefix_str, + bgp_evpn_route2str((struct prefix_evpn *)evp, prefix_str, sizeof(prefix_str)); if (type && evp->prefix.route_type != type) @@ -2703,13 +2715,14 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, json_object *json_rd = NULL; /* contains routes for an RD */ int add_rd_to_json = 0; uint64_t tbl_ver; + const struct prefix *rd_rnp = bgp_node_get_prefix(rd_rn); table = bgp_node_get_bgp_table_info(rd_rn); if (table == NULL) continue; tbl_ver = table->version; - prefix_rd2str((struct prefix_rd *)&rd_rn->p, rd_str, + prefix_rd2str((struct prefix_rd *)rd_rnp, rd_str, sizeof(rd_str)); if (json) @@ -2723,12 +2736,15 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, NULL; /* contains prefix under a RD */ json_object *json_paths = NULL; /* array of paths under a prefix*/ - struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + const struct prefix_evpn *evp = + (const struct prefix_evpn *)bgp_node_get_prefix( + rn); char prefix_str[BUFSIZ]; int add_prefix_to_json = 0; + const struct prefix *p = bgp_node_get_prefix(rn); - bgp_evpn_route2str((struct prefix_evpn *)&rn->p, - prefix_str, sizeof(prefix_str)); + bgp_evpn_route2str((struct prefix_evpn *)p, prefix_str, + sizeof(prefix_str)); if (type && evp->prefix.route_type != type) continue; @@ -2764,15 +2780,15 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, json_object_string_add(json_prefix, "prefix", prefix_str); json_object_int_add(json_prefix, "prefixLen", - rn->p.prefixlen); + p->prefixlen); } /* Prefix and num paths displayed once per prefix. */ if (detail) route_vty_out_detail_header( vty, bgp, rn, - (struct prefix_rd *)&rd_rn->p, - AFI_L2VPN, SAFI_EVPN, json_prefix); + (struct prefix_rd *)rd_rnp, AFI_L2VPN, + SAFI_EVPN, json_prefix); /* For EVPN, the prefix is displayed for each path (to * fit in @@ -2792,8 +2808,8 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, vty, bgp, rn, pi, AFI_L2VPN, SAFI_EVPN, json_path); } else - route_vty_out(vty, &rn->p, pi, 0, - SAFI_EVPN, json_path); + route_vty_out(vty, p, pi, 0, SAFI_EVPN, + json_path); if (json) json_object_array_add(json_paths, diff --git a/bgpd/bgp_flowspec_util.c b/bgpd/bgp_flowspec_util.c index 79d5268c7d..9d824a8641 100644 --- a/bgpd/bgp_flowspec_util.c +++ b/bgpd/bgp_flowspec_util.c @@ -607,7 +607,8 @@ bool bgp_flowspec_get_first_nh(struct bgp *bgp, struct bgp_path_info *pi, struct bgp_pbr_entry_action *api_action; memset(&api, 0, sizeof(struct bgp_pbr_entry_main)); - if (bgp_pbr_build_and_validate_entry(&rn->p, pi, &api) < 0) + if (bgp_pbr_build_and_validate_entry(bgp_node_get_prefix(rn), pi, &api) + < 0) return true; for (i = 0; i < api.action_num; i++) { api_action = &api.actions[i]; diff --git a/bgpd/bgp_flowspec_vty.c b/bgpd/bgp_flowspec_vty.c index c453c4e812..c852e18c46 100644 --- a/bgpd/bgp_flowspec_vty.c +++ b/bgpd/bgp_flowspec_vty.c @@ -409,8 +409,8 @@ int bgp_show_table_flowspec(struct vty *vty, struct bgp *bgp, afi_t afi, } for (; pi; pi = pi->next) { total_count++; - route_vty_out_flowspec(vty, &rn->p, pi, display, - json_paths); + route_vty_out_flowspec(vty, bgp_node_get_prefix(rn), pi, + display, json_paths); } if (use_json) { vty_out(vty, "%s\n", @@ -554,18 +554,18 @@ extern int bgp_flowspec_display_match_per_ip(afi_t afi, struct bgp_table *rib, json_object *json_paths) { struct bgp_node *rn; - struct prefix *prefix; + const struct prefix *prefix; int display = 0; for (rn = bgp_table_top(rib); rn; rn = bgp_route_next(rn)) { - prefix = &rn->p; + prefix = bgp_node_get_prefix(rn); if (prefix->family != AF_FLOWSPEC) continue; if (bgp_flowspec_contains_prefix(prefix, match, prefix_check)) { route_vty_out_flowspec( - vty, &rn->p, bgp_node_get_bgp_path_info(rn), + vty, prefix, bgp_node_get_bgp_path_info(rn), use_json ? NLRI_STRING_FORMAT_JSON : NLRI_STRING_FORMAT_LARGE, json_paths); diff --git a/bgpd/bgp_label.c b/bgpd/bgp_label.c index ff1ab1a37d..ec44037bf7 100644 --- a/bgpd/bgp_label.c +++ b/bgpd/bgp_label.c @@ -132,7 +132,6 @@ int bgp_reg_for_label_callback(mpls_label_t new_label, void *labelid, { struct bgp_path_info *pi; struct bgp_node *rn; - char addr[PREFIX_STRLEN]; pi = labelid; /* Is this path still valid? */ @@ -145,10 +144,9 @@ int bgp_reg_for_label_callback(mpls_label_t new_label, void *labelid, } rn = pi->net; - prefix2str(&rn->p, addr, PREFIX_STRLEN); if (BGP_DEBUG(labelpool, LABELPOOL)) - zlog_debug("%s: FEC %s label=%u, allocated=%d", __func__, addr, + zlog_debug("%s: FEC %pRN label=%u, allocated=%d", __func__, rn, new_label, allocated); if (!allocated) { @@ -174,8 +172,8 @@ int bgp_reg_for_label_callback(mpls_label_t new_label, void *labelid, if (pi->attr->label_index != MPLS_INVALID_LABEL_INDEX) { flog_err( EC_BGP_LABEL, - "%s: FEC %s Rejecting allocated label %u as Label Index is %u", - __func__, addr, new_label, pi->attr->label_index); + "%s: FEC %pRN Rejecting allocated label %u as Label Index is %u", + __func__, rn, new_label, pi->attr->label_index); bgp_register_for_label(pi->net, pi); @@ -189,8 +187,8 @@ int bgp_reg_for_label_callback(mpls_label_t new_label, void *labelid, } /* Shouldn't happen: different label allocation */ flog_err(EC_BGP_LABEL, - "%s: %s had label %u but got new assignment %u", - __func__, addr, pi->attr->label, new_label); + "%s: %pRN had label %u but got new assignment %u", + __func__, rn, pi->attr->label, new_label); /* continue means use new one */ } @@ -210,14 +208,14 @@ void bgp_reg_dereg_for_label(struct bgp_node *rn, struct bgp_path_info *pi, { bool with_label_index = false; struct stream *s; - struct prefix *p; + const struct prefix *p; mpls_label_t *local_label; int command; uint16_t flags = 0; size_t flags_pos = 0; char addr[PREFIX_STRLEN]; - p = &(rn->p); + p = bgp_node_get_prefix(rn); local_label = &(rn->local_label); /* this prevents the loop when we're called by * bgp_reg_for_label_callback() @@ -473,7 +471,7 @@ int bgp_nlri_parse_label(struct peer *peer, struct attr *attr, if (pnt != lim) { flog_err( EC_BGP_UPDATE_RCV, - "%s [Error] Update packet error / L-U (%zu data remaining after parsing)", + "%s [Error] Update packet error / L-U (%td data remaining after parsing)", peer->host, lim - pnt); return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; } diff --git a/bgpd/bgp_mac.c b/bgpd/bgp_mac.c index fed88fd508..af20e5fdd7 100644 --- a/bgpd/bgp_mac.c +++ b/bgpd/bgp_mac.c @@ -142,13 +142,15 @@ static void bgp_process_mac_rescan_table(struct bgp *bgp, struct peer *peer, for (prn = bgp_table_top(table); prn; prn = bgp_route_next(prn)) { struct bgp_table *sub = prn->info; + const struct prefix *prn_p = bgp_node_get_prefix(prn); if (!sub) continue; for (rn = bgp_table_top(sub); rn; rn = bgp_route_next(rn)) { bool rn_affected; - struct prefix_evpn *pevpn = (struct prefix_evpn *)&rn->p; + const struct prefix *p = bgp_node_get_prefix(rn); + const struct prefix_evpn *pevpn = (const struct prefix_evpn *)p; struct prefix_rd prd; uint32_t num_labels = 0; mpls_label_t *label_pnt = NULL; @@ -156,7 +158,7 @@ static void bgp_process_mac_rescan_table(struct bgp *bgp, struct peer *peer, if (pevpn->family == AF_EVPN && pevpn->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE && - memcmp(&rn->p.u.prefix_evpn.macip_addr.mac, + memcmp(&p->u.prefix_evpn.macip_addr.mac, macaddr, ETH_ALEN) == 0) rn_affected = true; else @@ -185,15 +187,15 @@ static void bgp_process_mac_rescan_table(struct bgp *bgp, struct peer *peer, prd.family = AF_UNSPEC; prd.prefixlen = 64; - memcpy(&prd.val, &prn->p.u.val, 8); + memcpy(&prd.val, prn_p->u.val, 8); if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) { - if (bgp_debug_update(peer, &rn->p, NULL, 1)) { + if (bgp_debug_update(peer, p, NULL, 1)) { char pfx_buf[BGP_PRD_PATH_STRLEN]; bgp_debug_rdpfxpath2str( AFI_L2VPN, SAFI_EVPN, &prd, - &rn->p, label_pnt, num_labels, + p, label_pnt, num_labels, pi->addpath_rx_id ? 1 : 0, pi->addpath_rx_id, pfx_buf, sizeof(pfx_buf)); @@ -205,7 +207,7 @@ static void bgp_process_mac_rescan_table(struct bgp *bgp, struct peer *peer, } memcpy(&evpn, &pi->attr->evpn_overlay, sizeof(evpn)); - int32_t ret = bgp_update(peer, &rn->p, + int32_t ret = bgp_update(peer, p, pi->addpath_rx_id, pi->attr, AFI_L2VPN, SAFI_EVPN, ZEBRA_ROUTE_BGP, diff --git a/bgpd/bgp_mpath.c b/bgpd/bgp_mpath.c index 7ec33556c7..cbef41bafd 100644 --- a/bgpd/bgp_mpath.c +++ b/bgpd/bgp_mpath.c @@ -447,7 +447,7 @@ void bgp_path_info_mpath_update(struct bgp_node *rn, struct listnode *mp_node, *mp_next_node; struct bgp_path_info *cur_mpath, *new_mpath, *next_mpath, *prev_mpath; int mpath_changed, debug; - char pfx_buf[PREFIX2STR_BUFFER], nh_buf[2][INET6_ADDRSTRLEN]; + char nh_buf[2][INET6_ADDRSTRLEN]; char path_buf[PATH_ADDPATH_STR_BUFFER]; mpath_changed = 0; @@ -459,9 +459,6 @@ void bgp_path_info_mpath_update(struct bgp_node *rn, mp_node = listhead(mp_list); debug = bgp_debug_bestpath(rn); - if (debug) - prefix2str(&rn->p, pfx_buf, sizeof(pfx_buf)); - if (new_best) { mpath_count++; if (new_best != old_best) @@ -480,8 +477,8 @@ void bgp_path_info_mpath_update(struct bgp_node *rn, if (debug) zlog_debug( - "%s: starting mpath update, newbest %s num candidates %d old-mpath-count %d", - pfx_buf, new_best ? new_best->peer->host : "NONE", + "%pRN: starting mpath update, newbest %s num candidates %d old-mpath-count %d", + rn, new_best ? new_best->peer->host : "NONE", mp_list ? listcount(mp_list) : 0, old_mpath_count); /* @@ -513,8 +510,8 @@ void bgp_path_info_mpath_update(struct bgp_node *rn, if (debug) zlog_debug( - "%s: comparing candidate %s with existing mpath %s", - pfx_buf, + "%pRN: comparing candidate %s with existing mpath %s", + rn, tmp_info ? tmp_info->peer->host : "NONE", cur_mpath ? cur_mpath->peer->host : "NONE"); @@ -537,8 +534,8 @@ void bgp_path_info_mpath_update(struct bgp_node *rn, bgp_path_info_path_with_addpath_rx_str( cur_mpath, path_buf); zlog_debug( - "%s: %s is still multipath, cur count %d", - pfx_buf, path_buf, mpath_count); + "%pRN: %s is still multipath, cur count %d", + rn, path_buf, mpath_count); } } else { mpath_changed = 1; @@ -546,8 +543,8 @@ void bgp_path_info_mpath_update(struct bgp_node *rn, bgp_path_info_path_with_addpath_rx_str( cur_mpath, path_buf); zlog_debug( - "%s: remove mpath %s nexthop %s, cur count %d", - pfx_buf, path_buf, + "%pRN: remove mpath %s nexthop %s, cur count %d", + rn, path_buf, inet_ntop(AF_INET, &cur_mpath->attr ->nexthop, @@ -579,8 +576,8 @@ void bgp_path_info_mpath_update(struct bgp_node *rn, bgp_path_info_path_with_addpath_rx_str( cur_mpath, path_buf); zlog_debug( - "%s: remove mpath %s nexthop %s, cur count %d", - pfx_buf, path_buf, + "%pRN: remove mpath %s nexthop %s, cur count %d", + rn, path_buf, inet_ntop(AF_INET, &cur_mpath->attr->nexthop, nh_buf[0], sizeof(nh_buf[0])), @@ -624,8 +621,8 @@ void bgp_path_info_mpath_update(struct bgp_node *rn, bgp_path_info_path_with_addpath_rx_str( new_mpath, path_buf); zlog_debug( - "%s: add mpath %s nexthop %s, cur count %d", - pfx_buf, path_buf, + "%pRN: add mpath %s nexthop %s, cur count %d", + rn, path_buf, inet_ntop(AF_INET, &new_mpath->attr ->nexthop, @@ -641,8 +638,8 @@ void bgp_path_info_mpath_update(struct bgp_node *rn, if (new_best) { if (debug) zlog_debug( - "%s: New mpath count (incl newbest) %d mpath-change %s", - pfx_buf, mpath_count, + "%pRN: New mpath count (incl newbest) %d mpath-change %s", + rn, mpath_count, mpath_changed ? "YES" : "NO"); bgp_path_info_mpath_count_set(new_best, mpath_count - 1); diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 8eecf984bf..fbcfe39c3a 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -244,7 +244,7 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, if (pnt != lim) { flog_err( EC_BGP_UPDATE_RCV, - "%s [Error] Update packet error / VPN (%zu data remaining after parsing)", + "%s [Error] Update packet error / VPN (%td data remaining after parsing)", peer->host, lim - pnt); return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; } @@ -468,18 +468,16 @@ leak_update(struct bgp *bgp, /* destination bgp instance */ struct bgp *bgp_orig, struct prefix *nexthop_orig, int nexthop_self_flag, int debug) { - struct prefix *p = &bn->p; + const struct prefix *p = bgp_node_get_prefix(bn); struct bgp_path_info *bpi; struct bgp_path_info *bpi_ultimate; struct bgp_path_info *new; - char buf_prefix[PREFIX_STRLEN]; - if (debug) { - prefix2str(&bn->p, buf_prefix, sizeof(buf_prefix)); - zlog_debug("%s: entry: leak-to=%s, p=%s, type=%d, sub_type=%d", - __func__, bgp->name_pretty, buf_prefix, - source_bpi->type, source_bpi->sub_type); - } + if (debug) + zlog_debug( + "%s: entry: leak-to=%s, p=%pRN, type=%d, sub_type=%d", + __func__, bgp->name_pretty, bn, source_bpi->type, + source_bpi->sub_type); /* * Routes that are redistributed into BGP from zebra do not get @@ -518,9 +516,8 @@ leak_update(struct bgp *bgp, /* destination bgp instance */ bgp_attr_unintern(&new_attr); if (debug) zlog_debug( - "%s: ->%s: %s: Found route, no change", - __func__, bgp->name_pretty, - buf_prefix); + "%s: ->%s: %pRN: Found route, no change", + __func__, bgp->name_pretty, bn); return NULL; } @@ -580,8 +577,8 @@ leak_update(struct bgp *bgp, /* destination bgp instance */ bgp_unlock_node(bn); if (debug) - zlog_debug("%s: ->%s: %s Found route, changed attr", - __func__, bgp->name_pretty, buf_prefix); + zlog_debug("%s: ->%s: %pRN Found route, changed attr", + __func__, bgp->name_pretty, bn); return bpi; } @@ -645,8 +642,8 @@ leak_update(struct bgp *bgp, /* destination bgp instance */ bgp_process(bgp, bn, afi, safi); if (debug) - zlog_debug("%s: ->%s: %s: Added new route", __func__, - bgp->name_pretty, buf_prefix); + zlog_debug("%s: ->%s: %pRN: Added new route", __func__, + bgp->name_pretty, bn); return new; } @@ -657,7 +654,7 @@ void vpn_leak_from_vrf_update(struct bgp *bgp_vpn, /* to */ struct bgp_path_info *path_vrf) /* route */ { int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF); - struct prefix *p = &path_vrf->net->p; + const struct prefix *p = bgp_node_get_prefix(path_vrf->net); afi_t afi = family2afi(p->family); struct attr static_attr = {0}; struct attr *new_attr = NULL; @@ -891,19 +888,17 @@ void vpn_leak_from_vrf_withdraw(struct bgp *bgp_vpn, /* to */ struct bgp_path_info *path_vrf) /* route */ { int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF); - struct prefix *p = &path_vrf->net->p; + const struct prefix *p = bgp_node_get_prefix(path_vrf->net); afi_t afi = family2afi(p->family); safi_t safi = SAFI_MPLS_VPN; struct bgp_path_info *bpi; struct bgp_node *bn; const char *debugmsg; - char buf_prefix[PREFIX_STRLEN]; if (debug) { - prefix2str(p, buf_prefix, sizeof(buf_prefix)); zlog_debug( - "%s: entry: leak-from=%s, p=%s, type=%d, sub_type=%d", - __func__, bgp_vrf->name_pretty, buf_prefix, + "%s: entry: leak-from=%s, p=%pRN, type=%d, sub_type=%d", + __func__, bgp_vrf->name_pretty, path_vrf->net, path_vrf->type, path_vrf->sub_type); } @@ -980,14 +975,10 @@ void vpn_leak_from_vrf_withdraw_all(struct bgp *bgp_vpn, /* to */ continue; for (bn = bgp_table_top(table); bn; bn = bgp_route_next(bn)) { - - char buf[PREFIX2STR_BUFFER]; - bpi = bgp_node_get_bgp_path_info(bn); if (debug && bpi) { - zlog_debug( - "%s: looking at prefix %s", __func__, - prefix2str(&bn->p, buf, sizeof(buf))); + zlog_debug("%s: looking at prefix %pRN", + __func__, bn); } for (; bpi; bpi = bpi->next) { @@ -1005,8 +996,10 @@ void vpn_leak_from_vrf_withdraw_all(struct bgp *bgp_vpn, /* to */ if (debug) zlog_debug("%s: deleting it", __func__); - bgp_aggregate_decrement(bgp_vpn, &bn->p, - bpi, afi, safi); + bgp_aggregate_decrement( + bgp_vpn, + bgp_node_get_prefix(bn), bpi, + afi, safi); bgp_path_info_delete(bn, bpi); bgp_process(bgp_vpn, bn, afi, safi); } @@ -1049,7 +1042,7 @@ vpn_leak_to_vrf_update_onevrf(struct bgp *bgp_vrf, /* to */ struct bgp *bgp_vpn, /* from */ struct bgp_path_info *path_vpn) /* route */ { - struct prefix *p = &path_vpn->net->p; + const struct prefix *p = bgp_node_get_prefix(path_vpn->net); afi_t afi = family2afi(p->family); struct attr static_attr = {0}; @@ -1228,12 +1221,9 @@ vpn_leak_to_vrf_update_onevrf(struct bgp *bgp_vrf, /* to */ } } - if (debug) { - char buf_prefix[PREFIX_STRLEN]; - prefix2str(p, buf_prefix, sizeof(buf_prefix)); - zlog_debug("%s: pfx %s: num_labels %d", __func__, buf_prefix, - num_labels); - } + if (debug) + zlog_debug("%s: pfx %pRN: num_labels %d", __func__, + path_vpn->net, num_labels); /* * For VRF-2-VRF route-leaking, @@ -1273,7 +1263,7 @@ void vpn_leak_to_vrf_update(struct bgp *bgp_vpn, /* from */ void vpn_leak_to_vrf_withdraw(struct bgp *bgp_vpn, /* from */ struct bgp_path_info *path_vpn) /* route */ { - struct prefix *p; + const struct prefix *p; afi_t afi; safi_t safi = SAFI_UNICAST; struct bgp *bgp; @@ -1281,15 +1271,12 @@ void vpn_leak_to_vrf_withdraw(struct bgp *bgp_vpn, /* from */ struct bgp_node *bn; struct bgp_path_info *bpi; const char *debugmsg; - char buf_prefix[PREFIX_STRLEN]; int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF); - if (debug) { - prefix2str(&path_vpn->net->p, buf_prefix, sizeof(buf_prefix)); - zlog_debug("%s: entry: p=%s, type=%d, sub_type=%d", __func__, - buf_prefix, path_vpn->type, path_vpn->sub_type); - } + if (debug) + zlog_debug("%s: entry: p=%pRN, type=%d, sub_type=%d", __func__, + path_vpn->net, path_vpn->type, path_vpn->sub_type); if (debug) zlog_debug("%s: start (path_vpn=%p)", __func__, path_vpn); @@ -1310,7 +1297,7 @@ void vpn_leak_to_vrf_withdraw(struct bgp *bgp_vpn, /* from */ return; } - p = &path_vpn->net->p; + p = bgp_node_get_prefix(path_vpn->net); afi = family2afi(p->family); /* Loop over VRFs */ @@ -1381,8 +1368,9 @@ void vpn_leak_to_vrf_withdraw_all(struct bgp *bgp_vrf, /* to */ && is_pi_family_vpn(bpi->extra->parent)) { /* delete route */ - bgp_aggregate_decrement(bgp_vrf, &bn->p, bpi, - afi, safi); + bgp_aggregate_decrement(bgp_vrf, + bgp_node_get_prefix(bn), + bpi, afi, safi); bgp_path_info_delete(bn, bpi); bgp_process(bgp_vrf, bn, afi, safi); } @@ -1405,7 +1393,7 @@ void vpn_leak_to_vrf_update_all(struct bgp *bgp_vrf, /* to */ */ for (prn = bgp_table_top(bgp_vpn->rib[afi][safi]); prn; prn = bgp_route_next(prn)) { - + const struct prefix *p = bgp_node_get_prefix(prn); struct bgp_table *table; struct bgp_node *bn; struct bgp_path_info *bpi; @@ -1413,7 +1401,7 @@ void vpn_leak_to_vrf_update_all(struct bgp *bgp_vrf, /* to */ memset(&prd, 0, sizeof(prd)); prd.family = AF_UNSPEC; prd.prefixlen = 64; - memcpy(prd.val, prn->p.u.val, 8); + memcpy(prd.val, &p->u.val, 8); /* This is the per-RD table of prefixes */ table = bgp_node_get_bgp_table_info(prn); diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index 23c5adbf28..c77238aa33 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -49,7 +49,7 @@ DEFINE_MTYPE_STATIC(BGPD, MARTIAN_STRING, "BGP Martian Address Intf String"); char *bnc_str(struct bgp_nexthop_cache *bnc, char *buf, int size) { - prefix2str(&(bnc->node->p), buf, size); + prefix2str(bgp_node_get_prefix(bnc->node), buf, size); return buf; } @@ -476,7 +476,7 @@ bool bgp_nexthop_self(struct bgp *bgp, afi_t afi, uint8_t type, uint8_t new_afi = afi == AFI_IP ? AF_INET : AF_INET6; struct bgp_addr tmp_addr = {{0}}, *addr = NULL; struct tip_addr tmp_tip, *tip = NULL; - + const struct prefix *p = bgp_node_get_prefix(rn); bool is_bgp_static_route = ((type == ZEBRA_ROUTE_BGP) && (sub_type == BGP_ROUTE_STATIC)) ? true @@ -489,8 +489,8 @@ bool bgp_nexthop_self(struct bgp *bgp, afi_t afi, uint8_t type, switch (new_afi) { case AF_INET: if (is_bgp_static_route) { - tmp_addr.p.u.prefix4 = rn->p.u.prefix4; - tmp_addr.p.prefixlen = rn->p.prefixlen; + tmp_addr.p.u.prefix4 = p->u.prefix4; + tmp_addr.p.prefixlen = p->prefixlen; } else { /* Here we need to find out which nexthop to be used*/ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP)) { @@ -510,8 +510,8 @@ bool bgp_nexthop_self(struct bgp *bgp, afi_t afi, uint8_t type, break; case AF_INET6: if (is_bgp_static_route) { - tmp_addr.p.u.prefix6 = rn->p.u.prefix6; - tmp_addr.p.prefixlen = rn->p.prefixlen; + tmp_addr.p.u.prefix6 = p->u.prefix6; + tmp_addr.p.prefixlen = p->prefixlen; } else { tmp_addr.p.u.prefix6 = attr->mp_nexthop_global; tmp_addr.p.prefixlen = IPV6_MAX_BITLEN; @@ -763,6 +763,7 @@ static void bgp_show_nexthops(struct vty *vty, struct bgp *bgp, int detail, for (rn = bgp_table_top(table[afi]); rn; rn = bgp_route_next(rn)) { struct peer *peer; + const struct prefix *p = bgp_node_get_prefix(rn); bnc = bgp_node_get_bgp_nexthop_info(rn); if (!bnc) @@ -772,8 +773,7 @@ static void bgp_show_nexthops(struct vty *vty, struct bgp *bgp, int detail, if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID)) { vty_out(vty, " %s valid [IGP metric %d], #paths %d", - inet_ntop(rn->p.family, - &rn->p.u.prefix, buf, + inet_ntop(p->family, &p->u.prefix, buf, sizeof(buf)), bnc->metric, bnc->path_count); if (peer) @@ -787,8 +787,7 @@ static void bgp_show_nexthops(struct vty *vty, struct bgp *bgp, int detail, } else { vty_out(vty, " %s invalid", - inet_ntop(rn->p.family, - &rn->p.u.prefix, buf, + inet_ntop(p->family, &p->u.prefix, buf, sizeof(buf))); if (peer) vty_out(vty, ", peer %s", peer->host); diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index dfa9ac9398..0531542a38 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -130,6 +130,7 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, struct bgp_nexthop_cache *bnc; struct prefix p; int is_bgp_static_route = 0; + const struct prefix *bnc_p; if (pi) { is_bgp_static_route = ((pi->type == ZEBRA_ROUTE_BGP) @@ -181,6 +182,8 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, } } + bnc_p = bgp_node_get_prefix(bnc->node); + bgp_unlock_node(rn); if (is_bgp_static_route) { SET_FLAG(bnc->flags, BGP_STATIC_ROUTE); @@ -226,8 +229,8 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, if (bgp_route->inst_type == BGP_INSTANCE_TYPE_VIEW) { SET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); SET_FLAG(bnc->flags, BGP_NEXTHOP_VALID); - } else if (!CHECK_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED) && - !is_default_host_route(&bnc->node->p)) + } else if (!CHECK_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED) + && !is_default_host_route(bnc_p)) register_zebra_rnh(bnc, is_bgp_static_route); if (pi && pi->nexthop != bnc) { @@ -528,7 +531,7 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p) ? 1 : 0; struct bgp_node *net = pi->net; - struct prefix *p_orig = &net->p; + const struct prefix *p_orig = bgp_node_get_prefix(net); if (p_orig->family == AF_FLOWSPEC) { if (!pi->peer) @@ -541,8 +544,8 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p) case AFI_IP: p->family = AF_INET; if (is_bgp_static) { - p->u.prefix4 = pi->net->p.u.prefix4; - p->prefixlen = pi->net->p.prefixlen; + p->u.prefix4 = p_orig->u.prefix4; + p->prefixlen = p_orig->prefixlen; } else { p->u.prefix4 = pi->attr->nexthop; p->prefixlen = IPV4_MAX_BITLEN; @@ -552,8 +555,8 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p) p->family = AF_INET6; if (is_bgp_static) { - p->u.prefix6 = pi->net->p.u.prefix6; - p->prefixlen = pi->net->p.prefixlen; + p->u.prefix6 = p_orig->u.prefix6; + p->prefixlen = p_orig->prefixlen; } else { p->u.prefix6 = pi->attr->mp_nexthop_global; p->prefixlen = IPV6_MAX_BITLEN; @@ -581,7 +584,7 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p) */ static void sendmsg_zebra_rnh(struct bgp_nexthop_cache *bnc, int command) { - struct prefix *p; + const struct prefix *p; bool exact_match = false; int ret; @@ -603,7 +606,7 @@ static void sendmsg_zebra_rnh(struct bgp_nexthop_cache *bnc, int command) "%s: We have not connected yet, cannot send nexthops", __func__); } - p = &(bnc->node->p); + p = bgp_node_get_prefix(bnc->node); if ((command == ZEBRA_NEXTHOP_REGISTER || command == ZEBRA_IMPORT_ROUTE_REGISTER) && (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED) @@ -691,6 +694,7 @@ static void evaluate_paths(struct bgp_nexthop_cache *bnc) struct bgp_table *table; safi_t safi; struct bgp *bgp_path; + const struct prefix *p; if (BGP_DEBUG(nht, NHT)) { char buf[PREFIX2STR_BUFFER]; @@ -710,7 +714,8 @@ static void evaluate_paths(struct bgp_nexthop_cache *bnc) rn = path->net; assert(rn && bgp_node_table(rn)); - afi = family2afi(rn->p.family); + p = bgp_node_get_prefix(rn); + afi = family2afi(p->family); table = bgp_node_table(rn); safi = table->safi; @@ -744,27 +749,23 @@ static void evaluate_paths(struct bgp_nexthop_cache *bnc) bgp_isvalid_nexthop(bnc) ? 1 : 0; } - if (BGP_DEBUG(nht, NHT)) { - char buf[PREFIX_STRLEN]; - - prefix2str(&rn->p, buf, PREFIX_STRLEN); - zlog_debug("%s: prefix %s (vrf %s) %svalid", - __func__, buf, bgp_path->name, - (bnc_is_valid_nexthop ? "" : "not ")); - } + if (BGP_DEBUG(nht, NHT)) + zlog_debug("%s: prefix %pRN (vrf %s) %svalid", __func__, + rn, bgp_path->name, + (bnc_is_valid_nexthop ? "" : "not ")); if ((CHECK_FLAG(path->flags, BGP_PATH_VALID) ? 1 : 0) != bnc_is_valid_nexthop) { if (CHECK_FLAG(path->flags, BGP_PATH_VALID)) { - bgp_aggregate_decrement(bgp_path, &rn->p, - path, afi, safi); + bgp_aggregate_decrement(bgp_path, p, path, afi, + safi); bgp_path_info_unset_flag(rn, path, BGP_PATH_VALID); } else { bgp_path_info_set_flag(rn, path, BGP_PATH_VALID); - bgp_aggregate_increment(bgp_path, &rn->p, - path, afi, safi); + bgp_aggregate_increment(bgp_path, p, path, afi, + safi); } } @@ -780,14 +781,13 @@ static void evaluate_paths(struct bgp_nexthop_cache *bnc) || CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_CHANGED)) SET_FLAG(path->flags, BGP_PATH_IGP_CHANGED); - if (safi == SAFI_EVPN && - bgp_evpn_is_prefix_nht_supported(&rn->p)) { + if (safi == SAFI_EVPN && bgp_evpn_is_prefix_nht_supported(p)) { if (CHECK_FLAG(path->flags, BGP_PATH_VALID)) - bgp_evpn_import_route(bgp_path, afi, safi, - &rn->p, path); + bgp_evpn_import_route(bgp_path, afi, safi, p, + path); else - bgp_evpn_unimport_route(bgp_path, afi, safi, - &rn->p, path); + bgp_evpn_unimport_route(bgp_path, afi, safi, p, + path); } bgp_process(bgp_path, rn, afi, safi); diff --git a/bgpd/bgp_rd.c b/bgpd/bgp_rd.c index 5facf3269e..6a229602b2 100644 --- a/bgpd/bgp_rd.c +++ b/bgpd/bgp_rd.c @@ -87,7 +87,7 @@ void decode_rd_ip(const uint8_t *pnt, struct rd_ip *rd_ip) #if ENABLE_BGP_VNC /* type == RD_TYPE_VNC_ETH */ -void decode_rd_vnc_eth(uint8_t *pnt, struct rd_vnc_eth *rd_vnc_eth) +void decode_rd_vnc_eth(const uint8_t *pnt, struct rd_vnc_eth *rd_vnc_eth) { rd_vnc_eth->type = RD_TYPE_VNC_ETH; rd_vnc_eth->local_nve_id = pnt[1]; @@ -159,9 +159,9 @@ out: return lret; } -char *prefix_rd2str(struct prefix_rd *prd, char *buf, size_t size) +char *prefix_rd2str(const struct prefix_rd *prd, char *buf, size_t size) { - uint8_t *pnt; + const uint8_t *pnt; uint16_t type; struct rd_as rd_as; struct rd_ip rd_ip; diff --git a/bgpd/bgp_rd.h b/bgpd/bgp_rd.h index 439dfd4a31..56b9023a2e 100644 --- a/bgpd/bgp_rd.h +++ b/bgpd/bgp_rd.h @@ -61,11 +61,12 @@ extern void decode_rd_as(const uint8_t *pnt, struct rd_as *rd_as); extern void decode_rd_as4(const uint8_t *pnt, struct rd_as *rd_as); extern void decode_rd_ip(const uint8_t *pnt, struct rd_ip *rd_ip); #if ENABLE_BGP_VNC -extern void decode_rd_vnc_eth(uint8_t *pnt, struct rd_vnc_eth *rd_vnc_eth); +extern void decode_rd_vnc_eth(const uint8_t *pnt, + struct rd_vnc_eth *rd_vnc_eth); #endif extern int str2prefix_rd(const char *, struct prefix_rd *); -extern char *prefix_rd2str(struct prefix_rd *, char *, size_t); +extern char *prefix_rd2str(const struct prefix_rd *, char *, size_t); extern void form_auto_rd(struct in_addr router_id, uint16_t rd_id, struct prefix_rd *prd); diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 842d305f80..9a3ab0d8ee 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -303,7 +303,6 @@ static int bgp_node_set_defer_flag(struct bgp_node *rn, bool delete) struct bgp_table *table = NULL; afi_t afi = 0; safi_t safi = 0; - char buf[PREFIX2STR_BUFFER]; /* If the flag BGP_NODE_SELECT_DEFER is set and new path is added * then the route selection is deferred @@ -312,12 +311,11 @@ static int bgp_node_set_defer_flag(struct bgp_node *rn, bool delete) return 0; if (CHECK_FLAG(rn->flags, BGP_NODE_PROCESS_SCHEDULED)) { - if (BGP_DEBUG(update, UPDATE_OUT)) { - prefix2str(&rn->p, buf, PREFIX2STR_BUFFER); + if (BGP_DEBUG(update, UPDATE_OUT)) zlog_debug( - "Route %s is in workqueue and being processed, not deferred.", - buf); - } + "Route %pRN is in workqueue and being processed, not deferred.", + rn); + return 0; } @@ -361,13 +359,12 @@ static int bgp_node_set_defer_flag(struct bgp_node *rn, bool delete) if (set_flag && table) { if (bgp && (bgp->gr_info[afi][safi].t_select_deferral)) { SET_FLAG(rn->flags, BGP_NODE_SELECT_DEFER); - prefix2str(&rn->p, buf, PREFIX2STR_BUFFER); if (rn->rt_node == NULL) rn->rt_node = listnode_add( bgp->gr_info[afi][safi].route_list, rn); if (BGP_DEBUG(update, UPDATE_OUT)) - zlog_debug("DEFER route %s, rn %p, node %p", - buf, rn, rn->rt_node); + zlog_debug("DEFER route %pRN, rn %p, node %p", + rn, rn, rn->rt_node); return 0; } } @@ -594,7 +591,8 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, */ if (newattr->sticky != existattr->sticky) { if (!debug) { - prefix2str(&new->net->p, pfx_buf, + prefix2str(bgp_node_get_prefix(new->net), + pfx_buf, sizeof(*pfx_buf) * PREFIX2STR_BUFFER); bgp_path_info_path_with_addpath_rx_str(new, @@ -1381,7 +1379,7 @@ static int bgp_input_modifier(struct peer *peer, const struct prefix *p, return RMAP_PERMIT; } -static int bgp_output_modifier(struct peer *peer, struct prefix *p, +static int bgp_output_modifier(struct peer *peer, const struct prefix *p, struct attr *attr, afi_t afi, safi_t safi, const char *rmap_name) { @@ -2100,7 +2098,7 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn, debug = bgp_debug_bestpath(rn); if (debug) - prefix2str(&rn->p, pfx_buf, sizeof(pfx_buf)); + prefix2str(bgp_node_get_prefix(rn), pfx_buf, sizeof(pfx_buf)); rn->reason = bgp_path_selection_none; /* bgp deterministic-med */ @@ -2320,13 +2318,13 @@ void subgroup_process_announce_selected(struct update_subgroup *subgrp, struct bgp_node *rn, uint32_t addpath_tx_id) { - struct prefix *p; + const struct prefix *p; struct peer *onlypeer; struct attr attr; afi_t afi; safi_t safi; - p = &rn->p; + p = bgp_node_get_prefix(rn); afi = SUBGRP_AFI(subgrp); safi = SUBGRP_SAFI(subgrp); onlypeer = ((SUBGRP_PCOUNT(subgrp) == 1) ? (SUBGRP_PFIRST(subgrp))->peer @@ -2446,18 +2444,15 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, struct bgp_path_info *new_select; struct bgp_path_info *old_select; struct bgp_path_info_pair old_and_new; - char pfx_buf[PREFIX2STR_BUFFER]; int debug = 0; if (CHECK_FLAG(bgp->flags, BGP_FLAG_DELETE_IN_PROGRESS)) { if (rn) debug = bgp_debug_bestpath(rn); - if (debug) { - prefix2str(&rn->p, pfx_buf, sizeof(pfx_buf)); + if (debug) zlog_debug( - "%s: bgp delete in progress, ignoring event, p=%s", - __func__, pfx_buf); - } + "%s: bgp delete in progress, ignoring event, p=%pRN", + __func__, rn); return; } /* Is it end of initial update? (after startup) */ @@ -2476,14 +2471,12 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, return; } - struct prefix *p = &rn->p; + const struct prefix *p = bgp_node_get_prefix(rn); debug = bgp_debug_bestpath(rn); - if (debug) { - prefix2str(&rn->p, pfx_buf, sizeof(pfx_buf)); - zlog_debug("%s: p=%s afi=%s, safi=%s start", __func__, pfx_buf, + if (debug) + zlog_debug("%s: p=%pRN afi=%s, safi=%s start", __func__, rn, afi2str(afi), safi2str(safi)); - } /* The best path calculation for the route is deferred if * BGP_NODE_SELECT_DEFER is set @@ -2540,13 +2533,11 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, bgp_unregister_for_label(rn); } - if (debug) { - prefix2str(&rn->p, pfx_buf, sizeof(pfx_buf)); + if (debug) zlog_debug( - "%s: p=%s afi=%s, safi=%s, old_select=%p, new_select=%p", - __func__, pfx_buf, afi2str(afi), safi2str(safi), + "%s: p=%pRN afi=%s, safi=%s, old_select=%p, new_select=%p", + __func__, rn, afi2str(afi), safi2str(safi), old_select, new_select); - } /* If best route remains the same and this is not due to user-initiated * clear, see exactly what needs to be done. @@ -2679,6 +2670,8 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, /* advertise/withdraw type-5 routes */ if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) { + const struct prefix *p = bgp_node_get_prefix(rn); + if (advertise_type5_routes(bgp, afi) && new_select && is_route_injectable_into_evpn(new_select)) { @@ -2702,18 +2695,17 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, ret = route_map_apply( bgp->adv_cmd_rmap[afi][safi].map, - &rn->p, RMAP_BGP, &rmap_path); + p, RMAP_BGP, &rmap_path); if (ret == RMAP_DENYMATCH) { bgp_attr_flush(&dummy_attr); bgp_evpn_withdraw_type5_route( - bgp, &rn->p, afi, safi); + bgp, p, afi, safi); } else bgp_evpn_advertise_type5_route( - bgp, &rn->p, &dummy_attr, + bgp, p, &dummy_attr, afi, safi); } else { - bgp_evpn_advertise_type5_route(bgp, - &rn->p, + bgp_evpn_advertise_type5_route(bgp, p, new_select->attr, afi, safi); @@ -2721,7 +2713,7 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, } else if (advertise_type5_routes(bgp, afi) && old_select && is_route_injectable_into_evpn(old_select)) - bgp_evpn_withdraw_type5_route(bgp, &rn->p, afi, safi); + bgp_evpn_withdraw_type5_route(bgp, p, afi, safi); } /* Clear any route change flags. */ @@ -3048,7 +3040,8 @@ void bgp_rib_remove(struct bgp_node *rn, struct bgp_path_info *pi, struct bgp *bgp = NULL; bool delete_route = false; - bgp_aggregate_decrement(peer->bgp, &rn->p, pi, afi, safi); + bgp_aggregate_decrement(peer->bgp, bgp_node_get_prefix(rn), + pi, afi, safi); if (!CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)) { bgp_path_info_delete(rn, pi); /* keep historical info */ @@ -3083,6 +3076,8 @@ static void bgp_rib_withdraw(struct bgp_node *rn, struct bgp_path_info *pi, struct peer *peer, afi_t afi, safi_t safi, struct prefix_rd *prd) { + const struct prefix *p = bgp_node_get_prefix(rn); + /* apply dampening, if result is suppressed, we'll be retaining * the bgp_path_info in the RIB for historical reference. */ @@ -3090,7 +3085,7 @@ static void bgp_rib_withdraw(struct bgp_node *rn, struct bgp_path_info *pi, && peer->sort == BGP_PEER_EBGP) if ((bgp_damp_withdraw(pi, rn, afi, safi, 0)) == BGP_DAMP_SUPPRESSED) { - bgp_aggregate_decrement(peer->bgp, &rn->p, pi, afi, + bgp_aggregate_decrement(peer->bgp, p, pi, afi, safi); return; } @@ -3106,23 +3101,22 @@ static void bgp_rib_withdraw(struct bgp_node *rn, struct bgp_path_info *pi, table = bgp_node_get_bgp_table_info(prn); vnc_import_bgp_del_vnc_host_route_mode_resolve_nve( - peer->bgp, prd, table, &rn->p, pi); + peer->bgp, prd, table, p, pi); } bgp_unlock_node(prn); } if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) { if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) { - vnc_import_bgp_del_route(peer->bgp, &rn->p, pi); - vnc_import_bgp_exterior_del_route(peer->bgp, &rn->p, - pi); + vnc_import_bgp_del_route(peer->bgp, p, pi); + vnc_import_bgp_exterior_del_route(peer->bgp, p, pi); } } #endif /* If this is an EVPN route, process for un-import. */ if (safi == SAFI_EVPN) - bgp_evpn_unimport_route(peer->bgp, afi, safi, &rn->p, pi); + bgp_evpn_unimport_route(peer->bgp, afi, safi, p, pi); bgp_rib_remove(rn, pi, peer, afi, safi); } @@ -4254,8 +4248,9 @@ static void bgp_soft_reconfig_table(struct peer *peer, afi_t afi, safi_t safi, else memset(&evpn, 0, sizeof(evpn)); - ret = bgp_update(peer, &rn->p, ain->addpath_rx_id, - ain->attr, afi, safi, ZEBRA_ROUTE_BGP, + ret = bgp_update(peer, bgp_node_get_prefix(rn), + ain->addpath_rx_id, ain->attr, + afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, prd, label_pnt, num_labels, 1, &evpn); @@ -4281,16 +4276,18 @@ void bgp_soft_reconfig_in(struct peer *peer, afi_t afi, safi_t safi) for (rn = bgp_table_top(peer->bgp->rib[afi][safi]); rn; rn = bgp_route_next(rn)) { table = bgp_node_get_bgp_table_info(rn); - if (table != NULL) { - struct prefix_rd prd; - prd.family = AF_UNSPEC; - prd.prefixlen = 64; - memcpy(&prd.val, rn->p.u.val, 8); + if (table == NULL) + continue; - bgp_soft_reconfig_table(peer, afi, safi, table, - &prd); - } + const struct prefix *p = bgp_node_get_prefix(rn); + struct prefix_rd prd; + + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + memcpy(&prd.val, p->u.val, 8); + + bgp_soft_reconfig_table(peer, afi, safi, table, &prd); } } @@ -4329,7 +4326,8 @@ static wq_item_status bgp_clear_route_node(struct work_queue *wq, void *data) /* If this is an EVPN route, process for * un-import. */ if (safi == SAFI_EVPN) - bgp_evpn_unimport_route(bgp, afi, safi, &rn->p, + bgp_evpn_unimport_route(bgp, afi, safi, + bgp_node_get_prefix(rn), pi); /* Handle withdraw for VRF route-leaking and L3VPN */ if (SAFI_UNICAST == safi @@ -4645,13 +4643,14 @@ static void bgp_cleanup_table(struct bgp *bgp, struct bgp_table *table, for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = next) { + const struct prefix *p = bgp_node_get_prefix(rn); + next = pi->next; /* Unimport EVPN routes from VRFs */ if (safi == SAFI_EVPN) bgp_evpn_unimport_route(bgp, AFI_L2VPN, - SAFI_EVPN, - &rn->p, pi); + SAFI_EVPN, p, pi); if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED) && pi->type == ZEBRA_ROUTE_BGP @@ -4660,8 +4659,7 @@ static void bgp_cleanup_table(struct bgp *bgp, struct bgp_table *table, || pi->sub_type == BGP_ROUTE_IMPORTED)) { if (bgp_fibupd_safi(safi)) - bgp_zebra_withdraw(&rn->p, pi, bgp, - safi); + bgp_zebra_withdraw(p, pi, bgp, safi); bgp_path_info_reap(rn, pi); } } @@ -4910,7 +4908,7 @@ static void bgp_static_free(struct bgp_static *bgp_static) XFREE(MTYPE_BGP_STATIC, bgp_static); } -void bgp_static_update(struct bgp *bgp, struct prefix *p, +void bgp_static_update(struct bgp *bgp, const struct prefix *p, struct bgp_static *bgp_static, afi_t afi, safi_t safi) { struct bgp_node *rn; @@ -5139,7 +5137,7 @@ void bgp_static_update(struct bgp *bgp, struct prefix *p, aspath_unintern(&attr.aspath); } -void bgp_static_withdraw(struct bgp *bgp, struct prefix *p, afi_t afi, +void bgp_static_withdraw(struct bgp *bgp, const struct prefix *p, afi_t afi, safi_t safi) { struct bgp_node *rn; @@ -5173,7 +5171,7 @@ void bgp_static_withdraw(struct bgp *bgp, struct prefix *p, afi_t afi, /* * Used for SAFI_MPLS_VPN and SAFI_ENCAP */ -static void bgp_static_withdraw_safi(struct bgp *bgp, struct prefix *p, +static void bgp_static_withdraw_safi(struct bgp *bgp, const struct prefix *p, afi_t afi, safi_t safi, struct prefix_rd *prd) { @@ -5208,7 +5206,7 @@ static void bgp_static_withdraw_safi(struct bgp *bgp, struct prefix *p, bgp_unlock_node(rn); } -static void bgp_static_update_safi(struct bgp *bgp, struct prefix *p, +static void bgp_static_update_safi(struct bgp *bgp, const struct prefix *p, struct bgp_static *bgp_static, afi_t afi, safi_t safi) { @@ -5542,13 +5540,14 @@ void bgp_static_add(struct bgp *bgp) bgp_static = bgp_node_get_bgp_static_info( rm); - bgp_static_update_safi(bgp, &rm->p, + bgp_static_update_safi(bgp, + bgp_node_get_prefix(rm), bgp_static, afi, safi); } } else { bgp_static_update( - bgp, &rn->p, + bgp, bgp_node_get_prefix(rn), bgp_node_get_bgp_static_info(rn), afi, safi); } @@ -5585,15 +5584,20 @@ void bgp_static_delete(struct bgp *bgp) continue; bgp_static_withdraw_safi( - bgp, &rm->p, AFI_IP, safi, - (struct prefix_rd *)&rn->p); + bgp, bgp_node_get_prefix(rm), + AFI_IP, safi, + (struct prefix_rd *) + bgp_node_get_prefix( + rn)); bgp_static_free(bgp_static); bgp_node_set_bgp_static_info(rn, NULL); bgp_unlock_node(rn); } } else { bgp_static = bgp_node_get_bgp_static_info(rn); - bgp_static_withdraw(bgp, &rn->p, afi, safi); + bgp_static_withdraw(bgp, + bgp_node_get_prefix(rn), + afi, safi); bgp_static_free(bgp_static); bgp_node_set_bgp_static_info(rn, NULL); bgp_unlock_node(rn); @@ -5627,13 +5631,15 @@ void bgp_static_redo_import_check(struct bgp *bgp) bgp_static = bgp_node_get_bgp_static_info( rm); - bgp_static_update_safi(bgp, &rm->p, + bgp_static_update_safi(bgp, + bgp_node_get_prefix(rm), bgp_static, afi, safi); } } else { bgp_static = bgp_node_get_bgp_static_info(rn); - bgp_static_update(bgp, &rn->p, bgp_static, afi, + bgp_static_update(bgp, bgp_node_get_prefix(rn), + bgp_static, afi, safi); } } @@ -5664,8 +5670,9 @@ static void bgp_purge_af_static_redist_routes(struct bgp *bgp, afi_t afi, || (pi->type != ZEBRA_ROUTE_BGP && pi->sub_type == BGP_ROUTE_REDISTRIBUTE))) { - bgp_aggregate_decrement(bgp, &rn->p, pi, afi, - safi); + bgp_aggregate_decrement(bgp, + bgp_node_get_prefix(rn), + pi, afi, safi); bgp_unlink_nexthop(pi); bgp_path_info_delete(rn, pi); bgp_process(bgp, rn, afi, safi); @@ -6058,14 +6065,11 @@ static bool bgp_aggregate_info_same(struct bgp_path_info *pi, uint8_t origin, return true; } -static void bgp_aggregate_install(struct bgp *bgp, afi_t afi, safi_t safi, - struct prefix *p, uint8_t origin, - struct aspath *aspath, - struct community *community, - struct ecommunity *ecommunity, - struct lcommunity *lcommunity, - uint8_t atomic_aggregate, - struct bgp_aggregate *aggregate) +static void bgp_aggregate_install( + struct bgp *bgp, afi_t afi, safi_t safi, const struct prefix *p, + uint8_t origin, struct aspath *aspath, struct community *community, + struct ecommunity *ecommunity, struct lcommunity *lcommunity, + uint8_t atomic_aggregate, struct bgp_aggregate *aggregate) { struct bgp_node *rn; struct bgp_table *table; @@ -6142,9 +6146,8 @@ static void bgp_aggregate_install(struct bgp *bgp, afi_t afi, safi_t safi, } /* Update an aggregate as routes are added/removed from the BGP table */ -void bgp_aggregate_route(struct bgp *bgp, struct prefix *p, - afi_t afi, safi_t safi, - struct bgp_aggregate *aggregate) +void bgp_aggregate_route(struct bgp *bgp, const struct prefix *p, afi_t afi, + safi_t safi, struct bgp_aggregate *aggregate) { struct bgp_table *table; struct bgp_node *top; @@ -6180,7 +6183,9 @@ void bgp_aggregate_route(struct bgp *bgp, struct prefix *p, top = bgp_node_get(table, p); for (rn = bgp_node_get(table, p); rn; rn = bgp_route_next_until(rn, top)) { - if (rn->p.prefixlen <= p->prefixlen) + const struct prefix *rn_p = bgp_node_get_prefix(rn); + + if (rn_p->prefixlen <= p->prefixlen) continue; match = 0; @@ -6314,8 +6319,8 @@ void bgp_aggregate_route(struct bgp *bgp, struct prefix *p, aggregate); } -void bgp_aggregate_delete(struct bgp *bgp, struct prefix *p, afi_t afi, - safi_t safi, struct bgp_aggregate *aggregate) +void bgp_aggregate_delete(struct bgp *bgp, const struct prefix *p, afi_t afi, + safi_t safi, struct bgp_aggregate *aggregate) { struct bgp_table *table; struct bgp_node *top; @@ -6329,7 +6334,9 @@ void bgp_aggregate_delete(struct bgp *bgp, struct prefix *p, afi_t afi, top = bgp_node_get(table, p); for (rn = bgp_node_get(table, p); rn; rn = bgp_route_next_until(rn, top)) { - if (rn->p.prefixlen <= p->prefixlen) + const struct prefix *rn_p = bgp_node_get_prefix(rn); + + if (rn_p->prefixlen <= p->prefixlen) continue; match = 0; @@ -6405,7 +6412,8 @@ void bgp_aggregate_delete(struct bgp *bgp, struct prefix *p, afi_t afi, bgp_unlock_node(top); } -static void bgp_add_route_to_aggregate(struct bgp *bgp, struct prefix *aggr_p, +static void bgp_add_route_to_aggregate(struct bgp *bgp, + const struct prefix *aggr_p, struct bgp_path_info *pinew, afi_t afi, safi_t safi, struct bgp_aggregate *aggregate) @@ -6511,7 +6519,7 @@ static void bgp_remove_route_from_aggregate(struct bgp *bgp, afi_t afi, safi_t safi, struct bgp_path_info *pi, struct bgp_aggregate *aggregate, - struct prefix *aggr_p) + const struct prefix *aggr_p) { uint8_t origin; struct aspath *aspath = NULL; @@ -6639,10 +6647,12 @@ void bgp_aggregate_increment(struct bgp *bgp, const struct prefix *p, /* Aggregate address configuration check. */ for (rn = child; rn; rn = bgp_node_parent_nolock(rn)) { + const struct prefix *rn_p = bgp_node_get_prefix(rn); + aggregate = bgp_node_get_bgp_aggregate_info(rn); - if (aggregate != NULL && rn->p.prefixlen < p->prefixlen) { - bgp_add_route_to_aggregate(bgp, &rn->p, pi, afi, - safi, aggregate); + if (aggregate != NULL && rn_p->prefixlen < p->prefixlen) { + bgp_add_route_to_aggregate(bgp, rn_p, pi, afi, safi, + aggregate); } } bgp_unlock_node(child); @@ -6669,10 +6679,12 @@ void bgp_aggregate_decrement(struct bgp *bgp, const struct prefix *p, /* Aggregate address configuration check. */ for (rn = child; rn; rn = bgp_node_parent_nolock(rn)) { + const struct prefix *rn_p = bgp_node_get_prefix(rn); + aggregate = bgp_node_get_bgp_aggregate_info(rn); - if (aggregate != NULL && rn->p.prefixlen < p->prefixlen) { - bgp_remove_route_from_aggregate(bgp, afi, safi, - del, aggregate, &rn->p); + if (aggregate != NULL && rn_p->prefixlen < p->prefixlen) { + bgp_remove_route_from_aggregate(bgp, afi, safi, del, + aggregate, rn_p); } } bgp_unlock_node(child); @@ -7335,8 +7347,8 @@ void bgp_redistribute_withdraw(struct bgp *bgp, afi_t afi, int type, vpn_leak_from_vrf_withdraw(bgp_get_default(), bgp, pi); } - bgp_aggregate_decrement(bgp, &rn->p, pi, afi, - SAFI_UNICAST); + bgp_aggregate_decrement(bgp, bgp_node_get_prefix(rn), + pi, afi, SAFI_UNICAST); bgp_path_info_delete(rn, pi); bgp_process(bgp, rn, afi, SAFI_UNICAST); } @@ -7934,8 +7946,9 @@ void route_vty_out(struct vty *vty, const struct prefix *p, } /* called from terminal list command */ -void route_vty_out_tmp(struct vty *vty, struct prefix *p, struct attr *attr, - safi_t safi, bool use_json, json_object *json_ar) +void route_vty_out_tmp(struct vty *vty, const struct prefix *p, + struct attr *attr, safi_t safi, bool use_json, + json_object *json_ar) { json_object *json_status = NULL; json_object *json_net = NULL; @@ -8314,9 +8327,10 @@ void route_vty_out_overlay(struct vty *vty, const struct prefix *p, } /* dampening route */ -static void damp_route_vty_out(struct vty *vty, struct prefix *p, - struct bgp_path_info *path, int display, afi_t afi, - safi_t safi, bool use_json, json_object *json) +static void damp_route_vty_out(struct vty *vty, const struct prefix *p, + struct bgp_path_info *path, int display, + afi_t afi, safi_t safi, bool use_json, + json_object *json) { struct attr *attr; int len; @@ -8378,9 +8392,10 @@ static void damp_route_vty_out(struct vty *vty, struct prefix *p, } /* flap route */ -static void flap_route_vty_out(struct vty *vty, struct prefix *p, - struct bgp_path_info *path, int display, afi_t afi, - safi_t safi, bool use_json, json_object *json) +static void flap_route_vty_out(struct vty *vty, const struct prefix *p, + struct bgp_path_info *path, int display, + afi_t afi, safi_t safi, bool use_json, + json_object *json) { struct attr *attr; struct bgp_damp_info *bdi; @@ -8643,8 +8658,10 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, } if (safi == SAFI_EVPN) { if (!json_paths) { - bgp_evpn_route2str((struct prefix_evpn *)&bn->p, - buf2, sizeof(buf2)); + bgp_evpn_route2str( + (struct prefix_evpn *) + bgp_node_get_prefix(bn), + buf2, sizeof(buf2)); vty_out(vty, " Route %s", buf2); if (tag_buf[0] != '\0') vty_out(vty, " VNI %s", tag_buf); @@ -8664,11 +8681,14 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, rn = parent_ri->net; if (rn && rn->prn) { prn = rn->prn; - prefix_rd2str((struct prefix_rd *)&prn->p, + prefix_rd2str((struct prefix_rd *) + bgp_node_get_prefix(prn), buf1, sizeof(buf1)); if (is_pi_family_evpn(parent_ri)) { - bgp_evpn_route2str((struct prefix_evpn *)&rn->p, - buf2, sizeof(buf2)); + bgp_evpn_route2str( + (struct prefix_evpn *) + bgp_node_get_prefix(rn), + buf2, sizeof(buf2)); vty_out(vty, " Imported from %s:%s, VNI %s\n", buf1, buf2, tag_buf); } else vty_out(vty, " Imported from %s:%s\n", buf1, buf2); @@ -8713,10 +8733,22 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, json_object_string_add( json_path, "aggregatorId", inet_ntoa(attr->aggregator_addr)); + if (attr->aggregator_as == BGP_AS_ZERO) + json_object_boolean_true_add( + json_path, "aggregatorAsMalformed"); + else + json_object_boolean_false_add( + json_path, "aggregatorAsMalformed"); } else { - vty_out(vty, ", (aggregated by %u %s)", - attr->aggregator_as, - inet_ntoa(attr->aggregator_addr)); + if (attr->aggregator_as == BGP_AS_ZERO) + vty_out(vty, + ", (aggregated by %u(malformed) %s)", + attr->aggregator_as, + inet_ntoa(attr->aggregator_addr)); + else + vty_out(vty, ", (aggregated by %u %s)", + attr->aggregator_as, + inet_ntoa(attr->aggregator_addr)); } } @@ -8757,8 +8789,10 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, /* Line2 display Next-hop, Neighbor, Router-id */ /* Display the nexthop */ - if ((bn->p.family == AF_INET || bn->p.family == AF_ETHERNET - || bn->p.family == AF_EVPN) + const struct prefix *bn_p = bgp_node_get_prefix(bn); + + if ((bn_p->family == AF_INET || bn_p->family == AF_ETHERNET + || bn_p->family == AF_EVPN) && (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP || safi == SAFI_EVPN || !BGP_ATTR_NEXTHOP_AFI_IP6(attr))) { if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP @@ -8859,7 +8893,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, if (path->peer == bgp->peer_self) { if (safi == SAFI_EVPN - || (bn->p.family == AF_INET + || (bn_p->family == AF_INET && !BGP_ATTR_NEXTHOP_AFI_IP6(attr))) { if (json_paths) json_object_string_add(json_peer, "peerId", @@ -9529,6 +9563,8 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, /* Start processing of routes. */ for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { + const struct prefix *rn_p = bgp_node_get_prefix(rn); + pi = bgp_node_get_bgp_path_info(rn); if (pi == NULL) continue; @@ -9558,7 +9594,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, if (type == bgp_show_type_prefix_list) { struct prefix_list *plist = output_arg; - if (prefix_list_apply(plist, &rn->p) + if (prefix_list_apply(plist, rn_p) != PREFIX_PERMIT) continue; } @@ -9580,7 +9616,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, path.peer = pi->peer; path.attr = &dummy_attr; - ret = route_map_apply(rmap, &rn->p, RMAP_BGP, + ret = route_map_apply(rmap, rn_p, RMAP_BGP, &path); if (ret == RMAP_DENYMATCH) continue; @@ -9598,20 +9634,20 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, if (type == bgp_show_type_cidr_only) { uint32_t destination; - destination = ntohl(rn->p.u.prefix4.s_addr); + destination = ntohl(rn_p->u.prefix4.s_addr); if (IN_CLASSC(destination) - && rn->p.prefixlen == 24) + && rn_p->prefixlen == 24) continue; if (IN_CLASSB(destination) - && rn->p.prefixlen == 16) + && rn_p->prefixlen == 16) continue; if (IN_CLASSA(destination) - && rn->p.prefixlen == 8) + && rn_p->prefixlen == 8) continue; } if (type == bgp_show_type_prefix_longer) { p = output_arg; - if (!prefix_match(p, &rn->p)) + if (!prefix_match(p, rn_p)) continue; } if (type == bgp_show_type_community_all) { @@ -9724,14 +9760,16 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, } if (type == bgp_show_type_dampend_paths || type == bgp_show_type_damp_neighbor) - damp_route_vty_out(vty, &rn->p, pi, display, AFI_IP, - safi, use_json, json_paths); + damp_route_vty_out(vty, rn_p, pi, display, + AFI_IP, safi, use_json, + json_paths); else if (type == bgp_show_type_flap_statistics || type == bgp_show_type_flap_neighbor) - flap_route_vty_out(vty, &rn->p, pi, display, AFI_IP, - safi, use_json, json_paths); + flap_route_vty_out(vty, rn_p, pi, display, + AFI_IP, safi, use_json, + json_paths); else - route_vty_out(vty, &rn->p, pi, display, safi, + route_vty_out(vty, rn_p, pi, display, safi, json_paths); display++; } @@ -9741,28 +9779,25 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, if (!use_json) continue; - p = &rn->p; /* encode prefix */ - if (p->family == AF_FLOWSPEC) { + if (rn_p->family == AF_FLOWSPEC) { char retstr[BGP_FLOWSPEC_STRING_DISPLAY_MAX]; - bgp_fs_nlri_get_string((unsigned char *) - p->u.prefix_flowspec.ptr, - p->u.prefix_flowspec - .prefixlen, - retstr, - NLRI_STRING_FORMAT_MIN, - NULL); + bgp_fs_nlri_get_string( + (unsigned char *) + rn_p->u.prefix_flowspec.ptr, + rn_p->u.prefix_flowspec.prefixlen, + retstr, NLRI_STRING_FORMAT_MIN, NULL); if (first) - vty_out(vty, "\"%s/%d\": ", - retstr, - p->u.prefix_flowspec.prefixlen); + vty_out(vty, "\"%s/%d\": ", retstr, + rn_p->u.prefix_flowspec + .prefixlen); else - vty_out(vty, ",\"%s/%d\": ", - retstr, - p->u.prefix_flowspec.prefixlen); + vty_out(vty, ",\"%s/%d\": ", retstr, + rn_p->u.prefix_flowspec + .prefixlen); } else { - prefix2str(p, buf2, sizeof(buf2)); + prefix2str(rn_p, buf2, sizeof(buf2)); if (first) vty_out(vty, "\"%s\": ", buf2); else @@ -9828,8 +9863,10 @@ int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi, show_msg = (!use_json && type == bgp_show_type_normal); for (rn = bgp_table_top(table); rn; rn = next) { + const struct prefix *rn_p = bgp_node_get_prefix(rn); + next = bgp_route_next(rn); - if (prd_match && memcmp(rn->p.u.val, prd_match->val, 8) != 0) + if (prd_match && memcmp(rn_p->u.val, prd_match->val, 8) != 0) continue; itable = bgp_node_get_bgp_table_info(rn); @@ -9837,7 +9874,7 @@ int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi, struct prefix_rd prd; char rd[RD_ADDRSTRLEN]; - memcpy(&prd, &(rn->p), sizeof(struct prefix_rd)); + memcpy(&prd, rn_p, sizeof(struct prefix_rd)); prefix_rd2str(&prd, rd, sizeof(rd)); bgp_show_table(vty, bgp, safi, itable, type, output_arg, use_json, rd, next == NULL, &output_cum, @@ -9940,7 +9977,7 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, json_object *json) { struct bgp_path_info *pi; - struct prefix *p; + const struct prefix *p; struct peer *peer; struct listnode *node, *nnode; char buf1[RD_ADDRSTRLEN]; @@ -9968,7 +10005,7 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, mpls_label_t label = 0; json_object *json_adv_to = NULL; - p = &rn->p; + p = bgp_node_get_prefix(rn); has_valid_label = bgp_is_valid_label(&rn->local_label); if (has_valid_label) @@ -10228,7 +10265,9 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp, if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP) { for (rn = bgp_table_top(rib); rn; rn = bgp_route_next(rn)) { - if (prd && memcmp(rn->p.u.val, prd->val, 8) != 0) + const struct prefix *rn_p = bgp_node_get_prefix(rn); + + if (prd && memcmp(rn_p->u.val, prd->val, 8) != 0) continue; table = bgp_node_get_bgp_table_info(rn); if (!table) @@ -10237,15 +10276,16 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp, if ((rm = bgp_node_match(table, &match)) == NULL) continue; + const struct prefix *rm_p = bgp_node_get_prefix(rm); if (prefix_check - && rm->p.prefixlen != match.prefixlen) { + && rm_p->prefixlen != match.prefixlen) { bgp_unlock_node(rm); continue; } - bgp_show_path_info((struct prefix_rd *)&rn->p, rm, - vty, bgp, afi, safi, json, - pathtype, &display); + bgp_show_path_info((struct prefix_rd *)rn_p, rm, vty, + bgp, afi, safi, json, pathtype, + &display); bgp_unlock_node(rm); } @@ -10254,7 +10294,9 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp, bool is_exact_pfxlen_match = FALSE; for (rn = bgp_table_top(rib); rn; rn = bgp_route_next(rn)) { - if (prd && memcmp(rn->p.u.val, prd->val, 8) != 0) + const struct prefix *rn_p = bgp_node_get_prefix(rn); + + if (prd && memcmp(&rn_p->u.val, prd->val, 8) != 0) continue; table = bgp_node_get_bgp_table_info(rn); if (!table) @@ -10270,15 +10312,18 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp, */ for (rm = bgp_table_top(table); rm; rm = bgp_route_next(rm)) { + const struct prefix *rm_p = + bgp_node_get_prefix(rm); /* * Get prefixlen of the ip-prefix within type5 * evpn route */ - if (evpn_type5_prefix_match(&rm->p, - &match) && rm->info) { + if (evpn_type5_prefix_match(rm_p, &match) + && rm->info) { longest_pfx = rm; int type5_pfxlen = - bgp_evpn_get_type5_prefixlen(&rm->p); + bgp_evpn_get_type5_prefixlen( + rm_p); if (type5_pfxlen == match.prefixlen) { is_exact_pfxlen_match = TRUE; bgp_unlock_node(rm); @@ -10296,9 +10341,9 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp, rm = longest_pfx; bgp_lock_node(rm); - bgp_show_path_info((struct prefix_rd *)&rn->p, rm, - vty, bgp, afi, safi, json, - pathtype, &display); + bgp_show_path_info((struct prefix_rd *)rn_p, rm, vty, + bgp, afi, safi, json, pathtype, + &display); bgp_unlock_node(rm); } @@ -10315,8 +10360,9 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp, json_object_object_add(json, "paths", json_paths); } else { if ((rn = bgp_node_match(rib, &match)) != NULL) { + const struct prefix *rn_p = bgp_node_get_prefix(rn); if (!prefix_check - || rn->p.prefixlen == match.prefixlen) { + || rn_p->prefixlen == match.prefixlen) { bgp_show_path_info(NULL, rn, vty, bgp, afi, safi, json, pathtype, &display); @@ -11027,6 +11073,7 @@ static void bgp_table_stats_rn(struct bgp_node *rn, struct bgp_node *top, { struct bgp_node *prn = bgp_node_parent_nolock(rn); struct bgp_path_info *pi; + const struct prefix *rn_p; if (rn == top) return; @@ -11034,14 +11081,15 @@ static void bgp_table_stats_rn(struct bgp_node *rn, struct bgp_node *top, if (!bgp_node_has_bgp_path_info_data(rn)) return; + rn_p = bgp_node_get_prefix(rn); ts->counts[BGP_STATS_PREFIXES]++; - ts->counts[BGP_STATS_TOTPLEN] += rn->p.prefixlen; + ts->counts[BGP_STATS_TOTPLEN] += rn_p->prefixlen; #if 0 ts->counts[BGP_STATS_AVGPLEN] = ravg_tally (ts->counts[BGP_STATS_PREFIXES], ts->counts[BGP_STATS_AVGPLEN], - rn->p.prefixlen); + rn_p->prefixlen); #endif /* check if the prefix is included by any other announcements */ @@ -11052,7 +11100,7 @@ static void bgp_table_stats_rn(struct bgp_node *rn, struct bgp_node *top, ts->counts[BGP_STATS_UNAGGREGATEABLE]++; /* announced address space */ if (space) - ts->total_space += pow(2.0, space - rn->p.prefixlen); + ts->total_space += pow(2.0, space - rn_p->prefixlen); } else if (bgp_node_has_bgp_path_info_data(prn)) ts->counts[BGP_STATS_MAX_AGGREGATEABLE]++; @@ -11724,14 +11772,17 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi, /* Filter prefix using distribute list, * filter list or prefix list */ - if ((bgp_input_filter(peer, &rn->p, &attr, afi, - safi)) == FILTER_DENY) + const struct prefix *rn_p = + bgp_node_get_prefix(rn); + if ((bgp_input_filter(peer, rn_p, &attr, afi, + safi)) + == FILTER_DENY) route_filtered = true; /* Filter prefix using route-map */ - ret = bgp_input_modifier(peer, &rn->p, &attr, - afi, safi, rmap_name, NULL, 0, - NULL); + ret = bgp_input_modifier(peer, rn_p, &attr, afi, + safi, rmap_name, NULL, + 0, NULL); if (type == bgp_show_adj_route_filtered && !route_filtered && ret != RMAP_DENY) { @@ -11743,7 +11794,7 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi, (route_filtered || ret == RMAP_DENY)) filtered_count++; - route_vty_out_tmp(vty, &rn->p, &attr, safi, + route_vty_out_tmp(vty, rn_p, &attr, safi, use_json, json_ar); bgp_attr_undup(&attr, ain->attr); output_count++; @@ -11820,16 +11871,18 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi, header2 = 0; } + const struct prefix *rn_p = + bgp_node_get_prefix(rn); + attr = *adj->attr; ret = bgp_output_modifier( - peer, &rn->p, &attr, afi, safi, + peer, rn_p, &attr, afi, safi, rmap_name); if (ret != RMAP_DENY) { - route_vty_out_tmp(vty, &rn->p, - &attr, safi, - use_json, - json_ar); + route_vty_out_tmp( + vty, rn_p, &attr, safi, + use_json, json_ar); output_count++; } else { filtered_count++; @@ -12659,7 +12712,9 @@ static int bgp_clear_damp_route(struct vty *vty, const char *view_name, || (safi == SAFI_EVPN)) { for (rn = bgp_table_top(bgp->rib[AFI_IP][safi]); rn; rn = bgp_route_next(rn)) { - if (prd && memcmp(rn->p.u.val, prd->val, 8) != 0) + const struct prefix *rn_p = bgp_node_get_prefix(rn); + + if (prd && memcmp(rn_p->u.val, prd->val, 8) != 0) continue; table = bgp_node_get_bgp_table_info(rn); if (!table) @@ -12667,8 +12722,10 @@ static int bgp_clear_damp_route(struct vty *vty, const char *view_name, if ((rm = bgp_node_match(table, &match)) == NULL) continue; + const struct prefix *rm_p = bgp_node_get_prefix(rn); + if (!prefix_check - || rm->p.prefixlen == match.prefixlen) { + || rm_p->prefixlen == match.prefixlen) { pi = bgp_node_get_bgp_path_info(rm); while (pi) { if (pi->extra && pi->extra->damp_info) { @@ -12687,8 +12744,10 @@ static int bgp_clear_damp_route(struct vty *vty, const char *view_name, } else { if ((rn = bgp_node_match(bgp->rib[afi][safi], &match)) != NULL) { + const struct prefix *rn_p = bgp_node_get_prefix(rn); + if (!prefix_check - || rn->p.prefixlen == match.prefixlen) { + || rn_p->prefixlen == match.prefixlen) { pi = bgp_node_get_bgp_path_info(rn); while (pi) { if (pi->extra && pi->extra->damp_info) { @@ -12824,8 +12883,8 @@ static void bgp_config_write_network_vpn(struct vty *vty, struct bgp *bgp, struct bgp_node *prn; struct bgp_node *rn; struct bgp_table *table; - struct prefix *p; - struct prefix_rd *prd; + const struct prefix *p; + const struct prefix_rd *prd; struct bgp_static *bgp_static; mpls_label_t label; char buf[SU_ADDRSTRLEN]; @@ -12843,8 +12902,9 @@ static void bgp_config_write_network_vpn(struct vty *vty, struct bgp *bgp, if (bgp_static == NULL) continue; - p = &rn->p; - prd = (struct prefix_rd *)&prn->p; + p = bgp_node_get_prefix(rn); + prd = (const struct prefix_rd *)bgp_node_get_prefix( + prn); /* "network" configuration display. */ prefix_rd2str(prd, rdbuf, sizeof(rdbuf)); @@ -12875,8 +12935,8 @@ static void bgp_config_write_network_evpn(struct vty *vty, struct bgp *bgp, struct bgp_node *prn; struct bgp_node *rn; struct bgp_table *table; - struct prefix *p; - struct prefix_rd *prd; + const struct prefix *p; + const struct prefix_rd *prd; struct bgp_static *bgp_static; char buf[PREFIX_STRLEN * 2]; char buf2[SU_ADDRSTRLEN]; @@ -12902,8 +12962,8 @@ static void bgp_config_write_network_evpn(struct vty *vty, struct bgp *bgp, bgp_static->router_mac, NULL, 0); if (bgp_static->eth_s_id) esi = esi2str(bgp_static->eth_s_id); - p = &rn->p; - prd = (struct prefix_rd *)&prn->p; + p = bgp_node_get_prefix(rn); + prd = (struct prefix_rd *)bgp_node_get_prefix(prn); /* "network" configuration display. */ prefix_rd2str(prd, rdbuf, sizeof(rdbuf)); @@ -12946,7 +13006,7 @@ void bgp_config_write_network(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi) { struct bgp_node *rn; - struct prefix *p; + const struct prefix *p; struct bgp_static *bgp_static; struct bgp_aggregate *bgp_aggregate; char buf[SU_ADDRSTRLEN]; @@ -12968,7 +13028,7 @@ void bgp_config_write_network(struct vty *vty, struct bgp *bgp, afi_t afi, if (bgp_static == NULL) continue; - p = &rn->p; + p = bgp_node_get_prefix(rn); vty_out(vty, " network %s/%d", inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), @@ -12994,7 +13054,7 @@ void bgp_config_write_network(struct vty *vty, struct bgp *bgp, afi_t afi, if (bgp_aggregate == NULL) continue; - p = &rn->p; + p = bgp_node_get_prefix(rn); vty_out(vty, " aggregate-address %s/%d", inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), @@ -13039,15 +13099,11 @@ void bgp_config_write_distance(struct vty *vty, struct bgp *bgp, afi_t afi, for (rn = bgp_table_top(bgp_distance_table[afi][safi]); rn; rn = bgp_route_next(rn)) { bdistance = bgp_node_get_bgp_distance_info(rn); - if (bdistance != NULL) { - char buf[PREFIX_STRLEN]; - - vty_out(vty, " distance %d %s %s\n", - bdistance->distance, - prefix2str(&rn->p, buf, sizeof(buf)), + if (bdistance != NULL) + vty_out(vty, " distance %d %pRN %s\n", + bdistance->distance, rn, bdistance->access_list ? bdistance->access_list : ""); - } } } diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 5f6a15891c..9438b328ad 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -544,9 +544,10 @@ extern void bgp_static_add(struct bgp *); extern void bgp_static_delete(struct bgp *); extern void bgp_static_redo_import_check(struct bgp *); extern void bgp_purge_static_redist_routes(struct bgp *bgp); -extern void bgp_static_update(struct bgp *, struct prefix *, - struct bgp_static *, afi_t, safi_t); -extern void bgp_static_withdraw(struct bgp *, struct prefix *, afi_t, safi_t); +extern void bgp_static_update(struct bgp *bgp, const struct prefix *p, + struct bgp_static *s, afi_t afi, safi_t safi); +extern void bgp_static_withdraw(struct bgp *bgp, const struct prefix *p, + afi_t afi, safi_t safi); extern int bgp_static_set_safi(afi_t afi, safi_t safi, struct vty *vty, const char *, const char *, const char *, @@ -584,10 +585,12 @@ extern void bgp_config_write_network(struct vty *, struct bgp *, afi_t, safi_t); extern void bgp_config_write_distance(struct vty *, struct bgp *, afi_t, safi_t); -extern void bgp_aggregate_delete(struct bgp *bgp, struct prefix *p, afi_t afi, - safi_t safi, struct bgp_aggregate *aggregate); -extern void bgp_aggregate_route(struct bgp *bgp, struct prefix *p, afi_t afi, - safi_t safi, struct bgp_aggregate *aggregate); +extern void bgp_aggregate_delete(struct bgp *bgp, const struct prefix *p, + afi_t afi, safi_t safi, + struct bgp_aggregate *aggregate); +extern void bgp_aggregate_route(struct bgp *bgp, const struct prefix *p, + afi_t afi, safi_t safi, + struct bgp_aggregate *aggregate); extern void bgp_aggregate_increment(struct bgp *bgp, const struct prefix *p, struct bgp_path_info *path, afi_t afi, safi_t safi); @@ -613,7 +616,7 @@ extern void route_vty_out(struct vty *vty, const struct prefix *p, extern void route_vty_out_tag(struct vty *vty, const struct prefix *p, struct bgp_path_info *path, int display, safi_t safi, json_object *json); -extern void route_vty_out_tmp(struct vty *vty, struct prefix *p, +extern void route_vty_out_tmp(struct vty *vty, const struct prefix *p, struct attr *attr, safi_t safi, bool use_json, json_object *json_ar); extern void route_vty_out_overlay(struct vty *vty, const struct prefix *p, diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 029570df35..2d92136450 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -1015,7 +1015,7 @@ route_match_rd(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { struct prefix_rd *prd_rule = NULL; - struct prefix_rd *prd_route = NULL; + const struct prefix_rd *prd_route = NULL; struct bgp_path_info *path = NULL; if (type == RMAP_BGP) { @@ -1028,7 +1028,8 @@ route_match_rd(void *rule, const struct prefix *prefix, if (path->net == NULL || path->net->prn == NULL) return RMAP_NOMATCH; - prd_route = (struct prefix_rd *)&path->net->prn->p; + prd_route = + (struct prefix_rd *)bgp_node_get_prefix(path->net->prn); if (memcmp(prd_route->val, prd_rule->val, ECOMMUNITY_SIZE) == 0) return RMAP_MATCH; } @@ -3637,14 +3638,17 @@ static void bgp_route_map_process_update(struct bgp *bgp, const char *rmap_name, bgp_static->rmap.map = map; if (route_update && !bgp_static->backdoor) { - if (bgp_debug_zebra(&bn->p)) + const struct prefix *bn_p = + bgp_node_get_prefix(bn); + + if (bgp_debug_zebra(bn_p)) zlog_debug( "Processing route_map %s update on static route %s", rmap_name, - inet_ntop(bn->p.family, - &bn->p.u.prefix, buf, + inet_ntop(bn_p->family, + &bn_p->u.prefix, buf, INET6_ADDRSTRLEN)); - bgp_static_update(bgp, &bn->p, bgp_static, afi, + bgp_static_update(bgp, bn_p, bgp_static, afi, safi); } } @@ -3666,14 +3670,17 @@ static void bgp_route_map_process_update(struct bgp *bgp, const char *rmap_name, aggregate->rmap.map = map; if (route_update) { - if (bgp_debug_zebra(&bn->p)) + const struct prefix *bn_p = + bgp_node_get_prefix(bn); + + if (bgp_debug_zebra(bn_p)) zlog_debug( "Processing route_map %s update on aggregate-address route %s", rmap_name, - inet_ntop(bn->p.family, - &bn->p.u.prefix, buf, + inet_ntop(bn_p->family, + &bn_p->u.prefix, buf, INET6_ADDRSTRLEN)); - bgp_aggregate_route(bgp, &bn->p, afi, safi, + bgp_aggregate_route(bgp, bn_p, afi, safi, aggregate); } } diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c index ee1c49666b..e40c7231a7 100644 --- a/bgpd/bgp_rpki.c +++ b/bgpd/bgp_rpki.c @@ -438,10 +438,10 @@ static void revalidate_bgp_node(struct bgp_node *bgp_node, afi_t afi, label = path->extra->label; num_labels = path->extra->num_labels; } - ret = bgp_update(ain->peer, &bgp_node->p, ain->addpath_rx_id, - ain->attr, afi, safi, ZEBRA_ROUTE_BGP, - BGP_ROUTE_NORMAL, NULL, label, num_labels, 1, - NULL); + ret = bgp_update(ain->peer, bgp_node_get_prefix(bgp_node), + ain->addpath_rx_id, ain->attr, afi, safi, + ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, label, + num_labels, 1, NULL); if (ret < 0) return; diff --git a/bgpd/bgp_snmp.c b/bgpd/bgp_snmp.c index 5cf0b73984..28eea46a5a 100644 --- a/bgpd/bgp_snmp.c +++ b/bgpd/bgp_snmp.c @@ -761,20 +761,23 @@ static struct bgp_path_info *bgp4PathAttrLookup(struct variable *v, oid name[], } if (min) { + const struct prefix *rn_p = + bgp_node_get_prefix(rn); + *length = v->namelen + BGP_PATHATTR_ENTRY_OFFSET; offset = name + v->namelen; - oid_copy_addr(offset, &rn->p.u.prefix4, + oid_copy_addr(offset, &rn_p->u.prefix4, IN_ADDR_SIZE); offset += IN_ADDR_SIZE; - *offset = rn->p.prefixlen; + *offset = rn_p->prefixlen; offset++; oid_copy_addr(offset, &min->peer->su.sin.sin_addr, IN_ADDR_SIZE); - addr->prefix = rn->p.u.prefix4; - addr->prefixlen = rn->p.prefixlen; + addr->prefix = rn_p->u.prefix4; + addr->prefixlen = rn_p->prefixlen; bgp_unlock_node(rn); diff --git a/bgpd/bgp_table.c b/bgpd/bgp_table.c index 3d74128da4..dcf9852a67 100644 --- a/bgpd/bgp_table.c +++ b/bgpd/bgp_table.c @@ -168,13 +168,22 @@ static struct bgp_node * bgp_route_next_until_maxlen(struct bgp_node *node, const struct bgp_node *limit, const uint8_t maxlen) { - if (node->l_left && node->p.prefixlen < maxlen - && node->l_left->p.prefixlen <= maxlen) { - return bgp_node_from_rnode(node->l_left); + const struct prefix *p = bgp_node_get_prefix(node); + + if (node->l_left) { + const struct prefix *left_p = + bgp_node_get_prefix(bgp_node_from_rnode(node->l_left)); + + if (p->prefixlen < maxlen && left_p->prefixlen <= maxlen) + return bgp_node_from_rnode(node->l_left); } - if (node->l_right && node->p.prefixlen < maxlen - && node->l_right->p.prefixlen <= maxlen) { - return bgp_node_from_rnode(node->l_right); + + if (node->l_right) { + const struct prefix *right_p = + bgp_node_get_prefix(bgp_node_from_rnode(node->l_right)); + + if (p->prefixlen < maxlen && right_p->prefixlen <= maxlen) + return bgp_node_from_rnode(node->l_right); } while (node->parent && node != limit) { @@ -197,24 +206,29 @@ void bgp_table_range_lookup(const struct bgp_table *table, if (node == NULL) return; - while (node && - node->p.prefixlen <= p->prefixlen && prefix_match(&node->p, p)) { + const struct prefix *node_p = bgp_node_get_prefix(node); + + while (node && node_p->prefixlen <= p->prefixlen + && prefix_match(node_p, p)) { if (bgp_node_has_bgp_path_info_data(node) - && node->p.prefixlen == p->prefixlen) { + && node_p->prefixlen == p->prefixlen) { matched = node; break; } node = bgp_node_from_rnode(node->link[prefix_bit( - &p->u.prefix, node->p.prefixlen)]); + &p->u.prefix, node_p->prefixlen)]); + node_p = bgp_node_get_prefix(node); } if (!node) return; - if (matched == NULL && node->p.prefixlen <= maxlen - && prefix_match(p, &node->p) && node->parent == NULL) + node_p = bgp_node_get_prefix(node); + if (matched == NULL && node_p->prefixlen <= maxlen + && prefix_match(p, node_p) && node->parent == NULL) matched = node; - else if ((matched == NULL && node->p.prefixlen > maxlen) || !node->parent) + else if ((matched == NULL && node_p->prefixlen > maxlen) + || !node->parent) return; else if (matched == NULL && node->parent) matched = node = bgp_node_from_rnode(node->parent); @@ -228,7 +242,8 @@ void bgp_table_range_lookup(const struct bgp_table *table, } while ((node = bgp_route_next_until_maxlen(node, matched, maxlen))) { - if (prefix_match(p, &node->p)) { + node_p = bgp_node_get_prefix(node); + if (prefix_match(p, node_p)) { if (bgp_node_has_bgp_path_info_data(node)) { bgp_lock_node(node); listnode_add(matches, node); diff --git a/bgpd/bgp_table.h b/bgpd/bgp_table.h index 7b468cc036..da2ca3181a 100644 --- a/bgpd/bgp_table.h +++ b/bgpd/bgp_table.h @@ -443,4 +443,13 @@ static inline bool bgp_node_has_bgp_path_info_data(struct bgp_node *node) return !!node->info; } +static inline const struct prefix *bgp_node_get_prefix(struct bgp_node *node) +{ + return &node->p; +} + +#ifdef _FRR_ATTRIBUTE_PRINTFRR +#pragma FRR printfrr_ext "%pRN" (struct bgp_node *) +#endif + #endif /* _QUAGGA_BGP_TABLE_H */ diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c index 34f80def8c..e40b3320ea 100644 --- a/bgpd/bgp_updgrp_adv.c +++ b/bgpd/bgp_updgrp_adv.c @@ -148,12 +148,9 @@ static int group_announce_route_walkcb(struct update_group *updgrp, void *arg) peer = UPDGRP_PEER(updgrp); addpath_capable = bgp_addpath_encode_tx(peer, afi, safi); - if (BGP_DEBUG(update, UPDATE_OUT)) { - char buf_prefix[PREFIX_STRLEN]; - prefix2str(&ctx->rn->p, buf_prefix, sizeof(buf_prefix)); - zlog_debug("%s: afi=%s, safi=%s, p=%s", __func__, afi2str(afi), - safi2str(safi), buf_prefix); - } + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("%s: afi=%s, safi=%s, p=%pRN", __func__, + afi2str(afi), safi2str(safi), ctx->rn); UPDGRP_FOREACH_SUBGRP (updgrp, subgrp) { @@ -242,7 +239,9 @@ static void subgrp_show_adjq_vty(struct update_subgroup *subgrp, output_count = 0; - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) + for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { + const struct prefix *rn_p = bgp_node_get_prefix(rn); + RB_FOREACH (adj, bgp_adj_out_rb, &rn->adj_out) if (adj->subgroup == subgrp) { if (header1) { @@ -261,20 +260,20 @@ static void subgrp_show_adjq_vty(struct update_subgroup *subgrp, } if ((flags & UPDWALK_FLAGS_ADVQUEUE) && adj->adv && adj->adv->baa) { - route_vty_out_tmp(vty, &rn->p, - adj->adv->baa->attr, - SUBGRP_SAFI(subgrp), - 0, NULL); + route_vty_out_tmp( + vty, rn_p, adj->adv->baa->attr, + SUBGRP_SAFI(subgrp), 0, NULL); output_count++; } if ((flags & UPDWALK_FLAGS_ADVERTISED) && adj->attr) { - route_vty_out_tmp( - vty, &rn->p, adj->attr, - SUBGRP_SAFI(subgrp), 0, NULL); + route_vty_out_tmp(vty, rn_p, adj->attr, + SUBGRP_SAFI(subgrp), + 0, NULL); output_count++; } } + } if (output_count != 0) vty_out(vty, "\nTotal number of prefixes %ld\n", output_count); } @@ -623,7 +622,9 @@ void subgroup_announce_table(struct update_subgroup *subgrp, PEER_FLAG_DEFAULT_ORIGINATE)) subgroup_default_originate(subgrp, 0); - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) + for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { + const struct prefix *rn_p = bgp_node_get_prefix(rn); + for (ri = bgp_node_get_bgp_path_info(rn); ri; ri = ri->next) if (CHECK_FLAG(ri->flags, BGP_PATH_SELECTED) @@ -632,7 +633,7 @@ void subgroup_announce_table(struct update_subgroup *subgrp, peer->addpath_type[afi][safi], ri))) { if (subgroup_announce_check(rn, ri, subgrp, - &rn->p, &attr)) + rn_p, &attr)) bgp_adj_out_set_subgroup(rn, subgrp, &attr, ri); else @@ -642,6 +643,7 @@ void subgroup_announce_table(struct update_subgroup *subgrp, peer, afi, safi, &ri->tx_addpath)); } + } /* * We walked through the whole table -- make sure our version number @@ -761,7 +763,8 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) for (rn = bgp_table_top(bgp->rib[afi][safi]); rn; rn = bgp_route_next(rn)) { ret = route_map_apply(peer->default_rmap[afi][safi].map, - &rn->p, RMAP_BGP, &bpi_rmap); + bgp_node_get_prefix(rn), RMAP_BGP, + &bpi_rmap); if (ret != RMAP_DENYMATCH) break; diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c index 6553211b0f..8d6fc1f6a2 100644 --- a/bgpd/bgp_updgrp_packet.c +++ b/bgpd/bgp_updgrp_packet.c @@ -726,8 +726,11 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp) adv = bgp_adv_fifo_first(&subgrp->sync->update); while (adv) { + const struct prefix *rn_p; + assert(adv->rn); rn = adv->rn; + rn_p = bgp_node_get_prefix(rn); adj = adv->adj; addpath_tx_id = adj->addpath_tx_id; path = adv->pathi; @@ -750,9 +753,8 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp) space_remaining = STREAM_CONCAT_REMAIN(s, snlri, STREAM_SIZE(s)) - BGP_MAX_PACKET_SIZE_OVERFLOW; - space_needed = - BGP_NLRI_LENGTH + addpath_overhead - + bgp_packet_mpattr_prefix_size(afi, safi, &rn->p); + space_needed = BGP_NLRI_LENGTH + addpath_overhead + + bgp_packet_mpattr_prefix_size(afi, safi, rn_p); /* When remaining space can't include NLRI and it's length. */ if (space_remaining < space_needed) @@ -798,7 +800,7 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp) - BGP_MAX_PACKET_SIZE_OVERFLOW; space_needed = BGP_NLRI_LENGTH + addpath_overhead + bgp_packet_mpattr_prefix_size( - afi, safi, &rn->p); + afi, safi, rn_p); /* If the attributes alone do not leave any room for * NLRI then @@ -828,12 +830,13 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp) if ((afi == AFI_IP && safi == SAFI_UNICAST) && !peer_cap_enhe(peer, afi, safi)) - stream_put_prefix_addpath(s, &rn->p, addpath_encode, + stream_put_prefix_addpath(s, rn_p, addpath_encode, addpath_tx_id); else { /* Encode the prefix in MP_REACH_NLRI attribute */ if (rn->prn) - prd = (struct prefix_rd *)&rn->prn->p; + prd = (struct prefix_rd *)bgp_node_get_prefix( + rn->prn); if (safi == SAFI_LABELED_UNICAST) { label = bgp_adv_label(rn, path, peer, afi, @@ -850,7 +853,7 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp) snlri, peer, afi, safi, &vecarr, adv->baa->attr); - bgp_packet_mpattr_prefix(snlri, afi, safi, &rn->p, prd, + bgp_packet_mpattr_prefix(snlri, afi, safi, rn_p, prd, label_pnt, num_labels, addpath_encode, addpath_tx_id, adv->baa->attr); @@ -858,7 +861,7 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp) num_pfx++; - if (bgp_debug_update(NULL, &rn->p, subgrp->update_group, 0)) { + if (bgp_debug_update(NULL, rn_p, subgrp->update_group, 0)) { char pfx_buf[BGP_PRD_PATH_STRLEN]; if (!send_attr_printed) { @@ -882,10 +885,10 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp) send_attr_printed = 1; } - bgp_debug_rdpfxpath2str(afi, safi, prd, &rn->p, - label_pnt, num_labels, - addpath_encode, addpath_tx_id, - pfx_buf, sizeof(pfx_buf)); + bgp_debug_rdpfxpath2str(afi, safi, prd, rn_p, label_pnt, + num_labels, addpath_encode, + addpath_tx_id, pfx_buf, + sizeof(pfx_buf)); zlog_debug("u%" PRIu64 ":s%" PRIu64 " send UPDATE %s", subgrp->update_group->id, subgrp->id, pfx_buf); @@ -964,7 +967,7 @@ struct bpacket *subgroup_withdraw_packet(struct update_subgroup *subgrp) int addpath_encode = 0; int addpath_overhead = 0; uint32_t addpath_tx_id = 0; - struct prefix_rd *prd = NULL; + const struct prefix_rd *prd = NULL; if (!subgrp) @@ -982,16 +985,19 @@ struct bpacket *subgroup_withdraw_packet(struct update_subgroup *subgrp) addpath_overhead = addpath_encode ? BGP_ADDPATH_ID_LEN : 0; while ((adv = bgp_adv_fifo_first(&subgrp->sync->withdraw)) != NULL) { + const struct prefix *rn_p; + assert(adv->rn); adj = adv->adj; rn = adv->rn; + rn_p = bgp_node_get_prefix(rn); addpath_tx_id = adj->addpath_tx_id; space_remaining = STREAM_WRITEABLE(s) - BGP_MAX_PACKET_SIZE_OVERFLOW; - space_needed = - BGP_NLRI_LENGTH + addpath_overhead + BGP_TOTAL_ATTR_LEN - + bgp_packet_mpattr_prefix_size(afi, safi, &rn->p); + space_needed = BGP_NLRI_LENGTH + addpath_overhead + + BGP_TOTAL_ATTR_LEN + + bgp_packet_mpattr_prefix_size(afi, safi, rn_p); if (space_remaining < space_needed) break; @@ -1004,13 +1010,15 @@ struct bpacket *subgroup_withdraw_packet(struct update_subgroup *subgrp) if (afi == AFI_IP && safi == SAFI_UNICAST && !peer_cap_enhe(peer, afi, safi)) - stream_put_prefix_addpath(s, &rn->p, addpath_encode, + stream_put_prefix_addpath(s, rn_p, addpath_encode, addpath_tx_id); else { if (rn->prn) - prd = (struct prefix_rd *)&rn->prn->p; + prd = (struct prefix_rd *)bgp_node_get_prefix( + rn->prn); - /* If first time, format the MP_UNREACH header */ + /* If first time, format the MP_UNREACH header + */ if (first_time) { iana_afi_t pkt_afi; iana_safi_t pkt_safi; @@ -1019,8 +1027,8 @@ struct bpacket *subgroup_withdraw_packet(struct update_subgroup *subgrp) pkt_safi = safi_int2iana(safi); attrlen_pos = stream_get_endp(s); - /* total attr length = 0 for now. reevaluate - * later */ + /* total attr length = 0 for now. + * reevaluate later */ stream_putw(s, 0); mp_start = stream_get_endp(s); mplen_pos = bgp_packet_mpunreach_start(s, afi, @@ -1034,17 +1042,17 @@ struct bpacket *subgroup_withdraw_packet(struct update_subgroup *subgrp) subgrp->id, pkt_afi, pkt_safi); } - bgp_packet_mpunreach_prefix(s, &rn->p, afi, safi, prd, + bgp_packet_mpunreach_prefix(s, rn_p, afi, safi, prd, NULL, 0, addpath_encode, addpath_tx_id, NULL); } num_pfx++; - if (bgp_debug_update(NULL, &rn->p, subgrp->update_group, 0)) { + if (bgp_debug_update(NULL, rn_p, subgrp->update_group, 0)) { char pfx_buf[BGP_PRD_PATH_STRLEN]; - bgp_debug_rdpfxpath2str(afi, safi, prd, &rn->p, NULL, 0, + bgp_debug_rdpfxpath2str(afi, safi, prd, rn_p, NULL, 0, addpath_encode, addpath_tx_id, pfx_buf, sizeof(pfx_buf)); zlog_debug("u%" PRIu64 ":s%" PRIu64 diff --git a/bgpd/bgp_vpn.c b/bgpd/bgp_vpn.c index b67b0c322e..2fd2443512 100644 --- a/bgpd/bgp_vpn.c +++ b/bgpd/bgp_vpn.c @@ -78,7 +78,9 @@ int show_adj_route_vpn(struct vty *vty, struct peer *peer, for (rn = bgp_table_top(bgp->rib[afi][safi]); rn; rn = bgp_route_next(rn)) { - if (prd && memcmp(rn->p.u.val, prd->val, 8) != 0) + const struct prefix *rn_p = bgp_node_get_prefix(rn); + + if (prd && memcmp(rn_p->u.val, prd->val, 8) != 0) continue; table = bgp_node_get_bgp_table_info(rn); @@ -156,9 +158,9 @@ int show_adj_route_vpn(struct vty *vty, struct peer *peer, #if ENABLE_BGP_VNC struct rd_vnc_eth rd_vnc_eth = {0}; #endif - uint8_t *pnt; + const uint8_t *pnt; - pnt = rn->p.u.val; + pnt = rn_p->u.val; /* Decode RD type. */ type = decode_rd_type(pnt); @@ -221,9 +223,8 @@ int show_adj_route_vpn(struct vty *vty, struct peer *peer, } rd_header = 0; } - route_vty_out_tmp(vty, &rm->p, attr, - safi, use_json, - json_routes); + route_vty_out_tmp(vty, bgp_node_get_prefix(rm), attr, + safi, use_json, json_routes); output_count++; } diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 544ca38a18..a35bfe47ed 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -6590,6 +6590,7 @@ DEFUN(no_neighbor_maximum_prefix_out, if (!peer) return CMD_WARNING_CONFIG_FAILED; + UNSET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_OUT); peer->pmax_out[afi][safi] = 0; return CMD_SUCCESS; @@ -7955,27 +7956,32 @@ static int bgp_clear_prefix(struct vty *vty, const char *view_name, if (safi == SAFI_MPLS_VPN) { for (rn = bgp_table_top(rib); rn; rn = bgp_route_next(rn)) { - if (prd && memcmp(rn->p.u.val, prd->val, 8) != 0) + const struct prefix *rn_p = bgp_node_get_prefix(rn); + + if (prd && memcmp(rn_p->u.val, prd->val, 8) != 0) continue; table = bgp_node_get_bgp_table_info(rn); - if (table != NULL) { + if (table == NULL) + continue; - if ((rm = bgp_node_match(table, &match)) - != NULL) { - if (rm->p.prefixlen - == match.prefixlen) { - SET_FLAG(rm->flags, - BGP_NODE_USER_CLEAR); - bgp_process(bgp, rm, afi, safi); - } - bgp_unlock_node(rm); + if ((rm = bgp_node_match(table, &match)) != NULL) { + const struct prefix *rm_p = + bgp_node_get_prefix(rm); + + if (rm_p->prefixlen == match.prefixlen) { + SET_FLAG(rm->flags, + BGP_NODE_USER_CLEAR); + bgp_process(bgp, rm, afi, safi); } + bgp_unlock_node(rm); } } } else { if ((rn = bgp_node_match(rib, &match)) != NULL) { - if (rn->p.prefixlen == match.prefixlen) { + const struct prefix *rn_p = bgp_node_get_prefix(rn); + + if (rn_p->prefixlen == match.prefixlen) { SET_FLAG(rn->flags, BGP_NODE_USER_CLEAR); bgp_process(bgp, rn, afi, safi); } @@ -9030,11 +9036,20 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, json_object_int_add(json_peer, "msgSent", PEER_TOTAL_TX(peer)); + atomic_size_t outq_count, inq_count; + outq_count = atomic_load_explicit( + &peer->obuf->count, + memory_order_relaxed); + inq_count = atomic_load_explicit( + &peer->ibuf->count, + memory_order_relaxed); + json_object_int_add(json_peer, "tableVersion", peer->version[afi][safi]); json_object_int_add(json_peer, "outq", - peer->obuf->count); - json_object_int_add(json_peer, "inq", 0); + outq_count); + json_object_int_add(json_peer, "inq", + inq_count); peer_uptime(peer->uptime, timebuf, BGP_UPTIME_LEN, use_json, json_peer); @@ -9116,10 +9131,21 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, vty_out(vty, "%*s", max_neighbor_width - len, " "); - vty_out(vty, "4 %10u %9u %9u %8" PRIu64 " %4d %4zd %8s", + atomic_size_t outq_count, inq_count; + outq_count = atomic_load_explicit( + &peer->obuf->count, + memory_order_relaxed); + inq_count = atomic_load_explicit( + &peer->ibuf->count, + memory_order_relaxed); + + vty_out(vty, + "4 %10u %9u %9u %8" PRIu64 + " %4zu %4zu %8s", peer->as, PEER_TOTAL_RX(peer), - PEER_TOTAL_TX(peer), peer->version[afi][safi], - 0, peer->obuf->count, + PEER_TOTAL_TX(peer), + peer->version[afi][safi], inq_count, + outq_count, peer_uptime(peer->uptime, timebuf, BGP_UPTIME_LEN, 0, NULL)); @@ -11703,9 +11729,17 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, json_object *json_stat = NULL; json_stat = json_object_new_object(); /* Packet counts. */ - json_object_int_add(json_stat, "depthInq", 0); + + atomic_size_t outq_count, inq_count; + outq_count = atomic_load_explicit(&p->obuf->count, + memory_order_relaxed); + inq_count = atomic_load_explicit(&p->ibuf->count, + memory_order_relaxed); + + json_object_int_add(json_stat, "depthInq", + (unsigned long)inq_count); json_object_int_add(json_stat, "depthOutq", - (unsigned long)p->obuf->count); + (unsigned long)outq_count); json_object_int_add(json_stat, "opensSent", atomic_load_explicit(&p->open_out, memory_order_relaxed)); @@ -11746,11 +11780,16 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, json_object_int_add(json_stat, "totalRecv", PEER_TOTAL_RX(p)); json_object_object_add(json_neigh, "messageStats", json_stat); } else { + atomic_size_t outq_count, inq_count; + outq_count = atomic_load_explicit(&p->obuf->count, + memory_order_relaxed); + inq_count = atomic_load_explicit(&p->ibuf->count, + memory_order_relaxed); + /* Packet counts. */ vty_out(vty, " Message statistics:\n"); - vty_out(vty, " Inq depth is 0\n"); - vty_out(vty, " Outq depth is %lu\n", - (unsigned long)p->obuf->count); + vty_out(vty, " Inq depth is %zu\n", inq_count); + vty_out(vty, " Outq depth is %zu\n", outq_count); vty_out(vty, " Sent Rcvd\n"); vty_out(vty, " Opens: %10d %10d\n", atomic_load_explicit(&p->open_out, diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 95b6548c74..404f17f69e 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -1184,7 +1184,8 @@ void bgp_zebra_announce(struct bgp_node *rn, const struct prefix *p, prefix2str(p, buf_prefix, sizeof(buf_prefix)); if (safi == SAFI_FLOWSPEC) { - bgp_pbr_update_entry(bgp, &rn->p, info, afi, safi, true); + bgp_pbr_update_entry(bgp, bgp_node_get_prefix(rn), + info, afi, safi, true); return; } @@ -1480,8 +1481,8 @@ void bgp_zebra_announce_table(struct bgp *bgp, afi_t afi, safi_t safi) && (pi->sub_type == BGP_ROUTE_NORMAL || pi->sub_type == BGP_ROUTE_IMPORTED))) - bgp_zebra_announce(rn, &rn->p, pi, bgp, afi, - safi); + bgp_zebra_announce(rn, bgp_node_get_prefix(rn), + pi, bgp, afi, safi); } void bgp_zebra_withdraw(const struct prefix *p, struct bgp_path_info *info, diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 9452770100..6534ac1900 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -4199,6 +4199,19 @@ static int peer_af_flag_modify(struct peer *peer, afi_t afi, safi_t safi, } } + /* + * If the peer is a route server client let's not + * muck with the nexthop on the way out the door + */ + if (flag & PEER_FLAG_RSERVER_CLIENT) { + if (set) + SET_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_NEXTHOP_UNCHANGED); + else + UNSET_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_NEXTHOP_UNCHANGED); + } + /* Inherit from peer-group or set/unset flags accordingly. */ if (peer_group_active(peer) && set == invert) peer_af_flag_inherit(peer, afi, safi, flag); diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c index 4d7fccbe93..d058fe3b28 100644 --- a/bgpd/rfapi/rfapi_import.c +++ b/bgpd/rfapi/rfapi_import.c @@ -4213,9 +4213,11 @@ static void rfapiBgpTableFilteredImport(struct bgp *bgp, safi))( it, /* which import table */ FIF_ACTION_UPDATE, bpi->peer, - NULL, &rn2->p, /* prefix */ + NULL, bgp_node_get_prefix(rn2), NULL, afi, - (struct prefix_rd *)&rn1->p, + (struct prefix_rd *) + bgp_node_get_prefix( + rn1), bpi->attr, bpi->type, bpi->sub_type, &label); } diff --git a/bgpd/rfapi/rfapi_monitor.c b/bgpd/rfapi/rfapi_monitor.c index 6aea18e0b4..0b8dfc3554 100644 --- a/bgpd/rfapi/rfapi_monitor.c +++ b/bgpd/rfapi/rfapi_monitor.c @@ -1273,7 +1273,7 @@ static void rfapiMonitorEthDetachImport( #if DEBUG_L2_EXTRA char buf_prefix[PREFIX_STRLEN]; - prefix2str(&rn->p, buf_prefix, sizeof(buf_prefix)); + prefix2str(agg_node_get_prefix(rn), buf_prefix, sizeof(buf_prefix)); #endif /* diff --git a/bgpd/rfapi/rfapi_rib.c b/bgpd/rfapi/rfapi_rib.c index c64658181c..04a538dc63 100644 --- a/bgpd/rfapi/rfapi_rib.c +++ b/bgpd/rfapi/rfapi_rib.c @@ -1813,7 +1813,8 @@ int rfapiRibFTDFilterRecentPrefix( { char buf_pfx[PREFIX_STRLEN]; - prefix2str(&it_rn->p, buf_pfx, sizeof(buf_pfx)); + prefix2str(agg_node_get_prefix(it_rn), buf_pfx, + sizeof(buf_pfx)); vnc_zlog_debug_verbose("%s: prefix %s", __func__, buf_pfx); } #endif @@ -1994,7 +1995,8 @@ rfapiRibPreload(struct bgp *bgp, struct rfapi_descriptor *rfd, } vnc_zlog_debug_verbose( "%s: RIB skiplist for this prefix follows", __func__); - rfapiRibShowRibSl(NULL, &rn->p, (struct skiplist *)rn->info); + rfapiRibShowRibSl(NULL, agg_node_get_prefix(rn), + (struct skiplist *)rn->info); #endif diff --git a/bgpd/rfapi/vnc_export_bgp.c b/bgpd/rfapi/vnc_export_bgp.c index 6e928c2a4e..a7aa4c66fa 100644 --- a/bgpd/rfapi/vnc_export_bgp.c +++ b/bgpd/rfapi/vnc_export_bgp.c @@ -487,9 +487,9 @@ static void vnc_direct_bgp_vpn_disable_ce(struct bgp *bgp, afi_t afi) && ri->sub_type == BGP_ROUTE_REDISTRIBUTE) { bgp_withdraw( - ri->peer, &rn->p, /* prefix */ - 0, /* addpath_id */ - NULL, /* ignored */ + ri->peer, bgp_node_get_prefix(rn), + 0, /* addpath_id */ + NULL, /* ignored */ AFI_IP, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, @@ -1841,11 +1841,12 @@ void vnc_direct_bgp_rh_vpn_enable(struct bgp *bgp, afi_t afi) struct bgp_table *table; struct bgp_node *rn; struct bgp_path_info *ri; + const struct prefix *prn_p = bgp_node_get_prefix(prn); memset(&prd, 0, sizeof(prd)); prd.family = AF_UNSPEC; prd.prefixlen = 64; - memcpy(prd.val, prn->p.u.val, 8); + memcpy(prd.val, prn_p->u.val, 8); /* This is the per-RD table of prefixes */ table = bgp_node_get_bgp_table_info(prn); @@ -1854,6 +1855,7 @@ void vnc_direct_bgp_rh_vpn_enable(struct bgp *bgp, afi_t afi) continue; for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { + const struct prefix *rn_p; /* * skip prefix list check if no routes here @@ -1861,21 +1863,17 @@ void vnc_direct_bgp_rh_vpn_enable(struct bgp *bgp, afi_t afi) if (!bgp_node_has_bgp_path_info_data(rn)) continue; - { - char prefixstr[PREFIX_STRLEN]; + vnc_zlog_debug_verbose("%s: checking prefix %pRN", + __func__, rn); - prefix2str(&rn->p, prefixstr, - sizeof(prefixstr)); - vnc_zlog_debug_verbose("%s: checking prefix %s", - __func__, prefixstr); - } + rn_p = bgp_node_get_prefix(rn); /* * prefix list check */ if (hc->plist_export_bgp[afi]) { if (prefix_list_apply(hc->plist_export_bgp[afi], - &rn->p) + rn_p) == PREFIX_DENY) { vnc_zlog_debug_verbose( @@ -1920,8 +1918,7 @@ void vnc_direct_bgp_rh_vpn_enable(struct bgp *bgp, afi_t afi) info.attr = &hattr; ret = route_map_apply( hc->routemap_export_bgp, - &rn->p, RMAP_BGP, - &info); + rn_p, RMAP_BGP, &info); if (ret == RMAP_DENYMATCH) { bgp_attr_flush(&hattr); vnc_zlog_debug_verbose( @@ -1940,7 +1937,7 @@ void vnc_direct_bgp_rh_vpn_enable(struct bgp *bgp, afi_t afi) * this route */ eti = vnc_eti_get( - bgp, EXPORT_TYPE_BGP, &rn->p, + bgp, EXPORT_TYPE_BGP, rn_p, ri->peer, ZEBRA_ROUTE_VNC_DIRECT_RH, BGP_ROUTE_REDISTRIBUTE); @@ -1961,19 +1958,19 @@ void vnc_direct_bgp_rh_vpn_enable(struct bgp *bgp, afi_t afi) "%s: calling bgp_update", __func__); - bgp_update( - ri->peer, &rn->p, /* prefix */ - 0, /* addpath_id */ - iattr, /* bgp_update copies - it */ - AFI_IP, SAFI_UNICAST, - ZEBRA_ROUTE_VNC_DIRECT_RH, - BGP_ROUTE_REDISTRIBUTE, NULL, - /* RD not used for unicast */ - NULL, - /* tag not used for unicast, - or EVPN */ - 0, 0, NULL); /* EVPN not used */ + bgp_update(ri->peer, rn_p, /* prefix */ + 0, /* addpath_id */ + iattr, /* bgp_update copies + it */ + AFI_IP, SAFI_UNICAST, + ZEBRA_ROUTE_VNC_DIRECT_RH, + BGP_ROUTE_REDISTRIBUTE, NULL, + /* RD not used for unicast */ + NULL, + /* tag not used for unicast, + or EVPN */ + 0, 0, + NULL); /* EVPN not used */ bgp_attr_unintern(&iattr); } @@ -2002,7 +1999,7 @@ void vnc_direct_bgp_rh_vpn_disable(struct bgp *bgp, afi_t afi) */ for (rn = bgp_table_top(bgp->rib[afi][SAFI_UNICAST]); rn; rn = bgp_route_next(rn)) { - + const struct prefix *rn_p = bgp_node_get_prefix(rn); struct bgp_path_info *ri; struct bgp_path_info *next; @@ -2019,7 +2016,7 @@ void vnc_direct_bgp_rh_vpn_disable(struct bgp *bgp, afi_t afi) * Delete routes immediately (no timer) */ eti = vnc_eti_checktimer( - bgp, EXPORT_TYPE_BGP, &rn->p, ri->peer, + bgp, EXPORT_TYPE_BGP, rn_p, ri->peer, ZEBRA_ROUTE_VNC_DIRECT_RH, BGP_ROUTE_REDISTRIBUTE); if (eti) { @@ -2028,8 +2025,8 @@ void vnc_direct_bgp_rh_vpn_disable(struct bgp *bgp, afi_t afi) vnc_eti_delete(eti); } - bgp_withdraw(ri->peer, &rn->p, /* prefix */ - 0, /* addpath_id */ + bgp_withdraw(ri->peer, rn_p, /* prefix */ + 0, /* addpath_id */ NULL, /* ignored */ AFI_IP, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT_RH, diff --git a/bgpd/rfapi/vnc_export_table.c b/bgpd/rfapi/vnc_export_table.c index 8bf1479a25..255f868bdf 100644 --- a/bgpd/rfapi/vnc_export_table.c +++ b/bgpd/rfapi/vnc_export_table.c @@ -165,8 +165,9 @@ void vnc_eti_delete(struct vnc_export_info *goner) struct vnc_export_info *vnc_eti_checktimer(struct bgp *bgp, vnc_export_type_t etype, - struct prefix *p, struct peer *peer, - uint8_t type, uint8_t subtype) + const struct prefix *p, + struct peer *peer, uint8_t type, + uint8_t subtype) { struct agg_node *etn; struct vnc_export_info *eti; diff --git a/bgpd/rfapi/vnc_export_table.h b/bgpd/rfapi/vnc_export_table.h index 5dcf4e6c1d..8a1fc9aaef 100644 --- a/bgpd/rfapi/vnc_export_table.h +++ b/bgpd/rfapi/vnc_export_table.h @@ -58,8 +58,9 @@ vnc_eti_get(struct bgp *bgp, vnc_export_type_t etype, const struct prefix *p, extern void vnc_eti_delete(struct vnc_export_info *goner); extern struct vnc_export_info * -vnc_eti_checktimer(struct bgp *bgp, vnc_export_type_t etype, struct prefix *p, - struct peer *peer, uint8_t type, uint8_t subtype); +vnc_eti_checktimer(struct bgp *bgp, vnc_export_type_t etype, + const struct prefix *p, struct peer *peer, uint8_t type, + uint8_t subtype); #endif /* _QUAGGA_VNC_VNC_EXPORT_TABLE_H_ */ diff --git a/bgpd/rfapi/vnc_import_bgp.c b/bgpd/rfapi/vnc_import_bgp.c index 50256d9949..915dfaabf2 100644 --- a/bgpd/rfapi/vnc_import_bgp.c +++ b/bgpd/rfapi/vnc_import_bgp.c @@ -674,8 +674,9 @@ static void vnc_import_bgp_add_route_mode_resolve_nve( continue; vnc_import_bgp_add_route_mode_resolve_nve_one_rd( - (struct prefix_rd *)&bnp->p, table, afi, bgp, prefix, - ecom, &local_pref, med, &pfx_unicast_nexthop); + (struct prefix_rd *)bgp_node_get_prefix(bnp), table, + afi, bgp, prefix, ecom, &local_pref, med, + &pfx_unicast_nexthop); } @@ -1377,8 +1378,8 @@ vnc_import_bgp_del_route_mode_resolve_nve(struct bgp *bgp, afi_t afi, continue; vnc_import_bgp_del_route_mode_resolve_nve_one_rd( - (struct prefix_rd *)&bnp->p, table, afi, bgp, prefix, - &pfx_unicast_nexthop); /* TBD how is this set? */ + (struct prefix_rd *)bgp_node_get_prefix(bnp), table, + afi, bgp, prefix, &pfx_unicast_nexthop); } if (ecom) @@ -2754,7 +2755,8 @@ void vnc_import_bgp_redist_enable(struct bgp *bgp, afi_t afi) if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) continue; - vnc_import_bgp_add_route(bgp, &rn->p, bpi); + vnc_import_bgp_add_route(bgp, bgp_node_get_prefix(rn), + bpi); } } vnc_zlog_debug_verbose( @@ -2795,8 +2797,8 @@ void vnc_import_bgp_exterior_redist_enable(struct bgp *bgp, afi_t afi) if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) continue; - vnc_import_bgp_exterior_add_route(bgp_exterior, &rn->p, - bpi); + vnc_import_bgp_exterior_add_route( + bgp_exterior, bgp_node_get_prefix(rn), bpi); } } vnc_zlog_debug_verbose( @@ -2842,7 +2844,8 @@ void vnc_import_bgp_exterior_redist_enable_it( continue; vnc_import_bgp_exterior_add_route_it( - bgp_exterior, &rn->p, bpi, it_only); + bgp_exterior, bgp_node_get_prefix(rn), bpi, + it_only); } } } @@ -2872,58 +2875,49 @@ void vnc_import_bgp_redist_disable(struct bgp *bgp, afi_t afi) */ for (rn1 = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); rn1; rn1 = bgp_route_next(rn1)) { + const struct prefix *rn1_p; - if (bgp_node_has_bgp_path_info_data(rn1)) { - - for (rn2 = bgp_table_top( - bgp_node_get_bgp_table_info(rn1)); - rn2; rn2 = bgp_route_next(rn2)) { - - struct bgp_path_info *bpi; - struct bgp_path_info *nextbpi; - - for (bpi = bgp_node_get_bgp_path_info(rn2); bpi; - bpi = nextbpi) { - - nextbpi = bpi->next; + if (!bgp_node_has_bgp_path_info_data(rn1)) + continue; - if (bpi->type - == ZEBRA_ROUTE_BGP_DIRECT) { + rn1_p = bgp_node_get_prefix(rn1); + for (rn2 = bgp_table_top(bgp_node_get_bgp_table_info(rn1)); rn2; + rn2 = bgp_route_next(rn2)) { + const struct prefix *rn2_p = bgp_node_get_prefix(rn2); + struct bgp_path_info *bpi; + struct bgp_path_info *nextbpi; - struct rfapi_descriptor *rfd; - vncHDBgpDirect.peer = bpi->peer; + for (bpi = bgp_node_get_bgp_path_info(rn2); bpi; + bpi = nextbpi) { - assert(bpi->extra); + nextbpi = bpi->next; - rfd = bpi->extra->vnc.export - .rfapi_handle; + if (bpi->type != ZEBRA_ROUTE_BGP_DIRECT) + continue; - vnc_zlog_debug_verbose( - "%s: deleting bpi=%p, bpi->peer=%p, bpi->type=%d, bpi->sub_type=%d, bpi->extra->vnc.export.rfapi_handle=%p [passing rfd=%p]", - __func__, bpi, - bpi->peer, bpi->type, - bpi->sub_type, - (bpi->extra - ? bpi->extra - ->vnc - .export - .rfapi_handle - : NULL), - rfd); + struct rfapi_descriptor *rfd; + vncHDBgpDirect.peer = bpi->peer; + assert(bpi->extra); - del_vnc_route( - rfd, bpi->peer, bgp, - SAFI_MPLS_VPN, &rn2->p, - (struct prefix_rd *)&rn1 - ->p, - bpi->type, - bpi->sub_type, NULL, - 1); /* kill */ + rfd = bpi->extra->vnc.export.rfapi_handle; - vncHDBgpDirect.peer = NULL; - } - } + vnc_zlog_debug_verbose( + "%s: deleting bpi=%p, bpi->peer=%p, bpi->type=%d, bpi->sub_type=%d, bpi->extra->vnc.export.rfapi_handle=%p [passing rfd=%p]", + __func__, bpi, bpi->peer, bpi->type, + bpi->sub_type, + (bpi->extra ? bpi->extra->vnc.export + .rfapi_handle + : NULL), + rfd); + + del_vnc_route(rfd, bpi->peer, bgp, + SAFI_MPLS_VPN, rn2_p, + (struct prefix_rd *)rn1_p, + bpi->type, bpi->sub_type, NULL, + 1); /* kill */ + + vncHDBgpDirect.peer = NULL; } } } @@ -2979,8 +2973,9 @@ void vnc_import_bgp_exterior_redist_disable(struct bgp *bgp, afi_t afi) if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) continue; - vnc_import_bgp_exterior_del_route(bgp_exterior, - &rn->p, bpi); + vnc_import_bgp_exterior_del_route( + bgp_exterior, bgp_node_get_prefix(rn), + bpi); } } #if DEBUG_RHN_LIST diff --git a/bgpd/rfapi/vnc_zebra.c b/bgpd/rfapi/vnc_zebra.c index ca4a32822b..686dc394a7 100644 --- a/bgpd/rfapi/vnc_zebra.c +++ b/bgpd/rfapi/vnc_zebra.c @@ -304,10 +304,12 @@ static void vnc_redistribute_withdraw(struct bgp *bgp, afi_t afi, uint8_t type) */ for (prn = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); prn; prn = bgp_route_next(prn)) { + const struct prefix *prn_p = bgp_node_get_prefix(prn); + memset(&prd, 0, sizeof(prd)); prd.family = AF_UNSPEC; prd.prefixlen = 64; - memcpy(prd.val, prn->p.u.val, 8); + memcpy(prd.val, prn_p->u.val, 8); /* This is the per-RD table of prefixes */ table = bgp_node_get_bgp_table_info(prn); @@ -329,7 +331,7 @@ static void vnc_redistribute_withdraw(struct bgp *bgp, afi_t afi, uint8_t type) del_vnc_route( &vncHD1VR, /* use dummy ptr as cookie */ vncHD1VR.peer, bgp, SAFI_MPLS_VPN, - &(rn->p), &prd, type, + bgp_node_get_prefix(rn), &prd, type, BGP_ROUTE_REDISTRIBUTE, NULL, 0); } } diff --git a/configure.ac b/configure.ac index d1391c67b2..fe389ebb35 100755 --- a/configure.ac +++ b/configure.ac @@ -182,13 +182,13 @@ dnl - specifically, options to control warnings AC_USE_SYSTEM_EXTENSIONS AC_DEFUN([AC_C_FLAG], [{ - m4_pushdef([cachename],[m4_translit([frr_cv_$1],[ =-+],[____])]) + m4_pushdef([cachename],[m4_translit([frr_cv_$1],[ =-+/{}$],[________])]) AC_CACHE_CHECK([[whether $CC supports $1]], cachename, [ AC_LANG_PUSH([C]) ac_c_flag_save="$CFLAGS" CFLAGS="$CFLAGS $1" AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM([[]])], + [AC_LANG_PROGRAM([[$4]])], [ cachename=yes ], [ @@ -354,6 +354,44 @@ if test "$enable_undefined_sanitizer" = "yes"; then fi AC_SUBST([SAN_FLAGS]) +dnl frr-format.so +if test "$with_frr_format" != "no" -a "$with_frr_format" != "yes" -a -n "$with_frr_format"; then + AC_C_FLAG([-fplugin=${with_frr_format}], [ + AC_MSG_ERROR([specified frr-format plugin ($with_frr_format) does not work]) + ],,[ +#ifndef _FRR_ATTRIBUTE_PRINTFRR +#error plugin not loaded +#endif +#if _FRR_ATTRIBUTE_PRINTFRR < 0x10000 +#error plugin too old +#endif + ]) +elif test "$with_frr_format" = "no"; then + : #nothing +else + AC_C_FLAG([-fplugin=tools/gcc-plugins/frr-format.so],[ + AC_C_FLAG([-fplugin=frr-format],[ + if test "$with_frr_format" = "yes"; then + AC_MSG_ERROR([frr-format plugin requested but not found]) + fi + ],,[ +#ifndef _FRR_ATTRIBUTE_PRINTFRR +#error plugin not loaded +#endif +#if _FRR_ATTRIBUTE_PRINTFRR < 0x10000 +#error plugin too old +#endif + ]) + ],,[ +#ifndef _FRR_ATTRIBUTE_PRINTFRR +#error plugin not loaded +#endif +#if _FRR_ATTRIBUTE_PRINTFRR < 0x10000 +#error plugin too old +#endif + ]) +fi + dnl ---------- dnl Essentials dnl ---------- @@ -600,6 +638,8 @@ AC_ARG_ENABLE([undefined-sanitizer], AS_HELP_STRING([--undefined-sanitizer], [enable UndefinedBehaviorSanitizer support for detecting undefined behavior])) AC_ARG_WITH([crypto], AS_HELP_STRING([--with-crypto=<internal|openssl>], [choose between different implementations of cryptographic functions(default value is --with-crypto=internal)])) +AC_ARG_WITH([frr-format], + AS_HELP_STRING([--with-frr-format[=<.../frr-format.so>]], [use frr-format GCC plugin])) #if openssl, else use the internal AS_IF([test "$with_crypto" = "openssl"], [ diff --git a/ldpd/lde.c b/ldpd/lde.c index 64c12e0df5..23a2dbd760 100644 --- a/ldpd/lde.c +++ b/ldpd/lde.c @@ -1698,8 +1698,11 @@ lde_address_list_free(struct lde_nbr *ln) static void zclient_sync_init(unsigned short instance) { + struct zclient_options options = zclient_options_default; + options.synchronous = true; + /* Initialize special zclient for synchronous message exchanges. */ - zclient_sync = zclient_new(master, &zclient_options_default); + zclient_sync = zclient_new(master, &options); zclient_sync->sock = -1; zclient_sync->redist_default = ZEBRA_ROUTE_LDP; zclient_sync->instance = instance; @@ -1712,6 +1715,12 @@ static void zclient_sync_init(unsigned short instance) /* make socket non-blocking */ sock_set_nonblock(zclient_sync->sock); + /* Send hello to notify zebra this is a synchronous client */ + while (zclient_send_hello(zclient_sync) < 0) { + log_warnx("Error sending hello for synchronous zclient!"); + sleep(1); + } + /* Connect to label manager */ while (lm_label_manager_connect(zclient_sync, 0) != 0) { log_warnx("Error connecting to label manager!"); diff --git a/lib/agg_table.h b/lib/agg_table.h index 9fddc975ed..e98476f1b7 100644 --- a/lib/agg_table.h +++ b/lib/agg_table.h @@ -161,6 +161,10 @@ agg_node_get_prefix(const struct agg_node *node) return &node->p; } +#ifdef _FRR_ATTRIBUTE_PRINTFRR +#pragma FRR printfrr_ext "%pRN" (struct agg_node *) +#endif + #ifdef __cplusplus } #endif diff --git a/lib/compiler.h b/lib/compiler.h index e430925e69..217a60d888 100644 --- a/lib/compiler.h +++ b/lib/compiler.h @@ -305,7 +305,14 @@ extern "C" { #include <inttypes.h> #ifdef _FRR_ATTRIBUTE_PRINTFRR -#define PRINTFRR(a, b) __attribute__((printfrr(a, b))) +#define PRINTFRR(a, b) __attribute__((frr_format("frr_printf", a, b))) + +#undef PRIu64 +#undef PRId64 +#undef PRIx64 +#define PRIu64 "Lu" +#define PRId64 "Ld" +#define PRIx64 "Lx" #else /* !_FRR_ATTRIBUTE_PRINTFRR */ #define PRINTFRR(a, b) __attribute__((format(printf, a, b))) diff --git a/lib/ipaddr.h b/lib/ipaddr.h index c6372f1abb..cd7f79a04e 100644 --- a/lib/ipaddr.h +++ b/lib/ipaddr.h @@ -112,7 +112,7 @@ static inline void ipv4_to_ipv4_mapped_ipv6(struct in6_addr *in6, /* * convert an ipv4 mapped ipv6 address back to ipv4 address */ -static inline void ipv4_mapped_ipv6_to_ipv4(struct in6_addr *in6, +static inline void ipv4_mapped_ipv6_to_ipv4(const struct in6_addr *in6, struct in_addr *in) { memset(in, 0, sizeof(struct in_addr)); diff --git a/lib/mpls.c b/lib/mpls.c index 759fe1206d..ac5792a686 100644 --- a/lib/mpls.c +++ b/lib/mpls.c @@ -79,7 +79,7 @@ int mpls_str2label(const char *label_str, uint8_t *num_labels, /* * Label to string conversion, labels in string separated by '/'. */ -char *mpls_label2str(uint8_t num_labels, mpls_label_t *labels, char *buf, +char *mpls_label2str(uint8_t num_labels, const mpls_label_t *labels, char *buf, int len, int pretty) { char label_buf[BUFSIZ]; diff --git a/lib/mpls.h b/lib/mpls.h index 635ecc77a1..05cf2935e8 100644 --- a/lib/mpls.h +++ b/lib/mpls.h @@ -209,10 +209,13 @@ static inline char *label2str(mpls_label_t label, char *buf, size_t len) int mpls_str2label(const char *label_str, uint8_t *num_labels, mpls_label_t *labels); +/* Generic string buffer for label-stack-to-str */ +#define MPLS_LABEL_STRLEN 1024 + /* * Label to string conversion, labels in string separated by '/'. */ -char *mpls_label2str(uint8_t num_labels, mpls_label_t *labels, char *buf, +char *mpls_label2str(uint8_t num_labels, const mpls_label_t *labels, char *buf, int len, int pretty); #ifdef __cplusplus diff --git a/lib/nexthop.c b/lib/nexthop.c index c3be0a71e6..0d239e091b 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -23,11 +23,9 @@ #include "table.h" #include "memory.h" #include "command.h" -#include "if.h" #include "log.h" #include "sockunion.h" #include "linklist.h" -#include "thread.h" #include "prefix.h" #include "nexthop.h" #include "mpls.h" @@ -155,7 +153,24 @@ static int _nexthop_cmp_no_labels(const struct nexthop *next1, } ret = _nexthop_source_cmp(next1, next2); + if (ret != 0) + goto done; + + if (!CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) && + CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP)) + return -1; + + if (CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) && + !CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP)) + return 1; + + if (next1->backup_idx < next2->backup_idx) + return -1; + + if (next1->backup_idx > next2->backup_idx) + return 1; +done: return ret; } @@ -240,7 +255,7 @@ struct nexthop *nexthop_new(void) * The linux kernel does some weird stuff with adding +1 to * all nexthop weights it gets over netlink. * To handle this, just default everything to 1 right from - * from the beggining so we don't have to special case + * from the beginning so we don't have to special case * default weights in the linux netlink code. * * 1 should be a valid on all platforms anyway. @@ -393,8 +408,8 @@ struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type) } /* Update nexthop with label information. */ -void nexthop_add_labels(struct nexthop *nexthop, enum lsp_types_t type, - uint8_t num_labels, mpls_label_t *label) +void nexthop_add_labels(struct nexthop *nexthop, enum lsp_types_t ltype, + uint8_t num_labels, const mpls_label_t *labels) { struct mpls_label_stack *nh_label; int i; @@ -402,13 +417,18 @@ void nexthop_add_labels(struct nexthop *nexthop, enum lsp_types_t type, if (num_labels == 0) return; - nexthop->nh_label_type = type; + /* Enforce limit on label stack size */ + if (num_labels > MPLS_MAX_LABELS) + num_labels = MPLS_MAX_LABELS; + + nexthop->nh_label_type = ltype; + nh_label = XCALLOC(MTYPE_NH_LABEL, sizeof(struct mpls_label_stack) + num_labels * sizeof(mpls_label_t)); nh_label->num_labels = num_labels; for (i = 0; i < num_labels; i++) - nh_label->label[i] = *(label + i); + nh_label->label[i] = *(labels + i); nexthop->nh_label = nh_label; } @@ -503,6 +523,7 @@ unsigned int nexthop_level(struct nexthop *nexthop) uint32_t nexthop_hash_quick(const struct nexthop *nexthop) { uint32_t key = 0x45afe398; + uint32_t val; key = jhash_3words(nexthop->type, nexthop->vrf_id, nexthop->nh_label_type, key); @@ -532,8 +553,12 @@ uint32_t nexthop_hash_quick(const struct nexthop *nexthop) key = jhash_1word(nexthop->nh_label->label[i], key); } - key = jhash_2words(nexthop->ifindex, - CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK), + val = 0; + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) + val = (uint32_t)nexthop->backup_idx; + + key = jhash_3words(nexthop->ifindex, + CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK), val, key); return key; @@ -573,6 +598,7 @@ void nexthop_copy_no_recurse(struct nexthop *copy, copy->type = nexthop->type; copy->flags = nexthop->flags; copy->weight = nexthop->weight; + copy->backup_idx = nexthop->backup_idx; memcpy(©->gate, &nexthop->gate, sizeof(nexthop->gate)); memcpy(©->src, &nexthop->src, sizeof(nexthop->src)); memcpy(©->rmap_src, &nexthop->rmap_src, sizeof(nexthop->rmap_src)); diff --git a/lib/nexthop.h b/lib/nexthop.h index 6710914e40..c4e88dd844 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -86,6 +86,8 @@ struct nexthop { * active one */ #define NEXTHOP_FLAG_RNH_FILTERED (1 << 5) /* rmap filtered, used by rnh */ +#define NEXTHOP_FLAG_HAS_BACKUP (1 << 6) /* Backup nexthop index is set */ + #define NEXTHOP_IS_ACTIVE(flags) \ (CHECK_FLAG(flags, NEXTHOP_FLAG_ACTIVE) \ && !CHECK_FLAG(flags, NEXTHOP_FLAG_DUPLICATE)) @@ -116,15 +118,31 @@ struct nexthop { /* Weight of the nexthop ( for unequal cost ECMP ) */ uint8_t weight; + + /* Index of a corresponding backup nexthop in a backup list; + * only meaningful if the HAS_BACKUP flag is set. + */ + uint8_t backup_idx; }; +/* Backup index value is limited */ +#define NEXTHOP_BACKUP_IDX_MAX 255 + +/* Utility to append one nexthop to another. */ +#define NEXTHOP_APPEND(to, new) \ + do { \ + (to)->next = (new); \ + (new)->prev = (to); \ + (new)->next = NULL; \ + } while (0) + struct nexthop *nexthop_new(void); void nexthop_free(struct nexthop *nexthop); void nexthops_free(struct nexthop *nexthop); -void nexthop_add_labels(struct nexthop *, enum lsp_types_t, uint8_t, - mpls_label_t *); +void nexthop_add_labels(struct nexthop *nexthop, enum lsp_types_t ltype, + uint8_t num_labels, const mpls_label_t *labels); void nexthop_del_labels(struct nexthop *); /* @@ -201,6 +219,10 @@ extern struct nexthop *nexthop_dup(const struct nexthop *nexthop, extern struct nexthop *nexthop_dup_no_recurse(const struct nexthop *nexthop, struct nexthop *rparent); +#ifdef _FRR_ATTRIBUTE_PRINTFRR +#pragma FRR printfrr_ext "%pNH" (struct nexthop *) +#endif + #ifdef __cplusplus } #endif diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index d660428bcd..8c3bbbdcd4 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -43,8 +43,12 @@ struct nexthop_hold { char *intf; char *labels; uint32_t weight; + int backup_idx; /* Index of backup nexthop, if >= 0 */ }; +/* Invalid/unset value for nexthop_hold's backup_idx */ +#define NHH_BACKUP_IDX_INVALID -1 + struct nexthop_group_hooks { void (*new)(const char *name); void (*add_nexthop)(const struct nexthop_group_cmd *nhg, @@ -225,6 +229,10 @@ void nexthop_group_copy(struct nexthop_group *to, void nexthop_group_delete(struct nexthop_group **nhg) { + /* OK to call with NULL group */ + if ((*nhg) == NULL) + return; + if ((*nhg)->nexthop) nexthops_free((*nhg)->nexthop); @@ -567,11 +575,36 @@ DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NHGNAME", return CMD_SUCCESS; } +DEFPY(nexthop_group_backup, nexthop_group_backup_cmd, + "backup-group WORD$name", + "Specify a group name containing backup nexthops\n" + "The name of the backup group\n") +{ + VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc); + + strlcpy(nhgc->backup_list_name, name, sizeof(nhgc->backup_list_name)); + + return CMD_SUCCESS; +} + +DEFPY(no_nexthop_group_backup, no_nexthop_group_backup_cmd, + "no backup-group [WORD$name]", + NO_STR + "Clear group name containing backup nexthops\n" + "The name of the backup group\n") +{ + VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc); + + nhgc->backup_list_name[0] = 0; + + return CMD_SUCCESS; +} + static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc, const char *nhvrf_name, const union sockunion *addr, const char *intf, const char *labels, - const uint32_t weight) + const uint32_t weight, int backup_idx) { struct nexthop_hold *nh; @@ -588,6 +621,8 @@ static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc, nh->weight = weight; + nh->backup_idx = backup_idx; + listnode_add_sort(nhgc->nhg_list, nh); } @@ -629,7 +664,7 @@ static bool nexthop_group_parse_nexthop(struct nexthop *nhop, const union sockunion *addr, const char *intf, const char *name, const char *labels, int *lbl_ret, - uint32_t weight) + uint32_t weight, int backup_idx) { int ret = 0; struct vrf *vrf; @@ -688,6 +723,15 @@ static bool nexthop_group_parse_nexthop(struct nexthop *nhop, nhop->weight = weight; + if (backup_idx != NHH_BACKUP_IDX_INVALID) { + /* Validate index value */ + if (backup_idx > NEXTHOP_BACKUP_IDX_MAX) + return false; + + SET_FLAG(nhop->flags, NEXTHOP_FLAG_HAS_BACKUP); + nhop->backup_idx = backup_idx; + } + return true; } @@ -699,7 +743,7 @@ static bool nexthop_group_parse_nhh(struct nexthop *nhop, { return (nexthop_group_parse_nexthop(nhop, nhh->addr, nhh->intf, nhh->nhvrf_name, nhh->labels, NULL, - nhh->weight)); + nhh->weight, nhh->backup_idx)); } DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, @@ -712,6 +756,7 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, nexthop-vrf NAME$vrf_name \ |label WORD \ |weight (1-255) \ + |backup-idx$bi_str (0-254)$idx \ }]", NO_STR "Specify one of the nexthops in this ECMP group\n" @@ -724,16 +769,23 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, "Specify label(s) for this nexthop\n" "One or more labels in the range (16-1048575) separated by '/'\n" "Weight to be used by the nexthop for purposes of ECMP\n" - "Weight value to be used\n") + "Weight value to be used\n" + "Backup nexthop index in another group\n" + "Nexthop index value\n") { VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc); struct nexthop nhop; struct nexthop *nh; int lbl_ret = 0; bool legal; + int backup_idx = idx; + bool add_update = false; + + if (bi_str == NULL) + backup_idx = NHH_BACKUP_IDX_INVALID; legal = nexthop_group_parse_nexthop(&nhop, addr, intf, vrf_name, label, - &lbl_ret, weight); + &lbl_ret, weight, backup_idx); if (nhop.type == NEXTHOP_TYPE_IPV6 && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) { @@ -765,19 +817,30 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, nh = nexthop_exists(&nhgc->nhg, &nhop); - if (no) { + if (no || nh) { + /* Remove or replace cases */ + + /* Remove existing config */ nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf, label, weight); if (nh) { + /* Remove nexthop object */ _nexthop_del(&nhgc->nhg, nh); if (nhg_hooks.del_nexthop) nhg_hooks.del_nexthop(nhgc, nh); nexthop_free(nh); + nh = NULL; } - } else if (!nh) { - /* must be adding new nexthop since !no and !nexthop_exists */ + } + + add_update = !no; + + if (add_update) { + /* Add or replace cases */ + + /* If valid config, add nexthop object */ if (legal) { nh = nexthop_new(); @@ -785,8 +848,9 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, _nexthop_add(&nhgc->nhg.nexthop, nh); } + /* Save config always */ nexthop_group_save_nhop(nhgc, vrf_name, addr, intf, label, - weight); + weight, backup_idx); if (legal && nhg_hooks.add_nexthop) nhg_hooks.add_nexthop(nhgc, nh); @@ -849,6 +913,9 @@ void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh) if (nh->weight) vty_out(vty, " weight %u", nh->weight); + if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) + vty_out(vty, " backup-idx %d", nh->backup_idx); + vty_out(vty, "\n"); } @@ -874,6 +941,9 @@ static void nexthop_group_write_nexthop_internal(struct vty *vty, if (nh->weight) vty_out(vty, " weight %u", nh->weight); + if (nh->backup_idx != NHH_BACKUP_IDX_INVALID) + vty_out(vty, " backup-idx %d", nh->backup_idx); + vty_out(vty, "\n"); } @@ -887,6 +957,10 @@ static int nexthop_group_write(struct vty *vty) vty_out(vty, "nexthop-group %s\n", nhgc->name); + if (nhgc->backup_list_name[0]) + vty_out(vty, " backup-group %s\n", + nhgc->backup_list_name); + for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) { vty_out(vty, " "); nexthop_group_write_nexthop_internal(vty, nh); @@ -1067,6 +1141,8 @@ void nexthop_group_init(void (*new)(const char *name), install_element(CONFIG_NODE, &no_nexthop_group_cmd); install_default(NH_GROUP_NODE); + install_element(NH_GROUP_NODE, &nexthop_group_backup_cmd); + install_element(NH_GROUP_NODE, &no_nexthop_group_backup_cmd); install_element(NH_GROUP_NODE, &ecmp_nexthops_cmd); memset(&nhg_hooks, 0, sizeof(nhg_hooks)); diff --git a/lib/nexthop_group.h b/lib/nexthop_group.h index f99a53f694..3a5a1299c1 100644 --- a/lib/nexthop_group.h +++ b/lib/nexthop_group.h @@ -57,6 +57,8 @@ void copy_nexthops(struct nexthop **tnh, const struct nexthop *nh, uint32_t nexthop_group_hash_no_recurse(const struct nexthop_group *nhg); uint32_t nexthop_group_hash(const struct nexthop_group *nhg); void nexthop_group_mark_duplicates(struct nexthop_group *nhg); + +/* Add a nexthop to a list, enforcing the canonical sort order. */ void nexthop_group_add_sorted(struct nexthop_group *nhg, struct nexthop *nexthop); @@ -79,11 +81,16 @@ void nexthop_group_add_sorted(struct nexthop_group *nhg, (nhop) = nexthop_next(nhop) +#define NHGC_NAME_SIZE 80 + struct nexthop_group_cmd { RB_ENTRY(nexthop_group_cmd) nhgc_entry; - char name[80]; + char name[NHGC_NAME_SIZE]; + + /* Name of group containing backup nexthops (if set) */ + char backup_list_name[NHGC_NAME_SIZE]; struct nexthop_group nhg; diff --git a/lib/northbound_grpc.cpp b/lib/northbound_grpc.cpp index 089899368d..b195f1aeca 100644 --- a/lib/northbound_grpc.cpp +++ b/lib/northbound_grpc.cpp @@ -884,7 +884,14 @@ static int frr_grpc_finish(void) return 0; } -static int frr_grpc_module_late_init(struct thread_master *tm) +/* + * This is done this way because module_init and module_late_init are both + * called during daemon pre-fork initialization. Because the GRPC library + * spawns threads internally, we need to delay initializing it until after + * fork. This is done by scheduling this init function as an event task, since + * the event loop doesn't run until after fork. + */ +static int frr_grpc_module_very_late_init(struct thread *thread) { static unsigned long port = GRPC_DEFAULT_PORT; const char *args = THIS_MODULE->load_args; @@ -910,15 +917,19 @@ static int frr_grpc_module_late_init(struct thread_master *tm) if (frr_grpc_init(&port) < 0) goto error; - hook_register(frr_fini, frr_grpc_finish); - - return 0; - error: flog_err(EC_LIB_GRPC_INIT, "failed to initialize the gRPC module"); return -1; } +static int frr_grpc_module_late_init(struct thread_master *tm) +{ + thread_add_event(tm, frr_grpc_module_very_late_init, NULL, 0, NULL); + hook_register(frr_fini, frr_grpc_finish); + + return 0; +} + static int frr_grpc_module_init(void) { hook_register(frr_late_init, frr_grpc_module_late_init); diff --git a/lib/plist.c b/lib/plist.c index 40131aebed..b7a020c6f7 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -778,7 +778,7 @@ static void __attribute__((unused)) prefix_list_print(struct prefix_list *plist) p = &pentry->prefix; - printf(" seq %" PRId64 " %s %s/%d", pentry->seq, + printf(" seq %lld %s %s/%d", (long long)pentry->seq, prefix_list_type_str(pentry), inet_ntop(p->family, p->u.val, buf, BUFSIZ), p->prefixlen); diff --git a/lib/prefix.h b/lib/prefix.h index 51b3dacb8d..f2952c38c3 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -544,6 +544,22 @@ static inline int is_default_host_route(const struct prefix *p) return 0; } +#ifdef _FRR_ATTRIBUTE_PRINTFRR +#pragma FRR printfrr_ext "%pI4" (struct in_addr *) +#pragma FRR printfrr_ext "%pI4" (in_addr_t *) + +#pragma FRR printfrr_ext "%pI6" (struct in6_addr *) + +#pragma FRR printfrr_ext "%pFX" (struct prefix *) +#pragma FRR printfrr_ext "%pFX" (struct prefix_ipv4 *) +#pragma FRR printfrr_ext "%pFX" (struct prefix_ipv6 *) +#pragma FRR printfrr_ext "%pFX" (struct prefix_eth *) +#pragma FRR printfrr_ext "%pFX" (struct prefix_evpn *) +#pragma FRR printfrr_ext "%pFX" (struct prefix_fs *) + +#pragma FRR printfrr_ext "%pSG4" (struct prefix_sg *) +#endif + #ifdef __cplusplus } #endif diff --git a/lib/printfrr.h b/lib/printfrr.h index f9584bcacc..7d9e288655 100644 --- a/lib/printfrr.h +++ b/lib/printfrr.h @@ -30,8 +30,7 @@ struct fbuf { size_t len; }; -#define at(a, b) \ - __attribute__((format(printf, a, b))) +#define at(a, b) PRINTFRR(a, b) #define atn(a, b) \ at(a, b) __attribute__((nonnull(1) _RET_NONNULL)) #define atm(a, b) \ @@ -73,8 +72,19 @@ char *vasnprintfrr(struct memtype *mt, char *out, size_t sz, char *asnprintfrr(struct memtype *mt, char *out, size_t sz, const char *fmt, ...) atn(4, 5); +#define printfrr(fmt, ...) \ + do { \ + char buf[256], *out; \ + out = asnprintfrr(MTYPE_TMP, buf, sizeof(buf), fmt, \ + ##__VA_ARGS__); \ + fputs(out, stdout); \ + if (out != buf) \ + XFREE(MTYPE_TMP, out); \ + } while (0) + #undef at #undef atm +#undef atn /* extension specs must start with a capital letter (this is a restriction * for both performance's and human understanding's sake.) diff --git a/lib/smux.h b/lib/smux.h index 3f860db0dc..6896f02354 100644 --- a/lib/smux.h +++ b/lib/smux.h @@ -105,7 +105,7 @@ extern int smux_trap(struct variable *, size_t, const oid *, size_t, extern int oid_compare(const oid *, int, const oid *, int); extern void oid2in_addr(oid[], int, struct in_addr *); extern void *oid_copy(void *, const void *, size_t); -extern void oid_copy_addr(oid[], struct in_addr *, int); +extern void oid_copy_addr(oid[], const struct in_addr *, int); #ifdef __cplusplus } diff --git a/lib/snmp.c b/lib/snmp.c index f11d9dc8cf..736a3c62b8 100644 --- a/lib/snmp.c +++ b/lib/snmp.c @@ -64,10 +64,10 @@ void oid2in_addr(oid oid[], int len, struct in_addr *addr) *pnt++ = oid[i]; } -void oid_copy_addr(oid oid[], struct in_addr *addr, int len) +void oid_copy_addr(oid oid[], const struct in_addr *addr, int len) { int i; - uint8_t *pnt; + const uint8_t *pnt; if (len == 0) return; diff --git a/lib/stream.c b/lib/stream.c index dd4d5bd96d..f046572f41 100644 --- a/lib/stream.c +++ b/lib/stream.c @@ -898,7 +898,7 @@ int stream_put_prefix(struct stream *s, const struct prefix *p) } /* Put NLRI with label */ -int stream_put_labeled_prefix(struct stream *s, struct prefix *p, +int stream_put_labeled_prefix(struct stream *s, const struct prefix *p, mpls_label_t *label, int addpath_encode, uint32_t addpath_tx_id) { diff --git a/lib/stream.h b/lib/stream.h index 425f0c5edd..6fcf9a53cf 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -196,7 +196,7 @@ extern int stream_put_prefix_addpath(struct stream *s, int addpath_encode, uint32_t addpath_tx_id); extern int stream_put_prefix(struct stream *s, const struct prefix *p); -extern int stream_put_labeled_prefix(struct stream *, struct prefix *, +extern int stream_put_labeled_prefix(struct stream *, const struct prefix *, mpls_label_t *, int addpath_encode, uint32_t addpath_tx_id); extern void stream_get(void *, struct stream *, size_t); diff --git a/lib/table.h b/lib/table.h index 7743d51681..9cd9503376 100644 --- a/lib/table.h +++ b/lib/table.h @@ -331,6 +331,10 @@ static inline int route_table_iter_started(route_table_iter_t *iter) return iter->state != RT_ITER_STATE_INIT; } +#ifdef _FRR_ATTRIBUTE_PRINTFRR +#pragma FRR printfrr_ext "%pRN" (struct route_node *) +#endif + #ifdef __cplusplus } #endif diff --git a/lib/thread.c b/lib/thread.c index 2217a60f0a..dbf668a699 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -114,11 +114,10 @@ static void vty_out_cpu_thread_history(struct vty *vty, struct cpu_thread_history *a) { vty_out(vty, "%5zu %10zu.%03zu %9zu %8zu %9zu %8zu %9zu", - (size_t)a->total_active, - a->cpu.total / 1000, a->cpu.total % 1000, - (size_t)a->total_calls, - a->cpu.total / a->total_calls, a->cpu.max, - a->real.total / a->total_calls, a->real.max); + (size_t)a->total_active, a->cpu.total / 1000, + a->cpu.total % 1000, (size_t)a->total_calls, + (size_t)(a->cpu.total / a->total_calls), a->cpu.max, + (size_t)(a->real.total / a->total_calls), a->real.max); vty_out(vty, " %c%c%c%c%c %s\n", a->types & (1 << THREAD_READ) ? 'R' : ' ', a->types & (1 << THREAD_WRITE) ? 'W' : ' ', @@ -231,8 +231,13 @@ int vty_out(struct vty *vty, const char *format, ...) strlen(filtered)); break; case VTY_SHELL: - fprintf(vty->of, "%s", filtered); - fflush(vty->of); + if (vty->of) { + fprintf(vty->of, "%s", filtered); + fflush(vty->of); + } else if (vty->of_saved) { + fprintf(vty->of_saved, "%s", filtered); + fflush(vty->of_saved); + } break; case VTY_SHELL_SERV: case VTY_FILE: diff --git a/lib/yang_wrappers.c b/lib/yang_wrappers.c index a308b18b73..2b502d635b 100644 --- a/lib/yang_wrappers.c +++ b/lib/yang_wrappers.c @@ -22,6 +22,7 @@ #include "log.h" #include "lib_errors.h" #include "northbound.h" +#include "printfrr.h" static const char *yang_get_default_value(const char *xpath) { @@ -443,7 +444,7 @@ struct yang_data *yang_data_new_int64(const char *xpath, int64_t value) { char value_str[BUFSIZ]; - snprintf(value_str, sizeof(value_str), "%" PRId64, value); + snprintfrr(value_str, sizeof(value_str), "%" PRId64, value); return yang_data_new(xpath, value_str); } @@ -651,7 +652,7 @@ struct yang_data *yang_data_new_uint64(const char *xpath, uint64_t value) { char value_str[BUFSIZ]; - snprintf(value_str, sizeof(value_str), "%" PRIu64, value); + snprintfrr(value_str, sizeof(value_str), "%" PRIu64, value); return yang_data_new(xpath, value_str); } diff --git a/lib/zclient.c b/lib/zclient.c index 93c5fe39af..d380267a70 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -52,7 +52,8 @@ static void zclient_event(enum event, struct zclient *); static void zebra_interface_if_set_value(struct stream *s, struct interface *ifp); -struct zclient_options zclient_options_default = {.receive_notify = false}; +struct zclient_options zclient_options_default = {.receive_notify = false, + .synchronous = false}; struct sockaddr_storage zclient_addr; socklen_t zclient_addr_len; @@ -76,6 +77,7 @@ struct zclient *zclient_new(struct thread_master *master, zclient->master = master; zclient->receive_notify = opt->receive_notify; + zclient->synchronous = opt->synchronous; return zclient; } @@ -374,11 +376,11 @@ static int zebra_message_send(struct zclient *zclient, int command, return zclient_send_message(zclient); } -static int zebra_hello_send(struct zclient *zclient) +int zclient_send_hello(struct zclient *zclient) { struct stream *s; - if (zclient->redist_default) { + if (zclient->redist_default || zclient->synchronous) { s = zclient->obuf; stream_reset(s); @@ -390,6 +392,10 @@ static int zebra_hello_send(struct zclient *zclient) stream_putc(s, 1); else stream_putc(s, 0); + if (zclient->synchronous) + stream_putc(s, 1); + else + stream_putc(s, 0); stream_putw_at(s, 0, stream_get_endp(s)); return zclient_send_message(zclient); @@ -629,7 +635,7 @@ int zclient_start(struct zclient *zclient) /* Create read thread. */ zclient_event(ZCLIENT_READ, zclient); - zebra_hello_send(zclient); + zclient_send_hello(zclient); zebra_message_send(zclient, ZEBRA_INTERFACE_ADD, VRF_DEFAULT); @@ -898,6 +904,7 @@ int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh, } } + /* If present, set 'weight' flag before encoding flags */ if (api_nh->weight) SET_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_WEIGHT); @@ -942,6 +949,10 @@ int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh, stream_put(s, &(api_nh->rmac), sizeof(struct ethaddr)); + /* Index of backup nexthop */ + if (CHECK_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) + stream_putc(s, api_nh->backup_idx); + done: return ret; } @@ -1001,6 +1012,10 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) return -1; } + /* We canonicalize the nexthops by sorting them; this allows + * zebra to resolve the list of nexthops to a nexthop-group + * more efficiently. + */ zapi_nexthop_group_sort(api->nexthops, api->nexthop_num); stream_putw(s, api->nexthop_num); @@ -1027,6 +1042,50 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) } } + /* Backup nexthops */ + if (CHECK_FLAG(api->message, ZAPI_MESSAGE_BACKUP_NEXTHOPS)) { + /* limit the number of nexthops if necessary */ + if (api->backup_nexthop_num > MULTIPATH_NUM) { + char buf[PREFIX2STR_BUFFER]; + + prefix2str(&api->prefix, buf, sizeof(buf)); + flog_err( + EC_LIB_ZAPI_ENCODE, + "%s: prefix %s: can't encode %u backup nexthops (maximum is %u)", + __func__, buf, api->backup_nexthop_num, + MULTIPATH_NUM); + return -1; + } + + /* Note that we do not sort the list of backup nexthops - + * this list is treated as an array and indexed by each + * primary nexthop that is associated with a backup. + */ + + stream_putw(s, api->backup_nexthop_num); + + for (i = 0; i < api->backup_nexthop_num; i++) { + api_nh = &api->backup_nexthops[i]; + + /* MPLS labels for BGP-LU or Segment Routing */ + if (api_nh->label_num > MPLS_MAX_LABELS) { + char buf[PREFIX2STR_BUFFER]; + + prefix2str(&api->prefix, buf, sizeof(buf)); + + flog_err(EC_LIB_ZAPI_ENCODE, + "%s: prefix %s: backup: can't encode %u labels (maximum is %u)", + __func__, buf, + api_nh->label_num, + MPLS_MAX_LABELS); + return -1; + } + + if (zapi_nexthop_encode(s, api_nh, api->flags) != 0) + return -1; + } + } + /* Attributes. */ if (CHECK_FLAG(api->message, ZAPI_MESSAGE_DISTANCE)) stream_putc(s, api->distance); @@ -1102,6 +1161,10 @@ static int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh, STREAM_GET(&(api_nh->rmac), s, sizeof(struct ethaddr)); + /* Backup nexthop index */ + if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) + STREAM_GETC(s, api_nh->backup_idx); + /* Success */ ret = 0; @@ -1208,6 +1271,24 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) } } + /* Backup nexthops. */ + if (CHECK_FLAG(api->message, ZAPI_MESSAGE_BACKUP_NEXTHOPS)) { + STREAM_GETW(s, api->backup_nexthop_num); + if (api->backup_nexthop_num > MULTIPATH_NUM) { + flog_err(EC_LIB_ZAPI_ENCODE, + "%s: invalid number of backup nexthops (%u)", + __func__, api->backup_nexthop_num); + return -1; + } + + for (i = 0; i < api->backup_nexthop_num; i++) { + api_nh = &api->backup_nexthops[i]; + + if (zapi_nexthop_decode(s, api_nh, api->flags) != 0) + return -1; + } + } + /* Attributes. */ if (CHECK_FLAG(api->message, ZAPI_MESSAGE_DISTANCE)) STREAM_GETC(s, api->distance); @@ -1382,7 +1463,7 @@ stream_failure: return false; } -struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh) +struct nexthop *nexthop_from_zapi_nexthop(const struct zapi_nexthop *znh) { struct nexthop *n = nexthop_new(); @@ -1399,6 +1480,11 @@ struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh) znh->labels); } + if (CHECK_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) { + SET_FLAG(n->flags, NEXTHOP_FLAG_HAS_BACKUP); + n->backup_idx = znh->backup_idx; + } + return n; } @@ -1414,10 +1500,16 @@ int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh, znh->type = nh->type; znh->vrf_id = nh->vrf_id; + znh->weight = nh->weight; znh->ifindex = nh->ifindex; znh->gate = nh->gate; if (nh->nh_label && (nh->nh_label->num_labels > 0)) { + + /* Validate */ + if (nh->nh_label->num_labels > MPLS_MAX_LABELS) + return -1; + for (i = 0; i < nh->nh_label->num_labels; i++) znh->labels[i] = nh->nh_label->label[i]; @@ -1425,10 +1517,31 @@ int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh, SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_LABEL); } + if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP); + znh->backup_idx = nh->backup_idx; + } + return 0; } /* + * Wrapper that converts backup nexthop + */ +int zapi_backup_nexthop_from_nexthop(struct zapi_nexthop *znh, + const struct nexthop *nh) +{ + int ret; + + /* Ensure that zapi flags are correct: backups don't have backups */ + ret = zapi_nexthop_from_nexthop(znh, nh); + if (ret == 0) + UNSET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP); + + return ret; +} + +/* * Decode the nexthop-tracking update message */ bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr) diff --git a/lib/zclient.h b/lib/zclient.h index f18511dc81..e747809f16 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -255,6 +255,9 @@ struct zclient { /* Do we care about failure events for route install? */ bool receive_notify; + /* Is this a synchronous client? */ + bool synchronous; + /* Socket to zebra daemon. */ int sock; @@ -338,6 +341,9 @@ struct zclient { #define ZAPI_MESSAGE_TAG 0x08 #define ZAPI_MESSAGE_MTU 0x10 #define ZAPI_MESSAGE_SRCPFX 0x20 +/* Backup nexthops are present */ +#define ZAPI_MESSAGE_BACKUP_NEXTHOPS 0x40 + /* * This should only be used by a DAEMON that needs to communicate * the table being used is not in the VRF. You must pass the @@ -374,14 +380,21 @@ struct zapi_nexthop { struct ethaddr rmac; uint32_t weight; + + /* Index of backup nexthop */ + uint8_t backup_idx; }; /* - * ZAPI nexthop flags values + * ZAPI nexthop flags values - we're encoding a single octet + * initially, so ensure that the on-the-wire encoding continues + * to match the number of valid flags. */ + #define ZAPI_NEXTHOP_FLAG_ONLINK 0x01 #define ZAPI_NEXTHOP_FLAG_LABEL 0x02 #define ZAPI_NEXTHOP_FLAG_WEIGHT 0x04 +#define ZAPI_NEXTHOP_FLAG_HAS_BACKUP 0x08 /* Nexthop has a backup */ /* * Some of these data structures do not map easily to @@ -445,6 +458,10 @@ struct zapi_route { uint16_t nexthop_num; struct zapi_nexthop nexthops[MULTIPATH_NUM]; + /* Support backup routes for IP FRR, TI-LFA, traffic engineering */ + uint16_t backup_nexthop_num; + struct zapi_nexthop backup_nexthops[MULTIPATH_NUM]; + uint8_t distance; uint32_t metric; @@ -569,6 +586,7 @@ enum zebra_neigh_state { ZEBRA_NEIGH_INACTIVE = 0, ZEBRA_NEIGH_ACTIVE = 1 }; struct zclient_options { bool receive_notify; + bool synchronous; }; extern struct zclient_options zclient_options_default; @@ -765,9 +783,12 @@ bool zapi_iptable_notify_decode(struct stream *s, uint32_t *unique, enum zapi_iptable_notify_owner *note); -extern struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh); +extern struct nexthop * +nexthop_from_zapi_nexthop(const struct zapi_nexthop *znh); int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh, const struct nexthop *nh); +int zapi_backup_nexthop_from_nexthop(struct zapi_nexthop *znh, + const struct nexthop *nh); extern bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr); @@ -796,4 +817,9 @@ extern void zclient_send_mlag_deregister(struct zclient *client); extern void zclient_send_mlag_data(struct zclient *client, struct stream *client_s); +/* Send the hello message. + * Returns 0 for success or -1 on an I/O error. + */ +extern int zclient_send_hello(struct zclient *client); + #endif /* _ZEBRA_ZCLIENT_H */ diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c index f75bd2c1bb..9060b6a95a 100644 --- a/pimd/pim_mroute.c +++ b/pimd/pim_mroute.c @@ -486,7 +486,10 @@ static int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp, struct pim_upstream *parent; struct pim_nexthop source; struct pim_rpf *rpf = RP(pim_ifp->pim, sg.grp); - if (!rpf || !rpf->source_nexthop.interface) + + /* No RPF or No RPF interface or No mcast on RPF interface */ + if (!rpf || !rpf->source_nexthop.interface + || !rpf->source_nexthop.interface->info) return 0; /* diff --git a/pimd/pim_tlv.c b/pimd/pim_tlv.c index 5a751ac929..881a3e332a 100644 --- a/pimd/pim_tlv.c +++ b/pimd/pim_tlv.c @@ -473,7 +473,7 @@ int pim_parse_addr_ucast(struct prefix *p, const uint8_t *buf, int buf_size) case PIM_MSG_ADDRESS_FAMILY_IPV4: if ((addr + sizeof(struct in_addr)) > pastend) { zlog_warn( - "%s: IPv4 unicast address overflow: left=%zd needed=%zu", + "%s: IPv4 unicast address overflow: left=%td needed=%zu", __func__, pastend - addr, sizeof(struct in_addr)); return -3; @@ -489,7 +489,7 @@ int pim_parse_addr_ucast(struct prefix *p, const uint8_t *buf, int buf_size) case PIM_MSG_ADDRESS_FAMILY_IPV6: if ((addr + sizeof(struct in6_addr)) > pastend) { zlog_warn( - "%s: IPv6 unicast address overflow: left=%zd needed %zu", + "%s: IPv6 unicast address overflow: left=%td needed %zu", __func__, pastend - addr, sizeof(struct in6_addr)); return -3; @@ -548,7 +548,7 @@ int pim_parse_addr_group(struct prefix_sg *sg, const uint8_t *buf, int buf_size) if ((addr + sizeof(struct in_addr)) > pastend) { zlog_warn( - "%s: IPv4 group address overflow: left=%zd needed=%zu from", + "%s: IPv4 group address overflow: left=%td needed=%zu from", __func__, pastend - addr, sizeof(struct in_addr)); return -3; @@ -607,7 +607,7 @@ int pim_parse_addr_source(struct prefix_sg *sg, uint8_t *flags, case PIM_MSG_ADDRESS_FAMILY_IPV4: if ((addr + sizeof(struct in_addr)) > pastend) { zlog_warn( - "%s: IPv4 source address overflow: left=%zd needed=%zu", + "%s: IPv4 source address overflow: left=%td needed=%zu", __func__, pastend - addr, sizeof(struct in_addr)); return -3; diff --git a/pimd/pim_zlookup.c b/pimd/pim_zlookup.c index fc486f4998..84fac4f951 100644 --- a/pimd/pim_zlookup.c +++ b/pimd/pim_zlookup.c @@ -61,6 +61,14 @@ static int zclient_lookup_connect(struct thread *t) zlookup->fail = 0; /* reset counter on connection */ } + if (zclient_send_hello(zlookup) < 0) { + if (close(zlookup->sock)) { + zlog_warn("%s: closing fd=%d: errno=%d %s", __func__, + zlookup->sock, errno, safe_strerror(errno)); + } + zlookup->sock = -1; + } + if (zlookup->sock < 0) { /* Since last connect failed, retry within 10 secs */ zclient_lookup_sched(zlookup, 10); @@ -125,7 +133,10 @@ void zclient_lookup_free(void) void zclient_lookup_new(void) { - zlookup = zclient_new(router->master, &zclient_options_default); + struct zclient_options options = zclient_options_default; + options.synchronous = true; + + zlookup = zclient_new(router->master, &options); if (!zlookup) { flog_err(EC_LIB_ZAPI_SOCKET, "%s: zclient_new() failure", __func__); @@ -161,6 +172,7 @@ static int zclient_read_nexthop(struct pim_instance *pim, if (PIM_DEBUG_PIM_NHT_DETAIL) { char addr_str[INET_ADDRSTRLEN]; + pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str)); zlog_debug("%s: addr=%s(%s)", __func__, addr_str, pim->vrf->name); diff --git a/sharpd/sharp_globals.h b/sharpd/sharp_globals.h index 4e5c933667..8eba57f4dd 100644 --- a/sharpd/sharp_globals.h +++ b/sharpd/sharp_globals.h @@ -28,9 +28,11 @@ struct sharp_routes { /* The original prefix for route installation */ struct prefix orig_prefix; - /* The nexthop group we are using for installation */ + /* The nexthop info we are using for installation */ struct nexthop nhop; + struct nexthop backup_nhop; struct nexthop_group nhop_group; + struct nexthop_group backup_nhop_group; uint32_t total_routes; uint32_t installed_routes; diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c index aa3d85624b..8a787c8e83 100644 --- a/sharpd/sharp_vty.c +++ b/sharpd/sharp_vty.c @@ -162,7 +162,12 @@ DEFPY (install_routes_data_dump, DEFPY (install_routes, install_routes_cmd, - "sharp install routes [vrf NAME$vrf_name] <A.B.C.D$start4|X:X::X:X$start6> <nexthop <A.B.C.D$nexthop4|X:X::X:X$nexthop6>|nexthop-group NHGNAME$nexthop_group> (1-1000000)$routes [instance (0-255)$instance] [repeat (2-1000)$rpt]", + "sharp install routes [vrf NAME$vrf_name]\ + <A.B.C.D$start4|X:X::X:X$start6>\ + <nexthop <A.B.C.D$nexthop4|X:X::X:X$nexthop6>|\ + nexthop-group NHGNAME$nexthop_group>\ + [backup$backup <A.B.C.D$backup_nexthop4|X:X::X:X$backup_nexthop6>] \ + (1-1000000)$routes [instance (0-255)$instance] [repeat (2-1000)$rpt]", "Sharp routing Protocol\n" "install some routes\n" "Routes to install\n" @@ -175,6 +180,9 @@ DEFPY (install_routes, "V6 Nexthop address to use\n" "Nexthop-Group to use\n" "The Name of the nexthop-group\n" + "Backup nexthop to use(Can be an IPv4 or IPv6 address)\n" + "Backup V4 Nexthop address to use\n" + "Backup V6 Nexthop address to use\n" "How many to create\n" "Instance to use\n" "Instance\n" @@ -197,6 +205,8 @@ DEFPY (install_routes, memset(&sg.r.orig_prefix, 0, sizeof(sg.r.orig_prefix)); memset(&sg.r.nhop, 0, sizeof(sg.r.nhop)); memset(&sg.r.nhop_group, 0, sizeof(sg.r.nhop_group)); + memset(&sg.r.backup_nhop, 0, sizeof(sg.r.nhop)); + memset(&sg.r.backup_nhop_group, 0, sizeof(sg.r.nhop_group)); if (start4.s_addr != 0) { prefix.family = AF_INET; @@ -219,6 +229,12 @@ DEFPY (install_routes, return CMD_WARNING; } + /* Explicit backup not available with named nexthop-group */ + if (backup && nexthop_group) { + vty_out(vty, "%% Invalid: cannot specify both nexthop-group and backup\n"); + return CMD_WARNING; + } + if (nexthop_group) { struct nexthop_group_cmd *nhgc = nhgc_find(nexthop_group); if (!nhgc) { @@ -229,6 +245,22 @@ DEFPY (install_routes, } sg.r.nhop_group.nexthop = nhgc->nhg.nexthop; + + /* Use group's backup nexthop info if present */ + if (nhgc->backup_list_name[0]) { + struct nexthop_group_cmd *bnhgc = + nhgc_find(nhgc->backup_list_name); + + if (!bnhgc) { + vty_out(vty, "%% Backup group %s not found for group %s\n", + nhgc->backup_list_name, + nhgc->name); + return CMD_WARNING; + } + + sg.r.backup_nhop.vrf_id = vrf->vrf_id; + sg.r.backup_nhop_group.nexthop = bnhgc->nhg.nexthop; + } } else { if (nexthop4.s_addr != INADDR_ANY) { sg.r.nhop.gate.ipv4 = nexthop4; @@ -242,11 +274,30 @@ DEFPY (install_routes, sg.r.nhop_group.nexthop = &sg.r.nhop; } + /* Use single backup nexthop if specified */ + if (backup) { + /* Set flag and index in primary nexthop */ + SET_FLAG(sg.r.nhop.flags, NEXTHOP_FLAG_HAS_BACKUP); + sg.r.nhop.backup_idx = 0; + + if (backup_nexthop4.s_addr != INADDR_ANY) { + sg.r.backup_nhop.gate.ipv4 = backup_nexthop4; + sg.r.backup_nhop.type = NEXTHOP_TYPE_IPV4; + } else { + sg.r.backup_nhop.gate.ipv6 = backup_nexthop6; + sg.r.backup_nhop.type = NEXTHOP_TYPE_IPV6; + } + + sg.r.backup_nhop.vrf_id = vrf->vrf_id; + sg.r.backup_nhop_group.nexthop = &sg.r.backup_nhop; + } + sg.r.inst = instance; sg.r.vrf_id = vrf->vrf_id; rts = routes; - sharp_install_routes_helper(&prefix, sg.r.vrf_id, - sg.r.inst, &sg.r.nhop_group, rts); + sharp_install_routes_helper(&prefix, sg.r.vrf_id, sg.r.inst, + &sg.r.nhop_group, &sg.r.backup_nhop_group, + rts); return CMD_SUCCESS; } diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c index 258a0a06dd..e1bd6f5722 100644 --- a/sharpd/sharp_zebra.c +++ b/sharpd/sharp_zebra.c @@ -143,7 +143,9 @@ int sharp_install_lsps_helper(bool install_p, const struct prefix *p, } void sharp_install_routes_helper(struct prefix *p, vrf_id_t vrf_id, - uint8_t instance, struct nexthop_group *nhg, + uint8_t instance, + const struct nexthop_group *nhg, + const struct nexthop_group *backup_nhg, uint32_t routes) { uint32_t temp, i; @@ -157,9 +159,13 @@ void sharp_install_routes_helper(struct prefix *p, vrf_id_t vrf_id, } else temp = ntohl(p->u.val32[3]); + /* Only use backup route/nexthops if present */ + if (backup_nhg && (backup_nhg->nexthop == NULL)) + backup_nhg = NULL; + monotime(&sg.r.t_start); for (i = 0; i < routes; i++) { - route_add(p, vrf_id, (uint8_t)instance, nhg); + route_add(p, vrf_id, (uint8_t)instance, nhg, backup_nhg); if (v4) p->u.prefix4.s_addr = htonl(++temp); else @@ -209,6 +215,7 @@ static void handle_repeated(bool installed) sg.r.installed_routes = 0; sharp_install_routes_helper(&p, sg.r.vrf_id, sg.r.inst, &sg.r.nhop_group, + &sg.r.backup_nhop_group, sg.r.total_routes); } } @@ -276,8 +283,9 @@ void vrf_label_add(vrf_id_t vrf_id, afi_t afi, mpls_label_t label) zclient_send_vrf_label(zclient, vrf_id, afi, label, ZEBRA_LSP_SHARP); } -void route_add(struct prefix *p, vrf_id_t vrf_id, - uint8_t instance, struct nexthop_group *nhg) +void route_add(const struct prefix *p, vrf_id_t vrf_id, + uint8_t instance, const struct nexthop_group *nhg, + const struct nexthop_group *backup_nhg) { struct zapi_route api; struct zapi_nexthop *api_nh; @@ -298,10 +306,27 @@ void route_add(struct prefix *p, vrf_id_t vrf_id, api_nh = &api.nexthops[i]; zapi_nexthop_from_nexthop(api_nh, nh); + i++; } api.nexthop_num = i; + /* Include backup nexthops, if present */ + if (backup_nhg && backup_nhg->nexthop) { + SET_FLAG(api.message, ZAPI_MESSAGE_BACKUP_NEXTHOPS); + + i = 0; + for (ALL_NEXTHOPS_PTR(backup_nhg, nh)) { + api_nh = &api.backup_nexthops[i]; + + zapi_backup_nexthop_from_nexthop(api_nh, nh); + + i++; + } + + api.backup_nexthop_num = i; + } + zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api); } diff --git a/sharpd/sharp_zebra.h b/sharpd/sharp_zebra.h index c995d557af..926bff676b 100644 --- a/sharpd/sharp_zebra.h +++ b/sharpd/sharp_zebra.h @@ -25,15 +25,17 @@ extern void sharp_zebra_init(void); extern void vrf_label_add(vrf_id_t vrf_id, afi_t afi, mpls_label_t label); -extern void route_add(struct prefix *p, vrf_id_t, uint8_t instance, - struct nexthop_group *nhg); +extern void route_add(const struct prefix *p, vrf_id_t, uint8_t instance, + const struct nexthop_group *nhg, + const struct nexthop_group *backup_nhg); extern void route_delete(struct prefix *p, vrf_id_t vrf_id, uint8_t instance); extern void sharp_zebra_nexthop_watch(struct prefix *p, vrf_id_t vrf_id, bool import, bool watch, bool connected); extern void sharp_install_routes_helper(struct prefix *p, vrf_id_t vrf_id, uint8_t instance, - struct nexthop_group *nhg, + const struct nexthop_group *nhg, + const struct nexthop_group *backup_nhg, uint32_t routes); extern void sharp_remove_routes_helper(struct prefix *p, vrf_id_t vrf_id, uint8_t instance, uint32_t routes); diff --git a/tests/bgpd/test_bgp_table.c b/tests/bgpd/test_bgp_table.c index 819c2d7282..79a8bb4408 100644 --- a/tests/bgpd/test_bgp_table.c +++ b/tests/bgpd/test_bgp_table.c @@ -82,7 +82,7 @@ static void print_range_result(struct list *list) for (ALL_LIST_ELEMENTS_RO(list, listnode, bnode)) { char buf[PREFIX2STR_BUFFER]; - prefix2str(&bnode->p, buf, PREFIX2STR_BUFFER); + prefix2str(bgp_node_get_prefix(bnode), buf, PREFIX2STR_BUFFER); printf("%s\n", buf); } } @@ -106,7 +106,7 @@ static void check_lookup_result(struct list *list, va_list arglist) assert(0); for (ALL_LIST_ELEMENTS_RO(list, listnode, bnode)) { - if (prefix_same(&bnode->p, &p)) + if (prefix_same(bgp_node_get_prefix(bnode), &p)) found = true; } diff --git a/tests/lib/test_atomlist.c b/tests/lib/test_atomlist.c index 238ee9539e..40837b4722 100644 --- a/tests/lib/test_atomlist.c +++ b/tests/lib/test_atomlist.c @@ -29,6 +29,7 @@ #include "atomlist.h" #include "seqlock.h" #include "monotime.h" +#include "printfrr.h" /* * maybe test: @@ -288,7 +289,7 @@ static void run_tr(struct testrun *tr) size_t c = 0, s = 0, n = 0; struct item *item, *prev, dummy; - printf("[%02u] %35s %s\n", seqlock_cur(&sqlo) >> 2, "", desc); + printfrr("[%02u] %35s %s\n", seqlock_cur(&sqlo) >> 2, "", desc); fflush(stdout); if (tr->prefill != NOCLEAR) @@ -324,7 +325,7 @@ static void run_tr(struct testrun *tr) } assert(c == alist_count(&ahead)); } - printf("\033[1A[%02u] %9"PRId64"us c=%5zu s=%5zu n=%5zu %s\n", + printfrr("\033[1A[%02u] %9"PRId64"us c=%5zu s=%5zu n=%5zu %s\n", sv >> 2, delta, c, s, n, desc); } @@ -334,9 +335,9 @@ static void dump(const char *lbl) struct item *item, *safe; size_t ctr = 0; - printf("dumping %s:\n", lbl); + printfrr("dumping %s:\n", lbl); frr_each_safe(alist, &ahead, item) { - printf("%s %3zu %p %3"PRIu64" %3"PRIu64"\n", lbl, ctr++, + printfrr("%s %3zu %p %3"PRIu64" %3"PRIu64"\n", lbl, ctr++, (void *)item, item->val1, item->val2); } } @@ -362,12 +363,12 @@ static void basic_tests(void) dump(""); alist_del(&ahead, &itm[1]); dump(""); - printf("POP: %p\n", alist_pop(&ahead)); + printfrr("POP: %p\n", alist_pop(&ahead)); dump(""); - printf("POP: %p\n", alist_pop(&ahead)); - printf("POP: %p\n", alist_pop(&ahead)); - printf("POP: %p\n", alist_pop(&ahead)); - printf("POP: %p\n", alist_pop(&ahead)); + printfrr("POP: %p\n", alist_pop(&ahead)); + printfrr("POP: %p\n", alist_pop(&ahead)); + printfrr("POP: %p\n", alist_pop(&ahead)); + printfrr("POP: %p\n", alist_pop(&ahead)); dump(""); } #else diff --git a/tests/lib/test_stream.c b/tests/lib/test_stream.c index 2ecfc87942..a45c2b4d54 100644 --- a/tests/lib/test_stream.c +++ b/tests/lib/test_stream.c @@ -23,6 +23,8 @@ #include <stream.h> #include <thread.h> +#include "printfrr.h" + static unsigned long long ham = 0xdeadbeefdeadbeef; struct thread_master *master; @@ -30,15 +32,15 @@ static void print_stream(struct stream *s) { size_t getp = stream_get_getp(s); - printf("endp: %zu, readable: %zu, writeable: %zu\n", stream_get_endp(s), - STREAM_READABLE(s), STREAM_WRITEABLE(s)); + printfrr("endp: %zu, readable: %zu, writeable: %zu\n", + stream_get_endp(s), STREAM_READABLE(s), STREAM_WRITEABLE(s)); while (STREAM_READABLE(s)) { - printf("0x%x ", *stream_pnt(s)); + printfrr("0x%x ", *stream_pnt(s)); stream_forward_getp(s, 1); } - printf("\n"); + printfrr("\n"); /* put getp back to where it was */ stream_set_getp(s, getp); @@ -61,10 +63,10 @@ int main(void) print_stream(s); - printf("c: 0x%hhx\n", stream_getc(s)); - printf("w: 0x%hx\n", stream_getw(s)); - printf("l: 0x%x\n", stream_getl(s)); - printf("q: 0x%" PRIx64 "\n", stream_getq(s)); + printfrr("c: 0x%hhx\n", stream_getc(s)); + printfrr("w: 0x%hx\n", stream_getw(s)); + printfrr("l: 0x%x\n", stream_getl(s)); + printfrr("q: 0x%" PRIx64 "\n", stream_getq(s)); return 0; } diff --git a/tests/lib/test_typelist.c b/tests/lib/test_typelist.c index 2438fb5f08..607e29e56b 100644 --- a/tests/lib/test_typelist.c +++ b/tests/lib/test_typelist.c @@ -35,6 +35,7 @@ #include "monotime.h" #include "jhash.h" #include "sha256.h" +#include "printfrr.h" #include "tests/helpers/c/prng.h" @@ -90,14 +91,14 @@ static void ts_ref(const char *text) { int64_t us; us = monotime_since(&ref, NULL); - printf("%7"PRId64"us %s\n", us, text); + printfrr("%7"PRId64"us %s\n", us, text); monotime(&ref); } static void ts_end(void) { int64_t us; us = monotime_since(&ref0, NULL); - printf("%7"PRId64"us total\n", us); + printfrr("%7"PRId64"us total\n", us); } #define TYPE LIST diff --git a/tests/lib/test_typelist.h b/tests/lib/test_typelist.h index 9039fa8a46..da3530e9c0 100644 --- a/tests/lib/test_typelist.h +++ b/tests/lib/test_typelist.h @@ -123,10 +123,10 @@ static void ts_hash(const char *text, const char *expect) for (i = 0; i < sizeof(hash); i++) sprintf(hashtext + i * 2, "%02x", hash[i]); - printf("%7"PRId64"us %-25s %s%s\n", us, text, + printfrr("%7"PRId64"us %-25s %s%s\n", us, text, expect ? " " : "*", hashtext); if (expect && strcmp(expect, hashtext)) { - printf("%-21s %s\n", "EXPECTED:", expect); + printfrr("%-21s %s\n", "EXPECTED:", expect); assert(0); } monotime(&ref); @@ -149,7 +149,7 @@ static void concat(test_, TYPE)(void) for (i = 0; i < NITEM; i++) itm[i].val = i; - printf("%s start\n", str(TYPE)); + printfrr("%s start\n", str(TYPE)); ts_start(); list_init(&head); @@ -530,7 +530,7 @@ static void concat(test_, TYPE)(void) list_fini(&head); ts_ref("fini"); ts_end(); - printf("%s end\n", str(TYPE)); + printfrr("%s end\n", str(TYPE)); } #undef ts_hashx diff --git a/tests/subdir.am b/tests/subdir.am index d87d348949..bce08c4034 100644 --- a/tests/subdir.am +++ b/tests/subdir.am @@ -38,8 +38,11 @@ else TESTS_OSPF6D = endif +tests/lib/cli/test_cli_clippy.c: $(CLIPPY_DEPS) tests/lib/cli/tests_lib_cli_test_cli-test_cli.$(OBJEXT): tests/lib/cli/test_cli_clippy.c tests/lib/cli/test_cli-test_cli.$(OBJEXT): tests/lib/cli/test_cli_clippy.c + +tests/ospf6d/test_lsdb_clippy.c: $(CLIPPY_DEPS) tests/ospf6d/tests_ospf6d_test_lsdb-test_lsdb.$(OBJEXT): tests/ospf6d/test_lsdb_clippy.c tests/ospf6d/test_lsdb-test_lsdb.$(OBJEXT): tests/ospf6d/test_lsdb_clippy.c diff --git a/tests/topotests/bgp_rr_ibgp/spine1/show_ip_route.json_ref b/tests/topotests/bgp_rr_ibgp/spine1/show_ip_route.json_ref index 552e96ddb9..75ce1b149e 100644 --- a/tests/topotests/bgp_rr_ibgp/spine1/show_ip_route.json_ref +++ b/tests/topotests/bgp_rr_ibgp/spine1/show_ip_route.json_ref @@ -11,15 +11,12 @@ "table":254, "internalStatus":16, "internalFlags":13, - "internalNextHopNum":1, - "internalNextHopActiveNum":1, "nexthops":[ { "flags":3, "fib":true, "ip":"192.168.2.1", "afi":"ipv4", - "interfaceIndex":2, "interfaceName":"spine1-eth0", "active":true } @@ -38,14 +35,11 @@ "table":254, "internalStatus":16, "internalFlags":8, - "internalNextHopNum":1, - "internalNextHopActiveNum":1, "nexthops":[ { "flags":3, "fib":true, "directlyConnected":true, - "interfaceIndex":2, "interfaceName":"spine1-eth0", "active":true } @@ -64,15 +58,12 @@ "table":254, "internalStatus":16, "internalFlags":13, - "internalNextHopNum":1, - "internalNextHopActiveNum":1, "nexthops":[ { "flags":3, "fib":true, "ip":"192.168.4.2", "afi":"ipv4", - "interfaceIndex":3, "interfaceName":"spine1-eth1", "active":true } @@ -91,23 +82,20 @@ "table":254, "internalStatus":16, "internalFlags":8, - "internalNextHopNum":1, - "internalNextHopActiveNum":1, "nexthops":[ { "flags":3, "fib":true, "directlyConnected":true, - "interfaceIndex":3, "interfaceName":"spine1-eth1", "active":true } ] } ], - "192.168.5.0\/24":[ + "192.168.5.1\/32":[ { - "prefix":"192.168.5.0\/24", + "prefix":"192.168.5.1\/32", "protocol":"bgp", "selected":true, "destSelected":true, @@ -117,24 +105,21 @@ "table":254, "internalStatus":16, "internalFlags":13, - "internalNextHopNum":1, - "internalNextHopActiveNum":1, "nexthops":[ { "flags":3, "fib":true, "ip":"192.168.2.1", "afi":"ipv4", - "interfaceIndex":2, "interfaceName":"spine1-eth0", "active":true } ] } ], - "192.168.6.0\/24":[ + "192.168.6.2\/32":[ { - "prefix":"192.168.6.0\/24", + "prefix":"192.168.6.2\/32", "protocol":"bgp", "selected":true, "destSelected":true, @@ -144,19 +129,16 @@ "table":254, "internalStatus":16, "internalFlags":13, - "internalNextHopNum":1, - "internalNextHopActiveNum":1, "nexthops":[ { "flags":3, "fib":true, "ip":"192.168.4.2", "afi":"ipv4", - "interfaceIndex":3, "interfaceName":"spine1-eth1", "active":true } ] } ] -} +}
\ No newline at end of file diff --git a/tests/topotests/bgp_rr_ibgp/spine2/bgpd.conf b/tests/topotests/bgp_rr_ibgp/spine2/bgpd.conf deleted file mode 100644 index a865b388ac..0000000000 --- a/tests/topotests/bgp_rr_ibgp/spine2/bgpd.conf +++ /dev/null @@ -1,8 +0,0 @@ -hostname spine2 -router bgp 99 - neighbor 192.168.5.1 remote-as internal - neighbor 192.168.6.2 remote-as internal - address-family ipv4 uni - redistribute connected - neighbor 192.168.5.1 route-reflector-client - neighbor 192.168.6.2 route-reflector-client diff --git a/tests/topotests/bgp_rr_ibgp/spine2/show_ip_route.json_ref b/tests/topotests/bgp_rr_ibgp/spine2/show_ip_route.json_ref deleted file mode 100644 index c428a8832f..0000000000 --- a/tests/topotests/bgp_rr_ibgp/spine2/show_ip_route.json_ref +++ /dev/null @@ -1,162 +0,0 @@ -{ - "192.168.1.0\/24":[ - { - "prefix":"192.168.1.0\/24", - "protocol":"bgp", - "selected":true, - "destSelected":true, - "distance":200, - "metric":0, - "installed":true, - "table":254, - "internalStatus":16, - "internalFlags":13, - "internalNextHopNum":1, - "internalNextHopActiveNum":1, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.5.1", - "afi":"ipv4", - "interfaceIndex":2, - "interfaceName":"spine2-eth0", - "active":true - } - ] - } - ], - "192.168.2.0\/24":[ - { - "prefix":"192.168.2.0\/24", - "protocol":"bgp", - "selected":true, - "destSelected":true, - "distance":200, - "metric":0, - "installed":true, - "table":254, - "internalStatus":16, - "internalFlags":13, - "internalNextHopNum":1, - "internalNextHopActiveNum":1, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.5.1", - "afi":"ipv4", - "interfaceIndex":2, - "interfaceName":"spine2-eth0", - "active":true - } - ] - } - ], - "192.168.3.0\/24":[ - { - "prefix":"192.168.3.0\/24", - "protocol":"bgp", - "selected":true, - "destSelected":true, - "distance":200, - "metric":0, - "installed":true, - "table":254, - "internalStatus":16, - "internalFlags":13, - "internalNextHopNum":1, - "internalNextHopActiveNum":1, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.6.2", - "afi":"ipv4", - "interfaceIndex":3, - "interfaceName":"spine2-eth1", - "active":true - } - ] - } - ], - "192.168.4.0\/24":[ - { - "prefix":"192.168.4.0\/24", - "protocol":"bgp", - "selected":true, - "destSelected":true, - "distance":200, - "metric":0, - "installed":true, - "table":254, - "internalStatus":16, - "internalFlags":13, - "internalNextHopNum":1, - "internalNextHopActiveNum":1, - "nexthops":[ - { - "flags":3, - "fib":true, - "ip":"192.168.6.2", - "afi":"ipv4", - "interfaceIndex":3, - "interfaceName":"spine2-eth1", - "active":true - } - ] - } - ], - "192.168.5.0\/24":[ - { - "prefix":"192.168.5.0\/24", - "protocol":"connected", - "selected":true, - "destSelected":true, - "distance":0, - "metric":0, - "installed":true, - "table":254, - "internalStatus":16, - "internalFlags":8, - "internalNextHopNum":1, - "internalNextHopActiveNum":1, - "nexthops":[ - { - "flags":3, - "fib":true, - "directlyConnected":true, - "interfaceIndex":2, - "interfaceName":"spine2-eth0", - "active":true - } - ] - } - ], - "192.168.6.0\/24":[ - { - "prefix":"192.168.6.0\/24", - "protocol":"connected", - "selected":true, - "destSelected":true, - "distance":0, - "metric":0, - "installed":true, - "table":254, - "internalStatus":16, - "internalFlags":8, - "internalNextHopNum":1, - "internalNextHopActiveNum":1, - "nexthops":[ - { - "flags":3, - "fib":true, - "directlyConnected":true, - "interfaceIndex":3, - "interfaceName":"spine2-eth1", - "active":true - } - ] - } - ] -} diff --git a/tests/topotests/bgp_rr_ibgp/spine2/staticd.conf b/tests/topotests/bgp_rr_ibgp/spine2/staticd.conf deleted file mode 100644 index 3ee14d262c..0000000000 --- a/tests/topotests/bgp_rr_ibgp/spine2/staticd.conf +++ /dev/null @@ -1 +0,0 @@ -hostname spine2 diff --git a/tests/topotests/bgp_rr_ibgp/spine2/zebra.conf b/tests/topotests/bgp_rr_ibgp/spine2/zebra.conf deleted file mode 100644 index a06681fbc4..0000000000 --- a/tests/topotests/bgp_rr_ibgp/spine2/zebra.conf +++ /dev/null @@ -1,9 +0,0 @@ -hostname spine2 -ip forwarding -ipv6 forwarding - -int spine2-eth0 - ip addr 192.168.5.4/24 - -int spine2-eth1 - ip addr 192.168.6.4/24 diff --git a/tests/topotests/bgp_rr_ibgp/test_bgp_rr_ibgp_topo1.py b/tests/topotests/bgp_rr_ibgp/test_bgp_rr_ibgp_topo1.py index c28394a7a7..c7daa06b76 100755 --- a/tests/topotests/bgp_rr_ibgp/test_bgp_rr_ibgp_topo1.py +++ b/tests/topotests/bgp_rr_ibgp/test_bgp_rr_ibgp_topo1.py @@ -25,47 +25,8 @@ """ test_bgp_rr_ibgp_topo1.py: Testing IBGP with RR and no IGP - - In a leaf/spine topology with only IBGP connections, where - the same network is being redistributed at multiple points - in the network ( say a redistribute connected at both leaf and spines ) - we end up in a state where zebra gets very confused. - - eva# show ip route - Codes: K - kernel route, C - connected, S - static, R - RIP, - O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP, - T - Table, v - VNC, V - VNC-Direct, A - Babel, D - SHARP, - F - PBR, f - OpenFabric, - > - selected route, * - FIB route, q - queued route, r - rejected route - - C>* 192.168.1.0/24 is directly connected, tor1-eth0, 00:00:30 - C>* 192.168.2.0/24 is directly connected, tor1-eth1, 00:00:30 - B 192.168.3.0/24 [200/0] via 192.168.4.2 inactive, 00:00:25 - via 192.168.6.2 inactive, 00:00:25 - B>* 192.168.4.0/24 [200/0] via 192.168.2.3, tor1-eth1, 00:00:25 - * via 192.168.6.2 inactive, 00:00:25 - C>* 192.168.5.0/24 is directly connected, tor1-eth2, 00:00:30 - B>* 192.168.6.0/24 [200/0] via 192.168.4.2 inactive, 00:00:25 - * via 192.168.5.4, tor1-eth2, 00:00:25 - - Effectively we have ibgp routes recursing through ibgp routes - and there is no metric to discern whom to listen to. - - This draft: - https://tools.ietf.org/html/draft-ietf-idr-bgp-optimal-route-reflection-19 - - appears to address this issue. From looking at both cisco and arista - deployments they are handling this issue by having the route reflector - prefer the localy learned routes over from their clients. - - Add this topology, in a broken state, so that when we do fix this issue - it is a simple matter of touching this topology up and re-adding it - to the normal daily builds. I also wanted to add this topology - since it is in a state of `doneness` and I wanted to move onto - my normal day job without having to remember about this test. - - This topology is not configured to be run as part of the normal - topotests. +Ensure that a basic rr topology comes up and correctly passes +routes around """ @@ -105,7 +66,6 @@ class NetworkTopo(Topo): tgen.add_router('tor1') tgen.add_router('tor2') tgen.add_router('spine1') - tgen.add_router('spine2') # First switch is for a dummy interface (for local network) # on tor1 @@ -128,15 +88,6 @@ class NetworkTopo(Topo): switch.add_link(tgen.gears['tor2']) switch.add_link(tgen.gears['spine1']) - # 192.168.5.0/24 - tor1 <-> spine2 connection - switch = tgen.add_switch('sw5') - switch.add_link(tgen.gears['tor1']) - switch.add_link(tgen.gears['spine2']) - - # 192.168.6.0/24 - tor2 <-> spine2 connection - switch = tgen.add_switch('sw6') - switch.add_link(tgen.gears['tor2']) - switch.add_link(tgen.gears['spine2']) ##################################################### ## diff --git a/tests/topotests/bgp_rr_ibgp/tor1/bgpd.conf b/tests/topotests/bgp_rr_ibgp/tor1/bgpd.conf index 44a78dffd7..e8ec0f7680 100644 --- a/tests/topotests/bgp_rr_ibgp/tor1/bgpd.conf +++ b/tests/topotests/bgp_rr_ibgp/tor1/bgpd.conf @@ -1,5 +1,4 @@ hostname tor1 router bgp 99 neighbor 192.168.2.3 remote-as internal - neighbor 192.168.5.4 remote-as internal redistribute connected diff --git a/tests/topotests/bgp_rr_ibgp/tor1/show_ip_route.json_ref b/tests/topotests/bgp_rr_ibgp/tor1/show_ip_route.json_ref index 223dcebbca..6cfa02441f 100644 --- a/tests/topotests/bgp_rr_ibgp/tor1/show_ip_route.json_ref +++ b/tests/topotests/bgp_rr_ibgp/tor1/show_ip_route.json_ref @@ -11,14 +11,11 @@ "table":254, "internalStatus":16, "internalFlags":8, - "internalNextHopNum":1, - "internalNextHopActiveNum":1, "nexthops":[ { "flags":3, "fib":true, "directlyConnected":true, - "interfaceIndex":2, "interfaceName":"tor1-eth0", "active":true } @@ -37,14 +34,11 @@ "table":254, "internalStatus":16, "internalFlags":8, - "internalNextHopNum":1, - "internalNextHopActiveNum":1, "nexthops":[ { "flags":3, "fib":true, "directlyConnected":true, - "interfaceIndex":3, "interfaceName":"tor1-eth1", "active":true } @@ -55,23 +49,29 @@ { "prefix":"192.168.3.0\/24", "protocol":"bgp", + "selected":true, + "destSelected":true, "distance":200, "metric":0, + "installed":true, "table":254, - "internalStatus":0, - "internalFlags":5, - "internalNextHopNum":2, - "internalNextHopActiveNum":0, + "internalStatus":16, + "internalFlags":13, "nexthops":[ { - "flags":0, + "flags":5, "ip":"192.168.4.2", - "afi":"ipv4" + "afi":"ipv4", + "active":true, + "recursive":true }, { - "flags":0, - "ip":"192.168.6.2", - "afi":"ipv4" + "flags":3, + "fib":true, + "ip":"192.168.2.3", + "afi":"ipv4", + "interfaceName":"tor1-eth1", + "active":true } ] } @@ -88,29 +88,21 @@ "table":254, "internalStatus":16, "internalFlags":13, - "internalNextHopNum":2, - "internalNextHopActiveNum":1, "nexthops":[ { "flags":3, "fib":true, "ip":"192.168.2.3", "afi":"ipv4", - "interfaceIndex":3, "interfaceName":"tor1-eth1", "active":true - }, - { - "flags":0, - "ip":"192.168.6.2", - "afi":"ipv4" } ] } ], - "192.168.5.0\/24":[ + "192.168.5.1\/32":[ { - "prefix":"192.168.5.0\/24", + "prefix":"192.168.5.1\/32", "protocol":"connected", "selected":true, "destSelected":true, @@ -120,23 +112,20 @@ "table":254, "internalStatus":16, "internalFlags":8, - "internalNextHopNum":1, - "internalNextHopActiveNum":1, "nexthops":[ { "flags":3, "fib":true, "directlyConnected":true, - "interfaceIndex":4, - "interfaceName":"tor1-eth2", + "interfaceName":"lo", "active":true } ] } ], - "192.168.6.0\/24":[ + "192.168.6.2\/32":[ { - "prefix":"192.168.6.0\/24", + "prefix":"192.168.6.2\/32", "protocol":"bgp", "selected":true, "destSelected":true, @@ -146,21 +135,20 @@ "table":254, "internalStatus":16, "internalFlags":13, - "internalNextHopNum":2, - "internalNextHopActiveNum":1, "nexthops":[ { - "flags":0, + "flags":5, "ip":"192.168.4.2", - "afi":"ipv4" + "afi":"ipv4", + "active":true, + "recursive":true }, { "flags":3, "fib":true, - "ip":"192.168.5.4", + "ip":"192.168.2.3", "afi":"ipv4", - "interfaceIndex":4, - "interfaceName":"tor1-eth2", + "interfaceName":"tor1-eth1", "active":true } ] diff --git a/tests/topotests/bgp_rr_ibgp/tor1/zebra.conf b/tests/topotests/bgp_rr_ibgp/tor1/zebra.conf index f2fa713507..25b4fcfd0f 100644 --- a/tests/topotests/bgp_rr_ibgp/tor1/zebra.conf +++ b/tests/topotests/bgp_rr_ibgp/tor1/zebra.conf @@ -8,5 +8,5 @@ int tor1-eth0 int tor1-eth1 ip addr 192.168.2.1/24 -int tor1-eth2 - ip addr 192.168.5.1/24 +int lo + ip addr 192.168.5.1/32 diff --git a/tests/topotests/bgp_rr_ibgp/tor2/bgpd.conf b/tests/topotests/bgp_rr_ibgp/tor2/bgpd.conf index 5ef1de260e..b091c97ac3 100644 --- a/tests/topotests/bgp_rr_ibgp/tor2/bgpd.conf +++ b/tests/topotests/bgp_rr_ibgp/tor2/bgpd.conf @@ -1,5 +1,4 @@ hostname tor2 router bgp 99 neighbor 192.168.4.3 remote-as internal - neighbor 192.168.6.4 remote-as internal redistribute connected diff --git a/tests/topotests/bgp_rr_ibgp/tor2/show_ip_route.json_ref b/tests/topotests/bgp_rr_ibgp/tor2/show_ip_route.json_ref index 5f041b8c62..d9e9290e61 100644 --- a/tests/topotests/bgp_rr_ibgp/tor2/show_ip_route.json_ref +++ b/tests/topotests/bgp_rr_ibgp/tor2/show_ip_route.json_ref @@ -3,23 +3,29 @@ { "prefix":"192.168.1.0\/24", "protocol":"bgp", + "selected":true, + "destSelected":true, "distance":200, "metric":0, + "installed":true, "table":254, - "internalStatus":0, - "internalFlags":5, - "internalNextHopNum":2, - "internalNextHopActiveNum":0, + "internalStatus":16, + "internalFlags":13, "nexthops":[ { - "flags":0, + "flags":5, "ip":"192.168.2.1", - "afi":"ipv4" + "afi":"ipv4", + "active":true, + "recursive":true }, { - "flags":0, - "ip":"192.168.5.1", - "afi":"ipv4" + "flags":3, + "fib":true, + "ip":"192.168.4.3", + "afi":"ipv4", + "interfaceName":"tor2-eth1", + "active":true } ] } @@ -36,22 +42,14 @@ "table":254, "internalStatus":16, "internalFlags":13, - "internalNextHopNum":2, - "internalNextHopActiveNum":1, "nexthops":[ { "flags":3, "fib":true, "ip":"192.168.4.3", "afi":"ipv4", - "interfaceIndex":3, "interfaceName":"tor2-eth1", "active":true - }, - { - "flags":0, - "ip":"192.168.5.1", - "afi":"ipv4" } ] } @@ -68,14 +66,11 @@ "table":254, "internalStatus":16, "internalFlags":8, - "internalNextHopNum":1, - "internalNextHopActiveNum":1, "nexthops":[ { "flags":3, "fib":true, "directlyConnected":true, - "interfaceIndex":2, "interfaceName":"tor2-eth0", "active":true } @@ -94,23 +89,20 @@ "table":254, "internalStatus":16, "internalFlags":8, - "internalNextHopNum":1, - "internalNextHopActiveNum":1, "nexthops":[ { "flags":3, "fib":true, "directlyConnected":true, - "interfaceIndex":3, "interfaceName":"tor2-eth1", "active":true } ] } ], - "192.168.5.0\/24":[ + "192.168.5.1\/32":[ { - "prefix":"192.168.5.0\/24", + "prefix":"192.168.5.1\/32", "protocol":"bgp", "selected":true, "destSelected":true, @@ -120,29 +112,28 @@ "table":254, "internalStatus":16, "internalFlags":13, - "internalNextHopNum":2, - "internalNextHopActiveNum":1, "nexthops":[ { - "flags":0, + "flags":5, "ip":"192.168.2.1", - "afi":"ipv4" + "afi":"ipv4", + "active":true, + "recursive":true }, { "flags":3, "fib":true, - "ip":"192.168.6.4", + "ip":"192.168.4.3", "afi":"ipv4", - "interfaceIndex":4, - "interfaceName":"tor2-eth2", + "interfaceName":"tor2-eth1", "active":true } ] } ], - "192.168.6.0\/24":[ + "192.168.6.2\/32":[ { - "prefix":"192.168.6.0\/24", + "prefix":"192.168.6.2\/32", "protocol":"connected", "selected":true, "destSelected":true, @@ -152,15 +143,12 @@ "table":254, "internalStatus":16, "internalFlags":8, - "internalNextHopNum":1, - "internalNextHopActiveNum":1, "nexthops":[ { "flags":3, "fib":true, "directlyConnected":true, - "interfaceIndex":4, - "interfaceName":"tor2-eth2", + "interfaceName":"lo", "active":true } ] diff --git a/tests/topotests/bgp_rr_ibgp/tor2/zebra.conf b/tests/topotests/bgp_rr_ibgp/tor2/zebra.conf index 3318cbb196..e1a06b14fc 100644 --- a/tests/topotests/bgp_rr_ibgp/tor2/zebra.conf +++ b/tests/topotests/bgp_rr_ibgp/tor2/zebra.conf @@ -9,5 +9,5 @@ int tor2-eth1 ip addr 192.168.4.2/24 -int tor2-eth2 - ip addr 192.168.6.2/24 +int lo + ip addr 192.168.6.2/32 diff --git a/tests/topotests/pytest.ini b/tests/topotests/pytest.ini index 62c825341f..ade5bfd501 100644 --- a/tests/topotests/pytest.ini +++ b/tests/topotests/pytest.ini @@ -1,6 +1,6 @@ # Skip pytests example directory [pytest] -norecursedirs = .git example-test example-topojson-test lib docker bgp_rr_ibgp +norecursedirs = .git example-test example-topojson-test lib docker [topogen] # Default configuration values diff --git a/tools/gcc-plugins/.gitignore b/tools/gcc-plugins/.gitignore new file mode 100644 index 0000000000..dd8d0cb7ee --- /dev/null +++ b/tools/gcc-plugins/.gitignore @@ -0,0 +1,7 @@ +*.so +*.o +debian/.debhelper +debian/files +debian/*.substvars +debian/gcc-9-frr-plugin +!gcc-retain-typeinfo.patch diff --git a/tools/gcc-plugins/COPYING.GPLv3 b/tools/gcc-plugins/COPYING.GPLv3 new file mode 100644 index 0000000000..94a9ed024d --- /dev/null +++ b/tools/gcc-plugins/COPYING.GPLv3 @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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 3 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. If not, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. diff --git a/tools/gcc-plugins/Makefile b/tools/gcc-plugins/Makefile new file mode 100644 index 0000000000..d6edd745ce --- /dev/null +++ b/tools/gcc-plugins/Makefile @@ -0,0 +1,19 @@ +all: frr-format.so + +CXX=g++-9 + +PLUGBASE=`$(CXX) -print-file-name=plugin` +CPPFLAGS=-I$(PLUGBASE)/include -I$(PLUGBASE)/include/c-family + +frr-format.so: frr-format.o + $(CXX) -g -shared -o $@ $^ + +frr-format.o: frr-format.c gcc-common.h + $(CXX) -g $(CPPFLAGS) -fPIC -Wall -Wextra -Wno-unused-parameter -c -o $@ $< + +install: + install -d $(DESTDIR)$(PLUGBASE) + install frr-format.so $(DESTDIR)$(PLUGBASE) + +clean: + rm -f frr-format.so frr-format.o diff --git a/tools/gcc-plugins/README.md b/tools/gcc-plugins/README.md new file mode 100644 index 0000000000..94a9635e76 --- /dev/null +++ b/tools/gcc-plugins/README.md @@ -0,0 +1,99 @@ +frr-format GCC plugin +===================== + +Context +------- + +This plugin provides improved type checking for Linux kernel style printf +extensions (i.e. `%pI4` printing `struct in_addr *` as `1.2.3.4`.) + +Other than additional warnings, (non-)usage of this plugin should not affect +the build outcome. It is perfectly fine to build FRR without this plugin. + + +Binary Debian packages +---------------------- + +Can be found at [https://deb.nox.tf/devel/]. + + +GCC requirements +---------------- + +To use this plugin, you need a **patched 9.3.0** version of GCC using the +[gcc-retain-typeinfo.patch] provided in this repo. Without this patch, GCC +strips type information too early during compilation, leaving to the plugin +being unable to perform more meaningful type checks. (Specifically, all +`typedef` types will be "cooked down" to their final type.) + +(@eqvinox has discussed this one-line diff with some GCC people on their +IRC channel around mid 2019, the consensus was that the line is an "early +optimization" and removing it should not be harmful. However, doing so is +likely to break GCC's unit tests since warnings would print different types.) + +Other versions of gcc are not supported. gcc 8 previously did work but isn't +actively tested/maintained. gcc 10 is not supported yet but may work. + + +Usage +----- + +First, all plugin-specific statements should be wrapped by an ifdef: + +``` +#ifdef _FRR_ATTRIBUTE_PRINTFRR +... +#endif +``` + +`_FRR_ATTRIBUTE_PRINTFRR` will be defined to the plugin's version (currently +0x10000) whenever the plugin is loaded. + +Then, annotate extended printf functions with the `frr_format` attribute. +This works exactly like the `format` attribute: + +``` +int printfn(const char *fmt, ...) __attribute__((frr_format("frr_printf", 1, 2))); +``` + +In the FRR codebase, use the `PRINTFRR` macro provided in +[../../lib/compiler.h]. + +Lastly, "declare" extensions with `#pragma FRR printfrr_ext`: +``` +#ifdef _FRR_ATTRIBUTE_PRINTFRR +#pragma FRR printfrr_ext "%pI4" (struct in_addr *) +#pragma FRR printfrr_ext "%pI4" (in_addr_t *) +#endif +``` + +Note that you can use multiple such lines if a particular extended printer +works for more than one type (as seen above.) + +The pragma type "parameter" looks like a C cast but unfortunately due to GCC +not exporting a good interface to proper type parsing, it is "ghetto parsed", +with only `struct`, `union`, `enum` being properly supported. `const` is +ignored if it occurs as the first token. (The plugin always accepts `const` +parameters for printf since printf shouldn't change the passed data it's +printing.) The last token may be zero or more counts of `*`, note that +qualifiers on the intermediate pointers (e.g. `const char * const *`) are not +supported. + + +TODOs and future direction +-------------------------- + +* support two-parameter extension printers that use the precision field + (e.g. `"%.*pI5" (int af, void *addr)` to print an IP address with the + address family in the "precision". + +* port to future GCC versions + +* get the one-liner patch upstreamed + + +License +------- + +This plugin is **derivative of GCC 9.x**. It was created by copying off +`c-format.c`. It must therefore adhere to GCC's GPLv3+ license. diff --git a/tools/gcc-plugins/debian/changelog b/tools/gcc-plugins/debian/changelog new file mode 100644 index 0000000000..62bbbcd46f --- /dev/null +++ b/tools/gcc-plugins/debian/changelog @@ -0,0 +1,5 @@ +gcc-frr-plugin (9.3.0d8+equi2) unstable; urgency=medium + + * package created (+equi1 used during development, never released.) + + -- David Lamparter <equinox-debian@diac24.net> Sun, 29 Mar 2020 08:32:24 +0200 diff --git a/tools/gcc-plugins/debian/compat b/tools/gcc-plugins/debian/compat new file mode 100644 index 0000000000..48082f72f0 --- /dev/null +++ b/tools/gcc-plugins/debian/compat @@ -0,0 +1 @@ +12 diff --git a/tools/gcc-plugins/debian/control b/tools/gcc-plugins/debian/control new file mode 100644 index 0000000000..6a9b886bef --- /dev/null +++ b/tools/gcc-plugins/debian/control @@ -0,0 +1,19 @@ +Source: gcc-frr-plugin +Section: devel +Priority: optional +Maintainer: David Lamparter <equinox-debian@diac24.net> +Build-Depends: + gcc-9-plugin-dev (=9.3.0-8+equi1), + debhelper (>= 12) +Standards-Version: 4.4.1 +Homepage: https://www.frrouting.org/ +Vcs-Browser: https://github.com/FRRouting/frr/ +Vcs-Git: https://github.com/FRRouting/frr.git + +Package: gcc-9-frr-plugin +Architecture: linux-any +Depends: + gcc-9 (=9.3.0-8+equi1), + ${misc:Depends}, + ${shlibs:Depends} +Description: GCC plugin for FRRouting diff --git a/tools/gcc-plugins/debian/copyright b/tools/gcc-plugins/debian/copyright new file mode 100644 index 0000000000..dcd9fa1770 --- /dev/null +++ b/tools/gcc-plugins/debian/copyright @@ -0,0 +1,9 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: FRR +Upstream-Contact: maintainers@frrouting.org, security@frrouting.org +Source: https://www.frrouting.org/ + +Files: * +Copyright: + 2019-2020 by David Lamparter + Code derived from GCC, please refer to gcc package copyright. diff --git a/tools/gcc-plugins/debian/rules b/tools/gcc-plugins/debian/rules new file mode 100755 index 0000000000..f8f42ad337 --- /dev/null +++ b/tools/gcc-plugins/debian/rules @@ -0,0 +1,11 @@ +#!/usr/bin/make -f + +# standard Debian options & profiles + +export DEB_BUILD_MAINT_OPTIONS = hardening=+all + +%: + dh $@ + +override_dh_auto_test: + true diff --git a/tools/gcc-plugins/debian/source/format b/tools/gcc-plugins/debian/source/format new file mode 100644 index 0000000000..af745b310b --- /dev/null +++ b/tools/gcc-plugins/debian/source/format @@ -0,0 +1 @@ +3.0 (git) diff --git a/tools/gcc-plugins/format-test.c b/tools/gcc-plugins/format-test.c new file mode 100644 index 0000000000..b031ca5ece --- /dev/null +++ b/tools/gcc-plugins/format-test.c @@ -0,0 +1,107 @@ +#include <stddef.h> +#include <stdlib.h> +#include <netinet/in.h> +#include <sys/types.h> +#include <unistd.h> + +typedef unsigned long mytype; +typedef size_t mysize; + +typedef unsigned int not_in_addr_t; +typedef in_addr_t yes_in_addr_t; +typedef struct in_addr in_addr_s; + +struct other { + int x; +}; + +int testfn(const char *fmt, ...) __attribute__((frr_format("frr_printf", 1, 2))); + +#ifndef _FRR_ATTRIBUTE_PRINTFRR +#error please load the frr-format plugin +#endif + +#pragma FRR printfrr_ext "%pI4" (struct in_addr *) +#pragma FRR printfrr_ext "%pI4" (in_addr_t *) + +int test(unsigned long long ay) +{ + size_t v_size_t = 0; + long v_long = 0; + int v_int = 0; + uint64_t v_uint64_t = 0; + mytype v_mytype = 0; + mysize v_mysize = 0; + pid_t v_pid_t = 0; + + testfn("%zu", v_size_t); // NOWARN + testfn("%zu", v_long); // WARN + testfn("%zu", v_int); // WARN + testfn("%zu", sizeof(v_int)); // NOWARN + testfn("%zu", v_mytype); // WARN + testfn("%zu", v_mysize); // NOWARN + testfn("%zu", v_uint64_t); // WARN + testfn("%zu", v_pid_t); // WARN + + testfn("%lu", v_long); // NOWARN PEDANTIC + testfn("%lu", v_int); // WARN + testfn("%lu", v_size_t); // WARN + testfn("%lu", sizeof(v_int)); // NOWARN (integer constant) + testfn("%lu", v_uint64_t); // WARN + testfn("%lu", v_pid_t); // WARN + + testfn("%ld", v_long); // NOWARN + testfn("%ld", v_int); // WARN + testfn("%ld", v_size_t); // WARN + testfn("%ld", sizeof(v_int)); // NOWARN (integer constant) + testfn("%ld", v_uint64_t); // WARN + testfn("%ld", v_pid_t); // WARN + + testfn("%d", v_int); // NOWARN + testfn("%d", v_long); // WARN + testfn("%d", v_size_t); // WARN + testfn("%d", sizeof(v_int)); // WARN + testfn("%d", v_uint64_t); // WARN + testfn("%d", v_pid_t); // WARN + + testfn("%Lu", v_size_t); // WARN + testfn("%Lu", v_long); // WARN + testfn("%Lu", v_int); // WARN + testfn("%Lu", sizeof(v_int)); // NOWARN (integer constant) + testfn("%Lu", v_mytype); // WARN + testfn("%Lu", v_mysize); // WARN + testfn("%Lu", v_pid_t); // WARN + testfn("%Lu", v_uint64_t); // NOWARN + + testfn("%Ld", v_size_t); // WARN + testfn("%Ld", v_long); // WARN + testfn("%Ld", v_int); // WARN + testfn("%Ld", sizeof(v_int)); // NOWARN (integer constant) + testfn("%Ld", v_mytype); // WARN + testfn("%Ld", v_mysize); // WARN + testfn("%Ld", v_pid_t); // WARN + testfn("%Ld", v_uint64_t); // NOWARN + + testfn("%pI4", &v_long); // WARN + + in_addr_t v_in_addr_t; + yes_in_addr_t v_yes_in_addr_t; + not_in_addr_t v_not_in_addr_t; + void *v_voidp = &v_in_addr_t; + + testfn("%pI4", &v_in_addr_t); // NOWARN + testfn("%pI4", &v_yes_in_addr_t); // NOWARN + testfn("%pI4", &v_not_in_addr_t); // WARN + testfn("%pI4", v_voidp); // WARN + + struct in_addr v_in_addr; + in_addr_s v_in_addr_s; + struct other v_other; + const struct in_addr *v_in_addr_const = &v_in_addr; + + testfn("%pI4", &v_in_addr); // NOWARN + testfn("%pI4", &v_in_addr_s); // NOWARN + testfn("%pI4", &v_other); // WARN + testfn("%pI4", v_in_addr_const); // NOWARN + return 0; +} diff --git a/tools/gcc-plugins/format-test.py b/tools/gcc-plugins/format-test.py new file mode 100644 index 0000000000..cc6ca6100e --- /dev/null +++ b/tools/gcc-plugins/format-test.py @@ -0,0 +1,57 @@ +import subprocess +import sys +import shlex +import os +import re + +os.environ['LC_ALL'] = 'C' +os.environ['LANG'] = 'C' +for k in list(os.environ.keys()): + if k.startswith('LC_'): + os.environ.pop(k) + +c_re = re.compile(r'//\s+(NO)?WARN') +expect = {} +lines = {} + +with open('format-test.c', 'r') as fd: + for lno, line in enumerate(fd.readlines(), 1): + lines[lno] = line.strip() + m = c_re.search(line) + if m is None: + continue + if m.group(1) is None: + expect[lno] = 'warn' + else: + expect[lno] = 'nowarn' + +cmd = shlex.split('gcc -Wall -Wextra -Wno-unused -fplugin=./frr-format.so -fno-diagnostics-show-caret -c -o format-test.o format-test.c') + +gcc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) +sout, serr = gcc.communicate() +gcc.wait() + +gcclines = serr.decode('UTF-8').splitlines() +line_re = re.compile(r'^format-test\.c:(\d+):(.*)$') +gcc_warns = {} + +for line in gcclines: + if line.find('In function') >= 0: + continue + m = line_re.match(line) + if m is None: + sys.stderr.write('cannot process GCC output: %s\n' % line) + continue + + lno = int(m.group(1)) + gcc_warns.setdefault(lno, []).append(line) + +for lno, val in expect.items(): + if val == 'nowarn' and lno in gcc_warns: + sys.stderr.write('unexpected gcc warning on line %d:\n\t%s\n\t%s\n' % (lno, lines[lno], '\n\t'.join(gcc_warns[lno]))) + if val == 'warn' and lno not in gcc_warns: + sys.stderr.write('expected warning on line %d but did not get one\n\t%s\n' % (lno, lines[lno])) + +leftover = set(gcc_warns.keys()) - set(expect.keys()) +for lno in sorted(leftover): + sys.stderr.write('unmarked gcc warning on line %d:\n\t%s\n\t%s\n' % (lno, lines[lno], '\n\t'.join(gcc_warns[lno]))) diff --git a/tools/gcc-plugins/frr-format.c b/tools/gcc-plugins/frr-format.c new file mode 100644 index 0000000000..174f403d48 --- /dev/null +++ b/tools/gcc-plugins/frr-format.c @@ -0,0 +1,4457 @@ +/* Check calls to formatted I/O functions (-Wformat). + Copyright (C) 1992-2019 Free Software Foundation, Inc. + + Extended for FRR's printfrr() with Linux kernel style extensions + Copyright (C) 2019-2020 David Lamparter, for NetDEF, Inc. + +This file is part of GCC. + +GCC 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 3, or (at your option) any later +version. + +GCC 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 GCC; see the file COPYING.GPLv3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "gcc-common.h" + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +//include "c-target.h" +#include "c-common.h" +#include "alloc-pool.h" +#include "stringpool.h" +#include "c-tree.h" +#include "c-objc.h" +#include "intl.h" +#include "langhooks.h" +#include "frr-format.h" +#include "diagnostic.h" +#include "substring-locations.h" +#include "selftest.h" +#include "selftest-diagnostic.h" +#ifndef FIRST_PSEUDO_REGISTER +#define FIRST_PSEUDO_REGISTER 0 +#endif +#include "builtins.h" +#include "attribs.h" +#include "gcc-rich-location.h" +#include "c-pretty-print.h" +#include "c-pragma.h" + +extern struct cpp_reader *parse_in; + +#pragma GCC visibility push(hidden) + +/* Handle attributes associated with format checking. */ + +/* This must be in the same order as format_types, except for + format_type_error. Target-specific format types do not have + matching enum values. */ +enum format_type { frr_printf_format_type, + format_type_error = -1}; + +struct function_format_info +{ + int format_type; /* type of format (printf, scanf, etc.) */ + unsigned HOST_WIDE_INT format_num; /* number of format argument */ + unsigned HOST_WIDE_INT first_arg_num; /* number of first arg (zero for varargs) */ +}; + +static GTY(()) tree local_uint64_t_node; +static GTY(()) tree local_int64_t_node; + +static GTY(()) tree local_size_t_node; +static GTY(()) tree local_ssize_t_node; +static GTY(()) tree local_atomic_size_t_node; +static GTY(()) tree local_atomic_ssize_t_node; +static GTY(()) tree local_ptrdiff_t_node; + +static GTY(()) tree local_pid_t_node; +static GTY(()) tree local_uid_t_node; +static GTY(()) tree local_gid_t_node; +static GTY(()) tree local_time_t_node; + +static GTY(()) tree local_socklen_t_node; +static GTY(()) tree local_in_addr_t_node; + +static struct type_special { + tree *match; + tree *replace; + tree *cousin; +} special_types[] = { + { &local_atomic_size_t_node, &local_size_t_node, &local_ssize_t_node, }, + { &local_atomic_ssize_t_node, &local_ssize_t_node, &local_size_t_node, }, + { &local_size_t_node, NULL, &local_ssize_t_node, }, + { &local_ssize_t_node, NULL, &local_size_t_node, }, + { &local_uint64_t_node, NULL, &local_int64_t_node, }, + { &local_int64_t_node, NULL, &local_uint64_t_node, }, + { &local_pid_t_node, NULL, &local_pid_t_node, }, + { &local_uid_t_node, NULL, &local_uid_t_node, }, + { &local_gid_t_node, NULL, &local_gid_t_node, }, + { &local_time_t_node, NULL, &local_time_t_node, }, + { NULL, NULL, NULL, } +}; + +static bool decode_format_attr (tree, function_format_info *, int); +static int decode_format_type (const char *); + +static bool check_format_string (tree argument, + unsigned HOST_WIDE_INT format_num, + int flags, bool *no_add_attrs, + int expected_format_type); +static bool get_constant (tree expr, unsigned HOST_WIDE_INT *value, + int validated_p); +static const char *convert_format_name_to_system_name (const char *attr_name); + +static int first_target_format_type; +static const char *format_name (int format_num); +static int format_flags (int format_num); + +/* Emit a warning as per format_warning_va, but construct the substring_loc + for the character at offset (CHAR_IDX - 1) within a string constant + FORMAT_STRING_CST at FMT_STRING_LOC. */ + +ATTRIBUTE_GCC_DIAG (5,6) +static bool +format_warning_at_char (location_t fmt_string_loc, tree format_string_cst, + int char_idx, int opt, const char *gmsgid, ...) +{ + va_list ap; + va_start (ap, gmsgid); + tree string_type = TREE_TYPE (format_string_cst); + + /* The callers are of the form: + format_warning (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + where format_chars has already been incremented, so that + CHAR_IDX is one character beyond where the warning should + be emitted. Fix it. */ + char_idx -= 1; + + substring_loc fmt_loc (fmt_string_loc, string_type, char_idx, char_idx, + char_idx); +#if BUILDING_GCC_VERSION >= 9000 + format_string_diagnostic_t diag (fmt_loc, NULL, UNKNOWN_LOCATION, NULL, + NULL); + bool warned = diag.emit_warning_va (opt, gmsgid, &ap); +#else + bool warned = format_warning_va (fmt_loc, UNKNOWN_LOCATION, NULL, + opt, gmsgid, &ap); +#endif + va_end (ap); + + return warned; +} + +/* Check that we have a pointer to a string suitable for use as a format. + The default is to check for a char type. + For objective-c dialects, this is extended to include references to string + objects validated by objc_string_ref_type_p (). + Targets may also provide a string object type that can be used within c and + c++ and shared with their respective objective-c dialects. In this case the + reference to a format string is checked for validity via a hook. + + The function returns true if strref points to any string type valid for the + language dialect and target. */ + +static bool +valid_stringptr_type_p (tree strref) +{ + return (strref != NULL + && TREE_CODE (strref) == POINTER_TYPE + && (TYPE_MAIN_VARIANT (TREE_TYPE (strref)) == char_type_node + || objc_string_ref_type_p (strref))); +// || (*targetcm.string_object_ref_type_p) ((const_tree) strref))); +} + +/* Handle a "format_arg" attribute; arguments as in + struct attribute_spec.handler. */ +tree +handle_frr_format_arg_attribute (tree *node, tree ARG_UNUSED (name), + tree args, int flags, bool *no_add_attrs) +{ + tree type = *node; + tree format_num_expr = TREE_VALUE (args); + unsigned HOST_WIDE_INT format_num = 0; + + if (!get_constant (format_num_expr, &format_num, 0)) + { + error ("format string has invalid operand number"); + *no_add_attrs = true; + return NULL_TREE; + } + + if (prototype_p (type)) + { + /* The format arg can be any string reference valid for the language and + target. We cannot be more specific in this case. */ + if (!check_format_string (type, format_num, flags, no_add_attrs, -1)) + return NULL_TREE; + } + + if (!valid_stringptr_type_p (TREE_TYPE (type))) + { + if (!(flags & (int) ATTR_FLAG_BUILT_IN)) + error ("function does not return string type"); + *no_add_attrs = true; + return NULL_TREE; + } + + return NULL_TREE; +} + +/* Verify that the format_num argument is actually a string reference suitable, + for the language dialect and target (in case the format attribute is in + error). When we know the specific reference type expected, this is also + checked. */ +static bool +check_format_string (tree fntype, unsigned HOST_WIDE_INT format_num, + int flags, bool *no_add_attrs, int expected_format_type) +{ + unsigned HOST_WIDE_INT i; + bool is_target_sref, is_char_ref; + tree ref; + int fmt_flags; + function_args_iterator iter; + + i = 1; + FOREACH_FUNCTION_ARGS (fntype, ref, iter) + { + if (i == format_num) + break; + i++; + } + + if (!ref + || !valid_stringptr_type_p (ref)) + { + if (!(flags & (int) ATTR_FLAG_BUILT_IN)) + error ("format string argument is not a string type"); + *no_add_attrs = true; + return false; + } + + /* We only know that we want a suitable string reference. */ + if (expected_format_type < 0) + return true; + + /* Now check that the arg matches the expected type. */ + is_char_ref = + (TYPE_MAIN_VARIANT (TREE_TYPE (ref)) == char_type_node); + + fmt_flags = format_flags (expected_format_type); + is_target_sref = false; + + if (!(fmt_flags & FMT_FLAG_PARSE_ARG_CONVERT_EXTERNAL)) + { + if (is_char_ref) + return true; /* OK, we expected a char and found one. */ + else + { + error ("found a %qT but the format argument should be a string", + ref); + *no_add_attrs = true; + return false; + } + } + + /* We expect a string object type as the format arg. */ + if (is_char_ref) + { + error ("format argument should be a %qs reference but" + " a string was found", format_name (expected_format_type)); + *no_add_attrs = true; + return false; + } + + /* We will allow a target string ref to match only itself. */ + if (first_target_format_type + && expected_format_type >= first_target_format_type + && is_target_sref) + return true; + else + { + error ("format argument should be a %qs reference", + format_name (expected_format_type)); + *no_add_attrs = true; + return false; + } + + gcc_unreachable (); +} + +/* Verify EXPR is a constant, and store its value. + If validated_p is true there should be no errors. + Returns true on success, false otherwise. */ +static bool +get_constant (tree expr, unsigned HOST_WIDE_INT *value, int validated_p) +{ + if (!tree_fits_uhwi_p (expr)) + { + gcc_assert (!validated_p); + return false; + } + + *value = TREE_INT_CST_LOW (expr); + + return true; +} + +/* Decode the arguments to a "format" attribute into a + function_format_info structure. It is already known that the list + is of the right length. If VALIDATED_P is true, then these + attributes have already been validated and must not be erroneous; + if false, it will give an error message. Returns true if the + attributes are successfully decoded, false otherwise. */ + +static bool +decode_format_attr (tree args, function_format_info *info, int validated_p) +{ + tree format_type_id = TREE_VALUE (args); + tree format_num_expr = TREE_VALUE (TREE_CHAIN (args)); + tree first_arg_num_expr + = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args))); + + if (TREE_CODE (format_type_id) != STRING_CST) + { + gcc_assert (!validated_p); + error ("unrecognized format specifier"); + return false; + } + else + { + const char *p = TREE_STRING_POINTER (format_type_id); + + p = convert_format_name_to_system_name (p); + + info->format_type = decode_format_type (p); + + if (info->format_type == format_type_error) + { + gcc_assert (!validated_p); + warning (OPT_Wformat_, "%qE is an unrecognized format function type", + format_type_id); + return false; + } + } + + if (!get_constant (format_num_expr, &info->format_num, validated_p)) + { + error ("format string has invalid operand number"); + return false; + } + + if (!get_constant (first_arg_num_expr, &info->first_arg_num, validated_p)) + { + error ("%<...%> has invalid operand number"); + return false; + } + + if (info->first_arg_num != 0 && info->first_arg_num <= info->format_num) + { + gcc_assert (!validated_p); + error ("format string argument follows the args to be formatted"); + return false; + } + + return true; +} + +/* Check a call to a format function against a parameter list. */ + +/* The C standard version C++ is treated as equivalent to + or inheriting from, for the purpose of format features supported. */ +#define CPLUSPLUS_STD_VER (cxx_dialect < cxx11 ? STD_C94 : STD_C99) +/* The C standard version we are checking formats against when pedantic. */ +#define C_STD_VER ((int) (c_dialect_cxx () \ + ? CPLUSPLUS_STD_VER \ + : (flag_isoc99 \ + ? STD_C99 \ + : (flag_isoc94 ? STD_C94 : STD_C89)))) +/* The name to give to the standard version we are warning about when + pedantic. FEATURE_VER is the version in which the feature warned out + appeared, which is higher than C_STD_VER. */ +#define C_STD_NAME(FEATURE_VER) (c_dialect_cxx () \ + ? (cxx_dialect < cxx11 ? "ISO C++98" \ + : "ISO C++11") \ + : ((FEATURE_VER) == STD_EXT \ + ? "ISO C" \ + : "ISO C90")) +/* Adjust a C standard version, which may be STD_C9L, to account for + -Wno-long-long. Returns other standard versions unchanged. */ +#define ADJ_STD(VER) ((int) ((VER) == STD_C9L \ + ? (warn_long_long ? STD_C99 : STD_C89) \ + : (VER))) + +/* Enum describing the kind of specifiers present in the format and + requiring an argument. */ +enum format_specifier_kind { + CF_KIND_FORMAT, + CF_KIND_FIELD_WIDTH, + CF_KIND_FIELD_PRECISION +}; + +static const char *kind_descriptions[] = { + N_("format"), + N_("field width specifier"), + N_("field precision specifier") +}; + +/* Structure describing details of a type expected in format checking, + and the type to check against it. */ +struct format_wanted_type +{ + /* The type wanted. */ + tree wanted_type; + /* The name of this type to use in diagnostics. */ + const char *wanted_type_name; + /* Should be type checked just for scalar width identity. */ + int scalar_identity_flag; + /* The level of indirection through pointers at which this type occurs. */ + int pointer_count; + /* Whether, when pointer_count is 1, to allow any character type when + pedantic, rather than just the character or void type specified. */ + int char_lenient_flag; + /* Whether the argument, dereferenced once, is written into and so the + argument must not be a pointer to a const-qualified type. */ + int writing_in_flag; + /* Whether the argument, dereferenced once, is read from and so + must not be a NULL pointer. */ + int reading_from_flag; + /* The kind of specifier that this type is used for. */ + enum format_specifier_kind kind; + /* The starting character of the specifier. This never includes the + initial percent sign. */ + const char *format_start; + /* The length of the specifier. */ + int format_length; + /* The actual parameter to check against the wanted type. */ + tree param; + /* The argument number of that parameter. */ + int arg_num; + /* The offset location of this argument with respect to the format + string location. */ + unsigned int offset_loc; + /* The next type to check for this format conversion, or NULL if none. */ + struct format_wanted_type *next; +}; + +/* Convenience macro for format_length_info meaning unused. */ +#define NO_FMT NULL, FMT_LEN_none, STD_C89 + +static const format_length_info printf_length_specs[] = +{ + { "h", FMT_LEN_h, STD_C89, "hh", FMT_LEN_hh, STD_C99, 0 }, + { "l", FMT_LEN_l, STD_C89, "ll", FMT_LEN_ll, STD_C9L, 0 }, + { "q", FMT_LEN_ll, STD_EXT, NO_FMT, 0 }, + { "L", FMT_LEN_L, STD_C89, NO_FMT, 0 }, + { "z", FMT_LEN_z, STD_C99, NO_FMT, 0 }, + { "Z", FMT_LEN_z, STD_EXT, NO_FMT, 0 }, + { "t", FMT_LEN_t, STD_C99, NO_FMT, 0 }, + { "j", FMT_LEN_j, STD_C99, NO_FMT, 0 }, + { "H", FMT_LEN_H, STD_EXT, NO_FMT, 0 }, + { "D", FMT_LEN_D, STD_EXT, "DD", FMT_LEN_DD, STD_EXT, 0 }, + { NO_FMT, NO_FMT, 0 } +}; + +static const format_flag_spec printf_flag_specs[] = +{ + { ' ', 0, 0, 0, N_("' ' flag"), N_("the ' ' printf flag"), STD_C89 }, + { '+', 0, 0, 0, N_("'+' flag"), N_("the '+' printf flag"), STD_C89 }, + { '#', 0, 0, 0, N_("'#' flag"), N_("the '#' printf flag"), STD_C89 }, + { '0', 0, 0, 0, N_("'0' flag"), N_("the '0' printf flag"), STD_C89 }, + { '-', 0, 0, 0, N_("'-' flag"), N_("the '-' printf flag"), STD_C89 }, + { '\'', 0, 0, 0, N_("''' flag"), N_("the ''' printf flag"), STD_EXT }, + { 'I', 0, 0, 0, N_("'I' flag"), N_("the 'I' printf flag"), STD_EXT }, + { 'w', 0, 0, 0, N_("field width"), N_("field width in printf format"), STD_C89 }, + { 'p', 0, 0, 0, N_("precision"), N_("precision in printf format"), STD_C89 }, + { 'L', 0, 0, 0, N_("length modifier"), N_("length modifier in printf format"), STD_C89 }, + { 0, 0, 0, 0, NULL, NULL, STD_C89 } +}; + + +static const format_flag_pair printf_flag_pairs[] = +{ + { ' ', '+', 1, 0 }, + { '0', '-', 1, 0 }, + { '0', 'p', 1, 'i' }, + { 0, 0, 0, 0 } +}; + +#define ETAB_SZ 128 +static kernel_ext_fmt ext_p[ETAB_SZ] = { + { NULL } +}; +static kernel_ext_fmt ext_d[ETAB_SZ] = { + { NULL } +}; + +static const format_char_info print_char_table[] = +{ + /* C89 conversion specifiers. */ + /* none, hh, h, l, ll, L, z, t, j, H, D, DD */ + { "di", 0, STD_C89, { T89_I, T99_SC, T89_S, T89_L, T9L_LL, TEX_S64, T99_SST, T99_PD, T99_IM, BADLEN, BADLEN, BADLEN }, "-wp0 +'I", "i", NULL, ext_d }, + { "oxX", 0, STD_C89, { T89_UI, T99_UC, T89_US, T89_UL, T9L_ULL, TEX_U64, T99_ST, T99_UPD, T99_UIM, BADLEN, BADLEN, BADLEN }, "-wp0#", "i", NULL, NULL }, + { "u", 0, STD_C89, { T89_UI, T99_UC, T89_US, T89_UL, T9L_ULL, TEX_U64, T99_ST, T99_UPD, T99_UIM, BADLEN, BADLEN, BADLEN }, "-wp0'I", "i", NULL, NULL }, + { "fgG", 0, STD_C89, { T89_D, BADLEN, BADLEN, T99_D, BADLEN, T89_LD, BADLEN, BADLEN, BADLEN, TEX_D32, TEX_D64, TEX_D128 }, "-wp0 +#'I", "", NULL, NULL }, + { "eE", 0, STD_C89, { T89_D, BADLEN, BADLEN, T99_D, BADLEN, T89_LD, BADLEN, BADLEN, BADLEN, TEX_D32, TEX_D64, TEX_D128 }, "-wp0 +#I", "", NULL, NULL }, + { "c", 0, STD_C89, { T89_I, BADLEN, BADLEN, T94_WI, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-w", "", NULL, NULL }, + { "s", 1, STD_C89, { T89_C, BADLEN, BADLEN, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp", "cR", NULL, NULL }, + { "p", 1, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp", "c", NULL, ext_p }, + { "n", 1, STD_C89, { T89_I, T99_SC, T89_S, T89_L, T9L_LL, BADLEN, T99_SST, T99_PD, T99_IM, BADLEN, BADLEN, BADLEN }, "", "W", NULL, NULL }, + /* C99 conversion specifiers. */ + { "F", 0, STD_C99, { T99_D, BADLEN, BADLEN, T99_D, BADLEN, T99_LD, BADLEN, BADLEN, BADLEN, TEX_D32, TEX_D64, TEX_D128 }, "-wp0 +#'I", "", NULL, NULL }, + { "aA", 0, STD_C99, { T99_D, BADLEN, BADLEN, T99_D, BADLEN, T99_LD, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp0 +#", "", NULL, NULL }, + /* X/Open conversion specifiers. */ + { "C", 0, STD_EXT, { TEX_WI, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-w", "", NULL, NULL }, + { "S", 1, STD_EXT, { TEX_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp", "R", NULL, NULL }, + /* GNU conversion specifiers. */ + { "m", 0, STD_EXT, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp", "", NULL, NULL }, + { NULL, 0, STD_C89, NOLENGTHS, NULL, NULL, NULL, NULL } +}; + +/* This must be in the same order as enum format_type. */ +static const format_kind_info format_types_orig[] = +{ + { "frr_printf", printf_length_specs, print_char_table, " +#0-'I", NULL, + printf_flag_specs, printf_flag_pairs, + FMT_FLAG_ARG_CONVERT|FMT_FLAG_DOLLAR_MULTIPLE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_EMPTY_PREC_OK, + 'w', 0, 'p', 0, 'L', 0, + &integer_type_node, &integer_type_node + }, +}; + +/* This layer of indirection allows GCC to reassign format_types with + new data if necessary, while still allowing the original data to be + const. */ +static const format_kind_info *format_types = format_types_orig; + +static int n_format_types = ARRAY_SIZE (format_types_orig); + +/* Structure detailing the results of checking a format function call + where the format expression may be a conditional expression with + many leaves resulting from nested conditional expressions. */ +struct format_check_results +{ + /* Number of leaves of the format argument that could not be checked + as they were not string literals. */ + int number_non_literal; + /* Number of leaves of the format argument that were null pointers or + string literals, but had extra format arguments. */ + int number_extra_args; + location_t extra_arg_loc; + /* Number of leaves of the format argument that were null pointers or + string literals, but had extra format arguments and used $ operand + numbers. */ + int number_dollar_extra_args; + /* Number of leaves of the format argument that were wide string + literals. */ + int number_wide; + /* Number of leaves of the format argument that are not array of "char". */ + int number_non_char; + /* Number of leaves of the format argument that were empty strings. */ + int number_empty; + /* Number of leaves of the format argument that were unterminated + strings. */ + int number_unterminated; + /* Number of leaves of the format argument that were not counted above. */ + int number_other; + /* Location of the format string. */ + location_t format_string_loc; +}; + +struct format_check_context +{ + format_check_results *res; + function_format_info *info; + tree params; + vec<location_t> *arglocs; +}; + +/* Return the format name (as specified in the original table) for the format + type indicated by format_num. */ +static const char * +format_name (int format_num) +{ + if (format_num >= 0 && format_num < n_format_types) + return format_types[format_num].name; + gcc_unreachable (); +} + +/* Return the format flags (as specified in the original table) for the format + type indicated by format_num. */ +static int +format_flags (int format_num) +{ + if (format_num >= 0 && format_num < n_format_types) + return format_types[format_num].flags; + gcc_unreachable (); +} + +static void check_format_info (function_format_info *, tree, + vec<location_t> *); +static void check_format_arg (void *, tree, unsigned HOST_WIDE_INT); +static void check_format_info_main (format_check_results *, + function_format_info *, const char *, + location_t, tree, + int, tree, + unsigned HOST_WIDE_INT, + object_allocator<format_wanted_type> &, + vec<location_t> *); + +static void init_dollar_format_checking (int, tree); +static int maybe_read_dollar_number (const char **, int, + tree, tree *, const format_kind_info *); +static bool avoid_dollar_number (const char *); +static void finish_dollar_format_checking (format_check_results *, int); + +static const format_flag_spec *get_flag_spec (const format_flag_spec *, + int, const char *); + +static void check_format_types (const substring_loc &fmt_loc, + format_wanted_type *, + const format_kind_info *fki, + int offset_to_type_start, + char conversion_char, + vec<location_t> *arglocs); +static void format_type_warning (const substring_loc &fmt_loc, + location_t param_loc, + format_wanted_type *, tree, + tree, + const format_kind_info *fki, + int offset_to_type_start, + char conversion_char, + const char *extra = NULL); + +static bool check_kef_type (const substring_loc &fmt_loc, + const struct kernel_ext_fmt *kef, + unsigned arg_num, + tree cur_param, + tree wanted_type, + const format_kind_info *fki, + int offset_to_type_start, + char conversion_char, + vec<location_t> *arglocs); + +/* Decode a format type from a string, returning the type, or + format_type_error if not valid, in which case the caller should print an + error message. */ +static int +decode_format_type (const char *s) +{ + int i; + int slen; + + s = convert_format_name_to_system_name (s); + slen = strlen (s); + for (i = 0; i < n_format_types; i++) + { + int alen; + if (!strcmp (s, format_types[i].name)) + return i; + alen = strlen (format_types[i].name); + if (slen == alen + 4 && s[0] == '_' && s[1] == '_' + && s[slen - 1] == '_' && s[slen - 2] == '_' + && !strncmp (s + 2, format_types[i].name, alen)) + return i; + } + return format_type_error; +} + + +/* Check the argument list of a call to printf, scanf, etc. + ATTRS are the attributes on the function type. There are NARGS argument + values in the array ARGARRAY. + Also, if -Wsuggest-attribute=format, + warn for calls to vprintf or vscanf in functions with no such format + attribute themselves. */ + +void +check_function_format (tree attrs, int nargs, tree *argarray, + vec<location_t> *arglocs) +{ + tree a; + + /* See if this function has any format attributes. */ + for (a = attrs; a; a = TREE_CHAIN (a)) + { + if (is_attribute_p ("frr_format", TREE_PURPOSE (a))) + { + /* Yup; check it. */ + function_format_info info; + decode_format_attr (TREE_VALUE (a), &info, /*validated=*/true); + if (warn_format) + { + /* FIXME: Rewrite all the internal functions in this file + to use the ARGARRAY directly instead of constructing this + temporary list. */ + tree params = NULL_TREE; + int i; + for (i = nargs - 1; i >= 0; i--) + params = tree_cons (NULL_TREE, argarray[i], params); + check_format_info (&info, params, arglocs); + } + + /* Attempt to detect whether the current function might benefit + from the format attribute if the called function is decorated + with it. Avoid using calls with string literal formats for + guidance since those are unlikely to be viable candidates. */ + if (warn_suggest_attribute_format + && current_function_decl != NULL_TREE + && info.first_arg_num == 0 + && (format_types[info.format_type].flags + & (int) FMT_FLAG_ARG_CONVERT) + /* c_strlen will fail for a function parameter but succeed + for a literal or constant array. */ + && !c_strlen (argarray[info.format_num - 1], 1)) + { + tree c; + for (c = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl)); + c; + c = TREE_CHAIN (c)) + if (is_attribute_p ("frr_format", TREE_PURPOSE (c)) + && (decode_format_type (IDENTIFIER_POINTER + (TREE_VALUE (TREE_VALUE (c)))) + == info.format_type)) + break; + if (c == NULL_TREE) + { + /* Check if the current function has a parameter to which + the format attribute could be attached; if not, it + can't be a candidate for a format attribute, despite + the vprintf-like or vscanf-like call. */ + tree args; + for (args = DECL_ARGUMENTS (current_function_decl); + args != 0; + args = DECL_CHAIN (args)) + { + if (TREE_CODE (TREE_TYPE (args)) == POINTER_TYPE + && (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (args))) + == char_type_node)) + break; + } + if (args != 0) + warning (OPT_Wsuggest_attribute_format, "function %qD " + "might be a candidate for %qs frr_format attribute", + current_function_decl, + format_types[info.format_type].name); + } + } + } + } +} + + +/* Variables used by the checking of $ operand number formats. */ +static char *dollar_arguments_used = NULL; +static char *dollar_arguments_pointer_p = NULL; +static int dollar_arguments_alloc = 0; +static int dollar_arguments_count; +static int dollar_first_arg_num; +static int dollar_max_arg_used; +static int dollar_format_warned; + +/* Initialize the checking for a format string that may contain $ + parameter number specifications; we will need to keep track of whether + each parameter has been used. FIRST_ARG_NUM is the number of the first + argument that is a parameter to the format, or 0 for a vprintf-style + function; PARAMS is the list of arguments starting at this argument. */ + +static void +init_dollar_format_checking (int first_arg_num, tree params) +{ + tree oparams = params; + + dollar_first_arg_num = first_arg_num; + dollar_arguments_count = 0; + dollar_max_arg_used = 0; + dollar_format_warned = 0; + if (first_arg_num > 0) + { + while (params) + { + dollar_arguments_count++; + params = TREE_CHAIN (params); + } + } + if (dollar_arguments_alloc < dollar_arguments_count) + { + free (dollar_arguments_used); + free (dollar_arguments_pointer_p); + dollar_arguments_alloc = dollar_arguments_count; + dollar_arguments_used = XNEWVEC (char, dollar_arguments_alloc); + dollar_arguments_pointer_p = XNEWVEC (char, dollar_arguments_alloc); + } + if (dollar_arguments_alloc) + { + memset (dollar_arguments_used, 0, dollar_arguments_alloc); + if (first_arg_num > 0) + { + int i = 0; + params = oparams; + while (params) + { + dollar_arguments_pointer_p[i] = (TREE_CODE (TREE_TYPE (TREE_VALUE (params))) + == POINTER_TYPE); + params = TREE_CHAIN (params); + i++; + } + } + } +} + + +/* Look for a decimal number followed by a $ in *FORMAT. If DOLLAR_NEEDED + is set, it is an error if one is not found; otherwise, it is OK. If + such a number is found, check whether it is within range and mark that + numbered operand as being used for later checking. Returns the operand + number if found and within range, zero if no such number was found and + this is OK, or -1 on error. PARAMS points to the first operand of the + format; PARAM_PTR is made to point to the parameter referred to. If + a $ format is found, *FORMAT is updated to point just after it. */ + +static int +maybe_read_dollar_number (const char **format, + int dollar_needed, tree params, tree *param_ptr, + const format_kind_info *fki) +{ + int argnum; + int overflow_flag; + const char *fcp = *format; + if (!ISDIGIT (*fcp)) + { + if (dollar_needed) + { + warning (OPT_Wformat_, "missing $ operand number in format"); + return -1; + } + else + return 0; + } + argnum = 0; + overflow_flag = 0; + while (ISDIGIT (*fcp)) + { + int nargnum; + nargnum = 10 * argnum + (*fcp - '0'); + if (nargnum < 0 || nargnum / 10 != argnum) + overflow_flag = 1; + argnum = nargnum; + fcp++; + } + if (*fcp != '$') + { + if (dollar_needed) + { + warning (OPT_Wformat_, "missing $ operand number in format"); + return -1; + } + else + return 0; + } + *format = fcp + 1; + if (pedantic && !dollar_format_warned) + { + warning (OPT_Wformat_, "%s does not support %%n$ operand number formats", + C_STD_NAME (STD_EXT)); + dollar_format_warned = 1; + } + if (overflow_flag || argnum == 0 + || (dollar_first_arg_num && argnum > dollar_arguments_count)) + { + warning (OPT_Wformat_, "operand number out of range in format"); + return -1; + } + if (argnum > dollar_max_arg_used) + dollar_max_arg_used = argnum; + /* For vprintf-style functions we may need to allocate more memory to + track which arguments are used. */ + while (dollar_arguments_alloc < dollar_max_arg_used) + { + int nalloc; + nalloc = 2 * dollar_arguments_alloc + 16; + dollar_arguments_used = XRESIZEVEC (char, dollar_arguments_used, + nalloc); + dollar_arguments_pointer_p = XRESIZEVEC (char, dollar_arguments_pointer_p, + nalloc); + memset (dollar_arguments_used + dollar_arguments_alloc, 0, + nalloc - dollar_arguments_alloc); + dollar_arguments_alloc = nalloc; + } + if (!(fki->flags & (int) FMT_FLAG_DOLLAR_MULTIPLE) + && dollar_arguments_used[argnum - 1] == 1) + { + dollar_arguments_used[argnum - 1] = 2; + warning (OPT_Wformat_, "format argument %d used more than once in %s format", + argnum, fki->name); + } + else + dollar_arguments_used[argnum - 1] = 1; + if (dollar_first_arg_num) + { + int i; + *param_ptr = params; + for (i = 1; i < argnum && *param_ptr != 0; i++) + *param_ptr = TREE_CHAIN (*param_ptr); + + /* This case shouldn't be caught here. */ + gcc_assert (*param_ptr); + } + else + *param_ptr = 0; + return argnum; +} + +/* Ensure that FORMAT does not start with a decimal number followed by + a $; give a diagnostic and return true if it does, false otherwise. */ + +static bool +avoid_dollar_number (const char *format) +{ + if (!ISDIGIT (*format)) + return false; + while (ISDIGIT (*format)) + format++; + if (*format == '$') + { + warning (OPT_Wformat_, "$ operand number used after format without operand number"); + return true; + } + return false; +} + + +/* Finish the checking for a format string that used $ operand number formats + instead of non-$ formats. We check for unused operands before used ones + (a serious error, since the implementation of the format function + can't know what types to pass to va_arg to find the later arguments). + and for unused operands at the end of the format (if we know how many + arguments the format had, so not for vprintf). If there were operand + numbers out of range on a non-vprintf-style format, we won't have reached + here. If POINTER_GAP_OK, unused arguments are OK if all arguments are + pointers. */ + +static void +finish_dollar_format_checking (format_check_results *res, int pointer_gap_ok) +{ + int i; + bool found_pointer_gap = false; + for (i = 0; i < dollar_max_arg_used; i++) + { + if (!dollar_arguments_used[i]) + { + if (pointer_gap_ok && (dollar_first_arg_num == 0 + || dollar_arguments_pointer_p[i])) + found_pointer_gap = true; + else + warning_at (res->format_string_loc, OPT_Wformat_, + "format argument %d unused before used argument %d in $-style format", + i + 1, dollar_max_arg_used); + } + } + if (found_pointer_gap + || (dollar_first_arg_num + && dollar_max_arg_used < dollar_arguments_count)) + { + res->number_other--; + res->number_dollar_extra_args++; + } +} + + +/* Retrieve the specification for a format flag. SPEC contains the + specifications for format flags for the applicable kind of format. + FLAG is the flag in question. If PREDICATES is NULL, the basic + spec for that flag must be retrieved and must exist. If + PREDICATES is not NULL, it is a string listing possible predicates + for the spec entry; if an entry predicated on any of these is + found, it is returned, otherwise NULL is returned. */ + +static const format_flag_spec * +get_flag_spec (const format_flag_spec *spec, int flag, const char *predicates) +{ + int i; + for (i = 0; spec[i].flag_char != 0; i++) + { + if (spec[i].flag_char != flag) + continue; + if (predicates != NULL) + { + if (spec[i].predicate != 0 + && strchr (predicates, spec[i].predicate) != 0) + return &spec[i]; + } + else if (spec[i].predicate == 0) + return &spec[i]; + } + gcc_assert (predicates); + return NULL; +} + + +/* Check the argument list of a call to printf, scanf, etc. + INFO points to the function_format_info structure. + PARAMS is the list of argument values. */ + +static void +check_format_info (function_format_info *info, tree params, + vec<location_t> *arglocs) +{ + format_check_context format_ctx; + unsigned HOST_WIDE_INT arg_num; + tree format_tree; + format_check_results res; + /* Skip to format argument. If the argument isn't available, there's + no work for us to do; prototype checking will catch the problem. */ + for (arg_num = 1; ; ++arg_num) + { + if (params == 0) + return; + if (arg_num == info->format_num) + break; + params = TREE_CHAIN (params); + } + format_tree = TREE_VALUE (params); + params = TREE_CHAIN (params); + if (format_tree == 0) + return; + + res.number_non_literal = 0; + res.number_extra_args = 0; + res.extra_arg_loc = UNKNOWN_LOCATION; + res.number_dollar_extra_args = 0; + res.number_wide = 0; + res.number_non_char = 0; + res.number_empty = 0; + res.number_unterminated = 0; + res.number_other = 0; + res.format_string_loc = input_location; + + format_ctx.res = &res; + format_ctx.info = info; + format_ctx.params = params; + format_ctx.arglocs = arglocs; + + check_function_arguments_recurse (check_format_arg, &format_ctx, + format_tree, arg_num); + + location_t loc = format_ctx.res->format_string_loc; + + if (res.number_non_literal > 0) + { + /* Functions taking a va_list normally pass a non-literal format + string. These functions typically are declared with + first_arg_num == 0, so avoid warning in those cases. */ + if (!(format_types[info->format_type].flags & (int) FMT_FLAG_ARG_CONVERT)) + { + /* For strftime-like formats, warn for not checking the format + string; but there are no arguments to check. */ + warning_at (loc, OPT_Wformat_nonliteral, + "format not a string literal, format string not checked"); + } + else if (info->first_arg_num != 0) + { + /* If there are no arguments for the format at all, we may have + printf (foo) which is likely to be a security hole. */ + while (arg_num + 1 < info->first_arg_num) + { + if (params == 0) + break; + params = TREE_CHAIN (params); + ++arg_num; + } + if (params == 0 && warn_format_security) + warning_at (loc, OPT_Wformat_security, + "format not a string literal and no format arguments"); + else if (params == 0 && warn_format_nonliteral) + warning_at (loc, OPT_Wformat_nonliteral, + "format not a string literal and no format arguments"); + else + warning_at (loc, OPT_Wformat_nonliteral, + "format not a string literal, argument types not checked"); + } + } + + /* If there were extra arguments to the format, normally warn. However, + the standard does say extra arguments are ignored, so in the specific + case where we have multiple leaves (conditional expressions or + ngettext) allow extra arguments if at least one leaf didn't have extra + arguments, but was otherwise OK (either non-literal or checked OK). + If the format is an empty string, this should be counted similarly to the + case of extra format arguments. */ + if (res.number_extra_args > 0 && res.number_non_literal == 0 + && res.number_other == 0) + { + if (res.extra_arg_loc == UNKNOWN_LOCATION) + res.extra_arg_loc = loc; + warning_at (res.extra_arg_loc, OPT_Wformat_extra_args, + "too many arguments for format"); + } + if (res.number_dollar_extra_args > 0 && res.number_non_literal == 0 + && res.number_other == 0) + warning_at (loc, OPT_Wformat_extra_args, "unused arguments in $-style format"); + if (res.number_empty > 0 && res.number_non_literal == 0 + && res.number_other == 0) + warning_at (loc, OPT_Wformat_zero_length, "zero-length %s format string", + format_types[info->format_type].name); + + if (res.number_wide > 0) + warning_at (loc, OPT_Wformat_, "format is a wide character string"); + + if (res.number_non_char > 0) + warning_at (loc, OPT_Wformat_, + "format string is not an array of type %qs", "char"); + + if (res.number_unterminated > 0) + warning_at (loc, OPT_Wformat_, "unterminated format string"); +} + +/* Callback from check_function_arguments_recurse to check a + format string. FORMAT_TREE is the format parameter. ARG_NUM + is the number of the format argument. CTX points to a + format_check_context. */ + +static void +check_format_arg (void *ctx, tree format_tree, + unsigned HOST_WIDE_INT arg_num) +{ + format_check_context *format_ctx = (format_check_context *) ctx; + format_check_results *res = format_ctx->res; + function_format_info *info = format_ctx->info; + tree params = format_ctx->params; + vec<location_t> *arglocs = format_ctx->arglocs; + + int format_length; + HOST_WIDE_INT offset; + const char *format_chars; + tree array_size = 0; + tree array_init; + + location_t fmt_param_loc = EXPR_LOC_OR_LOC (format_tree, input_location); + + /* Pull out a constant value if the front end didn't, and handle location + wrappers. */ + format_tree = fold_for_warn (format_tree); + STRIP_NOPS (format_tree); + + if (integer_zerop (format_tree)) + { + /* Skip to first argument to check, so we can see if this format + has any arguments (it shouldn't). */ + while (arg_num + 1 < info->first_arg_num) + { + if (params == 0) + return; + params = TREE_CHAIN (params); + ++arg_num; + } + + if (params == 0) + res->number_other++; + else + { + if (res->number_extra_args == 0) + res->extra_arg_loc = EXPR_LOC_OR_LOC (TREE_VALUE (params), + input_location); + res->number_extra_args++; + } + return; + } + + offset = 0; + if (TREE_CODE (format_tree) == POINTER_PLUS_EXPR) + { + tree arg0, arg1; + + arg0 = TREE_OPERAND (format_tree, 0); + arg1 = TREE_OPERAND (format_tree, 1); + STRIP_NOPS (arg0); + STRIP_NOPS (arg1); + if (TREE_CODE (arg1) == INTEGER_CST) + format_tree = arg0; + else + { + res->number_non_literal++; + return; + } + /* POINTER_PLUS_EXPR offsets are to be interpreted signed. */ + if (!cst_and_fits_in_hwi (arg1)) + { + res->number_non_literal++; + return; + } + offset = int_cst_value (arg1); + } + if (TREE_CODE (format_tree) != ADDR_EXPR) + { + res->number_non_literal++; + return; + } + res->format_string_loc = EXPR_LOC_OR_LOC (format_tree, input_location); + format_tree = TREE_OPERAND (format_tree, 0); + if (format_types[info->format_type].flags + & (int) FMT_FLAG_PARSE_ARG_CONVERT_EXTERNAL) + { + /* We cannot examine this string here - but we can check that it is + a valid type. */ + if (TREE_CODE (format_tree) != CONST_DECL) + { + res->number_non_literal++; + return; + } + /* Skip to first argument to check. */ + while (arg_num + 1 < info->first_arg_num) + { + if (params == 0) + return; + params = TREE_CHAIN (params); + ++arg_num; + } + return; + } + if (TREE_CODE (format_tree) == ARRAY_REF + && tree_fits_shwi_p (TREE_OPERAND (format_tree, 1)) + && (offset += tree_to_shwi (TREE_OPERAND (format_tree, 1))) >= 0) + format_tree = TREE_OPERAND (format_tree, 0); + if (offset < 0) + { + res->number_non_literal++; + return; + } + if (VAR_P (format_tree) + && TREE_CODE (TREE_TYPE (format_tree)) == ARRAY_TYPE + && (array_init = decl_constant_value (format_tree)) != format_tree + && TREE_CODE (array_init) == STRING_CST) + { + /* Extract the string constant initializer. Note that this may include + a trailing NUL character that is not in the array (e.g. + const char a[3] = "foo";). */ + array_size = DECL_SIZE_UNIT (format_tree); + format_tree = array_init; + } + if (TREE_CODE (format_tree) != STRING_CST) + { + res->number_non_literal++; + return; + } + tree underlying_type + = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (format_tree))); + if (underlying_type != char_type_node) + { + if (underlying_type == char16_type_node + || underlying_type == char32_type_node + || underlying_type == wchar_type_node) + res->number_wide++; + else + res->number_non_char++; + return; + } + format_chars = TREE_STRING_POINTER (format_tree); + format_length = TREE_STRING_LENGTH (format_tree); + if (array_size != 0) + { + /* Variable length arrays can't be initialized. */ + gcc_assert (TREE_CODE (array_size) == INTEGER_CST); + + if (tree_fits_shwi_p (array_size)) + { + HOST_WIDE_INT array_size_value = tree_to_shwi (array_size); + if (array_size_value > 0 + && array_size_value == (int) array_size_value + && format_length > array_size_value) + format_length = array_size_value; + } + } + if (offset) + { + if (offset >= format_length) + { + res->number_non_literal++; + return; + } + format_chars += offset; + format_length -= offset; + } + if (format_length < 1 || format_chars[--format_length] != 0) + { + res->number_unterminated++; + return; + } + if (format_length == 0) + { + res->number_empty++; + return; + } + + /* Skip to first argument to check. */ + while (arg_num + 1 < info->first_arg_num) + { + if (params == 0) + return; + params = TREE_CHAIN (params); + ++arg_num; + } + /* Provisionally increment res->number_other; check_format_info_main + will decrement it if it finds there are extra arguments, but this way + need not adjust it for every return. */ + res->number_other++; + object_allocator <format_wanted_type> fwt_pool ("format_wanted_type pool"); + check_format_info_main (res, info, format_chars, fmt_param_loc, format_tree, + format_length, params, arg_num, fwt_pool, arglocs); +} + +/* Support class for argument_parser and check_format_info_main. + Tracks any flag characters that have been applied to the + current argument. */ + +class flag_chars_t +{ + public: + flag_chars_t (); + bool has_char_p (char ch) const; + void add_char (char ch); + void validate (const format_kind_info *fki, + const format_char_info *fci, + const format_flag_spec *flag_specs, + const char * const format_chars, + tree format_string_cst, + location_t format_string_loc, + const char * const orig_format_chars, + char format_char, + bool quoted); + int get_alloc_flag (const format_kind_info *fki); + int assignment_suppression_p (const format_kind_info *fki); + + private: + char m_flag_chars[256]; +}; + +/* Support struct for argument_parser and check_format_info_main. + Encapsulates any length modifier applied to the current argument. */ + +struct length_modifier +{ + length_modifier () + : chars (NULL), val (FMT_LEN_none), std (STD_C89), + scalar_identity_flag (0) + { + } + + length_modifier (const char *chars_, + enum format_lengths val_, + enum format_std_version std_, + int scalar_identity_flag_) + : chars (chars_), val (val_), std (std_), + scalar_identity_flag (scalar_identity_flag_) + { + } + + const char *chars; + enum format_lengths val; + enum format_std_version std; + int scalar_identity_flag; +}; + +/* Parsing one argument within a format string. */ + +class argument_parser +{ + public: + argument_parser (function_format_info *info, const char *&format_chars, + tree format_string_cst, + const char * const orig_format_chars, + location_t format_string_loc, flag_chars_t &flag_chars, + int &has_operand_number, tree first_fillin_param, + object_allocator <format_wanted_type> &fwt_pool_, + vec<location_t> *arglocs); + + bool read_any_dollar (); + + bool read_format_flags (); + + bool + read_any_format_width (tree ¶ms, + unsigned HOST_WIDE_INT &arg_num); + + void + read_any_format_left_precision (); + + bool + read_any_format_precision (tree ¶ms, + unsigned HOST_WIDE_INT &arg_num); + + void handle_alloc_chars (); + + length_modifier read_any_length_modifier (); + + void read_any_other_modifier (); + + const format_char_info *find_format_char_info (char format_char); + + void + validate_flag_pairs (const format_char_info *fci, + char format_char); + + void + give_y2k_warnings (const format_char_info *fci, + char format_char); + + void parse_any_scan_set (const format_char_info *fci); + + bool handle_conversions (const format_char_info *fci, + const length_modifier &len_modifier, + tree &wanted_type, + const char *&wanted_type_name, + unsigned HOST_WIDE_INT &arg_num, + tree ¶ms, + char format_char); + + bool + check_argument_type (const format_char_info *fci, + const struct kernel_ext_fmt *kef, + const length_modifier &len_modifier, + tree &wanted_type, + const char *&wanted_type_name, + const bool suppressed, + unsigned HOST_WIDE_INT &arg_num, + tree ¶ms, + const int alloc_flag, + const char * const format_start, + const char * const type_start, + location_t fmt_param_loc, + char conversion_char); + + private: + const function_format_info *const info; + const format_kind_info * const fki; + const format_flag_spec * const flag_specs; + const char *start_of_this_format; + const char *&format_chars; + const tree format_string_cst; + const char * const orig_format_chars; + const location_t format_string_loc; + object_allocator <format_wanted_type> &fwt_pool; + flag_chars_t &flag_chars; + int main_arg_num; + tree main_arg_params; + int &has_operand_number; + const tree first_fillin_param; + format_wanted_type width_wanted_type; + format_wanted_type precision_wanted_type; + public: + format_wanted_type main_wanted_type; + private: + format_wanted_type *first_wanted_type; + format_wanted_type *last_wanted_type; + vec<location_t> *arglocs; +}; + +/* flag_chars_t's constructor. */ + +flag_chars_t::flag_chars_t () +{ + m_flag_chars[0] = 0; +} + +/* Has CH been seen as a flag within the current argument? */ + +bool +flag_chars_t::has_char_p (char ch) const +{ + return strchr (m_flag_chars, ch) != 0; +} + +/* Add CH to the flags seen within the current argument. */ + +void +flag_chars_t::add_char (char ch) +{ + int i = strlen (m_flag_chars); + m_flag_chars[i++] = ch; + m_flag_chars[i] = 0; +} + +/* Validate the individual flags used, removing any that are invalid. */ + +void +flag_chars_t::validate (const format_kind_info *fki, + const format_char_info *fci, + const format_flag_spec *flag_specs, + const char * const format_chars, + tree format_string_cst, + location_t format_string_loc, + const char * const orig_format_chars, + char format_char, + bool quoted) +{ + int i; + int d = 0; + bool quotflag = false; + + for (i = 0; m_flag_chars[i] != 0; i++) + { + const format_flag_spec *s = get_flag_spec (flag_specs, + m_flag_chars[i], NULL); + m_flag_chars[i - d] = m_flag_chars[i]; + if (m_flag_chars[i] == fki->length_code_char) + continue; + + /* Remember if a quoting flag is seen. */ + quotflag |= s->quoting; + + if (strchr (fci->flag_chars, m_flag_chars[i]) == 0) + { + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "%s used with %<%%%c%> %s format", + _(s->name), format_char, fki->name); + d++; + continue; + } + if (pedantic) + { + const format_flag_spec *t; + if (ADJ_STD (s->std) > C_STD_VER) + warning_at (format_string_loc, OPT_Wformat_, + "%s does not support %s", + C_STD_NAME (s->std), _(s->long_name)); + t = get_flag_spec (flag_specs, m_flag_chars[i], fci->flags2); + if (t != NULL && ADJ_STD (t->std) > ADJ_STD (s->std)) + { + const char *long_name = (t->long_name != NULL + ? t->long_name + : s->long_name); + if (ADJ_STD (t->std) > C_STD_VER) + warning_at (format_string_loc, OPT_Wformat_, + "%s does not support %s with" + " the %<%%%c%> %s format", + C_STD_NAME (t->std), _(long_name), + format_char, fki->name); + } + } + + /* Detect quoting directives used within a quoted sequence, such + as GCC's "%<...%qE". */ + if (quoted && s->quoting) + { + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars - 1, + OPT_Wformat_, + "%s used within a quoted sequence", + _(s->name)); + } + } + m_flag_chars[i - d] = 0; + + if (!quoted + && !quotflag + && strchr (fci->flags2, '\'')) + { + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "%qc conversion used unquoted", + format_char); + } +} + +/* Determine if an assignment-allocation has been set, requiring + an extra char ** for writing back a dynamically-allocated char *. + This is for handling the optional 'm' character in scanf. */ + +int +flag_chars_t::get_alloc_flag (const format_kind_info *fki) +{ + if ((fki->flags & (int) FMT_FLAG_SCANF_A_KLUDGE) + && has_char_p ('a')) + return 1; + if (fki->alloc_char && has_char_p (fki->alloc_char)) + return 1; + return 0; +} + +/* Determine if an assignment-suppression character was seen. + ('*' in scanf, for discarding the converted input). */ + +int +flag_chars_t::assignment_suppression_p (const format_kind_info *fki) +{ + if (fki->suppression_char + && has_char_p (fki->suppression_char)) + return 1; + return 0; +} + +/* Constructor for argument_parser. Initialize for parsing one + argument within a format string. */ + +argument_parser:: +argument_parser (function_format_info *info_, const char *&format_chars_, + tree format_string_cst_, + const char * const orig_format_chars_, + location_t format_string_loc_, + flag_chars_t &flag_chars_, + int &has_operand_number_, + tree first_fillin_param_, + object_allocator <format_wanted_type> &fwt_pool_, + vec<location_t> *arglocs_) +: info (info_), + fki (&format_types[info->format_type]), + flag_specs (fki->flag_specs), + start_of_this_format (format_chars_), + format_chars (format_chars_), + format_string_cst (format_string_cst_), + orig_format_chars (orig_format_chars_), + format_string_loc (format_string_loc_), + fwt_pool (fwt_pool_), + flag_chars (flag_chars_), + main_arg_num (0), + main_arg_params (NULL), + has_operand_number (has_operand_number_), + first_fillin_param (first_fillin_param_), + first_wanted_type (NULL), + last_wanted_type (NULL), + arglocs (arglocs_) +{ +} + +/* Handle dollars at the start of format arguments, setting up main_arg_params + and main_arg_num. + + Return true if format parsing is to continue, false otherwise. */ + +bool +argument_parser::read_any_dollar () +{ + if ((fki->flags & (int) FMT_FLAG_USE_DOLLAR) && has_operand_number != 0) + { + /* Possibly read a $ operand number at the start of the format. + If one was previously used, one is required here. If one + is not used here, we can't immediately conclude this is a + format without them, since it could be printf %m or scanf %*. */ + int opnum; + opnum = maybe_read_dollar_number (&format_chars, 0, + first_fillin_param, + &main_arg_params, fki); + if (opnum == -1) + return false; + else if (opnum > 0) + { + has_operand_number = 1; + main_arg_num = opnum + info->first_arg_num - 1; + } + } + else if (fki->flags & FMT_FLAG_USE_DOLLAR) + { + if (avoid_dollar_number (format_chars)) + return false; + } + return true; +} + +/* Read any format flags, but do not yet validate them beyond removing + duplicates, since in general validation depends on the rest of + the format. + + Return true if format parsing is to continue, false otherwise. */ + +bool +argument_parser::read_format_flags () +{ + while (*format_chars != 0 + && strchr (fki->flag_chars, *format_chars) != 0) + { + const format_flag_spec *s = get_flag_spec (flag_specs, + *format_chars, NULL); + if (flag_chars.has_char_p (*format_chars)) + { + format_warning_at_char (format_string_loc, format_string_cst, + format_chars + 1 - orig_format_chars, + OPT_Wformat_, + "repeated %s in format", _(s->name)); + } + else + flag_chars.add_char (*format_chars); + + if (s->skip_next_char) + { + ++format_chars; + if (*format_chars == 0) + { + warning_at (format_string_loc, OPT_Wformat_, + "missing fill character at end of strfmon format"); + return false; + } + } + ++format_chars; + } + + return true; +} + +/* Read any format width, possibly * or *m$. + + Return true if format parsing is to continue, false otherwise. */ + +bool +argument_parser:: +read_any_format_width (tree ¶ms, + unsigned HOST_WIDE_INT &arg_num) +{ + if (!fki->width_char) + return true; + + if (fki->width_type != NULL && *format_chars == '*') + { + flag_chars.add_char (fki->width_char); + /* "...a field width...may be indicated by an asterisk. + In this case, an int argument supplies the field width..." */ + ++format_chars; + if (has_operand_number != 0) + { + int opnum; + opnum = maybe_read_dollar_number (&format_chars, + has_operand_number == 1, + first_fillin_param, + ¶ms, fki); + if (opnum == -1) + return false; + else if (opnum > 0) + { + has_operand_number = 1; + arg_num = opnum + info->first_arg_num - 1; + } + else + has_operand_number = 0; + } + else + { + if (avoid_dollar_number (format_chars)) + return false; + } + if (info->first_arg_num != 0) + { + tree cur_param; + if (params == 0) + cur_param = NULL; + else + { + cur_param = TREE_VALUE (params); + if (has_operand_number <= 0) + { + params = TREE_CHAIN (params); + ++arg_num; + } + } + width_wanted_type.wanted_type = *fki->width_type; + width_wanted_type.wanted_type_name = NULL; + width_wanted_type.pointer_count = 0; + width_wanted_type.char_lenient_flag = 0; + width_wanted_type.scalar_identity_flag = 0; + width_wanted_type.writing_in_flag = 0; + width_wanted_type.reading_from_flag = 0; + width_wanted_type.kind = CF_KIND_FIELD_WIDTH; + width_wanted_type.format_start = format_chars - 1; + width_wanted_type.format_length = 1; + width_wanted_type.param = cur_param; + width_wanted_type.arg_num = arg_num; + width_wanted_type.offset_loc = + format_chars - orig_format_chars; + width_wanted_type.next = NULL; + if (last_wanted_type != 0) + last_wanted_type->next = &width_wanted_type; + if (first_wanted_type == 0) + first_wanted_type = &width_wanted_type; + last_wanted_type = &width_wanted_type; + } + } + else + { + /* Possibly read a numeric width. If the width is zero, + we complain if appropriate. */ + int non_zero_width_char = FALSE; + int found_width = FALSE; + while (ISDIGIT (*format_chars)) + { + found_width = TRUE; + if (*format_chars != '0') + non_zero_width_char = TRUE; + ++format_chars; + } + if (found_width && !non_zero_width_char && + (fki->flags & (int) FMT_FLAG_ZERO_WIDTH_BAD)) + warning_at (format_string_loc, OPT_Wformat_, + "zero width in %s format", fki->name); + if (found_width) + flag_chars.add_char (fki->width_char); + } + + return true; +} + +/* Read any format left precision (must be a number, not *). */ +void +argument_parser::read_any_format_left_precision () +{ + if (fki->left_precision_char == 0) + return; + if (*format_chars != '#') + return; + + ++format_chars; + flag_chars.add_char (fki->left_precision_char); + if (!ISDIGIT (*format_chars)) + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "empty left precision in %s format", fki->name); + while (ISDIGIT (*format_chars)) + ++format_chars; +} + +/* Read any format precision, possibly * or *m$. + + Return true if format parsing is to continue, false otherwise. */ + +bool +argument_parser:: +read_any_format_precision (tree ¶ms, + unsigned HOST_WIDE_INT &arg_num) +{ + if (fki->precision_char == 0) + return true; + if (*format_chars != '.') + return true; + + ++format_chars; + flag_chars.add_char (fki->precision_char); + if (fki->precision_type != NULL && *format_chars == '*') + { + /* "...a...precision...may be indicated by an asterisk. + In this case, an int argument supplies the...precision." */ + ++format_chars; + if (has_operand_number != 0) + { + int opnum; + opnum = maybe_read_dollar_number (&format_chars, + has_operand_number == 1, + first_fillin_param, + ¶ms, fki); + if (opnum == -1) + return false; + else if (opnum > 0) + { + has_operand_number = 1; + arg_num = opnum + info->first_arg_num - 1; + } + else + has_operand_number = 0; + } + else + { + if (avoid_dollar_number (format_chars)) + return false; + } + if (info->first_arg_num != 0) + { + tree cur_param; + if (params == 0) + cur_param = NULL; + else + { + cur_param = TREE_VALUE (params); + if (has_operand_number <= 0) + { + params = TREE_CHAIN (params); + ++arg_num; + } + } + precision_wanted_type.wanted_type = *fki->precision_type; + precision_wanted_type.wanted_type_name = NULL; + precision_wanted_type.pointer_count = 0; + precision_wanted_type.char_lenient_flag = 0; + precision_wanted_type.scalar_identity_flag = 0; + precision_wanted_type.writing_in_flag = 0; + precision_wanted_type.reading_from_flag = 0; + precision_wanted_type.kind = CF_KIND_FIELD_PRECISION; + precision_wanted_type.param = cur_param; + precision_wanted_type.format_start = format_chars - 2; + precision_wanted_type.format_length = 2; + precision_wanted_type.arg_num = arg_num; + precision_wanted_type.offset_loc = + format_chars - orig_format_chars; + precision_wanted_type.next = NULL; + if (last_wanted_type != 0) + last_wanted_type->next = &precision_wanted_type; + if (first_wanted_type == 0) + first_wanted_type = &precision_wanted_type; + last_wanted_type = &precision_wanted_type; + } + } + else + { + if (!(fki->flags & (int) FMT_FLAG_EMPTY_PREC_OK) + && !ISDIGIT (*format_chars)) + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "empty precision in %s format", fki->name); + while (ISDIGIT (*format_chars)) + ++format_chars; + } + + return true; +} + +/* Parse any assignment-allocation flags, which request an extra + char ** for writing back a dynamically-allocated char *. + This is for handling the optional 'm' character in scanf, + and, before C99, 'a' (for compatibility with a non-standard + GNU libc extension). */ + +void +argument_parser::handle_alloc_chars () +{ + if (fki->alloc_char && fki->alloc_char == *format_chars) + { + flag_chars.add_char (fki->alloc_char); + format_chars++; + } + + /* Handle the scanf allocation kludge. */ + if (fki->flags & (int) FMT_FLAG_SCANF_A_KLUDGE) + { + if (*format_chars == 'a' && !flag_isoc99) + { + if (format_chars[1] == 's' || format_chars[1] == 'S' + || format_chars[1] == '[') + { + /* 'a' is used as a flag. */ + flag_chars.add_char ('a'); + format_chars++; + } + } + } +} + +/* Look for length modifiers within the current format argument, + returning a length_modifier instance describing it (or the + default if one is not found). + + Issue warnings about non-standard modifiers. */ + +length_modifier +argument_parser::read_any_length_modifier () +{ + length_modifier result; + + const format_length_info *fli = fki->length_char_specs; + if (!fli) + return result; + + while (fli->name != 0 + && strncmp (fli->name, format_chars, strlen (fli->name))) + fli++; + if (fli->name != 0) + { + format_chars += strlen (fli->name); + if (fli->double_name != 0 && fli->name[0] == *format_chars) + { + format_chars++; + result = length_modifier (fli->double_name, fli->double_index, + fli->double_std, 0); + } + else + { + result = length_modifier (fli->name, fli->index, fli->std, + fli->scalar_identity_flag); + } + flag_chars.add_char (fki->length_code_char); + } + if (pedantic) + { + /* Warn if the length modifier is non-standard. */ + if (ADJ_STD (result.std) > C_STD_VER) + warning_at (format_string_loc, OPT_Wformat_, + "%s does not support the %qs %s length modifier", + C_STD_NAME (result.std), result.chars, + fki->name); + } + + return result; +} + +/* Read any other modifier (strftime E/O). */ + +void +argument_parser::read_any_other_modifier () +{ + if (fki->modifier_chars == NULL) + return; + + while (*format_chars != 0 + && strchr (fki->modifier_chars, *format_chars) != 0) + { + if (flag_chars.has_char_p (*format_chars)) + { + const format_flag_spec *s = get_flag_spec (flag_specs, + *format_chars, NULL); + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "repeated %s in format", _(s->name)); + } + else + flag_chars.add_char (*format_chars); + ++format_chars; + } +} + +/* Return the format_char_info corresponding to FORMAT_CHAR, + potentially issuing a warning if the format char is + not supported in the C standard version we are checking + against. + + Issue a warning and return NULL if it is not found. + + Issue warnings about non-standard modifiers. */ + +const format_char_info * +argument_parser::find_format_char_info (char format_char) +{ + const format_char_info *fci = fki->conversion_specs; + + while (fci->format_chars != 0 + && strchr (fci->format_chars, format_char) == 0) + ++fci; + if (fci->format_chars == 0) + { + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "unknown conversion type character" + " %qc in format", + format_char); + return NULL; + } + + if (pedantic) + { + if (ADJ_STD (fci->std) > C_STD_VER) + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "%s does not support the %<%%%c%> %s format", + C_STD_NAME (fci->std), format_char, fki->name); + } + + return fci; +} + +/* Validate the pairs of flags used. + Issue warnings about incompatible combinations of flags. */ + +void +argument_parser::validate_flag_pairs (const format_char_info *fci, + char format_char) +{ + const format_flag_pair * const bad_flag_pairs = fki->bad_flag_pairs; + + for (int i = 0; bad_flag_pairs[i].flag_char1 != 0; i++) + { + const format_flag_spec *s, *t; + if (!flag_chars.has_char_p (bad_flag_pairs[i].flag_char1)) + continue; + if (!flag_chars.has_char_p (bad_flag_pairs[i].flag_char2)) + continue; + if (bad_flag_pairs[i].predicate != 0 + && strchr (fci->flags2, bad_flag_pairs[i].predicate) == 0) + continue; + s = get_flag_spec (flag_specs, bad_flag_pairs[i].flag_char1, NULL); + t = get_flag_spec (flag_specs, bad_flag_pairs[i].flag_char2, NULL); + if (bad_flag_pairs[i].ignored) + { + if (bad_flag_pairs[i].predicate != 0) + warning_at (format_string_loc, OPT_Wformat_, + "%s ignored with %s and %<%%%c%> %s format", + _(s->name), _(t->name), format_char, + fki->name); + else + warning_at (format_string_loc, OPT_Wformat_, + "%s ignored with %s in %s format", + _(s->name), _(t->name), fki->name); + } + else + { + if (bad_flag_pairs[i].predicate != 0) + warning_at (format_string_loc, OPT_Wformat_, + "use of %s and %s together with %<%%%c%> %s format", + _(s->name), _(t->name), format_char, + fki->name); + else + warning_at (format_string_loc, OPT_Wformat_, + "use of %s and %s together in %s format", + _(s->name), _(t->name), fki->name); + } + } +} + +/* Give Y2K warnings. */ + +void +argument_parser::give_y2k_warnings (const format_char_info *fci, + char format_char) +{ + if (!warn_format_y2k) + return; + + int y2k_level = 0; + if (strchr (fci->flags2, '4') != 0) + if (flag_chars.has_char_p ('E')) + y2k_level = 3; + else + y2k_level = 2; + else if (strchr (fci->flags2, '3') != 0) + y2k_level = 3; + else if (strchr (fci->flags2, '2') != 0) + y2k_level = 2; + if (y2k_level == 3) + warning_at (format_string_loc, OPT_Wformat_y2k, + "%<%%%c%> yields only last 2 digits of " + "year in some locales", format_char); + else if (y2k_level == 2) + warning_at (format_string_loc, OPT_Wformat_y2k, + "%<%%%c%> yields only last 2 digits of year", + format_char); +} + +/* Parse any "scan sets" enclosed in square brackets, e.g. + for scanf-style calls. */ + +void +argument_parser::parse_any_scan_set (const format_char_info *fci) +{ + if (strchr (fci->flags2, '[') == NULL) + return; + + /* Skip over scan set, in case it happens to have '%' in it. */ + if (*format_chars == '^') + ++format_chars; + /* Find closing bracket; if one is hit immediately, then + it's part of the scan set rather than a terminator. */ + if (*format_chars == ']') + ++format_chars; + while (*format_chars && *format_chars != ']') + ++format_chars; + if (*format_chars != ']') + /* The end of the format string was reached. */ + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "no closing %<]%> for %<%%[%> format"); +} + +/* Return true if this argument is to be continued to be parsed, + false to skip to next argument. */ + +bool +argument_parser::handle_conversions (const format_char_info *fci, + const length_modifier &len_modifier, + tree &wanted_type, + const char *&wanted_type_name, + unsigned HOST_WIDE_INT &arg_num, + tree ¶ms, + char format_char) +{ + enum format_std_version wanted_type_std; + + if (!(fki->flags & (int) FMT_FLAG_ARG_CONVERT)) + return true; + + wanted_type = (fci->types[len_modifier.val].type + ? *fci->types[len_modifier.val].type : 0); + wanted_type_name = fci->types[len_modifier.val].name; + wanted_type_std = fci->types[len_modifier.val].std; + if (wanted_type == 0) + { + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "use of %qs length modifier with %qc type" + " character has either no effect" + " or undefined behavior", + len_modifier.chars, format_char); + /* Heuristic: skip one argument when an invalid length/type + combination is encountered. */ + arg_num++; + if (params != 0) + params = TREE_CHAIN (params); + return false; + } + else if (pedantic + /* Warn if non-standard, provided it is more non-standard + than the length and type characters that may already + have been warned for. */ + && ADJ_STD (wanted_type_std) > ADJ_STD (len_modifier.std) + && ADJ_STD (wanted_type_std) > ADJ_STD (fci->std)) + { + if (ADJ_STD (wanted_type_std) > C_STD_VER) + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "%s does not support the %<%%%s%c%> %s format", + C_STD_NAME (wanted_type_std), + len_modifier.chars, + format_char, fki->name); + } + + return true; +} + +/* Check type of argument against desired type. + + Return true if format parsing is to continue, false otherwise. */ + +bool +argument_parser:: +check_argument_type (const format_char_info *fci, + const struct kernel_ext_fmt *kef, + const length_modifier &len_modifier, + tree &wanted_type, + const char *&wanted_type_name, + const bool suppressed, + unsigned HOST_WIDE_INT &arg_num, + tree ¶ms, + const int alloc_flag, + const char * const format_start, + const char * const type_start, + location_t fmt_param_loc, + char conversion_char) +{ + if (info->first_arg_num == 0) + return true; + + if ((fci->pointer_count == 0 && wanted_type == void_type_node) + || suppressed) + { + if (main_arg_num != 0) + { + if (suppressed) + warning_at (format_string_loc, OPT_Wformat_, + "operand number specified with " + "suppressed assignment"); + else + warning_at (format_string_loc, OPT_Wformat_, + "operand number specified for format " + "taking no argument"); + } + } + else + { + format_wanted_type *wanted_type_ptr; + + if (main_arg_num != 0) + { + arg_num = main_arg_num; + params = main_arg_params; + } + else + { + ++arg_num; + if (has_operand_number > 0) + { + warning_at (format_string_loc, OPT_Wformat_, + "missing $ operand number in format"); + return false; + } + else + has_operand_number = 0; + } + + wanted_type_ptr = &main_wanted_type; + while (fci) + { + tree cur_param; + if (params == 0) + cur_param = NULL; + else + { + cur_param = TREE_VALUE (params); + params = TREE_CHAIN (params); + } + + wanted_type_ptr->wanted_type = wanted_type; + wanted_type_ptr->wanted_type_name = wanted_type_name; + wanted_type_ptr->pointer_count = fci->pointer_count + alloc_flag; + wanted_type_ptr->char_lenient_flag = 0; + if (strchr (fci->flags2, 'c') != 0) + wanted_type_ptr->char_lenient_flag = 1; + wanted_type_ptr->scalar_identity_flag = 0; + if (len_modifier.scalar_identity_flag) + wanted_type_ptr->scalar_identity_flag = 1; + wanted_type_ptr->writing_in_flag = 0; + wanted_type_ptr->reading_from_flag = 0; + if (alloc_flag) + wanted_type_ptr->writing_in_flag = 1; + else + { + if (strchr (fci->flags2, 'W') != 0) + wanted_type_ptr->writing_in_flag = 1; + if (strchr (fci->flags2, 'R') != 0) + wanted_type_ptr->reading_from_flag = 1; + } + wanted_type_ptr->kind = CF_KIND_FORMAT; + wanted_type_ptr->param = cur_param; + wanted_type_ptr->arg_num = arg_num; + wanted_type_ptr->format_start = format_start; + wanted_type_ptr->format_length = format_chars - format_start; + wanted_type_ptr->offset_loc = format_chars - orig_format_chars; + wanted_type_ptr->next = NULL; + if (last_wanted_type != 0) + last_wanted_type->next = wanted_type_ptr; + if (first_wanted_type == 0) + first_wanted_type = wanted_type_ptr; + last_wanted_type = wanted_type_ptr; + + fci = fci->chain; + if (fci) + { + wanted_type_ptr = fwt_pool.allocate (); + arg_num++; + wanted_type = *fci->types[len_modifier.val].type; + wanted_type_name = fci->types[len_modifier.val].name; + } + } + } + + if (first_wanted_type != 0) + { + ptrdiff_t offset_to_format_start = (start_of_this_format - 1) - orig_format_chars; + ptrdiff_t offset_to_format_end = (format_chars - 1) - orig_format_chars; + /* By default, use the end of the range for the caret location. */ + substring_loc fmt_loc (fmt_param_loc, TREE_TYPE (format_string_cst), + offset_to_format_end, + offset_to_format_start, offset_to_format_end); + ptrdiff_t offset_to_type_start = type_start - orig_format_chars; + check_format_types (fmt_loc, first_wanted_type, fki, + offset_to_type_start, + conversion_char, arglocs); + + /* note printf extension type checks are *additional* - %p must always + * be pointer compatible, %d always int compatible. + */ + if (!kef) + return true; + + const struct kernel_ext_fmt *kef_now; + bool success; + + for (kef_now = kef; kef_now->suffix && !strcmp (kef->suffix, kef_now->suffix); kef_now++) + { + success = check_kef_type (fmt_loc, kef_now, + first_wanted_type->arg_num, + first_wanted_type->param, + kef_now->type, fki, offset_to_type_start, conversion_char, arglocs); + + if (success) + return true; + } + + location_t param_loc; + + if (EXPR_HAS_LOCATION (first_wanted_type->param)) + param_loc = EXPR_LOCATION (first_wanted_type->param); + else if (arglocs) + { + /* arg_num is 1-based. */ + gcc_assert (first_wanted_type->arg_num > 0); + param_loc = (*arglocs)[first_wanted_type->arg_num - 1]; + } + + format_type_warning (fmt_loc, param_loc, first_wanted_type, + kef->type, TREE_TYPE (first_wanted_type->param), + fki, offset_to_type_start, conversion_char); + } + + return true; +} + +/* Do the main part of checking a call to a format function. FORMAT_CHARS + is the NUL-terminated format string (which at this point may contain + internal NUL characters); FORMAT_LENGTH is its length (excluding the + terminating NUL character). ARG_NUM is one less than the number of + the first format argument to check; PARAMS points to that format + argument in the list of arguments. */ + +static void +check_format_info_main (format_check_results *res, + function_format_info *info, const char *format_chars, + location_t fmt_param_loc, tree format_string_cst, + int format_length, tree params, + unsigned HOST_WIDE_INT arg_num, + object_allocator <format_wanted_type> &fwt_pool, + vec<location_t> *arglocs) +{ + const char * const orig_format_chars = format_chars; + const tree first_fillin_param = params; + + const format_kind_info * const fki = &format_types[info->format_type]; + const format_flag_spec * const flag_specs = fki->flag_specs; + const location_t format_string_loc = res->format_string_loc; + + /* -1 if no conversions taking an operand have been found; 0 if one has + and it didn't use $; 1 if $ formats are in use. */ + int has_operand_number = -1; + + /* Vector of pointers to opening quoting directives (like GCC "%<"). */ + auto_vec<const char*> quotdirs; + + /* Pointers to the most recent color directives (like GCC's "%r or %R"). + A starting color directive much be terminated before the end of + the format string. A terminating directive makes no sense without + a prior starting directive. */ + const char *color_begin = NULL; + const char *color_end = NULL; + + init_dollar_format_checking (info->first_arg_num, first_fillin_param); + + while (*format_chars != 0) + { + if (*format_chars++ != '%') + continue; + if (*format_chars == 0) + { + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "spurious trailing %<%%%> in format"); + continue; + } + if (*format_chars == '%') + { + ++format_chars; + continue; + } + + flag_chars_t flag_chars; + argument_parser arg_parser (info, format_chars, format_string_cst, + orig_format_chars, format_string_loc, + flag_chars, has_operand_number, + first_fillin_param, fwt_pool, arglocs); + + if (!arg_parser.read_any_dollar ()) + return; + + if (!arg_parser.read_format_flags ()) + return; + + /* Read any format width, possibly * or *m$. */ + if (!arg_parser.read_any_format_width (params, arg_num)) + return; + + /* Read any format left precision (must be a number, not *). */ + arg_parser.read_any_format_left_precision (); + + /* Read any format precision, possibly * or *m$. */ + if (!arg_parser.read_any_format_precision (params, arg_num)) + return; + + const char *format_start = format_chars; + + arg_parser.handle_alloc_chars (); + + /* The rest of the conversion specification is the length modifier + (if any), and the conversion specifier, so this is where the + type information starts. If we need to issue a suggestion + about a type mismatch, then we should preserve everything up + to here. */ + const char *type_start = format_chars; + + /* Read any length modifier, if this kind of format has them. */ + const length_modifier len_modifier + = arg_parser.read_any_length_modifier (); + + /* Read any modifier (strftime E/O). */ + arg_parser.read_any_other_modifier (); + + char format_char = *format_chars; + if (format_char == 0 + || (!(fki->flags & (int) FMT_FLAG_FANCY_PERCENT_OK) + && format_char == '%')) + { + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "conversion lacks type at end of format"); + continue; + } + format_chars++; + + const format_char_info * const fci + = arg_parser.find_format_char_info (format_char); + if (!fci) + continue; + + struct kernel_ext_fmt *etab = fci->kernel_ext; + + if (etab && format_chars[0] >= 'A' && format_chars[0] <= 'Z') + { + struct kernel_ext_fmt *etab_end = etab + ETAB_SZ; + + for (; etab < etab_end && etab->suffix; etab++) + { + if (!strncmp (etab->suffix, format_chars, strlen (etab->suffix))) + break; + } + + if (!etab->suffix || etab == etab_end) + { + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars + 1, + OPT_Wformat_, + "unrecognized printf extension suffix"); + etab = NULL; + } + else + { + format_chars += strlen (etab->suffix); + } + } + else + etab = NULL; + + flag_chars.validate (fki, fci, flag_specs, format_chars, + format_string_cst, + format_string_loc, orig_format_chars, format_char, + quotdirs.length () > 0); + + const int alloc_flag = flag_chars.get_alloc_flag (fki); + const bool suppressed = flag_chars.assignment_suppression_p (fki); + + /* Diagnose nested or unmatched quoting directives such as GCC's + "%<...%<" and "%>...%>". */ + bool quot_begin_p = strchr (fci->flags2, '<'); + bool quot_end_p = strchr (fci->flags2, '>'); + + if (quot_begin_p && !quot_end_p) + { + if (quotdirs.length ()) + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "nested quoting directive"); + quotdirs.safe_push (format_chars); + } + else if (!quot_begin_p && quot_end_p) + { + if (quotdirs.length ()) + quotdirs.pop (); + else + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "unmatched quoting directive"); + } + + bool color_begin_p = strchr (fci->flags2, '/'); + if (color_begin_p) + { + color_begin = format_chars; + color_end = NULL; + } + else if (strchr (fci->flags2, '\\')) + { + if (color_end) + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "%qc directive redundant after prior " + "occurence of the same", format_char); + else if (!color_begin) + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "unmatched color reset directive"); + color_end = format_chars; + } + + /* Diagnose directives that shouldn't appear in a quoted sequence. + (They are denoted by a double quote in FLAGS2.) */ + if (quotdirs.length ()) + { + if (strchr (fci->flags2, '"')) + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "%qc conversion used within a quoted " + "sequence", + format_char); + } + + /* Validate the pairs of flags used. */ + arg_parser.validate_flag_pairs (fci, format_char); + + arg_parser.give_y2k_warnings (fci, format_char); + + arg_parser.parse_any_scan_set (fci); + + tree wanted_type = NULL; + const char *wanted_type_name = NULL; + + if (!arg_parser.handle_conversions (fci, len_modifier, + wanted_type, wanted_type_name, + arg_num, + params, + format_char)) + continue; + + arg_parser.main_wanted_type.next = NULL; + + /* Finally. . .check type of argument against desired type! */ + if (!arg_parser.check_argument_type (fci, etab, len_modifier, + wanted_type, wanted_type_name, + suppressed, + arg_num, params, + alloc_flag, + format_start, type_start, + fmt_param_loc, + format_char)) + return; + } + + if (format_chars - orig_format_chars != format_length) + format_warning_at_char (format_string_loc, format_string_cst, + format_chars + 1 - orig_format_chars, + OPT_Wformat_contains_nul, + "embedded %<\\0%> in format"); + if (info->first_arg_num != 0 && params != 0 + && has_operand_number <= 0) + { + res->number_other--; + res->number_extra_args++; + } + if (has_operand_number > 0) + finish_dollar_format_checking (res, fki->flags & (int) FMT_FLAG_DOLLAR_GAP_POINTER_OK); + + if (quotdirs.length ()) + format_warning_at_char (format_string_loc, format_string_cst, + quotdirs.pop () - orig_format_chars, + OPT_Wformat_, "unterminated quoting directive"); + if (color_begin && !color_end) + format_warning_at_char (format_string_loc, format_string_cst, + color_begin - orig_format_chars, + OPT_Wformat_, "unterminated color directive"); +} + +/* Check the argument types from a single format conversion (possibly + including width and precision arguments). + + FMT_LOC is the location of the format conversion. + + TYPES is a singly-linked list expressing the parts of the format + conversion that expect argument types, and the arguments they + correspond to. + + OFFSET_TO_TYPE_START is the offset within the execution-charset encoded + format string to where type information begins for the conversion + (the length modifier and conversion specifier). + + CONVERSION_CHAR is the user-provided conversion specifier. + + For example, given: + + sprintf (d, "before %-+*.*lld after", arg3, arg4, arg5); + + then FMT_LOC covers this range: + + sprintf (d, "before %-+*.*lld after", arg3, arg4, arg5); + ^^^^^^^^^ + + and TYPES in this case is a three-entry singly-linked list consisting of: + (1) the check for the field width here: + sprintf (d, "before %-+*.*lld after", arg3, arg4, arg5); + ^ ^^^^ + against arg3, and + (2) the check for the field precision here: + sprintf (d, "before %-+*.*lld after", arg3, arg4, arg5); + ^^ ^^^^ + against arg4, and + (3) the check for the length modifier and conversion char here: + sprintf (d, "before %-+*.*lld after", arg3, arg4, arg5); + ^^^ ^^^^ + against arg5. + + OFFSET_TO_TYPE_START is 13, the offset to the "lld" within the + STRING_CST: + + 0000000000111111111122 + 0123456789012345678901 + sprintf (d, "before %-+*.*lld after", arg3, arg4, arg5); + ^ ^ + | ` CONVERSION_CHAR: 'd' + type starts here. */ +tree type_normalize (tree type, tree *cousin, tree target = NULL) +{ + while (1) + { + if (TREE_CODE (type) == FUNCTION_TYPE || TREE_CODE (type) == POINTER_TYPE) + return type; + if (target) + /* Strip off any "const" etc. */ + type = build_qualified_type (type, 0); + if (TREE_CODE (TYPE_NAME (type)) != TYPE_DECL) + return type; + + if (target && (type == target || TYPE_NAME (type) == target)) + return target; + + struct type_special *t; + for (t = special_types; t->match; t++) + { + if (!*t->match) + continue; + if (TYPE_NAME (type) != *t->match) + continue; + if (t->cousin && *t->cousin) + *cousin = *t->cousin; + if (t->replace) + return *t->replace ? *t->replace : type; + return type; + } + + tree orig = DECL_ORIGINAL_TYPE (TYPE_NAME (type)); + if (!orig) + return type; + + type = orig; + } + return type; +} + +static void +check_format_types (const substring_loc &fmt_loc, + format_wanted_type *types, const format_kind_info *fki, + int offset_to_type_start, + char conversion_char, + vec<location_t> *arglocs) +{ + for (; types != 0; types = types->next) + { + tree cur_param; + tree cur_type; + tree cur_type_cousin = NULL; + tree orig_cur_type; + tree wanted_type; + int arg_num; + int i; + int char_type_flag; + + wanted_type = types->wanted_type; + arg_num = types->arg_num; + + /* The following should not occur here. */ + gcc_assert (wanted_type); + gcc_assert (wanted_type != void_type_node || types->pointer_count); + + if (types->pointer_count == 0) + wanted_type = lang_hooks.types.type_promotes_to (wanted_type); + + switch (TREE_CODE (wanted_type)) + { + case IDENTIFIER_NODE: + break; + case TYPE_DECL: + wanted_type = TYPE_MAIN_VARIANT (DECL_ORIGINAL_TYPE (wanted_type)); + break; + default: + wanted_type = TYPE_MAIN_VARIANT (wanted_type); + break; + } + + cur_param = types->param; + if (!cur_param) + { + format_type_warning (fmt_loc, UNKNOWN_LOCATION, types, wanted_type, + NULL, fki, offset_to_type_start, + conversion_char); + continue; + } + + cur_type = TREE_TYPE (cur_param); + if (cur_type == error_mark_node) + continue; + orig_cur_type = cur_type; + char_type_flag = 0; + + location_t param_loc = UNKNOWN_LOCATION; + if (EXPR_HAS_LOCATION (cur_param)) + param_loc = EXPR_LOCATION (cur_param); + else if (arglocs) + { + /* arg_num is 1-based. */ + gcc_assert (types->arg_num > 0); + param_loc = (*arglocs)[types->arg_num - 1]; + } + + STRIP_NOPS (cur_param); + + /* Check the types of any additional pointer arguments + that precede the "real" argument. */ + for (i = 0; i < types->pointer_count; ++i) + { + if (TREE_CODE (cur_type) == POINTER_TYPE) + { + cur_type = TREE_TYPE (cur_type); + if (cur_type == error_mark_node) + break; + + /* Check for writing through a NULL pointer. */ + if (types->writing_in_flag + && i == 0 + && cur_param != 0 + && integer_zerop (cur_param)) + warning (OPT_Wformat_, "writing through null pointer " + "(argument %d)", arg_num); + + /* Check for reading through a NULL pointer. */ + if (types->reading_from_flag + && i == 0 + && cur_param != 0 + && integer_zerop (cur_param)) + warning (OPT_Wformat_, "reading through null pointer " + "(argument %d)", arg_num); + + if (cur_param != 0 && TREE_CODE (cur_param) == ADDR_EXPR) + cur_param = TREE_OPERAND (cur_param, 0); + else + cur_param = 0; + + /* See if this is an attempt to write into a const type with + scanf or with printf "%n". Note: the writing in happens + at the first indirection only, if for example + void * const * is passed to scanf %p; passing + const void ** is simply passing an incompatible type. */ + if (types->writing_in_flag + && i == 0 + && (TYPE_READONLY (cur_type) + || (cur_param != 0 + && (CONSTANT_CLASS_P (cur_param) + || (DECL_P (cur_param) + && TREE_READONLY (cur_param)))))) + warning (OPT_Wformat_, "writing into constant object " + "(argument %d)", arg_num); + + /* If there are extra type qualifiers beyond the first + indirection, then this makes the types technically + incompatible. */ + if (i > 0 + && pedantic + && (TYPE_READONLY (cur_type) + || TYPE_VOLATILE (cur_type) + || TYPE_ATOMIC (cur_type) + || TYPE_RESTRICT (cur_type))) + warning (OPT_Wformat_, "extra type qualifiers in format " + "argument (argument %d)", + arg_num); + + } + else + { + format_type_warning (fmt_loc, param_loc, + types, wanted_type, orig_cur_type, fki, + offset_to_type_start, conversion_char); + break; + } + } + + if (i < types->pointer_count) + continue; + + cur_type = type_normalize (cur_type, &cur_type_cousin); + + /* Check whether the argument type is a character type. This leniency + only applies to certain formats, flagged with 'c'. */ + if (types->char_lenient_flag) + char_type_flag = (cur_type == char_type_node + || cur_type == signed_char_type_node + || cur_type == unsigned_char_type_node); + + int compat = lang_hooks.types_compatible_p (wanted_type, cur_type); + /* Check the type of the "real" argument, if there's a type we want. */ + if ((TREE_CODE (wanted_type) != INTEGER_TYPE || types->pointer_count) + && compat) + continue; + if (TREE_CODE (wanted_type) == INTEGER_TYPE && !types->pointer_count + && compat) + { +compat_inner: + if (TREE_CODE (cur_param) == INTEGER_CST) + continue; + + if (TREE_CODE (types->wanted_type) == TYPE_DECL + && TREE_CODE (cur_type) == TYPE_DECL) + { + if (types->wanted_type == cur_type) + continue; + format_type_warning (fmt_loc, param_loc, types, + wanted_type, orig_cur_type, fki, + offset_to_type_start, conversion_char, + " (strict match required [A])"); + continue; + } + else if (TREE_CODE (types->wanted_type) == TYPE_DECL) + { + if (types->wanted_type == TYPE_NAME(cur_type)) + continue; + format_type_warning (fmt_loc, param_loc, types, + wanted_type, orig_cur_type, fki, + offset_to_type_start, conversion_char, + " (strict match required [B])"); + continue; + } + else if (wanted_type == cur_type) + continue; + else if (cur_type_cousin) + { + format_type_warning (fmt_loc, param_loc, types, + wanted_type, orig_cur_type, fki, + offset_to_type_start, conversion_char, + " (strict match required [C])"); + } + + /* + format_type_warning (fmt_loc, param_loc, types, + wanted_type, orig_cur_type, fki, + offset_to_type_start, conversion_char, + " (ultra-pedantic mode)"); + */ + continue; + } + + /* If we want 'void *', allow any pointer type. + (Anything else would already have got a warning.) + With -Wpedantic, only allow pointers to void and to character + types. */ + if (wanted_type == void_type_node + && (!pedantic || (i == 1 && char_type_flag))) + continue; + /* Don't warn about differences merely in signedness, unless + -Wpedantic. With -Wpedantic, warn if the type is a pointer + target and not a character type, and for character types at + a second level of indirection. */ + if (TREE_CODE (wanted_type) == INTEGER_TYPE + && TREE_CODE (cur_type) == INTEGER_TYPE + && ((!pedantic && !warn_format_signedness) + || (i == 0 && !warn_format_signedness) + || (i == 1 && char_type_flag)) + && (TYPE_UNSIGNED (wanted_type) + ? wanted_type == c_common_unsigned_type (cur_type) + : wanted_type == c_common_signed_type (cur_type))) + { + if (cur_type_cousin) + { + if (TREE_CODE (types->wanted_type) == TYPE_DECL + && TREE_CODE (cur_type_cousin) == TYPE_DECL) + { + if (types->wanted_type == cur_type_cousin) + continue; + format_type_warning (fmt_loc, param_loc, types, + wanted_type, orig_cur_type, fki, + offset_to_type_start, conversion_char, + " (strict match required [X])"); + continue; + } + else if (TREE_CODE (types->wanted_type) == TYPE_DECL) + { + if (types->wanted_type == TYPE_NAME(cur_type_cousin)) + continue; + format_type_warning (fmt_loc, param_loc, types, + wanted_type, orig_cur_type, fki, + offset_to_type_start, conversion_char, + " (strict match required [Y])"); + continue; + } + else if (wanted_type == cur_type_cousin) + continue; + else + { + format_type_warning (fmt_loc, param_loc, types, + wanted_type, orig_cur_type, fki, + offset_to_type_start, conversion_char, + " (strict match required [Z])"); + } + } + + goto compat_inner; + } + /* Don't warn about differences merely in signedness if we know + that the current type is integer-promoted and its original type + was unsigned such as that it is in the range of WANTED_TYPE. */ + if (TREE_CODE (wanted_type) == INTEGER_TYPE + && TREE_CODE (cur_type) == INTEGER_TYPE + && warn_format_signedness + && TYPE_UNSIGNED (wanted_type) + && cur_param != NULL_TREE + && TREE_CODE (cur_param) == NOP_EXPR) + { + tree t = TREE_TYPE (TREE_OPERAND (cur_param, 0)); + if (TYPE_UNSIGNED (t) + && cur_type == lang_hooks.types.type_promotes_to (t)) + continue; + } + /* Likewise, "signed char", "unsigned char" and "char" are + equivalent but the above test won't consider them equivalent. */ + if (wanted_type == char_type_node + && (!pedantic || i < 2) + && char_type_flag) + continue; + if (types->scalar_identity_flag + && (TREE_CODE (cur_type) == TREE_CODE (wanted_type) + || (INTEGRAL_TYPE_P (cur_type) + && INTEGRAL_TYPE_P (wanted_type))) + && TYPE_PRECISION (cur_type) == TYPE_PRECISION (wanted_type)) + continue; + /* Now we have a type mismatch. */ + format_type_warning (fmt_loc, param_loc, types, + wanted_type, orig_cur_type, fki, + offset_to_type_start, conversion_char); + } +} + +static bool +check_kef_type (const substring_loc &fmt_loc, + const struct kernel_ext_fmt *kef, + unsigned arg_num, + tree cur_param, + tree wanted_type, + const format_kind_info *fki, + int offset_to_type_start, + char conversion_char, + vec<location_t> *arglocs) +{ + tree cur_type; + bool ok = true; + int i; + + /* The following should not occur here. */ + gcc_assert (wanted_type); + gcc_assert (wanted_type != void_type_node || kef->ptrlevel); + + if (TREE_CODE (wanted_type) == TYPE_DECL) + wanted_type = DECL_ORIGINAL_TYPE (wanted_type); + + if (!cur_param) + return false; + + cur_type = TREE_TYPE (cur_param); + if (cur_type == error_mark_node) + return false; + + location_t param_loc = UNKNOWN_LOCATION; + if (EXPR_HAS_LOCATION (cur_param)) + param_loc = EXPR_LOCATION (cur_param); + else if (arglocs) + { + /* arg_num is 1-based. */ + gcc_assert (arg_num > 0); + param_loc = (*arglocs)[arg_num - 1]; + } + (void)param_loc; + + STRIP_NOPS (cur_param); + + /* Check the types of any additional pointer arguments + that precede the "real" argument. */ + for (i = 0; i < kef->ptrlevel; ++i) + { + if (TREE_CODE (cur_type) == POINTER_TYPE) + { + cur_type = TREE_TYPE (cur_type); + if (cur_type == error_mark_node) + break; + + if (cur_param != 0 && TREE_CODE (cur_param) == ADDR_EXPR) + cur_param = TREE_OPERAND (cur_param, 0); + else + cur_param = 0; + + /* If there are extra type qualifiers beyond the first + indirection, then this makes the types technically + incompatible. */ + if (i > 0 + && pedantic + && (TYPE_READONLY (cur_type) + || TYPE_VOLATILE (cur_type) + || TYPE_ATOMIC (cur_type) + || TYPE_RESTRICT (cur_type))) + warning (OPT_Wformat_, "extra type qualifiers in format " + "argument (argument %d)", + arg_num); + + } + else + { + ok = false; + break; + } + } + + if (i < kef->ptrlevel) + return ok; + + int compat = lang_hooks.types_compatible_p (wanted_type, cur_type); + + if (!compat) + return false; + + tree cousin; + tree normal_type; + + normal_type = type_normalize (cur_type, &cousin, wanted_type); + + return normal_type == wanted_type; +} + + +/* Given type TYPE, attempt to dereference the type N times + (e.g. from ("int ***", 2) to "int *") + + Return the derefenced type, with any qualifiers + such as "const" stripped from the result, or + NULL if unsuccessful (e.g. TYPE is not a pointer type). */ + +static tree +deref_n_times (tree type, int n) +{ + gcc_assert (type); + + for (int i = n; i > 0; i--) + { + if (TREE_CODE (type) != POINTER_TYPE) + return NULL_TREE; + type = TREE_TYPE (type); + } + /* Strip off any "const" etc. */ + return build_qualified_type (type, 0); +} + +/* Lookup the format code for FORMAT_LEN within FLI, + returning the string code for expressing it, or NULL + if it is not found. */ + +static const char * +get_modifier_for_format_len (const format_length_info *fli, + enum format_lengths format_len) +{ + for (; fli->name; fli++) + { + if (fli->index == format_len) + return fli->name; + if (fli->double_index == format_len) + return fli->double_name; + } + return NULL; +} + +#if CHECKING_P + +namespace selftest { + +static void +test_get_modifier_for_format_len () +{ + ASSERT_STREQ ("h", + get_modifier_for_format_len (printf_length_specs, FMT_LEN_h)); + ASSERT_STREQ ("hh", + get_modifier_for_format_len (printf_length_specs, FMT_LEN_hh)); + ASSERT_STREQ ("L", + get_modifier_for_format_len (printf_length_specs, FMT_LEN_L)); + ASSERT_EQ (NULL, + get_modifier_for_format_len (printf_length_specs, FMT_LEN_none)); +} + +} // namespace selftest + +#endif /* CHECKING_P */ + +/* Determine if SPEC_TYPE and ARG_TYPE are sufficiently similar for a + format_type_detail using SPEC_TYPE to be offered as a suggestion for + Wformat type errors where the argument has type ARG_TYPE. */ + +static bool +matching_type_p (tree spec_type, tree arg_type) +{ + gcc_assert (spec_type); + gcc_assert (arg_type); + + /* If any of the types requires structural equality, we can't compare + their canonical types. */ + if (TYPE_STRUCTURAL_EQUALITY_P (spec_type) + || TYPE_STRUCTURAL_EQUALITY_P (arg_type)) + return false; + + spec_type = TYPE_CANONICAL (spec_type); + arg_type = TYPE_CANONICAL (arg_type); + + if (TREE_CODE (spec_type) == INTEGER_TYPE + && TREE_CODE (arg_type) == INTEGER_TYPE + && (TYPE_UNSIGNED (spec_type) + ? spec_type == c_common_unsigned_type (arg_type) + : spec_type == c_common_signed_type (arg_type))) + return true; + + return spec_type == arg_type; +} + +/* Subroutine of get_format_for_type. + + Generate a string containing the length modifier and conversion specifier + that should be used to format arguments of type ARG_TYPE within FKI + (effectively the inverse of the checking code). + + If CONVERSION_CHAR is not zero (the first pass), the resulting suggestion + is required to use it, for correcting bogus length modifiers. + If CONVERSION_CHAR is zero (the second pass), then allow any suggestion + that matches ARG_TYPE. + + If successful, returns a non-NULL string which should be freed + by the caller. + Otherwise, returns NULL. */ + +static char * +get_format_for_type_1 (const format_kind_info *fki, tree arg_type, + char conversion_char) +{ + gcc_assert (arg_type); + + const format_char_info *spec; + for (spec = &fki->conversion_specs[0]; + spec->format_chars; + spec++) + { + if (conversion_char) + if (!strchr (spec->format_chars, conversion_char)) + continue; + + tree effective_arg_type = deref_n_times (arg_type, + spec->pointer_count); + if (!effective_arg_type) + continue; + for (int i = 0; i < FMT_LEN_MAX; i++) + { + const format_type_detail *ftd = &spec->types[i]; + if (!ftd->type) + continue; + if (matching_type_p (*ftd->type, effective_arg_type)) + { + const char *len_modifier + = get_modifier_for_format_len (fki->length_char_specs, + (enum format_lengths)i); + if (!len_modifier) + len_modifier = ""; + + if (conversion_char) + /* We found a match, using the given conversion char - the + length modifier was incorrect (or absent). + Provide a suggestion using the conversion char with the + correct length modifier for the type. */ + return xasprintf ("%s%c", len_modifier, conversion_char); + else + /* 2nd pass: no match was possible using the user-provided + conversion char, but we do have a match without using it. + Provide a suggestion using the first conversion char + listed for the given type. */ + return xasprintf ("%s%c", len_modifier, spec->format_chars[0]); + } + } + } + + return NULL; +} + +/* Generate a string containing the length modifier and conversion specifier + that should be used to format arguments of type ARG_TYPE within FKI + (effectively the inverse of the checking code). + + If successful, returns a non-NULL string which should be freed + by the caller. + Otherwise, returns NULL. */ + +static char * +get_format_for_type (const format_kind_info *fki, tree arg_type, + char conversion_char) +{ + gcc_assert (arg_type); + gcc_assert (conversion_char); + + /* First pass: look for a format_char_info containing CONVERSION_CHAR + If we find one, then presumably the length modifier was incorrect + (or absent). */ + char *result = get_format_for_type_1 (fki, arg_type, conversion_char); + if (result) + return result; + + /* Second pass: we didn't find a match for CONVERSION_CHAR, so try + matching just on the type. */ + return get_format_for_type_1 (fki, arg_type, '\0'); +} + +/* Attempt to get a string for use as a replacement fix-it hint for the + source range in FMT_LOC. + + Preserve all of the text within the range of FMT_LOC up to + OFFSET_TO_TYPE_START, replacing the rest with an appropriate + length modifier and conversion specifier for ARG_TYPE, attempting + to keep the user-provided CONVERSION_CHAR if possible. + + For example, given a long vs long long mismatch for arg5 here: + + 000000000111111111122222222223333333333| + 123456789012345678901234567890123456789` column numbers + 0000000000111111111122| + 0123456789012345678901` string offsets + V~~~~~~~~ : range of FMT_LOC, from cols 23-31 + sprintf (d, "before %-+*.*lld after", arg3, arg4, arg5); + ^ ^ + | ` CONVERSION_CHAR: 'd' + type starts here + + where OFFSET_TO_TYPE_START is 13 (the offset to the "lld" within the + STRING_CST), where the user provided: + %-+*.*lld + the result (assuming "long" argument 5) should be: + %-+*.*ld + + If successful, returns a non-NULL string which should be freed + by the caller. + Otherwise, returns NULL. */ + +static char * +get_corrected_substring (const substring_loc &fmt_loc, + format_wanted_type *type, tree arg_type, + const format_kind_info *fki, + int offset_to_type_start, char conversion_char) +{ + /* Attempt to provide hints for argument types, but not for field widths + and precisions. */ + if (!arg_type) + return NULL; + if (type->kind != CF_KIND_FORMAT) + return NULL; + + /* Locate the current code within the source range, rejecting + any awkward cases where the format string occupies more than + one line. + Lookup the place where the type starts (including any length + modifiers), getting it as the caret location. */ + substring_loc type_loc (fmt_loc); + type_loc.set_caret_index (offset_to_type_start); + + location_t fmt_substring_loc; + const char *err = type_loc.get_location (&fmt_substring_loc); + if (err) + return NULL; + + source_range fmt_substring_range + = get_range_from_loc (line_table, fmt_substring_loc); + + expanded_location caret + = expand_location_to_spelling_point (fmt_substring_loc); + expanded_location start + = expand_location_to_spelling_point (fmt_substring_range.m_start); + expanded_location finish + = expand_location_to_spelling_point (fmt_substring_range.m_finish); + if (caret.file != start.file) + return NULL; + if (start.file != finish.file) + return NULL; + if (caret.line != start.line) + return NULL; + if (start.line != finish.line) + return NULL; + if (start.column > caret.column) + return NULL; + if (start.column > finish.column) + return NULL; + if (caret.column > finish.column) + return NULL; + +#if BUILDING_GCC_VERSION >= 9000 + char_span line = location_get_source_line (start.file, start.line); + if (!line) + return NULL; + + /* If we got this far, then we have the line containing the + existing conversion specification. + + Generate a trimmed copy, containing the prefix part of the conversion + specification, up to the (but not including) the length modifier. + In the above example, this would be "%-+*.*". */ + int length_up_to_type = caret.column - start.column; + char_span prefix_span = line.subspan (start.column - 1, length_up_to_type); + char *prefix = prefix_span.xstrdup (); +#else + char *prefix = NULL; +#endif + + /* Now attempt to generate a suggestion for the rest of the specification + (length modifier and conversion char), based on ARG_TYPE and + CONVERSION_CHAR. + In the above example, this would be "ld". */ + char *format_for_type = get_format_for_type (fki, arg_type, conversion_char); + if (!format_for_type) + { + free (prefix); + return NULL; + } + + /* Success. Generate the resulting suggestion for the whole range of + FMT_LOC by concatenating the two strings. + In the above example, this would be "%-+*.*ld". */ + char *result = concat (prefix, format_for_type, NULL); + free (format_for_type); + free (prefix); + return result; +} + +/* Helper class for adding zero or more trailing '*' to types. + + The format type and name exclude any '*' for pointers, so those + must be formatted manually. For all the types we currently have, + this is adequate, but formats taking pointers to functions or + arrays would require the full type to be built up in order to + print it with %T. */ + +class indirection_suffix +{ + public: + indirection_suffix (int pointer_count) : m_pointer_count (pointer_count) {} + + /* Determine the size of the buffer (including NUL-terminator). */ + + size_t get_buffer_size () const + { + return m_pointer_count + 2; + } + + /* Write the '*' to DST and add a NUL-terminator. */ + + void fill_buffer (char *dst) const + { + if (m_pointer_count == 0) + dst[0] = 0; + else if (c_dialect_cxx ()) + { + memset (dst, '*', m_pointer_count); + dst[m_pointer_count] = 0; + } + else + { + dst[0] = ' '; + memset (dst + 1, '*', m_pointer_count); + dst[m_pointer_count + 1] = 0; + } + } + + private: + int m_pointer_count; +}; + +#if BUILDING_GCC_VERSION >= 9000 +/* not exported by GCC... need a local copy :( */ +class frr_range_label_for_type_mismatch : public range_label +{ + public: + frr_range_label_for_type_mismatch (tree labelled_type, tree other_type) + : m_labelled_type (labelled_type), m_other_type (other_type) + { + } + + label_text get_text (unsigned range_idx) const OVERRIDE; + + protected: + tree m_labelled_type; + tree m_other_type; +}; + +/* Print T to CPP. */ + +static void +print_type (c_pretty_printer *cpp, tree t, bool *quoted) +{ + gcc_assert (TYPE_P (t)); + struct obstack *ob = pp_buffer (cpp)->obstack; + char *p = (char *) obstack_base (ob); + /* Remember the end of the initial dump. */ + int len = obstack_object_size (ob); + + tree name = TYPE_NAME (t); + if (name && TREE_CODE (name) == TYPE_DECL && DECL_NAME (name)) + pp_identifier (cpp, lang_hooks.decl_printable_name (name, 2)); + else + cpp->type_id (t); + + /* If we're printing a type that involves typedefs, also print the + stripped version. But sometimes the stripped version looks + exactly the same, so we don't want it after all. To avoid + printing it in that case, we play ugly obstack games. */ + if (TYPE_CANONICAL (t) && t != TYPE_CANONICAL (t)) + { + c_pretty_printer cpp2; + /* Print the stripped version into a temporary printer. */ + cpp2.type_id (TYPE_CANONICAL (t)); + struct obstack *ob2 = cpp2.buffer->obstack; + /* Get the stripped version from the temporary printer. */ + const char *aka = (char *) obstack_base (ob2); + int aka_len = obstack_object_size (ob2); + int type1_len = obstack_object_size (ob) - len; + + /* If they are identical, bail out. */ + if (aka_len == type1_len && memcmp (p + len, aka, aka_len) == 0) + return; + + /* They're not, print the stripped version now. */ + if (*quoted) + pp_end_quote (cpp, pp_show_color (cpp)); + pp_c_whitespace (cpp); + pp_left_brace (cpp); + pp_c_ws_string (cpp, _("aka")); + pp_c_whitespace (cpp); + if (*quoted) + pp_begin_quote (cpp, pp_show_color (cpp)); + cpp->type_id (TYPE_CANONICAL (t)); + if (*quoted) + pp_end_quote (cpp, pp_show_color (cpp)); + pp_right_brace (cpp); + /* No further closing quotes are needed. */ + *quoted = false; + } +} + +/* C-specific implementation of range_label::get_text () vfunc for + range_label_for_type_mismatch. */ + +label_text +frr_range_label_for_type_mismatch::get_text (unsigned /*range_idx*/) const +{ + if (m_labelled_type == NULL_TREE) + return label_text (NULL, false); + + c_pretty_printer cpp; + bool quoted = false; + print_type (&cpp, m_labelled_type, "ed); + return label_text (xstrdup (pp_formatted_text (&cpp)), true); +} + +#define range_label_for_type_mismatch frr_range_label_for_type_mismatch +#endif + +/* Subclass of range_label for labelling the range in the format string + with the type in question, adding trailing '*' for pointer_count. */ + +class range_label_for_format_type_mismatch + : public range_label_for_type_mismatch +{ + public: + range_label_for_format_type_mismatch (tree labelled_type, tree other_type, + int pointer_count) + : range_label_for_type_mismatch (labelled_type, other_type), + m_pointer_count (pointer_count) + { + } + + label_text get_text (unsigned range_idx) const FINAL OVERRIDE + { + label_text text = range_label_for_type_mismatch::get_text (range_idx); + if (text.m_buffer == NULL) + return text; + + indirection_suffix suffix (m_pointer_count); + char *p = (char *) alloca (suffix.get_buffer_size ()); + suffix.fill_buffer (p); + + char *result = concat (text.m_buffer, p, NULL); + text.maybe_free (); + return label_text (result, true); + } + + private: + int m_pointer_count; +}; + +/* Give a warning about a format argument of different type from that expected. + The range of the diagnostic is taken from WHOLE_FMT_LOC; the caret location + is based on the location of the char at TYPE->offset_loc. + PARAM_LOC is the location of the relevant argument, or UNKNOWN_LOCATION + if this is unavailable. + WANTED_TYPE is the type the argument should have, + possibly stripped of pointer dereferences. The description (such as "field + precision"), the placement in the format string, a possibly more + friendly name of WANTED_TYPE, and the number of pointer dereferences + are taken from TYPE. ARG_TYPE is the type of the actual argument, + or NULL if it is missing. + + OFFSET_TO_TYPE_START is the offset within the execution-charset encoded + format string to where type information begins for the conversion + (the length modifier and conversion specifier). + CONVERSION_CHAR is the user-provided conversion specifier. + + For example, given a type mismatch for argument 5 here: + + 00000000011111111112222222222333333333344444444445555555555| + 12345678901234567890123456789012345678901234567890123456789` column numbers + 0000000000111111111122| + 0123456789012345678901` offsets within STRING_CST + V~~~~~~~~ : range of WHOLE_FMT_LOC, from cols 23-31 + sprintf (d, "before %-+*.*lld after", int_expr, int_expr, long_expr); + ^ ^ ^~~~~~~~~ + | ` CONVERSION_CHAR: 'd' PARAM_LOC + type starts here + + OFFSET_TO_TYPE_START is 13, the offset to the "lld" within the + STRING_CST. */ + +static void +format_type_warning (const substring_loc &whole_fmt_loc, + location_t param_loc, + format_wanted_type *type, + tree wanted_type, tree arg_type, + const format_kind_info *fki, + int offset_to_type_start, + char conversion_char, + const char *extra) +{ + enum format_specifier_kind kind = type->kind; + const char *wanted_type_name = type->wanted_type_name; + const char *format_start = type->format_start; + int format_length = type->format_length; + int pointer_count = type->pointer_count; + int arg_num = type->arg_num; + + if (!extra) + extra = ""; + + /* If ARG_TYPE is a typedef with a misleading name (for example, + size_t but not the standard size_t expected by printf %zu), avoid + printing the typedef name. */ + if (wanted_type_name + && arg_type + && TYPE_NAME (arg_type) + && TREE_CODE (TYPE_NAME (arg_type)) == TYPE_DECL + && DECL_NAME (TYPE_NAME (arg_type)) + && !strcmp (wanted_type_name, + lang_hooks.decl_printable_name (TYPE_NAME (arg_type), 2))) + arg_type = TYPE_MAIN_VARIANT (arg_type); + + indirection_suffix suffix (pointer_count); + char *p = (char *) alloca (suffix.get_buffer_size ()); + suffix.fill_buffer (p); + + /* WHOLE_FMT_LOC has the caret at the end of the range. + Set the caret to be at the offset from TYPE. Subtract one + from the offset for the same reason as in format_warning_at_char. */ + substring_loc fmt_loc (whole_fmt_loc); + fmt_loc.set_caret_index (type->offset_loc - 1); + +#if BUILDING_GCC_VERSION >= 9000 + range_label_for_format_type_mismatch fmt_label (wanted_type, arg_type, + pointer_count); + range_label_for_type_mismatch param_label (arg_type, wanted_type); + + /* Get a string for use as a replacement fix-it hint for the range in + fmt_loc, or NULL. */ + char *corrected_substring + = get_corrected_substring (fmt_loc, type, arg_type, fki, + offset_to_type_start, conversion_char); + format_string_diagnostic_t diag (fmt_loc, &fmt_label, param_loc, ¶m_label, + corrected_substring); +# define format_warning_at_substring(a,b,c,d,e,...) \ + diag.emit_warning(__VA_ARGS__) +#else +# define format_warning_at_substring(a,b,c,d,...) \ + format_warning_at_substring(a,c,__VA_ARGS__) + /* Get a string for use as a replacement fix-it hint for the range in + fmt_loc, or NULL. */ + char *corrected_substring + = get_corrected_substring (fmt_loc, type, arg_type, fki, + offset_to_type_start, conversion_char); + +#endif + + if (wanted_type_name) + { + if (arg_type) + format_warning_at_substring + (fmt_loc, &fmt_label, param_loc, ¶m_label, + corrected_substring, OPT_Wformat_, + "%s %<%s%.*s%> expects argument of type %<%s%s%>, " + "but argument %d has type %qT%s", + gettext (kind_descriptions[kind]), + (kind == CF_KIND_FORMAT ? "%" : ""), + format_length, format_start, + wanted_type_name, p, arg_num, arg_type, extra); + else + format_warning_at_substring + (fmt_loc, &fmt_label, param_loc, ¶m_label, + corrected_substring, OPT_Wformat_, + "%s %<%s%.*s%> expects a matching %<%s%s%> argument%s", + gettext (kind_descriptions[kind]), + (kind == CF_KIND_FORMAT ? "%" : ""), + format_length, format_start, wanted_type_name, p, extra); + } + else + { + if (arg_type) + format_warning_at_substring + (fmt_loc, &fmt_label, param_loc, ¶m_label, + corrected_substring, OPT_Wformat_, + "%s %<%s%.*s%> expects argument of type %<%T%s%>, " + "but argument %d has type %qT%s", + gettext (kind_descriptions[kind]), + (kind == CF_KIND_FORMAT ? "%" : ""), + format_length, format_start, + wanted_type, p, arg_num, arg_type, extra); + else + format_warning_at_substring + (fmt_loc, &fmt_label, param_loc, ¶m_label, + corrected_substring, OPT_Wformat_, + "%s %<%s%.*s%> expects a matching %<%T%s%> argument%s", + gettext (kind_descriptions[kind]), + (kind == CF_KIND_FORMAT ? "%" : ""), + format_length, format_start, wanted_type, p, extra); + } + + free (corrected_substring); +} + + +#if 0 +/* Given a format_char_info array FCI, and a character C, this function + returns the index into the conversion_specs where that specifier's + data is located. The character must exist. */ +static unsigned int +find_char_info_specifier_index (const format_char_info *fci, int c) +{ + unsigned i; + + for (i = 0; fci->format_chars; i++, fci++) + if (strchr (fci->format_chars, c)) + return i; + + /* We shouldn't be looking for a non-existent specifier. */ + gcc_unreachable (); +} + +/* Given a format_length_info array FLI, and a character C, this + function returns the index into the conversion_specs where that + modifier's data is located. The character must exist. */ +static unsigned int +find_length_info_modifier_index (const format_length_info *fli, int c) +{ + unsigned i; + + for (i = 0; fli->name; i++, fli++) + if (strchr (fli->name, c)) + return i; + + /* We shouldn't be looking for a non-existent modifier. */ + gcc_unreachable (); +} +#endif + +#ifdef TARGET_FORMAT_TYPES +extern const format_kind_info TARGET_FORMAT_TYPES[]; +#endif + +#ifdef TARGET_OVERRIDES_FORMAT_ATTRIBUTES +extern const target_ovr_attr TARGET_OVERRIDES_FORMAT_ATTRIBUTES[]; +#endif +#ifdef TARGET_OVERRIDES_FORMAT_INIT + extern void TARGET_OVERRIDES_FORMAT_INIT (void); +#endif + +/* Attributes such as "printf" are equivalent to those such as + "gnu_printf" unless this is overridden by a target. */ +static const target_ovr_attr gnu_target_overrides_format_attributes[] = +{ + { NULL, NULL } +}; + +/* Translate to unified attribute name. This is used in decode_format_type and + decode_format_attr. In attr_name the user specified argument is passed. It + returns the unified format name from TARGET_OVERRIDES_FORMAT_ATTRIBUTES + or the attr_name passed to this function, if there is no matching entry. */ +static const char * +convert_format_name_to_system_name (const char *attr_name) +{ + int i; + + if (attr_name == NULL || *attr_name == 0 + || strncmp (attr_name, "gcc_", 4) == 0) + return attr_name; +#ifdef TARGET_OVERRIDES_FORMAT_INIT + TARGET_OVERRIDES_FORMAT_INIT (); +#endif + +#ifdef TARGET_OVERRIDES_FORMAT_ATTRIBUTES + /* Check if format attribute is overridden by target. */ + if (TARGET_OVERRIDES_FORMAT_ATTRIBUTES != NULL + && TARGET_OVERRIDES_FORMAT_ATTRIBUTES_COUNT > 0) + { + for (i = 0; i < TARGET_OVERRIDES_FORMAT_ATTRIBUTES_COUNT; ++i) + { + if (cmp_attribs (TARGET_OVERRIDES_FORMAT_ATTRIBUTES[i].named_attr_src, + attr_name)) + return attr_name; + if (cmp_attribs (TARGET_OVERRIDES_FORMAT_ATTRIBUTES[i].named_attr_dst, + attr_name)) + return TARGET_OVERRIDES_FORMAT_ATTRIBUTES[i].named_attr_src; + } + } +#endif + /* Otherwise default to gnu format. */ + for (i = 0; + gnu_target_overrides_format_attributes[i].named_attr_src != NULL; + ++i) + { + if (cmp_attribs (gnu_target_overrides_format_attributes[i].named_attr_src, + attr_name)) + return attr_name; + if (cmp_attribs (gnu_target_overrides_format_attributes[i].named_attr_dst, + attr_name)) + return gnu_target_overrides_format_attributes[i].named_attr_src; + } + + return attr_name; +} + +/* Handle a "format" attribute; arguments as in + struct attribute_spec.handler. */ +tree +handle_frr_format_attribute (tree *node, tree ARG_UNUSED (name), tree args, + int flags, bool *no_add_attrs) +{ + tree type = *node; + function_format_info info; + + /* Canonicalize name of format function. */ + if (TREE_CODE (TREE_VALUE (args)) == IDENTIFIER_NODE) + TREE_VALUE (args) = canonicalize_attr_name (TREE_VALUE (args)); + + if (!decode_format_attr (args, &info, 0)) + { + *no_add_attrs = true; + return NULL_TREE; + } + + if (prototype_p (type)) + { + if (!check_format_string (type, info.format_num, flags, + no_add_attrs, info.format_type)) + return NULL_TREE; + + if (info.first_arg_num != 0) + { + unsigned HOST_WIDE_INT arg_num = 1; + function_args_iterator iter; + tree arg_type; + + /* Verify that first_arg_num points to the last arg, + the ... */ + FOREACH_FUNCTION_ARGS (type, arg_type, iter) + arg_num++; + + if (arg_num != info.first_arg_num) + { + if (!(flags & (int) ATTR_FLAG_BUILT_IN)) + error ("args to be formatted is not %<...%>"); + *no_add_attrs = true; + return NULL_TREE; + } + } + } + + /* Check if this is a strftime variant. Just for this variant + FMT_FLAG_ARG_CONVERT is not set. */ + if ((format_types[info.format_type].flags & (int) FMT_FLAG_ARG_CONVERT) == 0 + && info.first_arg_num != 0) + { + error ("strftime formats cannot format arguments"); + *no_add_attrs = true; + return NULL_TREE; + } + + return NULL_TREE; +} + +#if CHECKING_P + +namespace selftest { + +/* Selftests of location handling. */ + +/* Get the format_kind_info with the given name. */ + +static const format_kind_info * +get_info (const char *name) +{ + int idx = decode_format_type (name); + const format_kind_info *fki = &format_types[idx]; + ASSERT_STREQ (fki->name, name); + return fki; +} + +/* Verify that get_format_for_type (FKI, TYPE, CONVERSION_CHAR) + is EXPECTED_FORMAT. */ + +static void +assert_format_for_type_streq (const location &loc, const format_kind_info *fki, + const char *expected_format, tree type, + char conversion_char) +{ + gcc_assert (fki); + gcc_assert (expected_format); + gcc_assert (type); + + char *actual_format = get_format_for_type (fki, type, conversion_char); + ASSERT_STREQ_AT (loc, expected_format, actual_format); + free (actual_format); +} + +/* Selftests for get_format_for_type. */ + +#define ASSERT_FORMAT_FOR_TYPE_STREQ(EXPECTED_FORMAT, TYPE, CONVERSION_CHAR) \ + assert_format_for_type_streq (SELFTEST_LOCATION, (fki), (EXPECTED_FORMAT), \ + (TYPE), (CONVERSION_CHAR)) + +/* Selftest for get_format_for_type for "printf"-style functions. */ + +static void +test_get_format_for_type_printf () +{ + const format_kind_info *fki = get_info ("gnu_printf"); + ASSERT_NE (fki, NULL); + + ASSERT_FORMAT_FOR_TYPE_STREQ ("f", double_type_node, 'i'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("Lf", long_double_type_node, 'i'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("f", double_type_node, 'o'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("Lf", long_double_type_node, 'o'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("f", double_type_node, 'x'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("Lf", long_double_type_node, 'x'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("f", double_type_node, 'X'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("Lf", long_double_type_node, 'X'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("d", integer_type_node, 'd'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("i", integer_type_node, 'i'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("o", integer_type_node, 'o'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("x", integer_type_node, 'x'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("X", integer_type_node, 'X'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("d", unsigned_type_node, 'd'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("i", unsigned_type_node, 'i'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("o", unsigned_type_node, 'o'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("x", unsigned_type_node, 'x'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("X", unsigned_type_node, 'X'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("ld", long_integer_type_node, 'd'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("li", long_integer_type_node, 'i'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("lx", long_integer_type_node, 'x'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("lo", long_unsigned_type_node, 'o'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("lx", long_unsigned_type_node, 'x'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("lld", long_long_integer_type_node, 'd'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("lli", long_long_integer_type_node, 'i'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("llo", long_long_unsigned_type_node, 'o'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("llx", long_long_unsigned_type_node, 'x'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("s", build_pointer_type (char_type_node), 'i'); +} + +/* Selftest for get_format_for_type for "scanf"-style functions. */ + +static void +test_get_format_for_type_scanf () +{ + const format_kind_info *fki = get_info ("gnu_scanf"); + ASSERT_NE (fki, NULL); + ASSERT_FORMAT_FOR_TYPE_STREQ ("d", build_pointer_type (integer_type_node), 'd'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("u", build_pointer_type (unsigned_type_node), 'u'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("ld", + build_pointer_type (long_integer_type_node), 'd'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("lu", + build_pointer_type (long_unsigned_type_node), 'u'); + ASSERT_FORMAT_FOR_TYPE_STREQ + ("lld", build_pointer_type (long_long_integer_type_node), 'd'); + ASSERT_FORMAT_FOR_TYPE_STREQ + ("llu", build_pointer_type (long_long_unsigned_type_node), 'u'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("e", build_pointer_type (float_type_node), 'e'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("le", build_pointer_type (double_type_node), 'e'); +} + +#undef ASSERT_FORMAT_FOR_TYPE_STREQ + +/* Exercise the type-printing label code, to give some coverage + under "make selftest-valgrind" (in particular, to ensure that + the label-printing machinery doesn't leak). */ + +static void +test_type_mismatch_range_labels () +{ + /* Create a tempfile and write some text to it. + ....................0000000001 11111111 12 22222222 + ....................1234567890 12345678 90 12345678. */ + const char *content = " printf (\"msg: %i\\n\", msg);\n"; + temp_source_file tmp (SELFTEST_LOCATION, ".c", content); + line_table_test ltt; + + linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1); + + location_t c17 = linemap_position_for_column (line_table, 17); + ASSERT_EQ (LOCATION_COLUMN (c17), 17); + location_t c18 = linemap_position_for_column (line_table, 18); + location_t c24 = linemap_position_for_column (line_table, 24); + location_t c26 = linemap_position_for_column (line_table, 26); + + /* Don't attempt to run the tests if column data might be unavailable. */ + if (c26 > LINE_MAP_MAX_LOCATION_WITH_COLS) + return; + + location_t fmt = make_location (c18, c17, c18); + ASSERT_EQ (LOCATION_COLUMN (fmt), 18); + + location_t param = make_location (c24, c24, c26); + ASSERT_EQ (LOCATION_COLUMN (param), 24); + + range_label_for_format_type_mismatch fmt_label (char_type_node, + integer_type_node, 1); + range_label_for_type_mismatch param_label (integer_type_node, + char_type_node); + gcc_rich_location richloc (fmt, &fmt_label); + richloc.add_range (param, SHOW_RANGE_WITHOUT_CARET, ¶m_label); + + test_diagnostic_context dc; + diagnostic_show_locus (&dc, &richloc, DK_ERROR); + if (c_dialect_cxx ()) + /* "char*", without a space. */ + ASSERT_STREQ ("\n" + " printf (\"msg: %i\\n\", msg);\n" + " ~^ ~~~\n" + " | |\n" + " char* int\n", + pp_formatted_text (dc.printer)); + else + /* "char *", with a space. */ + ASSERT_STREQ ("\n" + " printf (\"msg: %i\\n\", msg);\n" + " ~^ ~~~\n" + " | |\n" + " | int\n" + " char *\n", + pp_formatted_text (dc.printer)); +} + +/* Run all of the selftests within this file. */ + +void +c_format_c_tests () +{ + test_get_modifier_for_format_len (); + test_get_format_for_type_printf (); + test_get_format_for_type_scanf (); + test_type_mismatch_range_labels (); +} + +} // namespace selftest + +#endif /* CHECKING_P */ + +// include "gt-c-family-c-format.h" + +static const struct attribute_spec frr_format_attribute_table[] = +{ + /* { name, min_len, max_len, decl_req, type_req, fn_type_req, + affects_type_identity, handler, exclude } */ + { "frr_format", 3, 3, false, true, true, false, + handle_frr_format_attribute, NULL }, + { "frr_format_arg", 1, 1, false, true, true, false, + handle_frr_format_arg_attribute, NULL }, + { NULL, 0, 0, false, false, false, false, NULL, NULL } +}; + +static void +register_attributes (void *event_data, void *data) +{ + // warning (0, G_("Callback to register attributes")); + register_attribute (frr_format_attribute_table); +} + +tree +cb_walk_tree_fn (tree * tp, int * walk_subtrees, void * data ATTRIBUTE_UNUSED) +{ + if (TREE_CODE (*tp) != CALL_EXPR) + return NULL_TREE; + + tree call_expr = *tp; + + int nargs = call_expr_nargs(call_expr); + tree fn = CALL_EXPR_FN(call_expr); + + if (!fn || TREE_CODE (fn) != ADDR_EXPR) + return NULL_TREE; + + tree fndecl = TREE_OPERAND (fn, 0); + if (TREE_CODE (fndecl) != FUNCTION_DECL) + return NULL_TREE; + +#if 0 + warning (0, G_("function call to %s, %d args"), + IDENTIFIER_POINTER (DECL_NAME (fndecl)), + nargs); +#endif + + tree *fargs = (tree *) alloca (nargs * sizeof (tree)); + + for (int j = 0; j < nargs; j++) + { + tree arg = CALL_EXPR_ARG(call_expr, j); + + /* For -Wformat undo the implicit passing by hidden reference + done by convert_arg_to_ellipsis. */ + if (TREE_CODE (arg) == ADDR_EXPR + && TREE_CODE (TREE_TYPE (arg)) == REFERENCE_TYPE) + fargs[j] = TREE_OPERAND (arg, 0); + else + fargs[j] = arg; + } + + check_function_format (TYPE_ATTRIBUTES (TREE_TYPE (fndecl)), nargs, fargs, NULL); + return NULL_TREE; +} + +static void +setup_type (const char *name, tree *dst) +{ + tree tmp; + + if (*dst && *dst != void_type_node) + return; + + *dst = maybe_get_identifier (name); + if (!*dst) + return; + + tmp = identifier_global_value (*dst); + if (tmp && TREE_CODE (tmp) != TYPE_DECL) + { + warning (0, "%<%s%> is not defined as a type", name); + *dst = NULL; + return; + } + if (tmp && TREE_CODE (tmp) == TYPE_DECL) + *dst = tmp; + else + *dst = NULL; +} + +static void +handle_finish_parse (void *event_data, void *data) +{ + tree fndecl = (tree) event_data; + gcc_assert (TREE_CODE (fndecl) == FUNCTION_DECL); + + setup_type ("uint64_t", &local_uint64_t_node); + setup_type ("int64_t", &local_int64_t_node); + + setup_type ("size_t", &local_size_t_node); + setup_type ("ssize_t", &local_ssize_t_node); + setup_type ("atomic_size_t", &local_atomic_size_t_node); + setup_type ("atomic_ssize_t", &local_atomic_ssize_t_node); + setup_type ("ptrdiff_t", &local_ptrdiff_t_node); + + setup_type ("pid_t", &local_pid_t_node); + setup_type ("uid_t", &local_uid_t_node); + setup_type ("gid_t", &local_gid_t_node); + setup_type ("time_t", &local_time_t_node); + + setup_type ("socklen_t", &local_socklen_t_node); + setup_type ("in_addr_t", &local_in_addr_t_node); + + const format_char_info *fci; + + for (fci = print_char_table; fci->format_chars; fci++) + { + if (!fci->kernel_ext) + continue; + + struct kernel_ext_fmt *etab = fci->kernel_ext; + struct kernel_ext_fmt *etab_end = etab + ETAB_SZ; + + for (; etab->suffix && etab < etab_end; etab++) + { + tree identifier, node; + + if (etab->type && etab->type != void_type_node) + continue; + + identifier = maybe_get_identifier (etab->type_str); + + if (!identifier || identifier == error_mark_node) + continue; + + if (etab->type_code) + { + node = identifier_global_tag (identifier); + if (!node) + continue; + + if (node->base.code != etab->type_code) + { + if (!etab->warned) + { + warning (0, "%qs tag category (struct/union/enum) mismatch", etab->type_str); + etab->warned = true; + } + continue; + } + } + else + { + node = identifier_global_value (identifier); + if (!node) + continue; + + if (TREE_CODE (node) != TYPE_DECL) + { + if (!etab->warned) + { + warning (0, "%qs is defined as a non-type", etab->type_str); + etab->warned = true; + } + continue; + } + node = TREE_TYPE (node); + } + + etab->type = node; + } + } + + walk_tree (&DECL_SAVED_TREE (fndecl), cb_walk_tree_fn, NULL, NULL); +} + +static void +handle_pragma_printfrr_ext (cpp_reader *dummy) +{ + tree token = 0; + location_t loc; + enum cpp_ttype ttype; + + ttype = pragma_lex (&token, &loc); + if (ttype != CPP_STRING) + { + error_at (loc, "%<#pragma FRR printfrr_ext%> requires string argument"); + return; + } + + const char *s = TREE_STRING_POINTER (token); + + if (s[0] != '%') + { + error_at (loc, "%<#pragma FRR printfrr_ext%>: invalid format string, needs to start with '%%'"); + return; + } + + switch (s[1]) + { + case 'p': + case 'd': + case 'i': + break; + default: + error_at (loc, "%<#pragma FRR printfrr_ext%>: invalid format string, needs to be %%p, %%d or %%i"); + return; + } + + const format_char_info *fci; + + for (fci = print_char_table; fci->format_chars; fci++) + if (strchr (fci->format_chars, s[1])) + break; + + gcc_assert (fci->format_chars); + gcc_assert (fci->kernel_ext); + + struct kernel_ext_fmt *etab = fci->kernel_ext; + struct kernel_ext_fmt *etab_end = etab + ETAB_SZ; + + switch (s[2]) + { + case 'A' ... 'Z': + break; + + default: + error_at (loc, "%<#pragma FRR printfrr_ext%>: invalid format string, suffix must start with an uppercase letter"); + return; + } + + /* -2 -- need to keep the sentinel at the end */ + if (etab[ETAB_SZ - 2].suffix) + { + error_at (loc, "%<#pragma FRR printfrr_ext%>: out of space for format suffixes"); + return; + } + + for (; etab->suffix && etab < etab_end; etab++) + { + if (!strcmp(s + 2, etab->suffix)) + { + memmove (etab + 1, etab, (etab_end - etab - 1) * sizeof (*etab)); + + if (0) + { + warning_at (loc, OPT_Wformat_, + "%<#pragma FRR printfrr_ext%>: duplicate printf format suffix \"%s\"", s); + warning_at (etab->origin_loc, OPT_Wformat_, + "%<#pragma FRR printfrr_ext%>: previous definition was here"); + return; + } + + break; + } + + if (!strncmp(s + 2, etab->suffix, MIN(strlen(s + 2), strlen(etab->suffix)))) + { + warning_at (loc, OPT_Wformat_, + "%<#pragma FRR printfrr_ext%>: overlapping printf format suffix \"%s\"", s); + warning_at (etab->origin_loc, OPT_Wformat_, + "%<#pragma FRR printfrr_ext%>: previous definition for \"%%%c%s\" was here", s[1], etab->suffix); + return; + } + } + + gcc_assert (etab < etab_end); + + memset (etab, 0, sizeof (*etab)); + etab->suffix = xstrdup(s + 2); + etab->origin_loc = loc; + etab->type = void_type_node; + + ttype = pragma_lex (&token, &loc); + if (ttype != CPP_OPEN_PAREN) + { + error_at (loc, "%<#pragma FRR printfrr_ext%> expected %<(%>"); + goto out_drop; + } + + ttype = pragma_lex (&token, &loc); + + /* qualifiers */ + if (ttype == CPP_NAME && !strcmp (IDENTIFIER_POINTER (token), "const")) + { + etab->t_const = true; + ttype = pragma_lex (&token, &loc); + } + + /* tagged types */ + if (ttype == CPP_NAME && !strcmp (IDENTIFIER_POINTER (token), "struct")) + { + etab->type_code = RECORD_TYPE; + ttype = pragma_lex (&token, &loc); + } + else if (ttype == CPP_NAME && !strcmp (IDENTIFIER_POINTER (token), "union")) + { + etab->type_code = UNION_TYPE; + ttype = pragma_lex (&token, &loc); + } + else if (ttype == CPP_NAME && !strcmp (IDENTIFIER_POINTER (token), "enum")) + { + etab->type_code = ENUMERAL_TYPE; + ttype = pragma_lex (&token, &loc); + } + + /* type name */ + if (ttype != CPP_NAME) + { + error_at (loc, "%<#pragma FRR printfrr_ext%>: expected typename identifier"); + goto out_drop; + } + + etab->type_str = xstrdup (IDENTIFIER_POINTER (token)); + + while ((ttype = pragma_lex (&token, &loc)) != CPP_CLOSE_PAREN) + { + switch (ttype) { + case CPP_NAME: + error_at (loc, "%<#pragma FRR printfrr_ext%>: unexpected identifier. Note the only supported qualifier is \"const\"."); + goto out_drop; + + case CPP_MULT: + etab->ptrlevel++; + break; + + case CPP_EOF: + error_at (loc, "%<#pragma FRR printfrr_ext%>: premature end of line, missing %<)%>"); + goto out_drop; + + default: + error_at (loc, "%<#pragma FRR printfrr_ext%>: unsupported token"); + goto out_drop; + } + } + + ttype = pragma_lex (&token, &loc); + if (ttype != CPP_EOF) + warning_at (loc, OPT_Wformat_, + "%<#pragma FRR printfrr_ext%>: garbage at end of line"); + + return; + +out_drop: + memset (etab, 0, sizeof (*etab)); +} + +static void +register_pragma_printfrr_ext (void *event_data, void *data) +{ + c_register_pragma_with_expansion ("FRR", "printfrr_ext", handle_pragma_printfrr_ext); +} + +static void +define_vars (void *gcc_data, void *user_data) +{ + cpp_define (parse_in, "_FRR_ATTRIBUTE_PRINTFRR=0x10000"); +} + +#ifndef __visible +#define __visible __attribute__((visibility("default"))) +#endif + +__visible int plugin_is_GPL_compatible; + +__visible int +plugin_init (struct plugin_name_args *plugin_info, + struct plugin_gcc_version *version) +{ + const char *plugin_name = plugin_info->base_name; + + if (!plugin_default_version_check(version, &gcc_version)) + { + error(G_("incompatible gcc/plugin versions")); + return 1; + } + + memset (ext_p, 0, sizeof (ext_p)); + memset (ext_d, 0, sizeof (ext_d)); + + register_callback (plugin_name, PLUGIN_FINISH_PARSE_FUNCTION, handle_finish_parse, NULL); + register_callback (plugin_name, PLUGIN_ATTRIBUTES, register_attributes, NULL); + register_callback (plugin_name, PLUGIN_START_UNIT, define_vars, NULL); + register_callback (plugin_name, PLUGIN_PRAGMAS, register_pragma_printfrr_ext, NULL); + return 0; +} diff --git a/tools/gcc-plugins/frr-format.h b/tools/gcc-plugins/frr-format.h new file mode 100644 index 0000000000..87d2049ed4 --- /dev/null +++ b/tools/gcc-plugins/frr-format.h @@ -0,0 +1,364 @@ +/* Check calls to formatted I/O functions (-Wformat). + Copyright (C) 1992-2018 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC 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 3, or (at your option) any later +version. + +GCC 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 GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#ifndef GCC_C_FORMAT_H +#define GCC_C_FORMAT_H + +/* The meaningfully distinct length modifiers for format checking recognized + by GCC. */ +enum format_lengths +{ + FMT_LEN_none, + FMT_LEN_hh, + FMT_LEN_h, + FMT_LEN_l, + FMT_LEN_ll, + FMT_LEN_L, + FMT_LEN_z, + FMT_LEN_t, + FMT_LEN_j, + FMT_LEN_H, + FMT_LEN_D, + FMT_LEN_DD, + FMT_LEN_MAX +}; + + +/* The standard versions in which various format features appeared. */ +enum format_std_version +{ + STD_C89, + STD_C94, + STD_C9L, /* C99, but treat as C89 if -Wno-long-long. */ + STD_C99, + STD_EXT +}; + +/* Flags that may apply to a particular kind of format checked by GCC. */ +enum +{ + /* This format converts arguments of types determined by the + format string. */ + FMT_FLAG_ARG_CONVERT = 1, + /* The scanf allocation 'a' kludge applies to this format kind. */ + FMT_FLAG_SCANF_A_KLUDGE = 2, + /* A % during parsing a specifier is allowed to be a modified % rather + that indicating the format is broken and we are out-of-sync. */ + FMT_FLAG_FANCY_PERCENT_OK = 4, + /* With $ operand numbers, it is OK to reference the same argument more + than once. */ + FMT_FLAG_DOLLAR_MULTIPLE = 8, + /* This format type uses $ operand numbers (strfmon doesn't). */ + FMT_FLAG_USE_DOLLAR = 16, + /* Zero width is bad in this type of format (scanf). */ + FMT_FLAG_ZERO_WIDTH_BAD = 32, + /* Empty precision specification is OK in this type of format (printf). */ + FMT_FLAG_EMPTY_PREC_OK = 64, + /* Gaps are allowed in the arguments with $ operand numbers if all + arguments are pointers (scanf). */ + FMT_FLAG_DOLLAR_GAP_POINTER_OK = 128, + /* The format arg is an opaque object that will be parsed by an external + facility. */ + FMT_FLAG_PARSE_ARG_CONVERT_EXTERNAL = 256 + /* Not included here: details of whether width or precision may occur + (controlled by width_char and precision_char); details of whether + '*' can be used for these (width_type and precision_type); details + of whether length modifiers can occur (length_char_specs). */ +}; + +/* Structure describing a length modifier supported in format checking, and + possibly a doubled version such as "hh". */ +struct format_length_info +{ + /* Name of the single-character length modifier. If prefixed by + a zero character, it describes a multi character length + modifier, like I64, I32, etc. */ + const char *name; + /* Index into a format_char_info.types array. */ + enum format_lengths index; + /* Standard version this length appears in. */ + enum format_std_version std; + /* Same, if the modifier can be repeated, or NULL if it can't. */ + const char *double_name; + enum format_lengths double_index; + enum format_std_version double_std; + + /* If this flag is set, just scalar width identity is checked, and + not the type identity itself. */ + int scalar_identity_flag; +}; + + +struct kernel_ext_fmt +{ + const char *suffix; + + /* RECORD_TYPE, UNION_TYPE, ENUMERAL_TYPE, or NULL for typedef */ + tree_code type_code; + int ptrlevel; + bool t_const; + bool warned; + + const char *type_str; + GTY(()) tree type; + + location_t origin_loc; +}; + + +/* Structure describing the combination of a conversion specifier + (or a set of specifiers which act identically) and a length modifier. */ +struct format_type_detail +{ + /* The standard version this combination of length and type appeared in. + This is only relevant if greater than those for length and type + individually; otherwise it is ignored. */ + enum format_std_version std; + /* The name to use for the type, if different from that generated internally + (e.g., "signed size_t"). */ + const char *name; + /* The type itself. */ + tree *type; +}; + + +/* Macros to fill out tables of these. */ +#define NOARGUMENTS { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN } +#define BADLEN { STD_C89, NULL, NULL } +#define NOLENGTHS { BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN } + + +/* Structure describing a format conversion specifier (or a set of specifiers + which act identically), and the length modifiers used with it. */ +struct format_char_info +{ + const char *format_chars; + int pointer_count; + enum format_std_version std; + /* Types accepted for each length modifier. */ + format_type_detail types[FMT_LEN_MAX]; + /* List of other modifier characters allowed with these specifiers. + This lists flags, and additionally "w" for width, "p" for precision + (right precision, for strfmon), "#" for left precision (strfmon), + "a" for scanf "a" allocation extension (not applicable in C99 mode), + "*" for scanf suppression, and "E" and "O" for those strftime + modifiers. */ + const char *flag_chars; + /* List of additional flags describing these conversion specifiers. + "c" for generic character pointers being allowed, "2" for strftime + two digit year formats, "3" for strftime formats giving two digit + years in some locales, "4" for "2" which becomes "3" with an "E" modifier, + "o" if use of strftime "O" is a GNU extension beyond C99, + "W" if the argument is a pointer which is dereferenced and written into, + "R" if the argument is a pointer which is dereferenced and read from, + "i" for printf integer formats where the '0' flag is ignored with + precision, and "[" for the starting character of a scanf scanset, + "<" if the specifier introduces a quoted sequence (such as "%<"), + ">" if the specifier terminates a quoted sequence (such as "%>"), + "[" if the specifier introduces a color sequence (such as "%r"), + "]" if the specifier terminates a color sequence (such as "%R"), + "'" (single quote) if the specifier is expected to be quoted when + it appears outside a quoted sequence and unquoted otherwise (such + as the GCC internal printf format directive "%T"), and + "\"" (double quote) if the specifier is not expected to appear in + a quoted sequence (such as the GCC internal format directive "%K". */ + const char *flags2; + /* If this format conversion character consumes more than one argument, + CHAIN points to information about the next argument. For later + arguments, only POINTER_COUNT, TYPES, and the "c", "R", and "W" flags + in FLAGS2 are used. */ + const struct format_char_info *chain; + + struct kernel_ext_fmt *kernel_ext; +}; + + +/* Structure describing a flag accepted by some kind of format. */ +struct format_flag_spec +{ + /* The flag character in question (0 for end of array). */ + int flag_char; + /* Zero if this entry describes the flag character in general, or a + nonzero character that may be found in flags2 if it describes the + flag when used with certain formats only. If the latter, only + the first such entry found that applies to the current conversion + specifier is used; the values of 'name' and 'long_name' it supplies + will be used, if non-NULL and the standard version is higher than + the unpredicated one, for any pedantic warning. For example, 'o' + for strftime formats (meaning 'O' is an extension over C99). */ + int predicate; + /* Nonzero if the next character after this flag in the format should + be skipped ('=' in strfmon), zero otherwise. */ + int skip_next_char; + /* True if the flag introduces quoting (as in GCC's %qE). */ + bool quoting; + /* The name to use for this flag in diagnostic messages. For example, + N_("'0' flag"), N_("field width"). */ + const char *name; + /* Long name for this flag in diagnostic messages; currently only used for + "ISO C does not support ...". For example, N_("the 'I' printf flag"). */ + const char *long_name; + /* The standard version in which it appeared. */ + enum format_std_version std; +}; + + +/* Structure describing a combination of flags that is bad for some kind + of format. */ +struct format_flag_pair +{ + /* The first flag character in question (0 for end of array). */ + int flag_char1; + /* The second flag character. */ + int flag_char2; + /* Nonzero if the message should say that the first flag is ignored with + the second, zero if the combination should simply be objected to. */ + int ignored; + /* Zero if this entry applies whenever this flag combination occurs, + a nonzero character from flags2 if it only applies in some + circumstances (e.g. 'i' for printf formats ignoring 0 with precision). */ + int predicate; +}; + + +/* Structure describing a particular kind of format processed by GCC. */ +struct format_kind_info +{ + /* The name of this kind of format, for use in diagnostics. Also + the name of the attribute (without preceding and following __). */ + const char *name; + /* Specifications of the length modifiers accepted; possibly NULL. */ + const format_length_info *length_char_specs; + /* Details of the conversion specification characters accepted. */ + const format_char_info *conversion_specs; + /* String listing the flag characters that are accepted. */ + const char *flag_chars; + /* String listing modifier characters (strftime) accepted. May be NULL. */ + const char *modifier_chars; + /* Details of the flag characters, including pseudo-flags. */ + const format_flag_spec *flag_specs; + /* Details of bad combinations of flags. */ + const format_flag_pair *bad_flag_pairs; + /* Flags applicable to this kind of format. */ + int flags; + /* Flag character to treat a width as, or 0 if width not used. */ + int width_char; + /* Flag character to treat a left precision (strfmon) as, + or 0 if left precision not used. */ + int left_precision_char; + /* Flag character to treat a precision (for strfmon, right precision) as, + or 0 if precision not used. */ + int precision_char; + /* If a flag character has the effect of suppressing the conversion of + an argument ('*' in scanf), that flag character, otherwise 0. */ + int suppression_char; + /* Flag character to treat a length modifier as (ignored if length + modifiers not used). Need not be placed in flag_chars for conversion + specifiers, but is used to check for bad combinations such as length + modifier with assignment suppression in scanf. */ + int length_code_char; + /* Assignment-allocation flag character ('m' in scanf), otherwise 0. */ + int alloc_char; + /* Pointer to type of argument expected if '*' is used for a width, + or NULL if '*' not used for widths. */ + tree *width_type; + /* Pointer to type of argument expected if '*' is used for a precision, + or NULL if '*' not used for precisions. */ + tree *precision_type; +}; + +#define T_I &integer_type_node +#define T89_I { STD_C89, NULL, T_I } +#define T_L &long_integer_type_node +#define T89_L { STD_C89, NULL, T_L } +#define T_LL &long_long_integer_type_node +#define T9L_LL { STD_C9L, NULL, T_LL } +#define TEX_LL { STD_EXT, NULL, T_LL } +#define T_U64 &local_uint64_t_node +#define TEX_U64 { STD_EXT, "uint64_t", T_U64 } +#define T_S64 &local_int64_t_node +#define TEX_S64 { STD_EXT, "int64_t", T_S64 } +#define T_S &short_integer_type_node +#define T89_S { STD_C89, NULL, T_S } +#define T_UI &unsigned_type_node +#define T89_UI { STD_C89, NULL, T_UI } +#define T_UL &long_unsigned_type_node +#define T89_UL { STD_C89, NULL, T_UL } +#define T_ULL &long_long_unsigned_type_node +#define T9L_ULL { STD_C9L, NULL, T_ULL } +#define TEX_ULL { STD_EXT, NULL, T_ULL } +#define T_US &short_unsigned_type_node +#define T89_US { STD_C89, NULL, T_US } +#define T_F &float_type_node +#define T89_F { STD_C89, NULL, T_F } +#define T99_F { STD_C99, NULL, T_F } +#define T_D &double_type_node +#define T89_D { STD_C89, NULL, T_D } +#define T99_D { STD_C99, NULL, T_D } +#define T_LD &long_double_type_node +#define T89_LD { STD_C89, NULL, T_LD } +#define T99_LD { STD_C99, NULL, T_LD } +#define T_C &char_type_node +#define T89_C { STD_C89, NULL, T_C } +#define T_SC &signed_char_type_node +#define T99_SC { STD_C99, NULL, T_SC } +#define T_UC &unsigned_char_type_node +#define T99_UC { STD_C99, NULL, T_UC } +#define T_V &void_type_node +#define T89_G { STD_C89, NULL, &local_gimple_ptr_node } +#define T89_T { STD_C89, NULL, &local_tree_type_node } +#define T89_V { STD_C89, NULL, T_V } +#define T_W &wchar_type_node +#define T94_W { STD_C94, "wchar_t", T_W } +#define TEX_W { STD_EXT, "wchar_t", T_W } +#define T_WI &wint_type_node +#define T94_WI { STD_C94, "wint_t", T_WI } +#define TEX_WI { STD_EXT, "wint_t", T_WI } +#define T_ST &local_size_t_node +#define T99_ST { STD_C99, "size_t", T_ST } +#define T_SST &local_ssize_t_node +#define T99_SST { STD_C99, "ssize_t", T_SST } +#define T_PD &ptrdiff_type_node +#define T99_PD { STD_C99, "ptrdiff_t", T_PD } +#define T_UPD &unsigned_ptrdiff_type_node +#define T99_UPD { STD_C99, "unsigned ptrdiff_t", T_UPD } +#define T_IM &intmax_type_node +#define T99_IM { STD_C99, "intmax_t", T_IM } +#define T_UIM &uintmax_type_node +#define T99_UIM { STD_C99, "uintmax_t", T_UIM } +#define T_D32 &dfloat32_type_node +#define TEX_D32 { STD_EXT, "_Decimal32", T_D32 } +#define T_D64 &dfloat64_type_node +#define TEX_D64 { STD_EXT, "_Decimal64", T_D64 } +#define T_D128 &dfloat128_type_node +#define TEX_D128 { STD_EXT, "_Decimal128", T_D128 } + +/* Structure describing how format attributes such as "printf" are + interpreted as "gnu_printf" or "ms_printf" on a particular system. + TARGET_OVERRIDES_FORMAT_ATTRIBUTES is used to specify target-specific + defaults. */ +struct target_ovr_attr +{ + /* The name of the to be copied format attribute. */ + const char *named_attr_src; + /* The name of the to be overridden format attribute. */ + const char *named_attr_dst; +}; + +#endif /* GCC_C_FORMAT_H */ diff --git a/tools/gcc-plugins/gcc-common.h b/tools/gcc-plugins/gcc-common.h new file mode 100644 index 0000000000..6b6c17231a --- /dev/null +++ b/tools/gcc-plugins/gcc-common.h @@ -0,0 +1,981 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* FRR: imported from Linux kernel on 2019-07-29 */ + +#ifndef GCC_COMMON_H_INCLUDED +#define GCC_COMMON_H_INCLUDED + +#include "bversion.h" +#if BUILDING_GCC_VERSION >= 6000 +#include "gcc-plugin.h" +#else +#include "plugin.h" +#endif +#include "plugin-version.h" +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "line-map.h" +#include "input.h" +#include "tree.h" + +#include "tree-inline.h" +#include "version.h" +#include "rtl.h" +#include "tm_p.h" +#include "flags.h" +#include "hard-reg-set.h" +#include "output.h" +#include "except.h" +#include "function.h" +#include "toplev.h" +#if BUILDING_GCC_VERSION >= 5000 +#include "expr.h" +#endif +#include "basic-block.h" +#include "intl.h" +#include "ggc.h" +#include "timevar.h" + +#include "params.h" + +#if BUILDING_GCC_VERSION <= 4009 +#include "pointer-set.h" +#else +#include "hash-map.h" +#endif + +#if BUILDING_GCC_VERSION >= 7000 +#include "memmodel.h" +#endif +#include "emit-rtl.h" +#include "debug.h" +#include "target.h" +#include "langhooks.h" +#include "cfgloop.h" +#include "cgraph.h" +#include "opts.h" + +#if BUILDING_GCC_VERSION == 4005 +#include <sys/mman.h> +#endif + +#if BUILDING_GCC_VERSION >= 4007 +#include "tree-pretty-print.h" +#include "gimple-pretty-print.h" +#endif + +#if BUILDING_GCC_VERSION >= 4006 +/* + * The c-family headers were moved into a subdirectory in GCC version + * 4.7, but most plugin-building users of GCC 4.6 are using the Debian + * or Ubuntu package, which has an out-of-tree patch to move this to the + * same location as found in 4.7 and later: + * https://sources.debian.net/src/gcc-4.6/4.6.3-14/debian/patches/pr45078.diff/ + */ +#include "c-family/c-common.h" +#else +#include "c-common.h" +#endif + +#if BUILDING_GCC_VERSION <= 4008 +#include "tree-flow.h" +#else +#include "tree-cfgcleanup.h" +#include "tree-ssa-operands.h" +#include "tree-into-ssa.h" +#endif + +#if BUILDING_GCC_VERSION >= 4008 +#include "is-a.h" +#endif + +#include "diagnostic.h" +#include "tree-dump.h" +#include "tree-pass.h" +#if BUILDING_GCC_VERSION >= 4009 +#include "pass_manager.h" +#endif +#include "predict.h" +#include "ipa-utils.h" + +#if BUILDING_GCC_VERSION >= 8000 +#include "stringpool.h" +#endif + +#if BUILDING_GCC_VERSION >= 4009 +#include "attribs.h" +#include "varasm.h" +#include "stor-layout.h" +#include "internal-fn.h" +#include "gimple-expr.h" +#include "gimple-fold.h" +#include "context.h" +#include "tree-ssa-alias.h" +#include "tree-ssa.h" +#include "stringpool.h" +#if BUILDING_GCC_VERSION >= 7000 +#include "tree-vrp.h" +#endif +#include "tree-ssanames.h" +#include "print-tree.h" +#include "tree-eh.h" +#include "stmt.h" +#include "gimplify.h" +#endif + +#include "gimple.h" + +#if BUILDING_GCC_VERSION >= 4009 +#include "tree-ssa-operands.h" +#include "tree-phinodes.h" +#include "tree-cfg.h" +#include "gimple-iterator.h" +#include "gimple-ssa.h" +#include "ssa-iterators.h" +#endif + +#if BUILDING_GCC_VERSION >= 5000 +#include "builtins.h" +#endif + +/* missing from basic_block.h... */ +void debug_dominance_info(enum cdi_direction dir); +void debug_dominance_tree(enum cdi_direction dir, basic_block root); + +#if BUILDING_GCC_VERSION == 4006 +void debug_gimple_stmt(gimple); +void debug_gimple_seq(gimple_seq); +void print_gimple_seq(FILE *, gimple_seq, int, int); +void print_gimple_stmt(FILE *, gimple, int, int); +void print_gimple_expr(FILE *, gimple, int, int); +void dump_gimple_stmt(pretty_printer *, gimple, int, int); +#endif + +#ifndef __unused +#define __unused __attribute__((__unused__)) +#endif +#ifndef __visible +#define __visible __attribute__((visibility("default"))) +#endif + +#define DECL_NAME_POINTER(node) IDENTIFIER_POINTER(DECL_NAME(node)) +#define DECL_NAME_LENGTH(node) IDENTIFIER_LENGTH(DECL_NAME(node)) +#define TYPE_NAME_POINTER(node) IDENTIFIER_POINTER(TYPE_NAME(node)) +#define TYPE_NAME_LENGTH(node) IDENTIFIER_LENGTH(TYPE_NAME(node)) + +#if BUILDING_GCC_VERSION < 9000 +/* should come from c-tree.h if only it were installed for gcc 4.5... */ +#define C_TYPE_FIELDS_READONLY(TYPE) TREE_LANG_FLAG_1(TYPE) +#endif + +static inline tree build_const_char_string(int len, const char *str) +{ + tree cstr, elem, index, type; + + cstr = build_string(len, str); + elem = build_type_variant(char_type_node, 1, 0); + index = build_index_type(size_int(len - 1)); + type = build_array_type(elem, index); + TREE_TYPE(cstr) = type; + TREE_CONSTANT(cstr) = 1; + TREE_READONLY(cstr) = 1; + TREE_STATIC(cstr) = 1; + return cstr; +} + +#define PASS_INFO(NAME, REF, ID, POS) \ +struct register_pass_info NAME##_pass_info = { \ + .pass = make_##NAME##_pass(), \ + .reference_pass_name = REF, \ + .ref_pass_instance_number = ID, \ + .pos_op = POS, \ +} + +#if BUILDING_GCC_VERSION == 4005 +#define FOR_EACH_LOCAL_DECL(FUN, I, D) \ + for (tree vars = (FUN)->local_decls, (I) = 0; \ + vars && ((D) = TREE_VALUE(vars)); \ + vars = TREE_CHAIN(vars), (I)++) +#define DECL_CHAIN(NODE) (TREE_CHAIN(DECL_MINIMAL_CHECK(NODE))) +#define FOR_EACH_VEC_ELT(T, V, I, P) \ + for (I = 0; VEC_iterate(T, (V), (I), (P)); ++(I)) +#define TODO_rebuild_cgraph_edges 0 +#define SCOPE_FILE_SCOPE_P(EXP) (!(EXP)) + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +typedef struct varpool_node *varpool_node_ptr; + +static inline bool gimple_call_builtin_p(gimple stmt, enum built_in_function code) +{ + tree fndecl; + + if (!is_gimple_call(stmt)) + return false; + fndecl = gimple_call_fndecl(stmt); + if (!fndecl || DECL_BUILT_IN_CLASS(fndecl) != BUILT_IN_NORMAL) + return false; + return DECL_FUNCTION_CODE(fndecl) == code; +} + +static inline bool is_simple_builtin(tree decl) +{ + if (decl && DECL_BUILT_IN_CLASS(decl) != BUILT_IN_NORMAL) + return false; + + switch (DECL_FUNCTION_CODE(decl)) { + /* Builtins that expand to constants. */ + case BUILT_IN_CONSTANT_P: + case BUILT_IN_EXPECT: + case BUILT_IN_OBJECT_SIZE: + case BUILT_IN_UNREACHABLE: + /* Simple register moves or loads from stack. */ + case BUILT_IN_RETURN_ADDRESS: + case BUILT_IN_EXTRACT_RETURN_ADDR: + case BUILT_IN_FROB_RETURN_ADDR: + case BUILT_IN_RETURN: + case BUILT_IN_AGGREGATE_INCOMING_ADDRESS: + case BUILT_IN_FRAME_ADDRESS: + case BUILT_IN_VA_END: + case BUILT_IN_STACK_SAVE: + case BUILT_IN_STACK_RESTORE: + /* Exception state returns or moves registers around. */ + case BUILT_IN_EH_FILTER: + case BUILT_IN_EH_POINTER: + case BUILT_IN_EH_COPY_VALUES: + return true; + + default: + return false; + } +} + +static inline void add_local_decl(struct function *fun, tree d) +{ + gcc_assert(TREE_CODE(d) == VAR_DECL); + fun->local_decls = tree_cons(NULL_TREE, d, fun->local_decls); +} +#endif + +#if BUILDING_GCC_VERSION <= 4006 +#define ANY_RETURN_P(rtx) (GET_CODE(rtx) == RETURN) +#define C_DECL_REGISTER(EXP) DECL_LANG_FLAG_4(EXP) +#define EDGE_PRESERVE 0ULL +#define HOST_WIDE_INT_PRINT_HEX_PURE "%" HOST_WIDE_INT_PRINT "x" +#define flag_fat_lto_objects true + +#define get_random_seed(noinit) ({ \ + unsigned HOST_WIDE_INT seed; \ + sscanf(get_random_seed(noinit), "%" HOST_WIDE_INT_PRINT "x", &seed); \ + seed * seed; }) + +#define int_const_binop(code, arg1, arg2) \ + int_const_binop((code), (arg1), (arg2), 0) + +static inline bool gimple_clobber_p(gimple s __unused) +{ + return false; +} + +static inline bool gimple_asm_clobbers_memory_p(const_gimple stmt) +{ + unsigned i; + + for (i = 0; i < gimple_asm_nclobbers(stmt); i++) { + tree op = gimple_asm_clobber_op(stmt, i); + + if (!strcmp(TREE_STRING_POINTER(TREE_VALUE(op)), "memory")) + return true; + } + + return false; +} + +static inline tree builtin_decl_implicit(enum built_in_function fncode) +{ + return implicit_built_in_decls[fncode]; +} + +static inline int ipa_reverse_postorder(struct cgraph_node **order) +{ + return cgraph_postorder(order); +} + +static inline struct cgraph_node *cgraph_create_node(tree decl) +{ + return cgraph_node(decl); +} + +static inline struct cgraph_node *cgraph_get_create_node(tree decl) +{ + struct cgraph_node *node = cgraph_get_node(decl); + + return node ? node : cgraph_node(decl); +} + +static inline bool cgraph_function_with_gimple_body_p(struct cgraph_node *node) +{ + return node->analyzed && !node->thunk.thunk_p && !node->alias; +} + +static inline struct cgraph_node *cgraph_first_function_with_gimple_body(void) +{ + struct cgraph_node *node; + + for (node = cgraph_nodes; node; node = node->next) + if (cgraph_function_with_gimple_body_p(node)) + return node; + return NULL; +} + +static inline struct cgraph_node *cgraph_next_function_with_gimple_body(struct cgraph_node *node) +{ + for (node = node->next; node; node = node->next) + if (cgraph_function_with_gimple_body_p(node)) + return node; + return NULL; +} + +static inline bool cgraph_for_node_and_aliases(cgraph_node_ptr node, bool (*callback)(cgraph_node_ptr, void *), void *data, bool include_overwritable) +{ + cgraph_node_ptr alias; + + if (callback(node, data)) + return true; + + for (alias = node->same_body; alias; alias = alias->next) { + if (include_overwritable || cgraph_function_body_availability(alias) > AVAIL_OVERWRITABLE) + if (cgraph_for_node_and_aliases(alias, callback, data, include_overwritable)) + return true; + } + + return false; +} + +#define FOR_EACH_FUNCTION_WITH_GIMPLE_BODY(node) \ + for ((node) = cgraph_first_function_with_gimple_body(); (node); \ + (node) = cgraph_next_function_with_gimple_body(node)) + +static inline void varpool_add_new_variable(tree decl) +{ + varpool_finalize_decl(decl); +} +#endif + +#if BUILDING_GCC_VERSION <= 4007 +#define FOR_EACH_FUNCTION(node) \ + for (node = cgraph_nodes; node; node = node->next) +#define FOR_EACH_VARIABLE(node) \ + for (node = varpool_nodes; node; node = node->next) +#define PROP_loops 0 +#define NODE_SYMBOL(node) (node) +#define NODE_DECL(node) (node)->decl +#define INSN_LOCATION(INSN) RTL_LOCATION(INSN) +#define vNULL NULL + +static inline int bb_loop_depth(const_basic_block bb) +{ + return bb->loop_father ? loop_depth(bb->loop_father) : 0; +} + +static inline bool gimple_store_p(gimple gs) +{ + tree lhs = gimple_get_lhs(gs); + + return lhs && !is_gimple_reg(lhs); +} + +static inline void gimple_init_singleton(gimple g __unused) +{ +} +#endif + +#if BUILDING_GCC_VERSION == 4007 || BUILDING_GCC_VERSION == 4008 +static inline struct cgraph_node *cgraph_alias_target(struct cgraph_node *n) +{ + return cgraph_alias_aliased_node(n); +} +#endif + +#if BUILDING_GCC_VERSION <= 4008 +#define ENTRY_BLOCK_PTR_FOR_FN(FN) ENTRY_BLOCK_PTR_FOR_FUNCTION(FN) +#define EXIT_BLOCK_PTR_FOR_FN(FN) EXIT_BLOCK_PTR_FOR_FUNCTION(FN) +#define basic_block_info_for_fn(FN) ((FN)->cfg->x_basic_block_info) +#define n_basic_blocks_for_fn(FN) ((FN)->cfg->x_n_basic_blocks) +#define n_edges_for_fn(FN) ((FN)->cfg->x_n_edges) +#define last_basic_block_for_fn(FN) ((FN)->cfg->x_last_basic_block) +#define label_to_block_map_for_fn(FN) ((FN)->cfg->x_label_to_block_map) +#define profile_status_for_fn(FN) ((FN)->cfg->x_profile_status) +#define BASIC_BLOCK_FOR_FN(FN, N) BASIC_BLOCK_FOR_FUNCTION((FN), (N)) +#define NODE_IMPLICIT_ALIAS(node) (node)->same_body_alias +#define VAR_P(NODE) (TREE_CODE(NODE) == VAR_DECL) + +static inline bool tree_fits_shwi_p(const_tree t) +{ + if (t == NULL_TREE || TREE_CODE(t) != INTEGER_CST) + return false; + + if (TREE_INT_CST_HIGH(t) == 0 && (HOST_WIDE_INT)TREE_INT_CST_LOW(t) >= 0) + return true; + + if (TREE_INT_CST_HIGH(t) == -1 && (HOST_WIDE_INT)TREE_INT_CST_LOW(t) < 0 && !TYPE_UNSIGNED(TREE_TYPE(t))) + return true; + + return false; +} + +static inline bool tree_fits_uhwi_p(const_tree t) +{ + if (t == NULL_TREE || TREE_CODE(t) != INTEGER_CST) + return false; + + return TREE_INT_CST_HIGH(t) == 0; +} + +static inline HOST_WIDE_INT tree_to_shwi(const_tree t) +{ + gcc_assert(tree_fits_shwi_p(t)); + return TREE_INT_CST_LOW(t); +} + +static inline unsigned HOST_WIDE_INT tree_to_uhwi(const_tree t) +{ + gcc_assert(tree_fits_uhwi_p(t)); + return TREE_INT_CST_LOW(t); +} + +static inline const char *get_tree_code_name(enum tree_code code) +{ + gcc_assert(code < MAX_TREE_CODES); + return tree_code_name[code]; +} + +#define ipa_remove_stmt_references(cnode, stmt) + +typedef union gimple_statement_d gasm; +typedef union gimple_statement_d gassign; +typedef union gimple_statement_d gcall; +typedef union gimple_statement_d gcond; +typedef union gimple_statement_d gdebug; +typedef union gimple_statement_d ggoto; +typedef union gimple_statement_d gphi; +typedef union gimple_statement_d greturn; + +static inline gasm *as_a_gasm(gimple stmt) +{ + return stmt; +} + +static inline const gasm *as_a_const_gasm(const_gimple stmt) +{ + return stmt; +} + +static inline gassign *as_a_gassign(gimple stmt) +{ + return stmt; +} + +static inline const gassign *as_a_const_gassign(const_gimple stmt) +{ + return stmt; +} + +static inline gcall *as_a_gcall(gimple stmt) +{ + return stmt; +} + +static inline const gcall *as_a_const_gcall(const_gimple stmt) +{ + return stmt; +} + +static inline gcond *as_a_gcond(gimple stmt) +{ + return stmt; +} + +static inline const gcond *as_a_const_gcond(const_gimple stmt) +{ + return stmt; +} + +static inline gdebug *as_a_gdebug(gimple stmt) +{ + return stmt; +} + +static inline const gdebug *as_a_const_gdebug(const_gimple stmt) +{ + return stmt; +} + +static inline ggoto *as_a_ggoto(gimple stmt) +{ + return stmt; +} + +static inline const ggoto *as_a_const_ggoto(const_gimple stmt) +{ + return stmt; +} + +static inline gphi *as_a_gphi(gimple stmt) +{ + return stmt; +} + +static inline const gphi *as_a_const_gphi(const_gimple stmt) +{ + return stmt; +} + +static inline greturn *as_a_greturn(gimple stmt) +{ + return stmt; +} + +static inline const greturn *as_a_const_greturn(const_gimple stmt) +{ + return stmt; +} +#endif + +#if BUILDING_GCC_VERSION == 4008 +#define NODE_SYMBOL(node) (&(node)->symbol) +#define NODE_DECL(node) (node)->symbol.decl +#endif + +#if BUILDING_GCC_VERSION >= 4008 +#define add_referenced_var(var) +#define mark_sym_for_renaming(var) +#define varpool_mark_needed_node(node) +#define create_var_ann(var) +#define TODO_dump_func 0 +#define TODO_dump_cgraph 0 +#endif + +#if BUILDING_GCC_VERSION <= 4009 +#define TODO_verify_il 0 +#define AVAIL_INTERPOSABLE AVAIL_OVERWRITABLE + +#define section_name_prefix LTO_SECTION_NAME_PREFIX +#define fatal_error(loc, gmsgid, ...) fatal_error((gmsgid), __VA_ARGS__) + +rtx emit_move_insn(rtx x, rtx y); + +typedef struct rtx_def rtx_insn; + +static inline const char *get_decl_section_name(const_tree decl) +{ + if (DECL_SECTION_NAME(decl) == NULL_TREE) + return NULL; + + return TREE_STRING_POINTER(DECL_SECTION_NAME(decl)); +} + +static inline void set_decl_section_name(tree node, const char *value) +{ + if (value) + DECL_SECTION_NAME(node) = build_string(strlen(value) + 1, value); + else + DECL_SECTION_NAME(node) = NULL; +} +#endif + +#if BUILDING_GCC_VERSION == 4009 +typedef struct gimple_statement_asm gasm; +typedef struct gimple_statement_base gassign; +typedef struct gimple_statement_call gcall; +typedef struct gimple_statement_base gcond; +typedef struct gimple_statement_base gdebug; +typedef struct gimple_statement_base ggoto; +typedef struct gimple_statement_phi gphi; +typedef struct gimple_statement_base greturn; + +static inline gasm *as_a_gasm(gimple stmt) +{ + return as_a<gasm>(stmt); +} + +static inline const gasm *as_a_const_gasm(const_gimple stmt) +{ + return as_a<const gasm>(stmt); +} + +static inline gassign *as_a_gassign(gimple stmt) +{ + return stmt; +} + +static inline const gassign *as_a_const_gassign(const_gimple stmt) +{ + return stmt; +} + +static inline gcall *as_a_gcall(gimple stmt) +{ + return as_a<gcall>(stmt); +} + +static inline const gcall *as_a_const_gcall(const_gimple stmt) +{ + return as_a<const gcall>(stmt); +} + +static inline gcond *as_a_gcond(gimple stmt) +{ + return stmt; +} + +static inline const gcond *as_a_const_gcond(const_gimple stmt) +{ + return stmt; +} + +static inline gdebug *as_a_gdebug(gimple stmt) +{ + return stmt; +} + +static inline const gdebug *as_a_const_gdebug(const_gimple stmt) +{ + return stmt; +} + +static inline ggoto *as_a_ggoto(gimple stmt) +{ + return stmt; +} + +static inline const ggoto *as_a_const_ggoto(const_gimple stmt) +{ + return stmt; +} + +static inline gphi *as_a_gphi(gimple stmt) +{ + return as_a<gphi>(stmt); +} + +static inline const gphi *as_a_const_gphi(const_gimple stmt) +{ + return as_a<const gphi>(stmt); +} + +static inline greturn *as_a_greturn(gimple stmt) +{ + return stmt; +} + +static inline const greturn *as_a_const_greturn(const_gimple stmt) +{ + return stmt; +} +#endif + +#if BUILDING_GCC_VERSION >= 4009 +#define TODO_ggc_collect 0 +#define NODE_SYMBOL(node) (node) +#define NODE_DECL(node) (node)->decl +#define cgraph_node_name(node) (node)->name() +#define NODE_IMPLICIT_ALIAS(node) (node)->cpp_implicit_alias + +static inline opt_pass *get_pass_for_id(int id) +{ + return g->get_passes()->get_pass_for_id(id); +} +#endif + +#if BUILDING_GCC_VERSION >= 5000 && BUILDING_GCC_VERSION < 6000 +/* gimple related */ +template <> +template <> +inline bool is_a_helper<const gassign *>::test(const_gimple gs) +{ + return gs->code == GIMPLE_ASSIGN; +} +#endif + +#if BUILDING_GCC_VERSION >= 5000 +#define TODO_verify_ssa TODO_verify_il +#define TODO_verify_flow TODO_verify_il +#define TODO_verify_stmts TODO_verify_il +#define TODO_verify_rtl_sharing TODO_verify_il + +#define INSN_DELETED_P(insn) (insn)->deleted() + +static inline const char *get_decl_section_name(const_tree decl) +{ + return DECL_SECTION_NAME(decl); +} + +/* symtab/cgraph related */ +#define debug_cgraph_node(node) (node)->debug() +#define cgraph_get_node(decl) cgraph_node::get(decl) +#define cgraph_get_create_node(decl) cgraph_node::get_create(decl) +#define cgraph_create_node(decl) cgraph_node::create(decl) +#define cgraph_n_nodes symtab->cgraph_count +#define cgraph_max_uid symtab->cgraph_max_uid +#define varpool_get_node(decl) varpool_node::get(decl) +#define dump_varpool_node(file, node) (node)->dump(file) + +#if BUILDING_GCC_VERSION >= 8000 +#define cgraph_create_edge(caller, callee, call_stmt, count, freq) \ + (caller)->create_edge((callee), (call_stmt), (count)) + +#define cgraph_create_edge_including_clones(caller, callee, \ + old_call_stmt, call_stmt, count, freq, reason) \ + (caller)->create_edge_including_clones((callee), \ + (old_call_stmt), (call_stmt), (count), (reason)) +#else +#define cgraph_create_edge(caller, callee, call_stmt, count, freq) \ + (caller)->create_edge((callee), (call_stmt), (count), (freq)) + +#define cgraph_create_edge_including_clones(caller, callee, \ + old_call_stmt, call_stmt, count, freq, reason) \ + (caller)->create_edge_including_clones((callee), \ + (old_call_stmt), (call_stmt), (count), (freq), (reason)) +#endif + +typedef struct cgraph_node *cgraph_node_ptr; +typedef struct cgraph_edge *cgraph_edge_p; +typedef struct varpool_node *varpool_node_ptr; + +static inline void change_decl_assembler_name(tree decl, tree name) +{ + symtab->change_decl_assembler_name(decl, name); +} + +static inline void varpool_finalize_decl(tree decl) +{ + varpool_node::finalize_decl(decl); +} + +static inline void varpool_add_new_variable(tree decl) +{ + varpool_node::add(decl); +} + +static inline unsigned int rebuild_cgraph_edges(void) +{ + return cgraph_edge::rebuild_edges(); +} + +static inline cgraph_node_ptr cgraph_function_node(cgraph_node_ptr node, enum availability *availability) +{ + return node->function_symbol(availability); +} + +static inline cgraph_node_ptr cgraph_function_or_thunk_node(cgraph_node_ptr node, enum availability *availability = NULL) +{ + return node->ultimate_alias_target(availability); +} + +static inline bool cgraph_only_called_directly_p(cgraph_node_ptr node) +{ + return node->only_called_directly_p(); +} + +static inline enum availability cgraph_function_body_availability(cgraph_node_ptr node) +{ + return node->get_availability(); +} + +static inline cgraph_node_ptr cgraph_alias_target(cgraph_node_ptr node) +{ + return node->get_alias_target(); +} + +static inline bool cgraph_for_node_and_aliases(cgraph_node_ptr node, bool (*callback)(cgraph_node_ptr, void *), void *data, bool include_overwritable) +{ + return node->call_for_symbol_thunks_and_aliases(callback, data, include_overwritable); +} + +static inline struct cgraph_node_hook_list *cgraph_add_function_insertion_hook(cgraph_node_hook hook, void *data) +{ + return symtab->add_cgraph_insertion_hook(hook, data); +} + +static inline void cgraph_remove_function_insertion_hook(struct cgraph_node_hook_list *entry) +{ + symtab->remove_cgraph_insertion_hook(entry); +} + +static inline struct cgraph_node_hook_list *cgraph_add_node_removal_hook(cgraph_node_hook hook, void *data) +{ + return symtab->add_cgraph_removal_hook(hook, data); +} + +static inline void cgraph_remove_node_removal_hook(struct cgraph_node_hook_list *entry) +{ + symtab->remove_cgraph_removal_hook(entry); +} + +static inline struct cgraph_2node_hook_list *cgraph_add_node_duplication_hook(cgraph_2node_hook hook, void *data) +{ + return symtab->add_cgraph_duplication_hook(hook, data); +} + +static inline void cgraph_remove_node_duplication_hook(struct cgraph_2node_hook_list *entry) +{ + symtab->remove_cgraph_duplication_hook(entry); +} + +static inline void cgraph_call_node_duplication_hooks(cgraph_node_ptr node, cgraph_node_ptr node2) +{ + symtab->call_cgraph_duplication_hooks(node, node2); +} + +static inline void cgraph_call_edge_duplication_hooks(cgraph_edge *cs1, cgraph_edge *cs2) +{ + symtab->call_edge_duplication_hooks(cs1, cs2); +} + +#if BUILDING_GCC_VERSION >= 6000 +typedef gimple *gimple_ptr; +typedef const gimple *const_gimple_ptr; +#define gimple gimple_ptr +#define const_gimple const_gimple_ptr +#undef CONST_CAST_GIMPLE +#define CONST_CAST_GIMPLE(X) CONST_CAST(gimple, (X)) +#endif + +/* gimple related */ +static inline gimple gimple_build_assign_with_ops(enum tree_code subcode, tree lhs, tree op1, tree op2 MEM_STAT_DECL) +{ + return gimple_build_assign(lhs, subcode, op1, op2 PASS_MEM_STAT); +} + +template <> +template <> +inline bool is_a_helper<const ggoto *>::test(const_gimple gs) +{ + return gs->code == GIMPLE_GOTO; +} + +template <> +template <> +inline bool is_a_helper<const greturn *>::test(const_gimple gs) +{ + return gs->code == GIMPLE_RETURN; +} + +static inline gasm *as_a_gasm(gimple stmt) +{ + return as_a<gasm *>(stmt); +} + +static inline const gasm *as_a_const_gasm(const_gimple stmt) +{ + return as_a<const gasm *>(stmt); +} + +static inline gassign *as_a_gassign(gimple stmt) +{ + return as_a<gassign *>(stmt); +} + +static inline const gassign *as_a_const_gassign(const_gimple stmt) +{ + return as_a<const gassign *>(stmt); +} + +static inline gcall *as_a_gcall(gimple stmt) +{ + return as_a<gcall *>(stmt); +} + +static inline const gcall *as_a_const_gcall(const_gimple stmt) +{ + return as_a<const gcall *>(stmt); +} + +static inline ggoto *as_a_ggoto(gimple stmt) +{ + return as_a<ggoto *>(stmt); +} + +static inline const ggoto *as_a_const_ggoto(const_gimple stmt) +{ + return as_a<const ggoto *>(stmt); +} + +static inline gphi *as_a_gphi(gimple stmt) +{ + return as_a<gphi *>(stmt); +} + +static inline const gphi *as_a_const_gphi(const_gimple stmt) +{ + return as_a<const gphi *>(stmt); +} + +static inline greturn *as_a_greturn(gimple stmt) +{ + return as_a<greturn *>(stmt); +} + +static inline const greturn *as_a_const_greturn(const_gimple stmt) +{ + return as_a<const greturn *>(stmt); +} + +/* IPA/LTO related */ +#define ipa_ref_list_referring_iterate(L, I, P) \ + (L)->referring.iterate((I), &(P)) +#define ipa_ref_list_reference_iterate(L, I, P) \ + (L)->reference.iterate((I), &(P)) + +static inline cgraph_node_ptr ipa_ref_referring_node(struct ipa_ref *ref) +{ + return dyn_cast<cgraph_node_ptr>(ref->referring); +} + +static inline void ipa_remove_stmt_references(symtab_node *referring_node, gimple stmt) +{ + referring_node->remove_stmt_references(stmt); +} +#endif + +#if BUILDING_GCC_VERSION < 6000 +#define get_inner_reference(exp, pbitsize, pbitpos, poffset, pmode, punsignedp, preversep, pvolatilep, keep_aligning) \ + get_inner_reference(exp, pbitsize, pbitpos, poffset, pmode, punsignedp, pvolatilep, keep_aligning) +#define gen_rtx_set(ARG0, ARG1) gen_rtx_SET(VOIDmode, (ARG0), (ARG1)) +#endif + +#if BUILDING_GCC_VERSION >= 6000 +#define gen_rtx_set(ARG0, ARG1) gen_rtx_SET((ARG0), (ARG1)) +#endif + +#ifdef __cplusplus +static inline void debug_tree(const_tree t) +{ + debug_tree(CONST_CAST_TREE(t)); +} + +static inline void debug_gimple_stmt(const_gimple s) +{ + debug_gimple_stmt(CONST_CAST_GIMPLE(s)); +} +#else +#define debug_tree(t) debug_tree(CONST_CAST_TREE(t)) +#define debug_gimple_stmt(s) debug_gimple_stmt(CONST_CAST_GIMPLE(s)) +#endif + +#if BUILDING_GCC_VERSION >= 7000 +#define get_inner_reference(exp, pbitsize, pbitpos, poffset, pmode, punsignedp, preversep, pvolatilep, keep_aligning) \ + get_inner_reference(exp, pbitsize, pbitpos, poffset, pmode, punsignedp, preversep, pvolatilep) +#endif + +#if BUILDING_GCC_VERSION < 7000 +#define SET_DECL_ALIGN(decl, align) DECL_ALIGN(decl) = (align) +#define SET_DECL_MODE(decl, mode) DECL_MODE(decl) = (mode) +#endif + +#endif diff --git a/tools/gcc-plugins/gcc-retain-typeinfo.patch b/tools/gcc-plugins/gcc-retain-typeinfo.patch new file mode 100644 index 0000000000..ec51f0be6f --- /dev/null +++ b/tools/gcc-plugins/gcc-retain-typeinfo.patch @@ -0,0 +1,11 @@ +--- a/src/gcc/c/c-typeck.c ++++ b/src/gcc/c/c-typeck.c +@@ -5716,8 +5716,6 @@ build_c_cast (location_t loc, tree type, tree expr) + if (objc_is_object_ptr (type) && objc_is_object_ptr (TREE_TYPE (expr))) + return build1 (NOP_EXPR, type, expr); + +- type = TYPE_MAIN_VARIANT (type); +- + if (TREE_CODE (type) == ARRAY_TYPE) + { + error_at (loc, "cast specifies array type"); diff --git a/watchfrr/watchfrr.c b/watchfrr/watchfrr.c index ed9616963d..05e6651229 100644 --- a/watchfrr/watchfrr.c +++ b/watchfrr/watchfrr.c @@ -318,9 +318,8 @@ static pid_t run_background(char *shell_cmd) } default: /* Parent process: we will reap the child later. */ - flog_err_sys(EC_LIB_SYSTEM_CALL, - "Forked background command [pid %d]: %s", - (int)child, shell_cmd); + zlog_info("Forked background command [pid %d]: %s", (int)child, + shell_cmd); return child; } } @@ -559,9 +558,9 @@ static int wakeup_init(struct thread *t_wakeup) dmn->t_wakeup = NULL; if (try_connect(dmn) < 0) { - flog_err(EC_WATCHFRR_CONNECTION, - "%s state -> down : initial connection attempt failed", - dmn->name); + zlog_info( + "%s state -> down : initial connection attempt failed", + dmn->name); dmn->state = DAEMON_DOWN; } phase_check(); diff --git a/zebra/redistribute.c b/zebra/redistribute.c index 30ea19fca5..115a69f2c8 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -291,6 +291,10 @@ void redistribute_delete(const struct prefix *p, const struct prefix *src_p, } for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + /* Do not send unsolicited messages to synchronous clients. */ + if (client->synchronous) + continue; + if (new_re) { /* Skip this client if it will receive an update for the * 'new' re @@ -472,6 +476,12 @@ void zebra_interface_up_update(struct interface *ifp) if (ifp->ptm_status || !ifp->ptm_enable) { for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + /* Do not send unsolicited messages to synchronous + * clients. + */ + if (client->synchronous) + continue; + zsend_interface_update(ZEBRA_INTERFACE_UP, client, ifp); zsend_interface_link_params(client, ifp); @@ -490,6 +500,10 @@ void zebra_interface_down_update(struct interface *ifp) ifp->name, ifp->vrf_id); for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + /* Do not send unsolicited messages to synchronous clients. */ + if (client->synchronous) + continue; + zsend_interface_update(ZEBRA_INTERFACE_DOWN, client, ifp); } } @@ -505,6 +519,10 @@ void zebra_interface_add_update(struct interface *ifp) ifp->vrf_id); for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + /* Do not send unsolicited messages to synchronous clients. */ + if (client->synchronous) + continue; + client->ifadd_cnt++; zsend_interface_add(client, ifp); zsend_interface_link_params(client, ifp); @@ -521,6 +539,10 @@ void zebra_interface_delete_update(struct interface *ifp) ifp->name, ifp->vrf_id); for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + /* Do not send unsolicited messages to synchronous clients. */ + if (client->synchronous) + continue; + client->ifdel_cnt++; zsend_interface_delete(client, ifp); } @@ -552,12 +574,17 @@ void zebra_interface_address_add_update(struct interface *ifp, router_id_add_address(ifc); - for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) + for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + /* Do not send unsolicited messages to synchronous clients. */ + if (client->synchronous) + continue; + if (CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL)) { client->connected_rt_add_cnt++; zsend_interface_address(ZEBRA_INTERFACE_ADDRESS_ADD, client, ifp, ifc); } + } } /* Interface address deletion. */ @@ -581,12 +608,17 @@ void zebra_interface_address_delete_update(struct interface *ifp, router_id_del_address(ifc); - for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) + for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + /* Do not send unsolicited messages to synchronous clients. */ + if (client->synchronous) + continue; + if (CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL)) { client->connected_rt_del_cnt++; zsend_interface_address(ZEBRA_INTERFACE_ADDRESS_DELETE, client, ifp, ifc); } + } } /* Interface VRF change. May need to delete from clients not interested in @@ -603,6 +635,10 @@ void zebra_interface_vrf_update_del(struct interface *ifp, vrf_id_t new_vrf_id) ifp->name, ifp->vrf_id, new_vrf_id); for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + /* Do not send unsolicited messages to synchronous clients. */ + if (client->synchronous) + continue; + /* Need to delete if the client is not interested in the new * VRF. */ zsend_interface_update(ZEBRA_INTERFACE_DOWN, client, ifp); @@ -626,6 +662,10 @@ void zebra_interface_vrf_update_add(struct interface *ifp, vrf_id_t old_vrf_id) ifp->name, old_vrf_id, ifp->vrf_id); for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + /* Do not send unsolicited messages to synchronous clients. */ + if (client->synchronous) + continue; + /* Need to add if the client is interested in the new VRF. */ client->ifadd_cnt++; zsend_interface_add(client, ifp); @@ -913,6 +953,11 @@ void zebra_interface_parameters_update(struct interface *ifp) zlog_debug("MESSAGE: ZEBRA_INTERFACE_LINK_PARAMS %s(%u)", ifp->name, ifp->vrf_id); - for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) + for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + /* Do not send unsolicited messages to synchronous clients. */ + if (client->synchronous) + continue; + zsend_interface_link_params(client, ifp); + } } diff --git a/zebra/rib.h b/zebra/rib.h index 931c97638e..3717a12814 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -107,7 +107,7 @@ struct route_entry { /* Uptime. */ time_t uptime; - /* Type fo this route. */ + /* Type of this route. */ int type; /* VRF identifier. */ @@ -347,10 +347,16 @@ extern int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, struct prefix_ipv6 *src_p, const struct nexthop *nh, uint32_t nhe_id, uint32_t table_id, uint32_t metric, uint32_t mtu, uint8_t distance, route_tag_t tag); - +/* + * Multipath route apis. + */ extern int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, struct prefix_ipv6 *src_p, struct route_entry *re, struct nexthop_group *ng); +extern int rib_add_multipath_nhe(afi_t afi, safi_t safi, struct prefix *p, + struct prefix_ipv6 *src_p, + struct route_entry *re, + struct nhg_hash_entry *nhe); extern void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, unsigned short instance, int flags, struct prefix *p, diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 683c6532e6..84c9bd098e 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1515,6 +1515,30 @@ static int netlink_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla, 0); } +static bool nexthop_set_src(const struct nexthop *nexthop, int family, + union g_addr *src) +{ + if (family == AF_INET) { + if (nexthop->rmap_src.ipv4.s_addr != INADDR_ANY) { + src->ipv4 = nexthop->rmap_src.ipv4; + return true; + } else if (nexthop->src.ipv4.s_addr != INADDR_ANY) { + src->ipv4 = nexthop->src.ipv4; + return true; + } + } else if (family == AF_INET6) { + if (!IN6_IS_ADDR_UNSPECIFIED(&nexthop->rmap_src.ipv6)) { + src->ipv6 = nexthop->rmap_src.ipv6; + return true; + } else if (!IN6_IS_ADDR_UNSPECIFIED(&nexthop->src.ipv6)) { + src->ipv6 = nexthop->src.ipv6; + return true; + } + } + + return false; +} + /* * Routing table change via netlink interface, using a dataplane context object */ @@ -1525,7 +1549,7 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx) unsigned int nexthop_num; int family; const char *routedesc; - int setsrc = 0; + bool setsrc = false; union g_addr src; const struct prefix *p, *src_p; uint32_t table_id; @@ -1641,13 +1665,30 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx) } if (kernel_nexthops_supported()) { + /* Kernel supports nexthop objects */ if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( "netlink_route_multipath(): %pFX nhg_id is %u", p, dplane_ctx_get_nhe_id(ctx)); - /* Kernel supports nexthop objects */ addattr32(&req.n, sizeof(req), RTA_NH_ID, dplane_ctx_get_nhe_id(ctx)); + + /* Have to determine src still */ + for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) { + if (setsrc) + break; + + setsrc = nexthop_set_src(nexthop, family, &src); + } + + if (setsrc) { + if (family == AF_INET) + addattr_l(&req.n, sizeof(req), RTA_PREFSRC, + &src.ipv4, bytelen); + else if (family == AF_INET6) + addattr_l(&req.n, sizeof(req), RTA_PREFSRC, + &src.ipv6, bytelen); + } goto skip; } @@ -1695,32 +1736,8 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx) if (setsrc) continue; - if (family == AF_INET) { - if (nexthop->rmap_src.ipv4.s_addr - != 0) { - src.ipv4 = - nexthop->rmap_src.ipv4; - setsrc = 1; - } else if (nexthop->src.ipv4.s_addr - != 0) { - src.ipv4 = - nexthop->src.ipv4; - setsrc = 1; - } - } else if (family == AF_INET6) { - if (!IN6_IS_ADDR_UNSPECIFIED( - &nexthop->rmap_src.ipv6)) { - src.ipv6 = - nexthop->rmap_src.ipv6; - setsrc = 1; - } else if ( - !IN6_IS_ADDR_UNSPECIFIED( - &nexthop->src.ipv6)) { - src.ipv6 = - nexthop->src.ipv6; - setsrc = 1; - } - } + setsrc = nexthop_set_src(nexthop, family, &src); + continue; } @@ -1737,7 +1754,7 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx) break; } } - if (setsrc && (cmd == RTM_NEWROUTE)) { + if (setsrc) { if (family == AF_INET) addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &src.ipv4, bytelen); @@ -1763,32 +1780,7 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx) if (setsrc) continue; - if (family == AF_INET) { - if (nexthop->rmap_src.ipv4.s_addr - != 0) { - src.ipv4 = - nexthop->rmap_src.ipv4; - setsrc = 1; - } else if (nexthop->src.ipv4.s_addr - != 0) { - src.ipv4 = - nexthop->src.ipv4; - setsrc = 1; - } - } else if (family == AF_INET6) { - if (!IN6_IS_ADDR_UNSPECIFIED( - &nexthop->rmap_src.ipv6)) { - src.ipv6 = - nexthop->rmap_src.ipv6; - setsrc = 1; - } else if ( - !IN6_IS_ADDR_UNSPECIFIED( - &nexthop->src.ipv6)) { - src.ipv6 = - nexthop->src.ipv6; - setsrc = 1; - } - } + setsrc = nexthop_set_src(nexthop, family, &src); continue; } @@ -1815,7 +1807,7 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx) } } } - if (setsrc && (cmd == RTM_NEWROUTE)) { + if (setsrc) { if (family == AF_INET) addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &src.ipv4, bytelen); @@ -1990,6 +1982,12 @@ static int netlink_nexthop(int cmd, struct zebra_dplane_ctx *ctx) addattr32(&req.n, req_size, NHA_ID, id); if (cmd == RTM_NEWNEXTHOP) { + /* + * We distinguish between a "group", which is a collection + * of ids, and a singleton nexthop with an id. The + * group is installed as an id that just refers to a list of + * other ids. + */ if (dplane_ctx_get_nhe_nh_grp_count(ctx)) _netlink_nexthop_build_group( &req.n, req_size, id, @@ -2076,14 +2074,13 @@ static int netlink_nexthop(int cmd, struct zebra_dplane_ctx *ctx) } } - nexthop_done: - if (IS_ZEBRA_DEBUG_KERNEL) { - char buf[NEXTHOP_STRLEN]; +nexthop_done: + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s: ID (%u): %pNHv (%u) %s ", + __func__, id, nh, nh->vrf_id, + label_buf); - snprintfrr(buf, sizeof(buf), "%pNHv", nh); - zlog_debug("%s: ID (%u): %s (%u) %s ", __func__, - id, buf, nh->vrf_id, label_buf); - } } req.nhm.nh_protocol = zebra2proto(dplane_ctx_get_nhe_type(ctx)); @@ -2111,43 +2108,19 @@ static int netlink_nexthop(int cmd, struct zebra_dplane_ctx *ctx) */ enum zebra_dplane_result kernel_nexthop_update(struct zebra_dplane_ctx *ctx) { + enum dplane_op_e op; int cmd = 0; int ret = 0; - switch (dplane_ctx_get_op(ctx)) { - case DPLANE_OP_NH_DELETE: - cmd = RTM_DELNEXTHOP; - break; - case DPLANE_OP_NH_INSTALL: - case DPLANE_OP_NH_UPDATE: + op = dplane_ctx_get_op(ctx); + if (op == DPLANE_OP_NH_INSTALL || op == DPLANE_OP_NH_UPDATE) cmd = RTM_NEWNEXTHOP; - break; - case DPLANE_OP_ROUTE_INSTALL: - case DPLANE_OP_ROUTE_UPDATE: - case DPLANE_OP_ROUTE_DELETE: - case DPLANE_OP_ROUTE_NOTIFY: - case DPLANE_OP_LSP_INSTALL: - case DPLANE_OP_LSP_UPDATE: - case DPLANE_OP_LSP_DELETE: - case DPLANE_OP_LSP_NOTIFY: - case DPLANE_OP_PW_INSTALL: - case DPLANE_OP_PW_UNINSTALL: - case DPLANE_OP_SYS_ROUTE_ADD: - case DPLANE_OP_SYS_ROUTE_DELETE: - case DPLANE_OP_ADDR_INSTALL: - case DPLANE_OP_ADDR_UNINSTALL: - case DPLANE_OP_MAC_INSTALL: - case DPLANE_OP_MAC_DELETE: - case DPLANE_OP_NEIGH_INSTALL: - case DPLANE_OP_NEIGH_UPDATE: - case DPLANE_OP_NEIGH_DELETE: - case DPLANE_OP_VTEP_ADD: - case DPLANE_OP_VTEP_DELETE: - case DPLANE_OP_NONE: - flog_err( - EC_ZEBRA_NHG_FIB_UPDATE, - "Context received for kernel nexthop update with incorrect OP code (%u)", - dplane_ctx_get_op(ctx)); + else if (op == DPLANE_OP_NH_DELETE) + cmd = RTM_DELNEXTHOP; + else { + flog_err(EC_ZEBRA_NHG_FIB_UPDATE, + "Context received for kernel nexthop update with incorrect OP code (%u)", + op); return ZEBRA_DPLANE_REQUEST_FAILURE; } diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index c0882c3f35..aabe533ee6 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -383,9 +383,14 @@ static void zebra_interface_nbr_address_add_update(struct interface *ifp, p->prefixlen, ifc->ifp->name); } - for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) + for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + /* Do not send unsolicited messages to synchronous clients. */ + if (client->synchronous) + continue; + zsend_interface_nbr_address(ZEBRA_INTERFACE_NBR_ADDRESS_ADD, client, ifp, ifc); + } } /* Interface address deletion. */ @@ -407,9 +412,14 @@ static void zebra_interface_nbr_address_delete_update(struct interface *ifp, p->prefixlen, ifc->ifp->name); } - for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) + for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + /* Do not send unsolicited messages to synchronous clients. */ + if (client->synchronous) + continue; + zsend_interface_nbr_address(ZEBRA_INTERFACE_NBR_ADDRESS_DELETE, client, ifp, ifc); + } } /* Send addresses on interface to client */ @@ -1403,6 +1413,132 @@ void zserv_nexthop_num_warn(const char *caller, const struct prefix *p, } } +/* + * Create a new nexthop based on a zapi nexthop. + */ +static struct nexthop *nexthop_from_zapi(struct route_entry *re, + const struct zapi_nexthop *api_nh, + const struct zapi_route *api) +{ + struct nexthop *nexthop = NULL; + struct ipaddr vtep_ip; + struct interface *ifp; + char nhbuf[INET6_ADDRSTRLEN] = ""; + + switch (api_nh->type) { + case NEXTHOP_TYPE_IFINDEX: + nexthop = nexthop_from_ifindex(api_nh->ifindex, api_nh->vrf_id); + break; + case NEXTHOP_TYPE_IPV4: + if (IS_ZEBRA_DEBUG_RECV) { + inet_ntop(AF_INET, &api_nh->gate.ipv4, nhbuf, + sizeof(nhbuf)); + zlog_debug("%s: nh=%s, vrf_id=%d", __func__, + nhbuf, api_nh->vrf_id); + } + nexthop = nexthop_from_ipv4(&api_nh->gate.ipv4, NULL, + api_nh->vrf_id); + break; + case NEXTHOP_TYPE_IPV4_IFINDEX: + if (IS_ZEBRA_DEBUG_RECV) { + inet_ntop(AF_INET, &api_nh->gate.ipv4, nhbuf, + sizeof(nhbuf)); + zlog_debug("%s: nh=%s, vrf_id=%d, ifindex=%d", + __func__, nhbuf, api_nh->vrf_id, + api_nh->ifindex); + } + + nexthop = nexthop_from_ipv4_ifindex( + &api_nh->gate.ipv4, NULL, api_nh->ifindex, + api_nh->vrf_id); + + ifp = if_lookup_by_index(api_nh->ifindex, api_nh->vrf_id); + if (ifp && connected_is_unnumbered(ifp)) + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK); + + /* Special handling for IPv4 routes sourced from EVPN: + * the nexthop and associated MAC need to be installed. + */ + if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE)) { + memset(&vtep_ip, 0, sizeof(struct ipaddr)); + vtep_ip.ipa_type = IPADDR_V4; + memcpy(&(vtep_ip.ipaddr_v4), &(api_nh->gate.ipv4), + sizeof(struct in_addr)); + zebra_vxlan_evpn_vrf_route_add( + api_nh->vrf_id, &api_nh->rmac, + &vtep_ip, &api->prefix); + } + break; + case NEXTHOP_TYPE_IPV6: + if (IS_ZEBRA_DEBUG_RECV) { + inet_ntop(AF_INET6, &api_nh->gate.ipv6, nhbuf, + sizeof(nhbuf)); + zlog_debug("%s: nh=%s, vrf_id=%d", __func__, + nhbuf, api_nh->vrf_id); + } + nexthop = nexthop_from_ipv6(&api_nh->gate.ipv6, api_nh->vrf_id); + break; + case NEXTHOP_TYPE_IPV6_IFINDEX: + if (IS_ZEBRA_DEBUG_RECV) { + inet_ntop(AF_INET6, &api_nh->gate.ipv6, nhbuf, + sizeof(nhbuf)); + zlog_debug("%s: nh=%s, vrf_id=%d, ifindex=%d", + __func__, nhbuf, api_nh->vrf_id, + api_nh->ifindex); + } + nexthop = nexthop_from_ipv6_ifindex(&api_nh->gate.ipv6, + api_nh->ifindex, + api_nh->vrf_id); + + /* Special handling for IPv6 routes sourced from EVPN: + * the nexthop and associated MAC need to be installed. + */ + if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE)) { + memset(&vtep_ip, 0, sizeof(struct ipaddr)); + vtep_ip.ipa_type = IPADDR_V6; + memcpy(&vtep_ip.ipaddr_v6, &(api_nh->gate.ipv6), + sizeof(struct in6_addr)); + zebra_vxlan_evpn_vrf_route_add( + api_nh->vrf_id, &api_nh->rmac, + &vtep_ip, &api->prefix); + } + break; + case NEXTHOP_TYPE_BLACKHOLE: + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: nh blackhole %d", + __func__, api_nh->bh_type); + + nexthop = nexthop_from_blackhole(api_nh->bh_type); + break; + } + + /* Return early if we couldn't process the zapi nexthop */ + if (nexthop == NULL) { + goto done; + } + + if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK)) + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK); + + if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_WEIGHT)) + nexthop->weight = api_nh->weight; + + if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) { + if (api_nh->backup_idx < api->backup_nexthop_num) { + /* Capture backup info */ + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP); + nexthop->backup_idx = api_nh->backup_idx; + } else { + /* Warn about invalid backup index */ + if (IS_ZEBRA_DEBUG_RECV || IS_ZEBRA_DEBUG_EVENT) + zlog_debug("%s: invalid backup nh idx %d", + __func__, api_nh->backup_idx); + } + } +done: + return nexthop; +} + static void zread_route_add(ZAPI_HANDLER_ARGS) { struct stream *s; @@ -1411,12 +1547,15 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) afi_t afi; struct prefix_ipv6 *src_p = NULL; struct route_entry *re; - struct nexthop *nexthop = NULL; + struct nexthop *nexthop = NULL, *last_nh; struct nexthop_group *ng = NULL; + struct nhg_backup_info *bnhg = NULL; int i, ret; vrf_id_t vrf_id; - struct ipaddr vtep_ip; - struct interface *ifp; + struct nhg_hash_entry nhe; + enum lsp_types_t label_type; + char nhbuf[NEXTHOP_STRLEN]; + char labelbuf[MPLS_LABEL_STRLEN]; s = msg; if (zapi_route_decode(s, &api) < 0) { @@ -1430,8 +1569,8 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) char buf_prefix[PREFIX_STRLEN]; prefix2str(&api.prefix, buf_prefix, sizeof(buf_prefix)); - zlog_debug("%s: p=%s, flags=0x%x", - __func__, buf_prefix, api.flags); + zlog_debug("%s: p=%s, msg flags=0x%x, flags=0x%x", + __func__, buf_prefix, (int)api.message, api.flags); } /* Allocate new route. */ @@ -1459,6 +1598,15 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) return; } + /* Report misuse of the backup flag */ + if (CHECK_FLAG(api.message, ZAPI_MESSAGE_BACKUP_NEXTHOPS) && + api.backup_nexthop_num == 0) { + if (IS_ZEBRA_DEBUG_RECV || IS_ZEBRA_DEBUG_EVENT) + zlog_debug("%s: client %s: BACKUP flag set but no backup nexthops, prefix %pFX", + __func__, + zebra_route_string(client->proto), &api.prefix); + } + /* Use temporary list of nexthops */ ng = nexthop_group_new(); @@ -1469,130 +1617,138 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) */ for (i = 0; i < api.nexthop_num; i++) { api_nh = &api.nexthops[i]; - ifindex_t ifindex = 0; - nexthop = NULL; + /* Convert zapi nexthop */ + nexthop = nexthop_from_zapi(re, api_nh, &api); + if (!nexthop) { + flog_warn( + EC_ZEBRA_NEXTHOP_CREATION_FAILED, + "%s: Nexthops Specified: %d but we failed to properly create one", + __func__, api.nexthop_num); + nexthop_group_delete(&ng); + XFREE(MTYPE_RE, re); + return; + } - if (IS_ZEBRA_DEBUG_RECV) - zlog_debug("nh type %d", api_nh->type); + /* MPLS labels for BGP-LU or Segment Routing */ + if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL) + && api_nh->type != NEXTHOP_TYPE_IFINDEX + && api_nh->type != NEXTHOP_TYPE_BLACKHOLE + && api_nh->label_num > 0) { - switch (api_nh->type) { - case NEXTHOP_TYPE_IFINDEX: - nexthop = nexthop_from_ifindex(api_nh->ifindex, - api_nh->vrf_id); - break; - case NEXTHOP_TYPE_IPV4: - if (IS_ZEBRA_DEBUG_RECV) { - char nhbuf[INET6_ADDRSTRLEN] = {0}; + label_type = lsp_type_from_re_type(client->proto); + nexthop_add_labels(nexthop, label_type, + api_nh->label_num, + &api_nh->labels[0]); + } - inet_ntop(AF_INET, &api_nh->gate.ipv4, nhbuf, - INET6_ADDRSTRLEN); - zlog_debug("%s: nh=%s, vrf_id=%d", __func__, - nhbuf, api_nh->vrf_id); - } - nexthop = nexthop_from_ipv4(&api_nh->gate.ipv4, - NULL, api_nh->vrf_id); - break; - case NEXTHOP_TYPE_IPV4_IFINDEX: + if (IS_ZEBRA_DEBUG_RECV) { + labelbuf[0] = '\0'; + nhbuf[0] = '\0'; - memset(&vtep_ip, 0, sizeof(struct ipaddr)); - ifindex = api_nh->ifindex; - if (IS_ZEBRA_DEBUG_RECV) { - char nhbuf[INET6_ADDRSTRLEN] = {0}; + nexthop2str(nexthop, nhbuf, sizeof(nhbuf)); - inet_ntop(AF_INET, &api_nh->gate.ipv4, nhbuf, - INET6_ADDRSTRLEN); - zlog_debug( - "%s: nh=%s, vrf_id=%d (re->vrf_id=%d), ifindex=%d", - __func__, nhbuf, api_nh->vrf_id, - re->vrf_id, ifindex); + if (nexthop->nh_label && + nexthop->nh_label->num_labels > 0) { + mpls_label2str(nexthop->nh_label->num_labels, + nexthop->nh_label->label, + labelbuf, sizeof(labelbuf), + false); } - nexthop = nexthop_from_ipv4_ifindex( - &api_nh->gate.ipv4, NULL, ifindex, - api_nh->vrf_id); - - ifp = if_lookup_by_index(ifindex, api_nh->vrf_id); - if (ifp && connected_is_unnumbered(ifp)) - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK); - /* Special handling for IPv4 routes sourced from EVPN: - * the nexthop and associated MAC need to be installed. - */ - if (CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)) { - vtep_ip.ipa_type = IPADDR_V4; - memcpy(&(vtep_ip.ipaddr_v4), - &(api_nh->gate.ipv4), - sizeof(struct in_addr)); - zebra_vxlan_evpn_vrf_route_add( - api_nh->vrf_id, &api_nh->rmac, - &vtep_ip, &api.prefix); - } - break; - case NEXTHOP_TYPE_IPV6: - nexthop = nexthop_from_ipv6(&api_nh->gate.ipv6, - api_nh->vrf_id); - break; - case NEXTHOP_TYPE_IPV6_IFINDEX: - memset(&vtep_ip, 0, sizeof(struct ipaddr)); - ifindex = api_nh->ifindex; - nexthop = nexthop_from_ipv6_ifindex(&api_nh->gate.ipv6, - ifindex, - api_nh->vrf_id); - - /* Special handling for IPv6 routes sourced from EVPN: - * the nexthop and associated MAC need to be installed. - */ - if (CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)) { - vtep_ip.ipa_type = IPADDR_V6; - memcpy(&vtep_ip.ipaddr_v6, &(api_nh->gate.ipv6), - sizeof(struct in6_addr)); - zebra_vxlan_evpn_vrf_route_add( - api_nh->vrf_id, &api_nh->rmac, - &vtep_ip, &api.prefix); - } - break; - case NEXTHOP_TYPE_BLACKHOLE: - nexthop = nexthop_from_blackhole(api_nh->bh_type); - break; + + zlog_debug("%s: nh=%s, vrf_id=%d %s", + __func__, nhbuf, api_nh->vrf_id, labelbuf); } + /* Add new nexthop to temporary list. This list is + * canonicalized - sorted - so that it can be hashed later + * in route processing. We expect that the sender has sent + * the list sorted, and the zapi client api attempts to enforce + * that, so this should be inexpensive - but it is necessary + * to support shared nexthop-groups. + */ + nexthop_group_add_sorted(ng, nexthop); + } + + /* Allocate temporary list of backup nexthops, if necessary */ + if (api.backup_nexthop_num > 0) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: adding %d backup nexthops", + __func__, api.backup_nexthop_num); + + bnhg = zebra_nhg_backup_alloc(); + nexthop = NULL; + last_nh = NULL; + } + + /* Copy backup nexthops also, if present */ + for (i = 0; i < api.backup_nexthop_num; i++) { + api_nh = &api.backup_nexthops[i]; + + /* Convert zapi backup nexthop */ + nexthop = nexthop_from_zapi(re, api_nh, &api); if (!nexthop) { flog_warn( EC_ZEBRA_NEXTHOP_CREATION_FAILED, - "%s: Nexthops Specified: %d but we failed to properly create one", - __func__, api.nexthop_num); + "%s: Backup Nexthops Specified: %d but we failed to properly create one", + __func__, api.backup_nexthop_num); nexthop_group_delete(&ng); + zebra_nhg_backup_free(&bnhg); XFREE(MTYPE_RE, re); return; } - if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK)) - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK); - - if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_WEIGHT)) - nexthop->weight = api_nh->weight; + /* Backup nexthops can't have backups; that's not valid. */ + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + if (IS_ZEBRA_DEBUG_RECV) { + nexthop2str(nexthop, nhbuf, sizeof(nhbuf)); + zlog_debug("%s: backup nh %s with BACKUP flag!", + __func__, nhbuf); + } + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP); + nexthop->backup_idx = 0; + } /* MPLS labels for BGP-LU or Segment Routing */ if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL) && api_nh->type != NEXTHOP_TYPE_IFINDEX - && api_nh->type != NEXTHOP_TYPE_BLACKHOLE) { - enum lsp_types_t label_type; + && api_nh->type != NEXTHOP_TYPE_BLACKHOLE + && api_nh->label_num > 0) { label_type = lsp_type_from_re_type(client->proto); - - if (IS_ZEBRA_DEBUG_RECV) { - zlog_debug( - "%s: adding %d labels of type %d (1st=%u)", - __func__, api_nh->label_num, label_type, - api_nh->labels[0]); - } - nexthop_add_labels(nexthop, label_type, api_nh->label_num, &api_nh->labels[0]); } - /* Add new nexthop to temporary list */ - nexthop_group_add_sorted(ng, nexthop); + if (IS_ZEBRA_DEBUG_RECV) { + labelbuf[0] = '\0'; + nhbuf[0] = '\0'; + + nexthop2str(nexthop, nhbuf, sizeof(nhbuf)); + + if (nexthop->nh_label && + nexthop->nh_label->num_labels > 0) { + mpls_label2str(nexthop->nh_label->num_labels, + nexthop->nh_label->label, + labelbuf, sizeof(labelbuf), + false); + } + + zlog_debug("%s: backup nh=%s, vrf_id=%d %s", + __func__, nhbuf, api_nh->vrf_id, labelbuf); + } + + /* Note that the order of the backup nexthops is significant, + * so we don't sort this list as we do the primary nexthops, + * we just append. + */ + if (last_nh) + NEXTHOP_APPEND(last_nh, nexthop); + else + bnhg->nhe->nhg.nexthop = nexthop; + + last_nh = nexthop; } if (CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE)) @@ -1610,6 +1766,7 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) "%s: Received SRC Prefix but afi is not v6", __func__); nexthop_group_delete(&ng); + zebra_nhg_backup_free(&bnhg); XFREE(MTYPE_RE, re); return; } @@ -1621,10 +1778,28 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) "%s: Received safi: %d but we can only accept UNICAST or MULTICAST", __func__, api.safi); nexthop_group_delete(&ng); + zebra_nhg_backup_free(&bnhg); XFREE(MTYPE_RE, re); return; } - ret = rib_add_multipath(afi, api.safi, &api.prefix, src_p, re, ng); + + /* Include backup info with the route. We use a temporary nhe here; + * if this is a new/unknown nhe, a new copy will be allocated + * and stored. + */ + zebra_nhe_init(&nhe, afi, ng->nexthop); + nhe.nhg.nexthop = ng->nexthop; + nhe.backup_info = bnhg; + ret = rib_add_multipath_nhe(afi, api.safi, &api.prefix, src_p, + re, &nhe); + + /* At this point, these allocations are not needed: 're' has been + * retained or freed, and if 're' still exists, it is using + * a reference to a shared group object. + */ + nexthop_group_delete(&ng); + if (bnhg) + zebra_nhg_backup_free(&bnhg); /* Stats */ switch (api.prefix.family) { @@ -1740,6 +1915,10 @@ void zsend_capabilities_all_clients(void) zvrf = vrf_info_lookup(VRF_DEFAULT); for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + /* Do not send unsolicited messages to synchronous clients. */ + if (client->synchronous) + continue; + zsend_capabilities(client, zvrf); } } @@ -1751,13 +1930,18 @@ static void zread_hello(ZAPI_HANDLER_ARGS) uint8_t proto; unsigned short instance; uint8_t notify; + uint8_t synchronous; STREAM_GETC(msg, proto); STREAM_GETW(msg, instance); STREAM_GETC(msg, notify); + STREAM_GETC(msg, synchronous); if (notify) client->notify_owner = true; + if (synchronous) + client->synchronous = true; + /* accept only dynamic routing protocols */ if ((proto < ZEBRA_ROUTE_MAX) && (proto > ZEBRA_ROUTE_CONNECT)) { zlog_notice( @@ -1774,8 +1958,10 @@ static void zread_hello(ZAPI_HANDLER_ARGS) zebra_gr_client_reconnect(client); } - zsend_capabilities(client, zvrf); - zebra_vrf_update_all(client); + if (!client->synchronous) { + zsend_capabilities(client, zvrf); + zebra_vrf_update_all(client); + } stream_failure: return; } diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 459d2bc620..a2365ee76b 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -113,10 +113,15 @@ struct dplane_route_info { struct dplane_nexthop_info nhe; /* Nexthops */ + uint32_t zd_nhg_id; struct nexthop_group zd_ng; + /* Backup nexthops (if present) */ + struct nexthop_group backup_ng; + /* "Previous" nexthops, used only in route updates without netlink */ struct nexthop_group zd_old_ng; + struct nexthop_group old_backup_ng; /* TODO -- use fixed array of nexthops, to avoid mallocs? */ @@ -472,6 +477,14 @@ static void dplane_ctx_free(struct zebra_dplane_ctx **pctx) (*pctx)->u.rinfo.zd_ng.nexthop = NULL; } + /* Free backup info also (if present) */ + if ((*pctx)->u.rinfo.backup_ng.nexthop) { + /* This deals with recursive nexthops too */ + nexthops_free((*pctx)->u.rinfo.backup_ng.nexthop); + + (*pctx)->u.rinfo.backup_ng.nexthop = NULL; + } + if ((*pctx)->u.rinfo.zd_old_ng.nexthop) { /* This deals with recursive nexthops too */ nexthops_free((*pctx)->u.rinfo.zd_old_ng.nexthop); @@ -479,6 +492,13 @@ static void dplane_ctx_free(struct zebra_dplane_ctx **pctx) (*pctx)->u.rinfo.zd_old_ng.nexthop = NULL; } + if ((*pctx)->u.rinfo.old_backup_ng.nexthop) { + /* This deals with recursive nexthops too */ + nexthops_free((*pctx)->u.rinfo.old_backup_ng.nexthop); + + (*pctx)->u.rinfo.old_backup_ng.nexthop = NULL; + } + break; case DPLANE_OP_NH_INSTALL: @@ -1038,6 +1058,12 @@ void dplane_ctx_set_nexthops(struct zebra_dplane_ctx *ctx, struct nexthop *nh) nexthop_group_copy_nh_sorted(&(ctx->u.rinfo.zd_ng), nh); } +uint32_t dplane_ctx_get_nhg_id(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return ctx->u.rinfo.zd_nhg_id; +} + const struct nexthop_group *dplane_ctx_get_ng( const struct zebra_dplane_ctx *ctx) { @@ -1046,14 +1072,30 @@ const struct nexthop_group *dplane_ctx_get_ng( return &(ctx->u.rinfo.zd_ng); } -const struct nexthop_group *dplane_ctx_get_old_ng( - const struct zebra_dplane_ctx *ctx) +const struct nexthop_group * +dplane_ctx_get_backup_ng(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &(ctx->u.rinfo.backup_ng); +} + +const struct nexthop_group * +dplane_ctx_get_old_ng(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); return &(ctx->u.rinfo.zd_old_ng); } +const struct nexthop_group * +dplane_ctx_get_old_backup_ng(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &(ctx->u.rinfo.old_backup_ng); +} + const struct zebra_dplane_info *dplane_ctx_get_ns( const struct zebra_dplane_ctx *ctx) { @@ -1514,6 +1556,13 @@ static int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, /* Copy nexthops; recursive info is included too */ copy_nexthops(&(ctx->u.rinfo.zd_ng.nexthop), re->nhe->nhg.nexthop, NULL); + ctx->u.rinfo.zd_nhg_id = re->nhe->id; + + /* Copy backup nexthop info, if present */ + if (re->nhe->backup_info && re->nhe->backup_info->nhe) { + copy_nexthops(&(ctx->u.rinfo.backup_ng.nexthop), + re->nhe->backup_info->nhe->nhg.nexthop, NULL); + } /* Ensure that the dplane nexthops' flags are clear. */ for (ALL_NEXTHOPS(ctx->u.rinfo.zd_ng, nexthop)) @@ -1532,9 +1581,8 @@ static int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, dplane_ctx_ns_init(ctx, zns, (op == DPLANE_OP_ROUTE_UPDATE)); #ifdef HAVE_NETLINK - if (re->nhe_id) { - struct nhg_hash_entry *nhe = - zebra_nhg_resolve(zebra_nhg_lookup_id(re->nhe_id)); + if (re->nhe) { + struct nhg_hash_entry *nhe = zebra_nhg_resolve(re->nhe); ctx->u.rinfo.nhe.id = nhe->id; /* @@ -1581,7 +1629,6 @@ static int dplane_ctx_nexthop_init(struct zebra_dplane_ctx *ctx, { struct zebra_vrf *zvrf = NULL; struct zebra_ns *zns = NULL; - int ret = EINVAL; if (!ctx || !nhe) @@ -1850,6 +1897,17 @@ dplane_route_update_internal(struct route_node *rn, */ copy_nexthops(&(ctx->u.rinfo.zd_old_ng.nexthop), old_re->nhe->nhg.nexthop, NULL); + + if (zebra_nhg_get_backup_nhg(old_re->nhe) != NULL) { + struct nexthop_group *nhg; + struct nexthop **nh; + + nhg = zebra_nhg_get_backup_nhg(old_re->nhe); + nh = &(ctx->u.rinfo.old_backup_ng.nexthop); + + if (nhg->nexthop) + copy_nexthops(nh, nhg->nexthop, NULL); + } #endif /* !HAVE_NETLINK */ } diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index c0b04e71b0..9ce4df197c 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -270,11 +270,19 @@ void dplane_ctx_set_distance(struct zebra_dplane_ctx *ctx, uint8_t distance); uint8_t dplane_ctx_get_old_distance(const struct zebra_dplane_ctx *ctx); void dplane_ctx_set_nexthops(struct zebra_dplane_ctx *ctx, struct nexthop *nh); + +uint32_t dplane_ctx_get_nhg_id(const struct zebra_dplane_ctx *ctx); const struct nexthop_group *dplane_ctx_get_ng( const struct zebra_dplane_ctx *ctx); const struct nexthop_group *dplane_ctx_get_old_ng( const struct zebra_dplane_ctx *ctx); +/* Backup nexthop information (list of nexthops) if present. */ +const struct nexthop_group * +dplane_ctx_get_backup_ng(const struct zebra_dplane_ctx *ctx); +const struct nexthop_group * +dplane_ctx_get_old_backup_ng(const struct zebra_dplane_ctx *ctx); + /* Accessors for nexthop information */ uint32_t dplane_ctx_get_nhe_id(const struct zebra_dplane_ctx *ctx); afi_t dplane_ctx_get_nhe_afi(const struct zebra_dplane_ctx *ctx); diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index d373fdf370..999e91486d 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -98,14 +98,14 @@ static void lsp_free(struct hash *lsp_table, zebra_lsp_t **plsp); static char *nhlfe2str(zebra_nhlfe_t *nhlfe, char *buf, int size); static int nhlfe_nhop_match(zebra_nhlfe_t *nhlfe, enum nexthop_types_t gtype, - union g_addr *gate, ifindex_t ifindex); + const union g_addr *gate, ifindex_t ifindex); static zebra_nhlfe_t *nhlfe_find(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, - enum nexthop_types_t gtype, union g_addr *gate, - ifindex_t ifindex); + enum nexthop_types_t gtype, + const union g_addr *gate, ifindex_t ifindex); static zebra_nhlfe_t *nhlfe_add(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, - enum nexthop_types_t gtype, union g_addr *gate, - ifindex_t ifindex, uint8_t num_labels, - mpls_label_t *labels); + enum nexthop_types_t gtype, + const union g_addr *gate, ifindex_t ifindex, + uint8_t num_labels, mpls_label_t *labels); static int nhlfe_del(zebra_nhlfe_t *snhlfe); static void nhlfe_out_label_update(zebra_nhlfe_t *nhlfe, struct mpls_label_stack *nh_label); @@ -117,13 +117,13 @@ static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty); static void lsp_print(zebra_lsp_t *lsp, void *ctxt); static void *slsp_alloc(void *p); static int snhlfe_match(zebra_snhlfe_t *snhlfe, enum nexthop_types_t gtype, - union g_addr *gate, ifindex_t ifindex); + const union g_addr *gate, ifindex_t ifindex); static zebra_snhlfe_t *snhlfe_find(zebra_slsp_t *slsp, enum nexthop_types_t gtype, - union g_addr *gate, ifindex_t ifindex); + const union g_addr *gate, ifindex_t ifindex); static zebra_snhlfe_t *snhlfe_add(zebra_slsp_t *slsp, enum nexthop_types_t gtype, - union g_addr *gate, ifindex_t ifindex, + const union g_addr *gate, ifindex_t ifindex, mpls_label_t out_label); static int snhlfe_del(zebra_snhlfe_t *snhlfe); static int snhlfe_del_all(zebra_slsp_t *slsp); @@ -960,7 +960,7 @@ static wq_item_status lsp_process(struct work_queue *wq, void *data) UNSET_FLAG(lsp->flags, LSP_FLAG_CHANGED); /* We leave the INSTALLED flag set here - * so we know an update in in-flight. + * so we know an update is in-flight. */ /* @@ -1149,7 +1149,7 @@ static char *nhlfe2str(zebra_nhlfe_t *nhlfe, char *buf, int size) * Check if NHLFE matches with search info passed. */ static int nhlfe_nhop_match(zebra_nhlfe_t *nhlfe, enum nexthop_types_t gtype, - union g_addr *gate, ifindex_t ifindex) + const union g_addr *gate, ifindex_t ifindex) { struct nexthop *nhop; int cmp = 1; @@ -1191,8 +1191,8 @@ static int nhlfe_nhop_match(zebra_nhlfe_t *nhlfe, enum nexthop_types_t gtype, * Locate NHLFE that matches with passed info. */ static zebra_nhlfe_t *nhlfe_find(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, - enum nexthop_types_t gtype, union g_addr *gate, - ifindex_t ifindex) + enum nexthop_types_t gtype, + const union g_addr *gate, ifindex_t ifindex) { zebra_nhlfe_t *nhlfe; @@ -1214,9 +1214,9 @@ static zebra_nhlfe_t *nhlfe_find(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, * check done. */ static zebra_nhlfe_t *nhlfe_add(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, - enum nexthop_types_t gtype, union g_addr *gate, - ifindex_t ifindex, uint8_t num_labels, - mpls_label_t labels[]) + enum nexthop_types_t gtype, + const union g_addr *gate, ifindex_t ifindex, + uint8_t num_labels, mpls_label_t labels[]) { zebra_nhlfe_t *nhlfe; struct nexthop *nexthop; @@ -1520,7 +1520,7 @@ static struct list *hash_get_sorted_list(struct hash *hash, void *cmp) /* * Compare two LSPs based on their label values. */ -static int lsp_cmp(zebra_lsp_t *lsp1, zebra_lsp_t *lsp2) +static int lsp_cmp(const zebra_lsp_t *lsp1, const zebra_lsp_t *lsp2) { if (lsp1->ile.in_label < lsp2->ile.in_label) return -1; @@ -1547,7 +1547,7 @@ static void *slsp_alloc(void *p) /* * Compare two static LSPs based on their label values. */ -static int slsp_cmp(zebra_slsp_t *slsp1, zebra_slsp_t *slsp2) +static int slsp_cmp(const zebra_slsp_t *slsp1, const zebra_slsp_t *slsp2) { if (slsp1->ile.in_label < slsp2->ile.in_label) return -1; @@ -1562,7 +1562,7 @@ static int slsp_cmp(zebra_slsp_t *slsp1, zebra_slsp_t *slsp2) * Check if static NHLFE matches with search info passed. */ static int snhlfe_match(zebra_snhlfe_t *snhlfe, enum nexthop_types_t gtype, - union g_addr *gate, ifindex_t ifindex) + const union g_addr *gate, ifindex_t ifindex) { int cmp = 1; @@ -1593,7 +1593,7 @@ static int snhlfe_match(zebra_snhlfe_t *snhlfe, enum nexthop_types_t gtype, */ static zebra_snhlfe_t *snhlfe_find(zebra_slsp_t *slsp, enum nexthop_types_t gtype, - union g_addr *gate, ifindex_t ifindex) + const union g_addr *gate, ifindex_t ifindex) { zebra_snhlfe_t *snhlfe; @@ -1615,7 +1615,7 @@ static zebra_snhlfe_t *snhlfe_find(zebra_slsp_t *slsp, */ static zebra_snhlfe_t *snhlfe_add(zebra_slsp_t *slsp, enum nexthop_types_t gtype, - union g_addr *gate, ifindex_t ifindex, + const union g_addr *gate, ifindex_t ifindex, mpls_label_t out_label) { zebra_snhlfe_t *snhlfe; @@ -2746,7 +2746,7 @@ int mpls_ftn_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type, mpls_label_t in_label, uint8_t num_out_labels, mpls_label_t out_labels[], enum nexthop_types_t gtype, - union g_addr *gate, ifindex_t ifindex) + const union g_addr *gate, ifindex_t ifindex) { struct hash *lsp_table; zebra_ile_t tmp_ile; @@ -2759,11 +2759,12 @@ int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type, if (!lsp_table) return -1; - /* If entry is present, exit. */ + /* Find or create LSP object */ tmp_ile.in_label = in_label; lsp = hash_get(lsp_table, &tmp_ile, lsp_alloc); if (!lsp) return -1; + nhlfe = nhlfe_find(lsp, type, gtype, gate, ifindex); if (nhlfe) { struct nexthop *nh = nhlfe->nexthop; @@ -2780,8 +2781,8 @@ int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type, return 0; if (IS_ZEBRA_DEBUG_MPLS) { - char buf2[BUFSIZ]; - char buf3[BUFSIZ]; + char buf2[MPLS_LABEL_STRLEN]; + char buf3[MPLS_LABEL_STRLEN]; nhlfe2str(nhlfe, buf, BUFSIZ); mpls_label2str(num_out_labels, out_labels, buf2, @@ -2842,7 +2843,7 @@ int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type, */ int mpls_lsp_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, mpls_label_t in_label, enum nexthop_types_t gtype, - union g_addr *gate, ifindex_t ifindex) + const union g_addr *gate, ifindex_t ifindex) { struct hash *lsp_table; zebra_ile_t tmp_ile; @@ -3056,11 +3057,12 @@ int zebra_mpls_static_lsp_add(struct zebra_vrf *zvrf, mpls_label_t in_label, if (!slsp_table) return -1; - /* If entry is present, exit. */ + /* Find or create LSP. */ tmp_ile.in_label = in_label; slsp = hash_get(slsp_table, &tmp_ile, slsp_alloc); if (!slsp) return -1; + snhlfe = snhlfe_find(slsp, gtype, gate, ifindex); if (snhlfe) { if (snhlfe->out_label == out_label) diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h index 2489e8e510..33cb614346 100644 --- a/zebra/zebra_mpls.h +++ b/zebra/zebra_mpls.h @@ -288,7 +288,7 @@ int mpls_ftn_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type, mpls_label_t in_label, uint8_t num_out_labels, mpls_label_t out_labels[], enum nexthop_types_t gtype, - union g_addr *gate, ifindex_t ifindex); + const union g_addr *gate, ifindex_t ifindex); /* * Uninstall a particular NHLFE in the forwarding table. If this is @@ -296,7 +296,7 @@ int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type, */ int mpls_lsp_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, mpls_label_t in_label, enum nexthop_types_t gtype, - union g_addr *gate, ifindex_t ifindex); + const union g_addr *gate, ifindex_t ifindex); /* * Uninstall all NHLFEs for a particular LSP forwarding entry. diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 7f229b3872..fceddcb745 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -63,6 +63,9 @@ static struct nhg_hash_entry * depends_find_id_add(struct nhg_connected_tree_head *head, uint32_t id); static void depends_decrement_free(struct nhg_connected_tree_head *head); +static struct nhg_backup_info * +nhg_backup_copy(const struct nhg_backup_info *orig); + static void nhg_connected_free(struct nhg_connected *dep) { @@ -295,7 +298,7 @@ static void zebra_nhg_set_if(struct nhg_hash_entry *nhe, struct interface *ifp) static void zebra_nhg_connect_depends(struct nhg_hash_entry *nhe, - struct nhg_connected_tree_head nhg_depends) + struct nhg_connected_tree_head *nhg_depends) { struct nhg_connected *rb_node_dep = NULL; @@ -304,31 +307,58 @@ zebra_nhg_connect_depends(struct nhg_hash_entry *nhe, * for now. Otherwise, their might be a time trade-off for repeated * alloc/frees as startup. */ - nhe->nhg_depends = nhg_depends; + nhe->nhg_depends = *nhg_depends; /* Attach backpointer to anything that it depends on */ zebra_nhg_dependents_init(nhe); if (!zebra_nhg_depends_is_empty(nhe)) { frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: nhe %p (%u), dep %p (%u)", + __func__, nhe, nhe->id, + rb_node_dep->nhe, + rb_node_dep->nhe->id); + zebra_nhg_dependents_add(rb_node_dep->nhe, nhe); } } +} - /* Add the ifp now if its not a group or recursive and has ifindex */ - if (zebra_nhg_depends_is_empty(nhe) && nhe->nhg.nexthop - && nhe->nhg.nexthop->ifindex) { - struct interface *ifp = NULL; +/* Init an nhe, for use in a hash lookup for example */ +void zebra_nhe_init(struct nhg_hash_entry *nhe, afi_t afi, + const struct nexthop *nh) +{ + memset(nhe, 0, sizeof(struct nhg_hash_entry)); + nhe->vrf_id = VRF_DEFAULT; + nhe->type = ZEBRA_ROUTE_NHG; + nhe->afi = AFI_UNSPEC; - ifp = if_lookup_by_index(nhe->nhg.nexthop->ifindex, - nhe->nhg.nexthop->vrf_id); - if (ifp) - zebra_nhg_set_if(nhe, ifp); - else - flog_err( - EC_ZEBRA_IF_LOOKUP_FAILED, - "Zebra failed to lookup an interface with ifindex=%d in vrf=%u for NHE id=%u", - nhe->nhg.nexthop->ifindex, - nhe->nhg.nexthop->vrf_id, nhe->id); + /* There are some special rules that apply to groups representing + * a single nexthop. + */ + if (nh && (nh->next == NULL)) { + switch (nh->type) { + case (NEXTHOP_TYPE_IFINDEX): + case (NEXTHOP_TYPE_BLACKHOLE): + /* + * This switch case handles setting the afi different + * for ipv4/v6 routes. Ifindex/blackhole nexthop + * objects cannot be ambiguous, they must be Address + * Family specific. If we get here, we will either use + * the AF of the route, or the one we got passed from + * here from the kernel. + */ + nhe->afi = afi; + break; + case (NEXTHOP_TYPE_IPV4_IFINDEX): + case (NEXTHOP_TYPE_IPV4): + nhe->afi = AFI_IP; + break; + case (NEXTHOP_TYPE_IPV6_IFINDEX): + case (NEXTHOP_TYPE_IPV6): + nhe->afi = AFI_IP6; + break; + } } } @@ -341,7 +371,7 @@ struct nhg_hash_entry *zebra_nhg_alloc(void) return nhe; } -static struct nhg_hash_entry *zebra_nhg_copy(const struct nhg_hash_entry *copy, +static struct nhg_hash_entry *zebra_nhg_copy(const struct nhg_hash_entry *orig, uint32_t id) { struct nhg_hash_entry *nhe; @@ -350,14 +380,18 @@ static struct nhg_hash_entry *zebra_nhg_copy(const struct nhg_hash_entry *copy, nhe->id = id; - nexthop_group_copy(&(nhe->nhg), &(copy->nhg)); + nexthop_group_copy(&(nhe->nhg), &(orig->nhg)); - nhe->vrf_id = copy->vrf_id; - nhe->afi = copy->afi; - nhe->type = copy->type ? copy->type : ZEBRA_ROUTE_NHG; + nhe->vrf_id = orig->vrf_id; + nhe->afi = orig->afi; + nhe->type = orig->type ? orig->type : ZEBRA_ROUTE_NHG; nhe->refcnt = 0; nhe->dplane_ref = zebra_router_get_next_sequence(); + /* Copy backup info also, if present */ + if (orig->backup_info) + nhe->backup_info = nhg_backup_copy(orig->backup_info); + return nhe; } @@ -372,7 +406,25 @@ static void *zebra_nhg_hash_alloc(void *arg) /* Mark duplicate nexthops in a group at creation time. */ nexthop_group_mark_duplicates(&(nhe->nhg)); - zebra_nhg_connect_depends(nhe, copy->nhg_depends); + zebra_nhg_connect_depends(nhe, &(copy->nhg_depends)); + + /* Add the ifp now if it's not a group or recursive and has ifindex */ + if (zebra_nhg_depends_is_empty(nhe) && nhe->nhg.nexthop + && nhe->nhg.nexthop->ifindex) { + struct interface *ifp = NULL; + + ifp = if_lookup_by_index(nhe->nhg.nexthop->ifindex, + nhe->nhg.nexthop->vrf_id); + if (ifp) + zebra_nhg_set_if(nhe, ifp); + else + flog_err( + EC_ZEBRA_IF_LOOKUP_FAILED, + "Zebra failed to lookup an interface with ifindex=%d in vrf=%u for NHE id=%u", + nhe->nhg.nexthop->ifindex, + nhe->nhg.nexthop->vrf_id, nhe->id); + } + zebra_nhg_insert_id(nhe); return nhe; @@ -381,12 +433,17 @@ static void *zebra_nhg_hash_alloc(void *arg) uint32_t zebra_nhg_hash_key(const void *arg) { const struct nhg_hash_entry *nhe = arg; + uint32_t val, key = 0x5a351234; + + val = nexthop_group_hash(&(nhe->nhg)); + if (nhe->backup_info) { + val = jhash_2words(val, + nexthop_group_hash( + &(nhe->backup_info->nhe->nhg)), + key); + } - uint32_t key = 0x5a351234; - - key = jhash_3words(nhe->vrf_id, nhe->afi, - nexthop_group_hash(&(nhe->nhg)), - key); + key = jhash_3words(nhe->vrf_id, nhe->afi, val, key); return key; } @@ -398,6 +455,50 @@ uint32_t zebra_nhg_id_key(const void *arg) return nhe->id; } +/* Helper with common nhg/nhe nexthop comparison logic */ +static bool nhg_compare_nexthops(const struct nexthop *nh1, + const struct nexthop *nh2) +{ + if (nh1 && !nh2) + return false; + + if (!nh1 && nh2) + return false; + + /* + * We have to check the active flag of each individual one, + * not just the overall active_num. This solves the special case + * issue of a route with a nexthop group with one nexthop + * resolving to itself and thus marking it inactive. If we + * have two different routes each wanting to mark a different + * nexthop inactive, they need to hash to two different groups. + * + * If we just hashed on num_active, they would hash the same + * which is incorrect. + * + * ex) + * 1.1.1.0/24 + * -> 1.1.1.1 dummy1 (inactive) + * -> 1.1.2.1 dummy2 + * + * 1.1.2.0/24 + * -> 1.1.1.1 dummy1 + * -> 1.1.2.1 dummy2 (inactive) + * + * Without checking each individual one, they would hash to + * the same group and both have 1.1.1.1 dummy1 marked inactive. + * + */ + if (CHECK_FLAG(nh1->flags, NEXTHOP_FLAG_ACTIVE) + != CHECK_FLAG(nh2->flags, NEXTHOP_FLAG_ACTIVE)) + return false; + + if (!nexthop_same(nh1, nh2)) + return false; + + return true; +} + bool zebra_nhg_hash_equal(const void *arg1, const void *arg2) { const struct nhg_hash_entry *nhe1 = arg1; @@ -415,45 +516,44 @@ bool zebra_nhg_hash_equal(const void *arg1, const void *arg2) if (nhe1->afi != nhe2->afi) return false; - /* Nexthops should be sorted */ + /* Nexthops should be in-order, so we simply compare them in-place */ for (nexthop1 = nhe1->nhg.nexthop, nexthop2 = nhe2->nhg.nexthop; nexthop1 || nexthop2; nexthop1 = nexthop1->next, nexthop2 = nexthop2->next) { - if (nexthop1 && !nexthop2) - return false; - if (!nexthop1 && nexthop2) + if (!nhg_compare_nexthops(nexthop1, nexthop2)) return false; + } - /* - * We have to check the active flag of each individual one, - * not just the overall active_num. This solves the special case - * issue of a route with a nexthop group with one nexthop - * resolving to itself and thus marking it inactive. If we - * have two different routes each wanting to mark a different - * nexthop inactive, they need to hash to two different groups. - * - * If we just hashed on num_active, they would hash the same - * which is incorrect. - * - * ex) - * 1.1.1.0/24 - * -> 1.1.1.1 dummy1 (inactive) - * -> 1.1.2.1 dummy2 - * - * 1.1.2.0/24 - * -> 1.1.1.1 dummy1 - * -> 1.1.2.1 dummy2 (inactive) - * - * Without checking each individual one, they would hash to - * the same group and both have 1.1.1.1 dummy1 marked inactive. - * - */ - if (CHECK_FLAG(nexthop1->flags, NEXTHOP_FLAG_ACTIVE) - != CHECK_FLAG(nexthop2->flags, NEXTHOP_FLAG_ACTIVE)) - return false; + /* If there's no backup info, comparison is done. */ + if ((nhe1->backup_info == NULL) && (nhe2->backup_info == NULL)) + return true; - if (!nexthop_same(nexthop1, nexthop2)) + /* Compare backup info also - test the easy things first */ + if (nhe1->backup_info && (nhe2->backup_info == NULL)) + return false; + if (nhe2->backup_info && (nhe1->backup_info == NULL)) + return false; + + /* Compare number of backups before actually comparing any */ + for (nexthop1 = nhe1->backup_info->nhe->nhg.nexthop, + nexthop2 = nhe2->backup_info->nhe->nhg.nexthop; + nexthop1 && nexthop2; + nexthop1 = nexthop1->next, nexthop2 = nexthop2->next) { + ; + } + + /* Did we find the end of one list before the other? */ + if (nexthop1 || nexthop2) + return false; + + /* Have to compare the backup nexthops */ + for (nexthop1 = nhe1->backup_info->nhe->nhg.nexthop, + nexthop2 = nhe2->backup_info->nhe->nhg.nexthop; + nexthop1 || nexthop2; + nexthop1 = nexthop1->next, nexthop2 = nexthop2->next) { + + if (!nhg_compare_nexthops(nexthop1, nexthop2)) return false; } @@ -512,29 +612,185 @@ static void handle_recursive_depend(struct nhg_connected_tree_head *nhg_depends, resolved_ng.nexthop = nh; + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: head %p, nh %pNHv", + __func__, nhg_depends, nh); + depend = zebra_nhg_rib_find(0, &resolved_ng, afi); + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: nh %pNHv => %p (%u)", + __func__, nh, depend, + depend ? depend->id : 0); + if (depend) depends_add(nhg_depends, depend); } +/* + * Lookup an nhe in the global hash, using data from another nhe. If 'lookup' + * has an id value, that's used. Create a new global/shared nhe if not found. + */ +static bool zebra_nhe_find(struct nhg_hash_entry **nhe, /* return value */ + struct nhg_hash_entry *lookup, + struct nhg_connected_tree_head *nhg_depends, + afi_t afi) +{ + bool created = false; + bool recursive = false; + struct nhg_hash_entry *newnhe, *backup_nhe; + struct nexthop *nh = NULL; + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: id %u, lookup %p, vrf %d, type %d, depends %p", + __func__, lookup->id, lookup, + lookup->vrf_id, lookup->type, + nhg_depends); + + if (lookup->id) + (*nhe) = zebra_nhg_lookup_id(lookup->id); + else + (*nhe) = hash_lookup(zrouter.nhgs, lookup); + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: lookup => %p (%u)", + __func__, (*nhe), + (*nhe) ? (*nhe)->id : 0); + + /* If we found an existing object, we're done */ + if (*nhe) + goto done; + + /* We're going to create/insert a new nhe: + * assign the next global id value if necessary. + */ + if (lookup->id == 0) + lookup->id = ++id_counter; + newnhe = hash_get(zrouter.nhgs, lookup, zebra_nhg_hash_alloc); + created = true; + + /* Mail back the new object */ + *nhe = newnhe; + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: => created %p (%u)", __func__, newnhe, + newnhe->id); + + /* Only hash/lookup the depends if the first lookup + * fails to find something. This should hopefully save a + * lot of cycles for larger ecmp sizes. + */ + if (nhg_depends) { + /* If you don't want to hash on each nexthop in the + * nexthop group struct you can pass the depends + * directly. Kernel-side we do this since it just looks + * them up via IDs. + */ + zebra_nhg_connect_depends(newnhe, nhg_depends); + goto done; + } + + /* Prepare dependency relationships if this is not a + * singleton nexthop. There are two cases: a single + * recursive nexthop, where we need a relationship to the + * resolving nexthop; or a group of nexthops, where we need + * relationships with the corresponding singletons. + */ + zebra_nhg_depends_init(lookup); + + nh = newnhe->nhg.nexthop; + + if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_ACTIVE)) + SET_FLAG(newnhe->flags, NEXTHOP_GROUP_VALID); + + if (nh->next == NULL) { + if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RECURSIVE)) { + /* Single recursive nexthop */ + handle_recursive_depend(&newnhe->nhg_depends, + nh->resolved, afi); + recursive = true; + } + } else { + /* List of nexthops */ + for (nh = newnhe->nhg.nexthop; nh; nh = nh->next) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: depends NH %pNHv %s", + __func__, nh, + CHECK_FLAG(nh->flags, + NEXTHOP_FLAG_RECURSIVE) ? + "(R)" : ""); + + depends_find_add(&newnhe->nhg_depends, nh, afi); + } + } + + if (recursive) + SET_FLAG((*nhe)->flags, NEXTHOP_GROUP_RECURSIVE); + + if (zebra_nhg_get_backup_nhg(newnhe) == NULL || + zebra_nhg_get_backup_nhg(newnhe)->nexthop == NULL) + goto done; + + /* If there are backup nexthops, add them to the backup + * depends tree. The rules here are a little different. + */ + recursive = false; + backup_nhe = newnhe->backup_info->nhe; + + nh = backup_nhe->nhg.nexthop; + + /* Singleton recursive NH */ + if (nh->next == NULL && + CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RECURSIVE)) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: backup depend NH %pNHv (R)", + __func__, nh); + + /* Single recursive nexthop */ + handle_recursive_depend(&backup_nhe->nhg_depends, + nh->resolved, afi); + recursive = true; + } else { + /* One or more backup NHs */ + for (; nh; nh = nh->next) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: backup depend NH %pNHv %s", + __func__, nh, + CHECK_FLAG(nh->flags, + NEXTHOP_FLAG_RECURSIVE) ? + "(R)" : ""); + + depends_find_add(&backup_nhe->nhg_depends, + nh, afi); + } + } + + if (recursive) + SET_FLAG(backup_nhe->flags, NEXTHOP_GROUP_RECURSIVE); + +done: + + return created; +} + +/* + * Lookup or create an nhe, based on an nhg or an nhe id. + */ static bool zebra_nhg_find(struct nhg_hash_entry **nhe, uint32_t id, struct nexthop_group *nhg, struct nhg_connected_tree_head *nhg_depends, vrf_id_t vrf_id, afi_t afi, int type) { struct nhg_hash_entry lookup = {}; - - uint32_t old_id_counter = id_counter; - bool created = false; - bool recursive = false; - /* - * If it has an id at this point, we must have gotten it from the kernel - */ - lookup.id = id ? id : ++id_counter; + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: id %u, nhg %p, vrf %d, type %d, depends %p", + __func__, id, nhg, vrf_id, type, + nhg_depends); + /* Use a temporary nhe and call into the superset/common code */ + lookup.id = id; lookup.type = type ? type : ZEBRA_ROUTE_NHG; lookup.nhg = *nhg; @@ -567,53 +823,8 @@ static bool zebra_nhg_find(struct nhg_hash_entry **nhe, uint32_t id, } } - if (id) - (*nhe) = zebra_nhg_lookup_id(id); - else - (*nhe) = hash_lookup(zrouter.nhgs, &lookup); - - /* If it found an nhe in our tables, this new ID is unused */ - if (*nhe) - id_counter = old_id_counter; - - if (!(*nhe)) { - /* Only hash/lookup the depends if the first lookup - * fails to find something. This should hopefully save a - * lot of cycles for larger ecmp sizes. - */ - if (nhg_depends) - /* If you don't want to hash on each nexthop in the - * nexthop group struct you can pass the depends - * directly. Kernel-side we do this since it just looks - * them up via IDs. - */ - lookup.nhg_depends = *nhg_depends; - else { - if (nhg->nexthop->next) { - zebra_nhg_depends_init(&lookup); - - /* If its a group, create a dependency tree */ - struct nexthop *nh = NULL; - - for (nh = nhg->nexthop; nh; nh = nh->next) - depends_find_add(&lookup.nhg_depends, - nh, afi); - } else if (CHECK_FLAG(nhg->nexthop->flags, - NEXTHOP_FLAG_RECURSIVE)) { - zebra_nhg_depends_init(&lookup); - handle_recursive_depend(&lookup.nhg_depends, - nhg->nexthop->resolved, - afi); - recursive = true; - } - } - - (*nhe) = hash_get(zrouter.nhgs, &lookup, zebra_nhg_hash_alloc); - created = true; + created = zebra_nhe_find(nhe, &lookup, nhg_depends, afi); - if (recursive) - SET_FLAG((*nhe)->flags, NEXTHOP_GROUP_RECURSIVE); - } return created; } @@ -629,6 +840,10 @@ zebra_nhg_find_nexthop(uint32_t id, struct nexthop *nh, afi_t afi, int type) zebra_nhg_find(&nhe, id, &nhg, NULL, vrf_id, afi, type); + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: nh %pNHv => %p (%u)", + __func__, nh, nhe, nhe ? nhe->id : 0); + return nhe; } @@ -807,6 +1022,9 @@ done: static void zebra_nhg_release(struct nhg_hash_entry *nhe) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: nhe %p (%u)", __func__, nhe, nhe->id); + /* Remove it from any lists it may be on */ zebra_nhg_depends_release(nhe); zebra_nhg_dependents_release(nhe); @@ -872,6 +1090,10 @@ static int nhg_ctx_process_new(struct nhg_ctx *ctx) lookup = zebra_nhg_lookup_id(id); + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: id %u, count %d, lookup => %p", + __func__, id, count, lookup); + if (lookup) { /* This is already present in our table, hence an update * that we did not initate. @@ -919,6 +1141,11 @@ static int nhg_ctx_process_new(struct nhg_ctx *ctx) */ kernel_nhe = zebra_nhg_copy(nhe, id); + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: copying kernel nhe (%u), dup of %u", + __func__, id, nhe->id); + zebra_nhg_insert_id(kernel_nhe); zebra_nhg_set_unhashable(kernel_nhe); } else if (zebra_nhg_contains_unhashable(nhe)) { @@ -926,10 +1153,18 @@ static int nhg_ctx_process_new(struct nhg_ctx *ctx) * depend, so lets mark this group as unhashable as well * and release it from the non-ID hash. */ + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: nhe %p (%u) unhashable", + __func__, nhe, nhe->id); + hash_release(zrouter.nhgs, nhe); zebra_nhg_set_unhashable(nhe); } else { /* It actually created a new nhe */ + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: nhe %p (%u) is new", + __func__, nhe, nhe->id); + SET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID); SET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED); } @@ -1038,6 +1273,10 @@ int zebra_nhg_kernel_find(uint32_t id, struct nexthop *nh, struct nh_grp *grp, { struct nhg_ctx *ctx = NULL; + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: nh %pNHv, id %u, count %d", + __func__, nh, id, (int)count); + if (id > id_counter) /* Increase our counter so we don't try to create * an ID that already exists @@ -1111,12 +1350,17 @@ static struct nhg_hash_entry *depends_find_singleton(const struct nexthop *nh, /* The copy may have allocated labels; free them if necessary. */ nexthop_del_labels(&lookup); + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: nh %pNHv => %p (%u)", + __func__, nh, nhe, nhe ? nhe->id : 0); + return nhe; } static struct nhg_hash_entry *depends_find(const struct nexthop *nh, afi_t afi) { struct nhg_hash_entry *nhe = NULL; + char rbuf[10]; if (!nh) goto done; @@ -1124,10 +1368,18 @@ static struct nhg_hash_entry *depends_find(const struct nexthop *nh, afi_t afi) /* We are separating these functions out to increase handling speed * in the non-recursive case (by not alloc/freeing) */ - if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RECURSIVE)) + if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RECURSIVE)) { nhe = depends_find_recursive(nh, afi); - else + strlcpy(rbuf, "(R)", sizeof(rbuf)); + } else { nhe = depends_find_singleton(nh, afi); + rbuf[0] = '\0'; + } + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: nh %pNHv %s => %p (%u)", + __func__, nh, rbuf, + nhe, nhe ? nhe->id : 0); done: return nhe; @@ -1136,6 +1388,10 @@ done: static void depends_add(struct nhg_connected_tree_head *head, struct nhg_hash_entry *depend) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: head %p nh %pNHv", + __func__, head, depend->nhg.nexthop); + /* If NULL is returned, it was successfully added and * needs to have its refcnt incremented. * @@ -1154,6 +1410,10 @@ depends_find_add(struct nhg_connected_tree_head *head, struct nexthop *nh, depend = depends_find(nh, afi); + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: nh %pNHv => %p", + __func__, nh, depend); + if (depend) depends_add(head, depend); @@ -1179,7 +1439,7 @@ static void depends_decrement_free(struct nhg_connected_tree_head *head) nhg_connected_tree_free(head); } -/* Rib-side, you get a nexthop group struct */ +/* Find an nhe based on a list of nexthops */ struct nhg_hash_entry * zebra_nhg_rib_find(uint32_t id, struct nexthop_group *nhg, afi_t rt_afi) { @@ -1195,13 +1455,107 @@ zebra_nhg_rib_find(uint32_t id, struct nexthop_group *nhg, afi_t rt_afi) zebra_nhg_find(&nhe, id, nhg, NULL, vrf_id, rt_afi, 0); + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: => nhe %p (%u)", + __func__, nhe, nhe ? nhe->id : 0); + + return nhe; +} + +/* Find an nhe based on a route's nhe */ +struct nhg_hash_entry * +zebra_nhg_rib_find_nhe(struct nhg_hash_entry *rt_nhe, afi_t rt_afi) +{ + struct nhg_hash_entry *nhe = NULL; + + if (!(rt_nhe && rt_nhe->nhg.nexthop)) { + flog_err(EC_ZEBRA_TABLE_LOOKUP_FAILED, + "No nexthop passed to %s", __func__); + return NULL; + } + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: rt_nhe %p (%u)", + __func__, rt_nhe, + rt_nhe ? rt_nhe->id : 0); + + zebra_nhe_find(&nhe, rt_nhe, NULL, rt_afi); + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: => nhe %p (%u)", + __func__, nhe, nhe ? nhe->id : 0); + return nhe; } +/* + * Allocate backup nexthop info object. Typically these are embedded in + * nhg_hash_entry objects. + */ +struct nhg_backup_info *zebra_nhg_backup_alloc(void) +{ + struct nhg_backup_info *p; + + p = XCALLOC(MTYPE_NHG, sizeof(struct nhg_backup_info)); + + p->nhe = zebra_nhg_alloc(); + + /* Identify the embedded group used to hold the list of backups */ + SET_FLAG(p->nhe->flags, NEXTHOP_GROUP_BACKUP); + + return p; +} + +/* + * Free backup nexthop info object, deal with any embedded allocations + */ +void zebra_nhg_backup_free(struct nhg_backup_info **p) +{ + if (p && *p) { + if ((*p)->nhe) + zebra_nhg_free((*p)->nhe); + + XFREE(MTYPE_NHG, (*p)); + } +} + +/* Accessor for backup nexthop group */ +struct nexthop_group *zebra_nhg_get_backup_nhg(struct nhg_hash_entry *nhe) +{ + struct nexthop_group *p = NULL; + + if (nhe) { + if (nhe->backup_info && nhe->backup_info->nhe) + p = &(nhe->backup_info->nhe->nhg); + } + + return p; +} + +/* + * Helper to return a copy of a backup_info - note that this is a shallow + * copy, meant to be used when creating a new nhe from info passed in with + * a route e.g. + */ +static struct nhg_backup_info * +nhg_backup_copy(const struct nhg_backup_info *orig) +{ + struct nhg_backup_info *b; + + b = zebra_nhg_backup_alloc(); + + /* Copy list of nexthops */ + nexthop_group_copy(&(b->nhe->nhg), &(orig->nhe->nhg)); + + return b; +} + static void zebra_nhg_free_members(struct nhg_hash_entry *nhe) { nexthops_free(nhe->nhg.nexthop); + zebra_nhg_backup_free(&nhe->backup_info); + /* Decrement to remove connection ref */ nhg_connected_tree_decrement_ref(&nhe->nhg_depends); nhg_connected_tree_free(&nhe->nhg_depends); @@ -1210,6 +1564,21 @@ static void zebra_nhg_free_members(struct nhg_hash_entry *nhe) void zebra_nhg_free(struct nhg_hash_entry *nhe) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) { + /* Group or singleton? */ + if (nhe->nhg.nexthop && nhe->nhg.nexthop->next) + zlog_debug("%s: nhe %p (%u), refcnt %d", + __func__, nhe, + (nhe ? nhe->id : 0), + (nhe ? nhe->refcnt : 0)); + else + zlog_debug("%s: nhe %p (%u), refcnt %d, NH %pNHv", + __func__, nhe, + (nhe ? nhe->id : 0), + (nhe ? nhe->refcnt : 0), + nhe->nhg.nexthop); + } + if (nhe->refcnt) zlog_debug("nhe_id=%u hash refcnt=%d", nhe->id, nhe->refcnt); @@ -1225,6 +1594,11 @@ void zebra_nhg_hash_free(void *p) void zebra_nhg_decrement_ref(struct nhg_hash_entry *nhe) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: nhe %p (%u) %d => %d", + __func__, nhe, nhe->id, nhe->refcnt, + nhe->refcnt - 1); + nhe->refcnt--; if (!zebra_nhg_depends_is_empty(nhe)) @@ -1236,6 +1610,11 @@ void zebra_nhg_decrement_ref(struct nhg_hash_entry *nhe) void zebra_nhg_increment_ref(struct nhg_hash_entry *nhe) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: nhe %p (%u) %d => %d", + __func__, nhe, nhe->id, nhe->refcnt, + nhe->refcnt + 1); + nhe->refcnt++; if (!zebra_nhg_depends_is_empty(nhe)) @@ -1385,6 +1764,10 @@ static int nexthop_active(afi_t afi, struct route_entry *re, nexthop->resolved = NULL; re->nexthop_mtu = 0; + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: re %p, nexthop %pNHv", + __func__, re, nexthop); + /* * If the kernel has sent us a NEW route, then * by golly gee whiz it's a good route. @@ -1533,6 +1916,12 @@ static int nexthop_active(afi_t afi, struct route_entry *re, || nexthop->type == NEXTHOP_TYPE_IPV6) nexthop->ifindex = newhop->ifindex; } + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: CONNECT match %p (%u), newhop %pNHv", + __func__, match, + match->nhe->id, newhop); + return 1; } else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_ALLOW_RECURSION)) { resolved = 0; @@ -1543,6 +1932,11 @@ static int nexthop_active(afi_t afi, struct route_entry *re, if (!nexthop_valid_resolve(nexthop, newhop)) continue; + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: RECURSIVE match %p (%u), newhop %pNHv", + __func__, match, + match->nhe->id, newhop); + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE); nexthop_set_resolved(afi, newhop, nexthop); @@ -1565,6 +1959,11 @@ static int nexthop_active(afi_t afi, struct route_entry *re, if (!nexthop_valid_resolve(nexthop, newhop)) continue; + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug("%s: STATIC match %p (%u), newhop %pNHv", + __func__, match, + match->nhe->id, newhop); + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE); nexthop_set_resolved(afi, newhop, nexthop); @@ -1683,11 +2082,11 @@ static unsigned nexthop_active_check(struct route_node *rn, default: break; } + if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) { if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug( - " %s: Unable to find a active nexthop", - __func__); + zlog_debug(" %s: Unable to find active nexthop", + __func__); return 0; } @@ -1737,46 +2136,68 @@ static unsigned nexthop_active_check(struct route_node *rn, return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); } +/* Helper function called after resolution to walk nhg rb trees + * and toggle the NEXTHOP_GROUP_VALID flag if the nexthop + * is active on singleton NHEs. + */ +static bool zebra_nhg_set_valid_if_active(struct nhg_hash_entry *nhe) +{ + struct nhg_connected *rb_node_dep = NULL; + bool valid = false; + + if (!zebra_nhg_depends_is_empty(nhe)) { + /* Is at least one depend valid? */ + frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) { + if (zebra_nhg_set_valid_if_active(rb_node_dep->nhe)) + valid = true; + } + + goto done; + } + + /* should be fully resolved singleton at this point */ + if (CHECK_FLAG(nhe->nhg.nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + valid = true; + +done: + if (valid) + SET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID); + + return valid; +} + /* - * Iterate over all nexthops of the given RIB entry and refresh their - * ACTIVE flag. If any nexthop is found to toggle the ACTIVE flag, - * the whole re structure is flagged with ROUTE_ENTRY_CHANGED. - * - * Return value is the new number of active nexthops. + * Process a list of nexthops, given the head of the list, determining + * whether each one is ACTIVE/installable at this time. */ -int nexthop_active_update(struct route_node *rn, struct route_entry *re) +static uint32_t nexthop_list_active_update(struct route_node *rn, + struct route_entry *re, + struct nexthop *nexthop) { - struct nexthop_group new_grp = {}; - struct nexthop *nexthop; union g_addr prev_src; unsigned int prev_active, new_active; ifindex_t prev_index; - uint8_t curr_active = 0; + uint32_t counter = 0; - afi_t rt_afi = family2afi(rn->p.family); - - UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED); - - /* Copy over the nexthops in current state */ - nexthop_group_copy(&new_grp, &(re->nhe->nhg)); - - for (nexthop = new_grp.nexthop; nexthop; nexthop = nexthop->next) { + /* Process nexthops one-by-one */ + for ( ; nexthop; nexthop = nexthop->next) { /* No protocol daemon provides src and so we're skipping - * tracking it */ + * tracking it + */ prev_src = nexthop->rmap_src; prev_active = CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); prev_index = nexthop->ifindex; /* * We need to respect the multipath_num here * as that what we should be able to install from - * a multipath perpsective should not be a data plane + * a multipath perspective should not be a data plane * decision point. */ new_active = nexthop_active_check(rn, re, nexthop); - if (new_active && curr_active >= zrouter.multipath_num) { + if (new_active && counter >= zrouter.multipath_num) { struct nexthop *nh; /* Set it and its resolved nexthop as inactive. */ @@ -1787,7 +2208,7 @@ int nexthop_active_update(struct route_node *rn, struct route_entry *re) } if (new_active) - curr_active++; + counter++; /* Don't allow src setting on IPv6 addr for now */ if (prev_active != new_active || prev_index != nexthop->ifindex @@ -1803,48 +2224,122 @@ int nexthop_active_update(struct route_node *rn, struct route_entry *re) SET_FLAG(re->status, ROUTE_ENTRY_CHANGED); } + return counter; +} + +/* + * Iterate over all nexthops of the given RIB entry and refresh their + * ACTIVE flag. If any nexthop is found to toggle the ACTIVE flag, + * the whole re structure is flagged with ROUTE_ENTRY_CHANGED. + * + * Return value is the new number of active nexthops. + */ +int nexthop_active_update(struct route_node *rn, struct route_entry *re) +{ + struct nhg_hash_entry *curr_nhe; + uint32_t curr_active = 0, backup_active = 0; + + afi_t rt_afi = family2afi(rn->p.family); + + UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED); + + /* Make a local copy of the existing nhe, so we don't work on/modify + * the shared nhe. + */ + curr_nhe = zebra_nhg_copy(re->nhe, re->nhe->id); + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: re %p nhe %p (%u), curr_nhe %p", + __func__, re, re->nhe, re->nhe->id, + curr_nhe); + + /* Clear the existing id, if any: this will avoid any confusion + * if the id exists, and will also force the creation + * of a new nhe reflecting the changes we may make in this local copy. + */ + curr_nhe->id = 0; + + /* Process nexthops */ + curr_active = nexthop_list_active_update(rn, re, curr_nhe->nhg.nexthop); + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: re %p curr_active %u", __func__, re, + curr_active); + + /* If there are no backup nexthops, we are done */ + if (zebra_nhg_get_backup_nhg(curr_nhe) == NULL) + goto backups_done; + + backup_active = nexthop_list_active_update( + rn, re, zebra_nhg_get_backup_nhg(curr_nhe)->nexthop); + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: re %p backup_active %u", __func__, re, + backup_active); + +backups_done: + + /* + * Ref or create an nhe that matches the current state of the + * nexthop(s). + */ if (CHECK_FLAG(re->status, ROUTE_ENTRY_CHANGED)) { struct nhg_hash_entry *new_nhe = NULL; - new_nhe = zebra_nhg_rib_find(0, &new_grp, rt_afi); + new_nhe = zebra_nhg_rib_find_nhe(curr_nhe, rt_afi); + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: re %p CHANGED: nhe %p (%u) => new_nhe %p (%u)", + __func__, re, re->nhe, + re->nhe->id, new_nhe, new_nhe->id); route_entry_update_nhe(re, new_nhe); } - if (curr_active) { - struct nhg_hash_entry *nhe = NULL; - - nhe = zebra_nhg_lookup_id(re->nhe_id); - if (nhe) - SET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID); - else - flog_err( - EC_ZEBRA_TABLE_LOOKUP_FAILED, - "Active update on NHE id=%u that we do not have in our tables", - re->nhe_id); - } + /* Walk the NHE depends tree and toggle NEXTHOP_GROUP_VALID + * flag where appropriate. + */ + if (curr_active) + zebra_nhg_set_valid_if_active(re->nhe); /* - * Do not need these nexthops anymore since they - * were either copied over into an nhe or not + * Do not need the old / copied nhe anymore since it + * was either copied over into a new nhe or not * used at all. */ - nexthops_free(new_grp.nexthop); + zebra_nhg_free(curr_nhe); return curr_active; } -/* Convert a nhe into a group array */ -uint8_t zebra_nhg_nhe2grp(struct nh_grp *grp, struct nhg_hash_entry *nhe, - int max_num) +/* Recursively construct a grp array of fully resolved IDs. + * + * This function allows us to account for groups within groups, + * by converting them into a flat array of IDs. + * + * nh_grp is modified at every level of recursion to append + * to it the next unique, fully resolved ID from the entire tree. + * + * + * Note: + * I'm pretty sure we only allow ONE level of group within group currently. + * But making this recursive just in case that ever changes. + */ +static uint8_t zebra_nhg_nhe2grp_internal(struct nh_grp *grp, + uint8_t curr_index, + struct nhg_hash_entry *nhe, + int max_num) { struct nhg_connected *rb_node_dep = NULL; struct nhg_hash_entry *depend = NULL; - uint8_t i = 0; + uint8_t i = curr_index; frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) { bool duplicate = false; + if (i >= max_num) + goto done; + depend = rb_node_dep->nhe; /* @@ -1861,27 +2356,78 @@ uint8_t zebra_nhg_nhe2grp(struct nh_grp *grp, struct nhg_hash_entry *nhe, } } - /* Check for duplicate IDs, kernel doesn't like that */ - for (int j = 0; j < i; j++) { - if (depend->id == grp[j].id) - duplicate = true; - } + if (!zebra_nhg_depends_is_empty(depend)) { + /* This is a group within a group */ + i = zebra_nhg_nhe2grp_internal(grp, i, depend, max_num); + } else { + if (!CHECK_FLAG(depend->flags, NEXTHOP_GROUP_VALID)) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED + || IS_ZEBRA_DEBUG_NHG) + zlog_debug( + "%s: Nexthop ID (%u) not valid, not appending to dataplane install group", + __func__, depend->id); + continue; + } + + /* If the nexthop not installed/queued for install don't + * put in the ID array. + */ + if (!(CHECK_FLAG(depend->flags, NEXTHOP_GROUP_INSTALLED) + || CHECK_FLAG(depend->flags, + NEXTHOP_GROUP_QUEUED))) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED + || IS_ZEBRA_DEBUG_NHG) + zlog_debug( + "%s: Nexthop ID (%u) not installed or queued for install, not appending to dataplane install group", + __func__, depend->id); + continue; + } + + /* Check for duplicate IDs, ignore if found. */ + for (int j = 0; j < i; j++) { + if (depend->id == grp[j].id) { + duplicate = true; + break; + } + } + + if (duplicate) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED + || IS_ZEBRA_DEBUG_NHG) + zlog_debug( + "%s: Nexthop ID (%u) is duplicate, not appending to dataplane install group", + __func__, depend->id); + continue; + } - if (!duplicate) { grp[i].id = depend->id; - /* We aren't using weights for anything right now */ grp[i].weight = depend->nhg.nexthop->weight; i++; } - - if (i >= max_num) - goto done; } + if (nhe->backup_info == NULL || nhe->backup_info->nhe == NULL) + goto done; + + /* TODO -- For now, we are not trying to use or install any + * backup info in this nexthop-id path: we aren't prepared + * to use the backups here yet. We're just debugging what we find. + */ + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: skipping backup nhe", __func__); + done: return i; } +/* Convert a nhe into a group array */ +uint8_t zebra_nhg_nhe2grp(struct nh_grp *grp, struct nhg_hash_entry *nhe, + int max_num) +{ + /* Call into the recursive function */ + return zebra_nhg_nhe2grp_internal(grp, 0, nhe, max_num); +} + void zebra_nhg_install_kernel(struct nhg_hash_entry *nhe) { struct nhg_connected *rb_node_dep = NULL; @@ -1894,7 +2440,8 @@ void zebra_nhg_install_kernel(struct nhg_hash_entry *nhe) zebra_nhg_install_kernel(rb_node_dep->nhe); } - if (!CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED) + if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_VALID) + && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED) && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED)) { /* Change its type to us since we are installing it */ nhe->type = ZEBRA_ROUTE_NHG; @@ -1955,7 +2502,7 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx) id = dplane_ctx_get_nhe_id(ctx); - if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL || IS_ZEBRA_DEBUG_NHG_DETAIL) zlog_debug( "Nexthop dplane ctx %p, op %s, nexthop ID (%u), result %s", ctx, dplane_op2str(op), id, dplane_res2str(status)); diff --git a/zebra/zebra_nhg.h b/zebra/zebra_nhg.h index dc3a47c020..0a9e97ab48 100644 --- a/zebra/zebra_nhg.h +++ b/zebra/zebra_nhg.h @@ -50,6 +50,9 @@ struct nhg_hash_entry { struct nexthop_group nhg; + /* If supported, a mapping of backup nexthops. */ + struct nhg_backup_info *backup_info; + /* If this is not a group, it * will be a single nexthop * and must have an interface @@ -72,6 +75,7 @@ struct nhg_hash_entry { * faster with ID's. */ struct nhg_connected_tree_head nhg_depends, nhg_dependents; + /* * Is this nexthop group valid, ie all nexthops are fully resolved. * What is fully resolved? It's a nexthop that is either self contained @@ -102,11 +106,25 @@ struct nhg_hash_entry { * from the kernel. Therefore, it is unhashable. */ #define NEXTHOP_GROUP_UNHASHABLE (1 << 4) + +/* + * Backup nexthop support - identify groups that are backups for + * another group. + */ +#define NEXTHOP_GROUP_BACKUP (1 << 5) + }; /* Was this one we created, either this session or previously? */ #define ZEBRA_NHG_CREATED(NHE) ((NHE->type) == ZEBRA_ROUTE_NHG) +/* + * Backup nexthops: this is a group object itself, so + * that the backup nexthops can use the same code as a normal object. + */ +struct nhg_backup_info { + struct nhg_hash_entry *nhe; +}; enum nhg_ctx_op_e { NHG_CTX_OP_NONE = 0, @@ -162,13 +180,26 @@ bool zebra_nhg_kernel_nexthops_enabled(void); /** * NHE abstracted tree functions. - * Use these where possible instead of the direct ones access ones. + * Use these where possible instead of direct access. */ struct nhg_hash_entry *zebra_nhg_alloc(void); void zebra_nhg_free(struct nhg_hash_entry *nhe); /* In order to clear a generic hash, we need a generic api, sigh. */ void zebra_nhg_hash_free(void *p); +/* Init an nhe, for use in a hash lookup for example. There's some fuzziness + * if the nhe represents only a single nexthop, so we try to capture that + * variant also. + */ +void zebra_nhe_init(struct nhg_hash_entry *nhe, afi_t afi, + const struct nexthop *nh); + +/* Allocate, free backup nexthop info objects */ +struct nhg_backup_info *zebra_nhg_backup_alloc(void); +void zebra_nhg_backup_free(struct nhg_backup_info **p); + +struct nexthop_group *zebra_nhg_get_backup_nhg(struct nhg_hash_entry *nhe); + extern struct nhg_hash_entry *zebra_nhg_resolve(struct nhg_hash_entry *nhe); extern unsigned int zebra_nhg_depends_count(const struct nhg_hash_entry *nhe); @@ -203,10 +234,14 @@ extern int zebra_nhg_kernel_find(uint32_t id, struct nexthop *nh, /* Del via kernel */ extern int zebra_nhg_kernel_del(uint32_t id, vrf_id_t vrf_id); -/* Find via route creation */ +/* Find an nhe based on a nexthop_group */ extern struct nhg_hash_entry * zebra_nhg_rib_find(uint32_t id, struct nexthop_group *nhg, afi_t rt_afi); +/* Find an nhe based on a route's nhe, used during route creation */ +struct nhg_hash_entry * +zebra_nhg_rib_find_nhe(struct nhg_hash_entry *rt_nhe, afi_t rt_afi); + /* Reference counter functions */ extern void zebra_nhg_decrement_ref(struct nhg_hash_entry *nhe); extern void zebra_nhg_increment_ref(struct nhg_hash_entry *nhe); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 29d59b515f..58967de778 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -112,7 +112,7 @@ static const struct { /* no entry/default: 150 */ }; -static void __attribute__((format(printf, 5, 6))) +static void PRINTFRR(5, 6) _rnode_zlog(const char *_func, vrf_id_t vrf_id, struct route_node *rn, int priority, const char *msgfmt, ...) { @@ -213,7 +213,7 @@ static void route_entry_attach_ref(struct route_entry *re, int route_entry_update_nhe(struct route_entry *re, struct nhg_hash_entry *new) { - struct nhg_hash_entry *old = NULL; + struct nhg_hash_entry *old; int ret = 0; if (new == NULL) { @@ -223,7 +223,7 @@ int route_entry_update_nhe(struct route_entry *re, struct nhg_hash_entry *new) goto done; } - if (re->nhe_id != new->id) { + if ((re->nhe_id != 0) && (re->nhe_id != new->id)) { old = re->nhe; route_entry_attach_ref(re, new); @@ -2338,7 +2338,6 @@ static void rib_addnode(struct route_node *rn, void rib_unlink(struct route_node *rn, struct route_entry *re) { rib_dest_t *dest; - struct nhg_hash_entry *nhe = NULL; assert(rn && re); @@ -2353,11 +2352,10 @@ void rib_unlink(struct route_node *rn, struct route_entry *re) if (dest->selected_fib == re) dest->selected_fib = NULL; - if (re->nhe_id) { - nhe = zebra_nhg_lookup_id(re->nhe_id); - if (nhe) - zebra_nhg_decrement_ref(nhe); - } else if (re->nhe->nhg.nexthop) + if (re->nhe && re->nhe_id) { + assert(re->nhe->id == re->nhe_id); + zebra_nhg_decrement_ref(re->nhe); + } else if (re->nhe && re->nhe->nhg.nexthop) nexthops_free(re->nhe->nhg.nexthop); nexthops_free(re->fib_ng.nexthop); @@ -2396,11 +2394,75 @@ void rib_delnode(struct route_node *rn, struct route_entry *re) } } +/* + * Helper that debugs a single nexthop within a route-entry + */ +static void _route_entry_dump_nh(const struct route_entry *re, + const char *straddr, + const struct nexthop *nexthop) +{ + char nhname[PREFIX_STRLEN]; + char backup_str[50]; + char wgt_str[50]; + struct interface *ifp; + struct vrf *vrf = vrf_lookup_by_id(nexthop->vrf_id); + + switch (nexthop->type) { + case NEXTHOP_TYPE_BLACKHOLE: + sprintf(nhname, "Blackhole"); + break; + case NEXTHOP_TYPE_IFINDEX: + ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id); + sprintf(nhname, "%s", ifp ? ifp->name : "Unknown"); + break; + case NEXTHOP_TYPE_IPV4: + /* fallthrough */ + case NEXTHOP_TYPE_IPV4_IFINDEX: + inet_ntop(AF_INET, &nexthop->gate, nhname, INET6_ADDRSTRLEN); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + inet_ntop(AF_INET6, &nexthop->gate, nhname, INET6_ADDRSTRLEN); + break; + } + + backup_str[0] = '\0'; + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + snprintf(backup_str, sizeof(backup_str), "backup %d,", + (int)nexthop->backup_idx); + } + + wgt_str[0] = '\0'; + if (nexthop->weight) + snprintf(wgt_str, sizeof(wgt_str), "wgt %d,", nexthop->weight); + + zlog_debug("%s: %s %s[%u] vrf %s(%u) %s%s with flags %s%s%s%s%s", + straddr, (nexthop->rparent ? " NH" : "NH"), nhname, + nexthop->ifindex, vrf ? vrf->name : "Unknown", + nexthop->vrf_id, + wgt_str, backup_str, + (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE) + ? "ACTIVE " + : ""), + (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED) + ? "FIB " + : ""), + (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE) + ? "RECURSIVE " + : ""), + (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK) + ? "ONLINK " + : ""), + (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE) + ? "DUPLICATE " + : "")); + +} + /* This function dumps the contents of a given RE entry into * standard debug log. Calling function name and IP prefix in * question are passed as 1st and 2nd arguments. */ - void _route_entry_dump(const char *func, union prefixconstptr pp, union prefixconstptr src_pp, const struct route_entry *re) @@ -2409,9 +2471,9 @@ void _route_entry_dump(const char *func, union prefixconstptr pp, bool is_srcdst = src_p && src_p->prefixlen; char straddr[PREFIX_STRLEN]; char srcaddr[PREFIX_STRLEN]; - char nhname[PREFIX_STRLEN]; struct nexthop *nexthop; struct vrf *vrf = vrf_lookup_by_id(re->vrf_id); + struct nexthop_group *nhg; zlog_debug("%s: dumping RE entry %p for %s%s%s vrf %s(%u)", func, (const void *)re, prefix2str(pp, straddr, sizeof(straddr)), @@ -2422,65 +2484,32 @@ void _route_entry_dump(const char *func, union prefixconstptr pp, zlog_debug("%s: uptime == %lu, type == %u, instance == %d, table == %d", straddr, (unsigned long)re->uptime, re->type, re->instance, re->table); - zlog_debug( - "%s: metric == %u, mtu == %u, distance == %u, flags == %u, status == %u", - straddr, re->metric, re->mtu, re->distance, re->flags, re->status); + zlog_debug("%s: metric == %u, mtu == %u, distance == %u, flags == %u, status == %u", + straddr, re->metric, re->mtu, re->distance, re->flags, + re->status); zlog_debug("%s: nexthop_num == %u, nexthop_active_num == %u", straddr, nexthop_group_nexthop_num(&(re->nhe->nhg)), nexthop_group_active_nexthop_num(&(re->nhe->nhg))); - for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) { - struct interface *ifp; - struct vrf *vrf = vrf_lookup_by_id(nexthop->vrf_id); + /* Dump nexthops */ + for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) + _route_entry_dump_nh(re, straddr, nexthop); - switch (nexthop->type) { - case NEXTHOP_TYPE_BLACKHOLE: - sprintf(nhname, "Blackhole"); - break; - case NEXTHOP_TYPE_IFINDEX: - ifp = if_lookup_by_index(nexthop->ifindex, - nexthop->vrf_id); - sprintf(nhname, "%s", ifp ? ifp->name : "Unknown"); - break; - case NEXTHOP_TYPE_IPV4: - /* fallthrough */ - case NEXTHOP_TYPE_IPV4_IFINDEX: - inet_ntop(AF_INET, &nexthop->gate, nhname, - INET6_ADDRSTRLEN); - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - inet_ntop(AF_INET6, &nexthop->gate, nhname, - INET6_ADDRSTRLEN); - break; - } - zlog_debug("%s: %s %s[%u] vrf %s(%u) with flags %s%s%s%s%s", - straddr, (nexthop->rparent ? " NH" : "NH"), nhname, - nexthop->ifindex, vrf ? vrf->name : "Unknown", - nexthop->vrf_id, - (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE) - ? "ACTIVE " - : ""), - (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED) - ? "FIB " - : ""), - (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE) - ? "RECURSIVE " - : ""), - (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK) - ? "ONLINK " - : ""), - (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE) - ? "DUPLICATE " - : "")); + if (zebra_nhg_get_backup_nhg(re->nhe)) { + zlog_debug("%s: backup nexthops:", straddr); + + nhg = zebra_nhg_get_backup_nhg(re->nhe); + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) + _route_entry_dump_nh(re, straddr, nexthop); } + zlog_debug("%s: dump complete", straddr); } -/* This is an exported helper to rtm_read() to dump the strange +/* + * This is an exported helper to rtm_read() to dump the strange * RE entry found by rib_lookup_ipv4_route() */ - void rib_lookup_and_dump(struct prefix_ipv4 *p, vrf_id_t vrf_id) { struct route_table *table; @@ -2574,9 +2603,16 @@ void rib_lookup_and_pushup(struct prefix_ipv4 *p, vrf_id_t vrf_id) } } -int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, - struct prefix_ipv6 *src_p, struct route_entry *re, - struct nexthop_group *ng) +/* + * Internal route-add implementation; there are a couple of different public + * signatures. Callers in this path are responsible for the memory they + * allocate: if they allocate a nexthop_group or backup nexthop info, they + * must free those objects. If this returns < 0, an error has occurred and the + * route_entry 're' has not been captured; the caller should free that also. + */ +int rib_add_multipath_nhe(afi_t afi, safi_t safi, struct prefix *p, + struct prefix_ipv6 *src_p, struct route_entry *re, + struct nhg_hash_entry *re_nhe) { struct nhg_hash_entry *nhe = NULL; struct route_table *table; @@ -2584,41 +2620,31 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, struct route_entry *same = NULL; int ret = 0; - if (!re) - return 0; + if (!re || !re_nhe) + return -1; assert(!src_p || !src_p->prefixlen || afi == AFI_IP6); /* Lookup table. */ table = zebra_vrf_get_table_with_table_id(afi, safi, re->vrf_id, re->table); - if (!table) { - if (ng) - nexthop_group_delete(&ng); - XFREE(MTYPE_RE, re); - return 0; - } + if (!table) + return -1; - if (re->nhe_id) { - nhe = zebra_nhg_lookup_id(re->nhe_id); + if (re_nhe->id > 0) { + nhe = zebra_nhg_lookup_id(re_nhe->id); if (!nhe) { flog_err( EC_ZEBRA_TABLE_LOOKUP_FAILED, "Zebra failed to find the nexthop hash entry for id=%u in a route entry", - re->nhe_id); - XFREE(MTYPE_RE, re); + re_nhe->id); + return -1; } } else { - nhe = zebra_nhg_rib_find(0, ng, afi); - - /* - * The nexthops got copied over into an nhe, - * so free them now. - */ - nexthop_group_delete(&ng); - + /* Lookup nhe from route information */ + nhe = zebra_nhg_rib_find_nhe(re_nhe, afi); if (!nhe) { char buf[PREFIX_STRLEN] = ""; char buf2[PREFIX_STRLEN] = ""; @@ -2631,7 +2657,6 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, src_p ? prefix2str(src_p, buf2, sizeof(buf2)) : ""); - XFREE(MTYPE_RE, re); return -1; } } @@ -2709,15 +2734,51 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, ret = 1; /* Free implicit route.*/ - if (same) { + if (same) rib_delnode(rn, same); - ret = -1; - } route_unlock_node(rn); return ret; } +/* + * Add a single route. + */ +int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, + struct prefix_ipv6 *src_p, struct route_entry *re, + struct nexthop_group *ng) +{ + int ret; + struct nhg_hash_entry nhe; + + if (!re) + return -1; + + /* We either need nexthop(s) or an existing nexthop id */ + if (ng == NULL && re->nhe_id == 0) + return -1; + + /* + * Use a temporary nhe to convey info to the common/main api. + */ + zebra_nhe_init(&nhe, afi, (ng ? ng->nexthop : NULL)); + if (ng) + nhe.nhg.nexthop = ng->nexthop; + else if (re->nhe_id > 0) + nhe.id = re->nhe_id; + + ret = rib_add_multipath_nhe(afi, safi, p, src_p, re, &nhe); + + /* In this path, the callers expect memory to be freed. */ + nexthop_group_delete(&ng); + + /* In error cases, free the route also */ + if (ret < 0) + XFREE(MTYPE_RE, re); + + return ret; +} + void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, unsigned short instance, int flags, struct prefix *p, struct prefix_ipv6 *src_p, const struct nexthop *nh, @@ -3188,6 +3249,9 @@ void rib_sweep_table(struct route_table *table) if (!table) return; + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug("%s: starting", __func__); + for (rn = route_top(table); rn; rn = srcdest_route_next(rn)) { RNODE_FOREACH_RE_SAFE (rn, re, next) { @@ -3234,6 +3298,9 @@ void rib_sweep_table(struct route_table *table) rib_delnode(rn, re); } } + + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug("%s: ends", __func__); } /* Sweep all RIB tables. */ diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index c392303760..dfa7d5ae92 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -60,8 +60,13 @@ static void zebra_vrf_add_update(struct zebra_vrf *zvrf) if (IS_ZEBRA_DEBUG_EVENT) zlog_debug("MESSAGE: ZEBRA_VRF_ADD %s", zvrf_name(zvrf)); - for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) + for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + /* Do not send unsolicited messages to synchronous clients. */ + if (client->synchronous) + continue; + zsend_vrf_add(client, zvrf); + } } static void zebra_vrf_delete_update(struct zebra_vrf *zvrf) @@ -72,8 +77,13 @@ static void zebra_vrf_delete_update(struct zebra_vrf *zvrf) if (IS_ZEBRA_DEBUG_EVENT) zlog_debug("MESSAGE: ZEBRA_VRF_DELETE %s", zvrf_name(zvrf)); - for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) + for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + /* Do not send unsolicited messages to synchronous clients. */ + if (client->synchronous) + continue; + zsend_vrf_delete(client, zvrf); + } } void zebra_vrf_update_all(struct zserv *client) diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index ccc6e9e46b..590ec57087 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -164,7 +164,8 @@ DEFUN (show_ip_rpf_addr, return CMD_SUCCESS; } -static char re_status_output_char(struct route_entry *re, struct nexthop *nhop) +static char re_status_output_char(const struct route_entry *re, + const struct nexthop *nhop) { if (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)) { if (!CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_DUPLICATE) && @@ -187,6 +188,152 @@ static char re_status_output_char(struct route_entry *re, struct nexthop *nhop) return ' '; } +/* + * TODO -- Show backup nexthop info + */ +static void show_nh_backup_helper(struct vty *vty, + const struct nhg_hash_entry *nhe, + const struct nexthop *nexthop) +{ + /* Double-check that there _is_ a backup */ + if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) + return; + + /* Locate the backup nexthop */ + + /* Format the backup (indented) */ + +} + +/* + * Helper api to format output for a nexthop, used in the 'detailed' + * output path. + */ +static void show_nexthop_detail_helper(struct vty *vty, + const struct route_entry *re, + const struct nexthop *nexthop) +{ + char addrstr[32]; + char buf[MPLS_LABEL_STRLEN]; + + vty_out(vty, " %c%s", + re_status_output_char(re, nexthop), + nexthop->rparent ? " " : ""); + + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + vty_out(vty, " %s", + inet_ntoa(nexthop->gate.ipv4)); + if (nexthop->ifindex) + vty_out(vty, ", via %s", + ifindex2ifname( + nexthop->ifindex, + nexthop->vrf_id)); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + vty_out(vty, " %s", + inet_ntop(AF_INET6, &nexthop->gate.ipv6, + buf, sizeof(buf))); + if (nexthop->ifindex) + vty_out(vty, ", via %s", + ifindex2ifname( + nexthop->ifindex, + nexthop->vrf_id)); + break; + + case NEXTHOP_TYPE_IFINDEX: + vty_out(vty, " directly connected, %s", + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); + break; + case NEXTHOP_TYPE_BLACKHOLE: + vty_out(vty, " unreachable"); + switch (nexthop->bh_type) { + case BLACKHOLE_REJECT: + vty_out(vty, " (ICMP unreachable)"); + break; + case BLACKHOLE_ADMINPROHIB: + vty_out(vty, + " (ICMP admin-prohibited)"); + break; + case BLACKHOLE_NULL: + vty_out(vty, " (blackhole)"); + break; + case BLACKHOLE_UNSPEC: + break; + } + break; + default: + break; + } + + if ((re->vrf_id != nexthop->vrf_id) + && (nexthop->type != NEXTHOP_TYPE_BLACKHOLE)) { + struct vrf *vrf = + vrf_lookup_by_id(nexthop->vrf_id); + + if (vrf) + vty_out(vty, "(vrf %s)", vrf->name); + else + vty_out(vty, "(vrf UNKNOWN)"); + } + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) + vty_out(vty, " (duplicate nexthop removed)"); + + if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + vty_out(vty, " inactive"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) + vty_out(vty, " onlink"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + vty_out(vty, " (recursive)"); + + /* Source specified? */ + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + if (nexthop->src.ipv4.s_addr) { + if (inet_ntop(AF_INET, &nexthop->src.ipv4, + addrstr, sizeof(addrstr))) + vty_out(vty, ", src %s", + addrstr); + } + break; + + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, + &in6addr_any)) { + if (inet_ntop(AF_INET6, &nexthop->src.ipv6, + addrstr, sizeof(addrstr))) + vty_out(vty, ", src %s", + addrstr); + } + break; + + default: + break; + } + + if (re->nexthop_mtu) + vty_out(vty, ", mtu %u", re->nexthop_mtu); + + /* Label information */ + if (nexthop->nh_label && nexthop->nh_label->num_labels) { + vty_out(vty, ", label %s", + mpls_label2str(nexthop->nh_label->num_labels, + nexthop->nh_label->label, buf, + sizeof(buf), 1 /*pretty*/)); + } + + if (nexthop->weight) + vty_out(vty, ", weight %u", nexthop->weight); +} + /* New RIB. Detailed information for IPv4 route. */ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn, int mcast, bool use_fib, bool show_ng) @@ -253,129 +400,122 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn, vty_out(vty, " Nexthop Group ID: %u\n", re->nhe_id); for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) { - char addrstr[32]; - - vty_out(vty, " %c%s", - re_status_output_char(re, nexthop), - nexthop->rparent ? " " : ""); - - switch (nexthop->type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - vty_out(vty, " %s", - inet_ntoa(nexthop->gate.ipv4)); - if (nexthop->ifindex) - vty_out(vty, ", via %s", - ifindex2ifname( - nexthop->ifindex, - nexthop->vrf_id)); - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - vty_out(vty, " %s", - inet_ntop(AF_INET6, &nexthop->gate.ipv6, - buf, sizeof(buf))); - if (nexthop->ifindex) - vty_out(vty, ", via %s", - ifindex2ifname( - nexthop->ifindex, - nexthop->vrf_id)); - break; - case NEXTHOP_TYPE_IFINDEX: - vty_out(vty, " directly connected, %s", - ifindex2ifname(nexthop->ifindex, - nexthop->vrf_id)); - break; - case NEXTHOP_TYPE_BLACKHOLE: - vty_out(vty, " unreachable"); - switch (nexthop->bh_type) { - case BLACKHOLE_REJECT: - vty_out(vty, " (ICMP unreachable)"); - break; - case BLACKHOLE_ADMINPROHIB: - vty_out(vty, - " (ICMP admin-prohibited)"); - break; - case BLACKHOLE_NULL: - vty_out(vty, " (blackhole)"); - break; - case BLACKHOLE_UNSPEC: - break; - } - break; - default: - break; - } - - if ((re->vrf_id != nexthop->vrf_id) - && (nexthop->type != NEXTHOP_TYPE_BLACKHOLE)) { - struct vrf *vrf = - vrf_lookup_by_id(nexthop->vrf_id); - - if (vrf) - vty_out(vty, "(vrf %s)", vrf->name); - else - vty_out(vty, "(vrf UNKNOWN)"); - } + /* Use helper to format each nexthop */ + show_nexthop_detail_helper(vty, re, nexthop); + vty_out(vty, "\n"); - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) - vty_out(vty, " (duplicate nexthop removed)"); + /* Include backup info, if present */ + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) + show_nh_backup_helper(vty, re->nhe, nexthop); + } + vty_out(vty, "\n"); + } +} - if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) - vty_out(vty, " inactive"); +/* + * Helper for nexthop output, used in the 'show ip route' path + */ +static void show_route_nexthop_helper(struct vty *vty, + const struct route_entry *re, + const struct nexthop *nexthop) +{ + char buf[MPLS_LABEL_STRLEN]; + + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + vty_out(vty, " via %s", inet_ntoa(nexthop->gate.ipv4)); + if (nexthop->ifindex) + vty_out(vty, ", %s", + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + vty_out(vty, " via %s", + inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf, + sizeof(buf))); + if (nexthop->ifindex) + vty_out(vty, ", %s", + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); + break; - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) - vty_out(vty, " onlink"); + case NEXTHOP_TYPE_IFINDEX: + vty_out(vty, " is directly connected, %s", + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); + break; + case NEXTHOP_TYPE_BLACKHOLE: + vty_out(vty, " unreachable"); + switch (nexthop->bh_type) { + case BLACKHOLE_REJECT: + vty_out(vty, " (ICMP unreachable)"); + break; + case BLACKHOLE_ADMINPROHIB: + vty_out(vty, " (ICMP admin-prohibited)"); + break; + case BLACKHOLE_NULL: + vty_out(vty, " (blackhole)"); + break; + case BLACKHOLE_UNSPEC: + break; + } + break; + default: + break; + } - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - vty_out(vty, " (recursive)"); + if ((re == NULL || (nexthop->vrf_id != re->vrf_id)) && + (nexthop->type != NEXTHOP_TYPE_BLACKHOLE)) { + struct vrf *vrf = vrf_lookup_by_id(nexthop->vrf_id); - switch (nexthop->type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - if (nexthop->src.ipv4.s_addr) { - if (inet_ntop(AF_INET, - &nexthop->src.ipv4, - addrstr, sizeof(addrstr))) - vty_out(vty, ", src %s", - addrstr); - } - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, - &in6addr_any)) { - if (inet_ntop(AF_INET6, - &nexthop->src.ipv6, - addrstr, sizeof(addrstr))) - vty_out(vty, ", src %s", - addrstr); - } - break; - default: - break; - } + if (vrf) + vty_out(vty, " (vrf %s)", vrf->name); + else + vty_out(vty, " (vrf UNKNOWN)"); + } - if (re->nexthop_mtu) - vty_out(vty, ", mtu %u", re->nexthop_mtu); + if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + vty_out(vty, " inactive"); - /* Label information */ - if (nexthop->nh_label - && nexthop->nh_label->num_labels) { - vty_out(vty, ", label %s", - mpls_label2str( - nexthop->nh_label->num_labels, - nexthop->nh_label->label, buf, - sizeof(buf), 1)); - } + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) + vty_out(vty, " onlink"); - if (nexthop->weight) - vty_out(vty, ", weight %u", nexthop->weight); + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + vty_out(vty, " (recursive)"); - vty_out(vty, "\n"); + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + if (nexthop->src.ipv4.s_addr) { + if (inet_ntop(AF_INET, &nexthop->src.ipv4, buf, + sizeof(buf))) + vty_out(vty, ", src %s", buf); } - vty_out(vty, "\n"); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, &in6addr_any)) { + if (inet_ntop(AF_INET6, &nexthop->src.ipv6, buf, + sizeof(buf))) + vty_out(vty, ", src %s", buf); + } + break; + default: + break; } + + /* Label information */ + if (nexthop->nh_label && nexthop->nh_label->num_labels) { + vty_out(vty, ", label %s", + mpls_label2str(nexthop->nh_label->num_labels, + nexthop->nh_label->label, buf, + sizeof(buf), 1)); + } + + if ((re == NULL) && nexthop->weight) + vty_out(vty, ", weight %u", nexthop->weight); } static void vty_show_ip_route(struct vty *vty, struct route_node *rn, @@ -660,105 +800,43 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, len - 3 + (2 * nexthop_level(nexthop)), ' '); } - switch (nexthop->type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - vty_out(vty, " via %s", inet_ntoa(nexthop->gate.ipv4)); - if (nexthop->ifindex) - vty_out(vty, ", %s", - ifindex2ifname(nexthop->ifindex, - nexthop->vrf_id)); - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - vty_out(vty, " via %s", - inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf, - sizeof(buf))); - if (nexthop->ifindex) - vty_out(vty, ", %s", - ifindex2ifname(nexthop->ifindex, - nexthop->vrf_id)); - break; - - case NEXTHOP_TYPE_IFINDEX: - vty_out(vty, " is directly connected, %s", - ifindex2ifname(nexthop->ifindex, - nexthop->vrf_id)); - break; - case NEXTHOP_TYPE_BLACKHOLE: - vty_out(vty, " unreachable"); - switch (nexthop->bh_type) { - case BLACKHOLE_REJECT: - vty_out(vty, " (ICMP unreachable)"); - break; - case BLACKHOLE_ADMINPROHIB: - vty_out(vty, " (ICMP admin-prohibited)"); - break; - case BLACKHOLE_NULL: - vty_out(vty, " (blackhole)"); - break; - case BLACKHOLE_UNSPEC: - break; - } - break; - default: - break; - } - - if ((nexthop->vrf_id != re->vrf_id) - && (nexthop->type != NEXTHOP_TYPE_BLACKHOLE)) { - struct vrf *vrf = vrf_lookup_by_id(nexthop->vrf_id); + show_route_nexthop_helper(vty, re, nexthop); - if (vrf) - vty_out(vty, "(vrf %s)", vrf->name); - else - vty_out(vty, "(vrf UNKNOWN)"); - } - - if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) - vty_out(vty, " inactive"); + vty_out(vty, ", %s\n", up_str); - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) - vty_out(vty, " onlink"); + /* Check for backup info */ + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + struct nexthop *backup; + int i; - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - vty_out(vty, " (recursive)"); + if (re->nhe->backup_info == NULL || + re->nhe->backup_info->nhe == NULL) + continue; - switch (nexthop->type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - if (nexthop->src.ipv4.s_addr) { - if (inet_ntop(AF_INET, &nexthop->src.ipv4, buf, - sizeof(buf))) - vty_out(vty, ", src %s", buf); - } - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, &in6addr_any)) { - if (inet_ntop(AF_INET6, &nexthop->src.ipv6, buf, - sizeof(buf))) - vty_out(vty, ", src %s", buf); + i = 0; + for (ALL_NEXTHOPS(re->nhe->backup_info->nhe->nhg, + backup)) { + if (i == nexthop->backup_idx) + break; + i++; } - break; - default: - break; - } - /* Label information */ - if (nexthop->nh_label && nexthop->nh_label->num_labels) { - vty_out(vty, ", label %s", - mpls_label2str(nexthop->nh_label->num_labels, - nexthop->nh_label->label, buf, - sizeof(buf), 1)); + /* Print useful backup info */ + if (backup) { + /* TODO -- install state is not accurate */ + vty_out(vty, " %*c [backup %d]", + /*re_status_output_char(re, backup),*/ + len - 3 + (2 * nexthop_level(nexthop)), + ' ', nexthop->backup_idx); + show_route_nexthop_helper(vty, re, backup); + vty_out(vty, "\n"); + } } - - vty_out(vty, ", %s\n", up_str); } } static void vty_show_ip_route_detail_json(struct vty *vty, - struct route_node *rn, bool use_fib) + struct route_node *rn, bool use_fib) { json_object *json = NULL; json_object *json_prefix = NULL; @@ -1028,9 +1106,8 @@ static void show_nexthop_group_out(struct vty *vty, struct nhg_hash_entry *nhe) { struct nexthop *nexthop = NULL; struct nhg_connected *rb_node_dep = NULL; - char buf[SRCDEST2STR_BUFFER]; - struct vrf *nhe_vrf = vrf_lookup_by_id(nhe->vrf_id); + struct nexthop_group *backup_nhg; vty_out(vty, "ID: %u\n", nhe->id); vty_out(vty, " RefCnt: %d\n", nhe->refcnt); @@ -1062,6 +1139,7 @@ static void show_nexthop_group_out(struct vty *vty, struct nhg_hash_entry *nhe) vty_out(vty, "\n"); } + /* Output nexthops */ for (ALL_NEXTHOPS(nhe->nhg, nexthop)) { if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) vty_out(vty, " "); @@ -1069,100 +1147,56 @@ static void show_nexthop_group_out(struct vty *vty, struct nhg_hash_entry *nhe) /* Make recursive nexthops a bit more clear */ vty_out(vty, " "); - switch (nexthop->type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - vty_out(vty, " %s", inet_ntoa(nexthop->gate.ipv4)); - if (nexthop->ifindex) - vty_out(vty, ", %s", - ifindex2ifname(nexthop->ifindex, - nexthop->vrf_id)); - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - vty_out(vty, " %s", - inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf, - sizeof(buf))); - if (nexthop->ifindex) - vty_out(vty, ", %s", - ifindex2ifname(nexthop->ifindex, - nexthop->vrf_id)); - break; + show_route_nexthop_helper(vty, NULL, nexthop); - case NEXTHOP_TYPE_IFINDEX: - vty_out(vty, " directly connected %s", - ifindex2ifname(nexthop->ifindex, - nexthop->vrf_id)); - break; - case NEXTHOP_TYPE_BLACKHOLE: - vty_out(vty, " unreachable"); - switch (nexthop->bh_type) { - case BLACKHOLE_REJECT: - vty_out(vty, " (ICMP unreachable)"); - break; - case BLACKHOLE_ADMINPROHIB: - vty_out(vty, " (ICMP admin-prohibited)"); - break; - case BLACKHOLE_NULL: - vty_out(vty, " (blackhole)"); - break; - case BLACKHOLE_UNSPEC: - break; - } - break; - default: - break; + if (nhe->backup_info == NULL || nhe->backup_info->nhe == NULL) { + if (CHECK_FLAG(nexthop->flags, + NEXTHOP_FLAG_HAS_BACKUP)) + vty_out(vty, " [backup %d]", + nexthop->backup_idx); + + vty_out(vty, "\n"); + continue; } - struct vrf *vrf = vrf_lookup_by_id(nexthop->vrf_id); + /* TODO -- print more useful backup info */ + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + struct nexthop *backup; + int i; - if (vrf) - vty_out(vty, " (vrf %s)", vrf->name); - else - vty_out(vty, " (vrf UNKNOWN)"); + i = 0; + for (ALL_NEXTHOPS(nhe->backup_info->nhe->nhg, backup)) { + if (i == nexthop->backup_idx) + break; + i++; + } - if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) - vty_out(vty, " inactive"); + /* TODO */ + if (backup) + vty_out(vty, " [backup %d]", + nexthop->backup_idx); + else + vty_out(vty, " [backup INVALID]"); + } - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) - vty_out(vty, " onlink"); + vty_out(vty, "\n"); + } - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - vty_out(vty, " (recursive)"); + /* Output backup nexthops (if any) */ + backup_nhg = zebra_nhg_get_backup_nhg(nhe); + if (backup_nhg) { + vty_out(vty, " Backups:\n"); - switch (nexthop->type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - if (nexthop->src.ipv4.s_addr) { - if (inet_ntop(AF_INET, &nexthop->src.ipv4, buf, - sizeof(buf))) - vty_out(vty, ", src %s", buf); - } - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, &in6addr_any)) { - if (inet_ntop(AF_INET6, &nexthop->src.ipv6, buf, - sizeof(buf))) - vty_out(vty, ", src %s", buf); - } - break; - default: - break; - } + for (ALL_NEXTHOPS_PTR(backup_nhg, nexthop)) { + if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + vty_out(vty, " "); + else + /* Make recursive nexthops a bit more clear */ + vty_out(vty, " "); - /* Label information */ - if (nexthop->nh_label && nexthop->nh_label->num_labels) { - vty_out(vty, ", label %s", - mpls_label2str(nexthop->nh_label->num_labels, - nexthop->nh_label->label, buf, - sizeof(buf), 1)); + show_route_nexthop_helper(vty, NULL, nexthop); + vty_out(vty, "\n"); } - - if (nexthop->weight) - vty_out(vty, ", weight %u", nexthop->weight); - - vty_out(vty, "\n"); } if (!zebra_nhg_dependents_is_empty(nhe)) { diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 2e1daa6fdf..aa2e5c91c9 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -121,11 +121,11 @@ static struct interface *zvni_map_to_macvlan(struct interface *br_if, /* l3-vni next-hop neigh related APIs */ static zebra_neigh_t *zl3vni_nh_lookup(zebra_l3vni_t *zl3vni, - struct ipaddr *ip); + const struct ipaddr *ip); static void *zl3vni_nh_alloc(void *p); static zebra_neigh_t *zl3vni_nh_add(zebra_l3vni_t *zl3vni, - struct ipaddr *vtep_ip, - struct ethaddr *rmac); + const struct ipaddr *vtep_ip, + const struct ethaddr *rmac); static int zl3vni_nh_del(zebra_l3vni_t *zl3vni, zebra_neigh_t *n); static int zl3vni_nh_install(zebra_l3vni_t *zl3vni, zebra_neigh_t *n); static int zl3vni_nh_uninstall(zebra_l3vni_t *zl3vni, zebra_neigh_t *n); @@ -133,10 +133,10 @@ static int zl3vni_nh_uninstall(zebra_l3vni_t *zl3vni, zebra_neigh_t *n); /* l3-vni rmac related APIs */ static void zl3vni_print_rmac_hash(struct hash_bucket *, void *); static zebra_mac_t *zl3vni_rmac_lookup(zebra_l3vni_t *zl3vni, - struct ethaddr *rmac); + const struct ethaddr *rmac); static void *zl3vni_rmac_alloc(void *p); static zebra_mac_t *zl3vni_rmac_add(zebra_l3vni_t *zl3vni, - struct ethaddr *rmac); + const struct ethaddr *rmac); static int zl3vni_rmac_del(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac); static int zl3vni_rmac_install(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac); static int zl3vni_rmac_uninstall(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac); @@ -4434,7 +4434,7 @@ static void zl3vni_cleanup_all(struct hash_bucket *bucket, void *args) } static void rb_find_or_add_host(struct host_rb_tree_entry *hrbe, - struct prefix *host) + const struct prefix *host) { struct host_rb_entry lookup; struct host_rb_entry *hle; @@ -4473,7 +4473,7 @@ static void rb_delete_host(struct host_rb_tree_entry *hrbe, struct prefix *host) * Look up MAC hash entry. */ static zebra_mac_t *zl3vni_rmac_lookup(zebra_l3vni_t *zl3vni, - struct ethaddr *rmac) + const struct ethaddr *rmac) { zebra_mac_t tmp; zebra_mac_t *pmac; @@ -4502,7 +4502,8 @@ static void *zl3vni_rmac_alloc(void *p) /* * Add RMAC entry to l3-vni */ -static zebra_mac_t *zl3vni_rmac_add(zebra_l3vni_t *zl3vni, struct ethaddr *rmac) +static zebra_mac_t *zl3vni_rmac_add(zebra_l3vni_t *zl3vni, + const struct ethaddr *rmac) { zebra_mac_t tmp_rmac; zebra_mac_t *zrmac = NULL; @@ -4632,9 +4633,10 @@ static int zl3vni_rmac_uninstall(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac) } /* handle rmac add */ -static int zl3vni_remote_rmac_add(zebra_l3vni_t *zl3vni, struct ethaddr *rmac, - struct ipaddr *vtep_ip, - struct prefix *host_prefix) +static int zl3vni_remote_rmac_add(zebra_l3vni_t *zl3vni, + const struct ethaddr *rmac, + const struct ipaddr *vtep_ip, + const struct prefix *host_prefix) { char buf[ETHER_ADDR_STRLEN]; char buf1[INET6_ADDRSTRLEN]; @@ -4709,7 +4711,8 @@ static void zl3vni_remote_rmac_del(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac, /* * Look up nh hash entry on a l3-vni. */ -static zebra_neigh_t *zl3vni_nh_lookup(zebra_l3vni_t *zl3vni, struct ipaddr *ip) +static zebra_neigh_t *zl3vni_nh_lookup(zebra_l3vni_t *zl3vni, + const struct ipaddr *ip) { zebra_neigh_t tmp; zebra_neigh_t *n; @@ -4739,8 +4742,9 @@ static void *zl3vni_nh_alloc(void *p) /* * Add neighbor entry. */ -static zebra_neigh_t *zl3vni_nh_add(zebra_l3vni_t *zl3vni, struct ipaddr *ip, - struct ethaddr *mac) +static zebra_neigh_t *zl3vni_nh_add(zebra_l3vni_t *zl3vni, + const struct ipaddr *ip, + const struct ethaddr *mac) { zebra_neigh_t tmp_n; zebra_neigh_t *n = NULL; @@ -4822,9 +4826,10 @@ static int zl3vni_nh_uninstall(zebra_l3vni_t *zl3vni, zebra_neigh_t *n) } /* add remote vtep as a neigh entry */ -static int zl3vni_remote_nh_add(zebra_l3vni_t *zl3vni, struct ipaddr *vtep_ip, - struct ethaddr *rmac, - struct prefix *host_prefix) +static int zl3vni_remote_nh_add(zebra_l3vni_t *zl3vni, + const struct ipaddr *vtep_ip, + const struct ethaddr *rmac, + const struct prefix *host_prefix) { char buf[ETHER_ADDR_STRLEN]; char buf1[ETHER_ADDR_STRLEN]; @@ -5960,9 +5965,9 @@ int is_l3vni_for_prefix_routes_only(vni_t vni) } /* handle evpn route in vrf table */ -void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id, struct ethaddr *rmac, - struct ipaddr *vtep_ip, - struct prefix *host_prefix) +void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id, const struct ethaddr *rmac, + const struct ipaddr *vtep_ip, + const struct prefix *host_prefix) { zebra_l3vni_t *zl3vni = NULL; struct ipaddr ipv4_vtep; diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h index 6ca93f6cb6..a5c13a59e3 100644 --- a/zebra/zebra_vxlan.h +++ b/zebra/zebra_vxlan.h @@ -199,9 +199,9 @@ extern void zebra_vxlan_cleanup_tables(struct zebra_vrf *); extern void zebra_vxlan_init(void); extern void zebra_vxlan_disable(void); extern void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id, - struct ethaddr *rmac, - struct ipaddr *ip, - struct prefix *host_prefix); + const struct ethaddr *rmac, + const struct ipaddr *ip, + const struct prefix *host_prefix); extern void zebra_vxlan_evpn_vrf_route_del(vrf_id_t vrf_id, struct ipaddr *vtep_ip, struct prefix *host_prefix); diff --git a/zebra/zserv.h b/zebra/zserv.h index 6ab7fbd918..08df664d56 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -131,6 +131,9 @@ struct zserv { bool notify_owner; + /* Indicates if client is synchronous. */ + bool synchronous; + /* client's protocol */ uint8_t proto; uint16_t instance; |
