summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bfdd/config.c6
-rw-r--r--bgpd/bgp_attr.c59
-rw-r--r--bgpd/bgp_attr.h26
-rw-r--r--bgpd/bgp_bmp.c24
-rw-r--r--bgpd/bgp_damp.c6
-rw-r--r--bgpd/bgp_debug.c4
-rw-r--r--bgpd/bgp_debug.h9
-rw-r--r--bgpd/bgp_dump.c13
-rw-r--r--bgpd/bgp_evpn.c126
-rw-r--r--bgpd/bgp_evpn.h11
-rw-r--r--bgpd/bgp_evpn_vty.c84
-rw-r--r--bgpd/bgp_flowspec_util.c3
-rw-r--r--bgpd/bgp_flowspec_vty.c10
-rw-r--r--bgpd/bgp_label.c18
-rw-r--r--bgpd/bgp_mac.c14
-rw-r--r--bgpd/bgp_mpath.c33
-rw-r--r--bgpd/bgp_mplsvpn.c86
-rw-r--r--bgpd/bgp_nexthop.c19
-rw-r--r--bgpd/bgp_nht.c56
-rw-r--r--bgpd/bgp_rd.c6
-rw-r--r--bgpd/bgp_rd.h5
-rw-r--r--bgpd/bgp_route.c446
-rw-r--r--bgpd/bgp_route.h19
-rw-r--r--bgpd/bgp_routemap.c27
-rw-r--r--bgpd/bgp_rpki.c8
-rw-r--r--bgpd/bgp_snmp.c11
-rw-r--r--bgpd/bgp_table.c43
-rw-r--r--bgpd/bgp_table.h9
-rw-r--r--bgpd/bgp_updgrp_adv.c37
-rw-r--r--bgpd/bgp_updgrp_packet.c56
-rw-r--r--bgpd/bgp_vpn.c13
-rw-r--r--bgpd/bgp_vty.c83
-rw-r--r--bgpd/bgp_zebra.c7
-rw-r--r--bgpd/bgpd.c13
-rw-r--r--bgpd/rfapi/rfapi_import.c6
-rw-r--r--bgpd/rfapi/rfapi_monitor.c2
-rw-r--r--bgpd/rfapi/rfapi_rib.c6
-rw-r--r--bgpd/rfapi/vnc_export_bgp.c61
-rw-r--r--bgpd/rfapi/vnc_export_table.c5
-rw-r--r--bgpd/rfapi/vnc_export_table.h5
-rw-r--r--bgpd/rfapi/vnc_import_bgp.c101
-rw-r--r--bgpd/rfapi/vnc_zebra.c6
-rwxr-xr-xconfigure.ac44
-rw-r--r--ldpd/lde.c11
-rw-r--r--lib/agg_table.h4
-rw-r--r--lib/compiler.h9
-rw-r--r--lib/ipaddr.h2
-rw-r--r--lib/mpls.c2
-rw-r--r--lib/mpls.h5
-rw-r--r--lib/nexthop.c44
-rw-r--r--lib/nexthop.h26
-rw-r--r--lib/nexthop_group.c94
-rw-r--r--lib/nexthop_group.h9
-rw-r--r--lib/northbound_grpc.cpp21
-rw-r--r--lib/plist.c2
-rw-r--r--lib/prefix.h16
-rw-r--r--lib/printfrr.h14
-rw-r--r--lib/smux.h2
-rw-r--r--lib/snmp.c4
-rw-r--r--lib/stream.c2
-rw-r--r--lib/stream.h2
-rw-r--r--lib/table.h4
-rw-r--r--lib/thread.c9
-rw-r--r--lib/vty.c9
-rw-r--r--lib/yang_wrappers.c5
-rw-r--r--lib/zclient.c123
-rw-r--r--lib/zclient.h30
-rw-r--r--pimd/pim_mroute.c5
-rw-r--r--pimd/pim_tlv.c8
-rw-r--r--pimd/pim_zlookup.c14
-rw-r--r--sharpd/sharp_globals.h4
-rw-r--r--sharpd/sharp_vty.c57
-rw-r--r--sharpd/sharp_zebra.c33
-rw-r--r--sharpd/sharp_zebra.h8
-rw-r--r--tests/bgpd/test_bgp_table.c4
-rw-r--r--tests/lib/test_atomlist.c19
-rw-r--r--tests/lib/test_stream.c18
-rw-r--r--tests/lib/test_typelist.c5
-rw-r--r--tests/lib/test_typelist.h8
-rw-r--r--tests/subdir.am3
-rw-r--r--tests/topotests/bgp_rr_ibgp/spine1/show_ip_route.json_ref28
-rw-r--r--tests/topotests/bgp_rr_ibgp/spine2/bgpd.conf8
-rw-r--r--tests/topotests/bgp_rr_ibgp/spine2/show_ip_route.json_ref162
-rw-r--r--tests/topotests/bgp_rr_ibgp/spine2/staticd.conf1
-rw-r--r--tests/topotests/bgp_rr_ibgp/spine2/zebra.conf9
-rwxr-xr-xtests/topotests/bgp_rr_ibgp/test_bgp_rr_ibgp_topo1.py53
-rw-r--r--tests/topotests/bgp_rr_ibgp/tor1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_rr_ibgp/tor1/show_ip_route.json_ref64
-rw-r--r--tests/topotests/bgp_rr_ibgp/tor1/zebra.conf4
-rw-r--r--tests/topotests/bgp_rr_ibgp/tor2/bgpd.conf1
-rw-r--r--tests/topotests/bgp_rr_ibgp/tor2/show_ip_route.json_ref64
-rw-r--r--tests/topotests/bgp_rr_ibgp/tor2/zebra.conf4
-rw-r--r--tests/topotests/pytest.ini2
-rw-r--r--tools/gcc-plugins/.gitignore7
-rw-r--r--tools/gcc-plugins/COPYING.GPLv3674
-rw-r--r--tools/gcc-plugins/Makefile19
-rw-r--r--tools/gcc-plugins/README.md99
-rw-r--r--tools/gcc-plugins/debian/changelog5
-rw-r--r--tools/gcc-plugins/debian/compat1
-rw-r--r--tools/gcc-plugins/debian/control19
-rw-r--r--tools/gcc-plugins/debian/copyright9
-rwxr-xr-xtools/gcc-plugins/debian/rules11
-rw-r--r--tools/gcc-plugins/debian/source/format1
-rw-r--r--tools/gcc-plugins/format-test.c107
-rw-r--r--tools/gcc-plugins/format-test.py57
-rw-r--r--tools/gcc-plugins/frr-format.c4457
-rw-r--r--tools/gcc-plugins/frr-format.h364
-rw-r--r--tools/gcc-plugins/gcc-common.h981
-rw-r--r--tools/gcc-plugins/gcc-retain-typeinfo.patch11
-rw-r--r--watchfrr/watchfrr.c11
-rw-r--r--zebra/redistribute.c51
-rw-r--r--zebra/rib.h10
-rw-r--r--zebra/rt_netlink.c165
-rw-r--r--zebra/zapi_msg.c398
-rw-r--r--zebra/zebra_dplane.c70
-rw-r--r--zebra/zebra_dplane.h8
-rw-r--r--zebra/zebra_mpls.c56
-rw-r--r--zebra/zebra_mpls.h4
-rw-r--r--zebra/zebra_nhg.c891
-rw-r--r--zebra/zebra_nhg.h39
-rw-r--r--zebra/zebra_rib.c239
-rw-r--r--zebra/zebra_vrf.c14
-rw-r--r--zebra/zebra_vty.c608
-rw-r--r--zebra/zebra_vxlan.c45
-rw-r--r--zebra/zebra_vxlan.h6
-rw-r--r--zebra/zserv.h3
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(&copy->gate, &nexthop->gate, sizeof(nexthop->gate));
memcpy(&copy->src, &nexthop->src, sizeof(nexthop->src));
memcpy(&copy->rmap_src, &nexthop->rmap_src, sizeof(nexthop->rmap_src));
diff --git a/lib/nexthop.h b/lib/nexthop.h
index 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' : ' ',
diff --git a/lib/vty.c b/lib/vty.c
index 4dd6ec1b35..8056236de9 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -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 &params,
+ unsigned HOST_WIDE_INT &arg_num);
+
+ void
+ read_any_format_left_precision ();
+
+ bool
+ read_any_format_precision (tree &params,
+ 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 &params,
+ 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 &params,
+ 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 &params,
+ 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,
+ &params, 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 &params,
+ 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,
+ &params, 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 &params,
+ 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 &params,
+ 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, &quoted);
+ 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, &param_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, &param_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, &param_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, &param_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, &param_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, &param_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;