From adbac85e10692e76c01e831d658958c9f269f18f Mon Sep 17 00:00:00 2001 From: Daniel Walton Date: Thu, 5 Nov 2015 17:29:43 +0000 Subject: BGP: support for addpath TX Signed-off-by: Daniel Walton Reviewed-by: Donald Sharp Reviewed-by: Vivek Venkataraman adj_out; adj; adj = adj->next) - SUBGRP_FOREACH_PEER(adj->subgroup, paf) - if (paf->peer == peer) - return adj; - return NULL; -} - int -bgp_adj_out_lookup (struct peer *peer, struct prefix *p, - afi_t afi, safi_t safi, struct bgp_node *rn) +bgp_adj_out_lookup (struct peer *peer, struct bgp_node *rn, + u_int32_t addpath_tx_id) { struct bgp_adj_out *adj; struct peer_af *paf; + afi_t afi; + safi_t safi; + int addpath_capable; for (adj = rn->adj_out; adj; adj = adj->next) SUBGRP_FOREACH_PEER(adj->subgroup, paf) if (paf->peer == peer) { + afi = SUBGRP_AFI (adj->subgroup); + safi = SUBGRP_SAFI (adj->subgroup); + addpath_capable = bgp_addpath_encode_tx (peer, afi, safi); + + /* Match on a specific addpath_tx_id if we are using addpath for this + * peer and if an addpath_tx_id was specified */ + if (addpath_capable && addpath_tx_id && adj->addpath_tx_id != addpath_tx_id) + continue; + return (adj->adv ? (adj->adv->baa ? 1 : 0) : (adj->attr ? 1 : 0)); diff --git a/bgpd/bgp_advertise.h b/bgpd/bgp_advertise.h index d5b737b9e..ae6e6bd14 100644 --- a/bgpd/bgp_advertise.h +++ b/bgpd/bgp_advertise.h @@ -82,6 +82,8 @@ struct bgp_adj_out /* Prefix information. */ struct bgp_node *rn; + u_int32_t addpath_tx_id; + /* Advertised attribute. */ struct attr *attr; @@ -168,9 +170,7 @@ struct bgp_synchronize ? NULL : (F)->next) /* Prototypes. */ -extern int bgp_adj_out_lookup (struct peer *, struct prefix *, afi_t, safi_t, - struct bgp_node *); - +extern int bgp_adj_out_lookup (struct peer *, struct bgp_node *, u_int32_t); extern void bgp_adj_in_set (struct bgp_node *, struct peer *, struct attr *, u_int32_t); extern void bgp_adj_in_unset (struct bgp_node *, struct peer *, u_int32_t); extern void bgp_adj_in_remove (struct bgp_node *, struct bgp_adj_in *); @@ -191,7 +191,5 @@ bgp_advertise_delete (struct bgp_advertise_attr *baa, struct bgp_advertise *adv); extern void bgp_advertise_unintern (struct hash *hash, struct bgp_advertise_attr *baa); -extern struct bgp_adj_out * -bgp_adj_peer_lookup (struct peer *peer, struct bgp_node *rn); #endif /* _QUAGGA_BGP_ADVERTISE_H */ diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 3a031e0db..d37fa1e79 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -2140,8 +2140,6 @@ bgp_attr_check (struct peer *peer, struct attr *attr, bgp_size_t nlri_len) return BGP_ATTR_PARSE_PROCEED; } -int stream_put_prefix (struct stream *, struct prefix *); - size_t bgp_packet_mpattr_start (struct stream *s, afi_t afi, safi_t safi, afi_t nh_afi, struct bpacket_attr_vec_arr *vecarr, @@ -2213,11 +2211,16 @@ bgp_packet_mpattr_start (struct stream *s, afi_t afi, safi_t safi, afi_t nh_afi, void bgp_packet_mpattr_prefix (struct stream *s, afi_t afi, safi_t safi, struct prefix *p, struct prefix_rd *prd, - u_char *tag) + u_char *tag, int addpath_encode, + u_int32_t addpath_tx_id) { switch (safi) { case SAFI_MPLS_VPN: + /* addpath TX ID */ + if (addpath_encode) + stream_putl(s, addpath_tx_id); + /* Tag, RD, Prefix write. */ stream_putc (s, p->prefixlen + 88); stream_put (s, tag, 3); @@ -2226,7 +2229,7 @@ bgp_packet_mpattr_prefix (struct stream *s, afi_t afi, safi_t safi, break; default: /* Prefix write. */ - stream_put_prefix (s, p); + stream_put_prefix_addpath (s, p, addpath_encode, addpath_tx_id); break; } } @@ -2245,7 +2248,9 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, struct stream *s, struct attr *attr, struct bpacket_attr_vec_arr *vecarr, struct prefix *p, afi_t afi, safi_t safi, - struct peer *from, struct prefix_rd *prd, u_char *tag) + struct peer *from, struct prefix_rd *prd, u_char *tag, + int addpath_encode, + u_int32_t addpath_tx_id) { size_t cp; size_t aspath_sizep; @@ -2267,7 +2272,8 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, mpattrlen_pos = bgp_packet_mpattr_start(s, afi, safi, (peer_cap_enhe(peer) ? AFI_IP6 : afi), vecarr, attr); - bgp_packet_mpattr_prefix(s, afi, safi, p, prd, tag); + bgp_packet_mpattr_prefix(s, afi, safi, p, prd, tag, + addpath_encode, addpath_tx_id); bgp_packet_mpattr_end(s, mpattrlen_pos); } @@ -2635,17 +2641,22 @@ bgp_packet_mpunreach_start (struct stream *s, afi_t afi, safi_t safi) void bgp_packet_mpunreach_prefix (struct stream *s, struct prefix *p, afi_t afi, safi_t safi, struct prefix_rd *prd, - u_char *tag) + u_char *tag, int addpath_encode, + u_int32_t addpath_tx_id) { if (safi == SAFI_MPLS_VPN) { + /* addpath TX ID */ + if (addpath_encode) + stream_putl(s, addpath_tx_id); + stream_putc (s, p->prefixlen + 88); stream_put (s, tag, 3); stream_put (s, prd->val, 8); stream_put (s, &p->u.prefix, PSIZE (p->prefixlen)); } else - stream_put_prefix (s, p); + stream_put_prefix_addpath (s, p, addpath_encode, addpath_tx_id); } void @@ -2691,6 +2702,8 @@ bgp_dump_routes_attr (struct stream *s, struct attr *attr, unsigned long len; size_t aspath_lenp; struct aspath *aspath; + int addpath_encode = 0; + u_int32_t addpath_tx_id = 0; /* Remember current pointer. */ cp = stream_get_endp (s); @@ -2810,7 +2823,7 @@ bgp_dump_routes_attr (struct stream *s, struct attr *attr, stream_putc(s, 0); /* Prefix */ - stream_put_prefix(s, prefix); + stream_put_prefix_addpath (s, prefix, addpath_encode, addpath_tx_id); /* Set MP attribute length. */ stream_putc_at (s, sizep, (stream_get_endp (s) - sizep) - 1); diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index a846faf67..789422f64 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -196,9 +196,9 @@ extern bgp_size_t bgp_packet_attribute (struct bgp *bgp, struct peer *, struct bpacket_attr_vec_arr *vecarr, struct prefix *, afi_t, safi_t, struct peer *, struct prefix_rd *, - u_char *); + u_char *, int, u_int32_t); extern void bgp_dump_routes_attr (struct stream *, struct attr *, - struct prefix *); + struct prefix *); extern int attrhash_cmp (const void *, const void *); extern unsigned int attrhash_key_make (void *); extern void attr_show_all (struct vty *); @@ -239,14 +239,15 @@ extern size_t bgp_packet_mpattr_start(struct stream *s, afi_t afi, safi_t safi, 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, - u_char *tag); + u_char *tag, int addpath_encode, + u_int32_t addpath_tx_id); 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, - u_char *tag); + u_char *tag, int, u_int32_t); extern void bgp_packet_mpunreach_end (struct stream *s, size_t attrlen_pnt); static inline int diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index b667c4a2c..d3081887c 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -93,7 +93,7 @@ bgp_nlri_parse_vpnv4 (struct peer *peer, struct attr *attr, u_char *tagpnt; afi_t afi; safi_t safi; - u_char addpath_encoded; + int addpath_encoded; u_int32_t addpath_id; /* Check peer status. */ diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c index e346d7896..2a168899f 100644 --- a/bgpd/bgp_open.c +++ b/bgpd/bgp_open.c @@ -1188,6 +1188,7 @@ bgp_open_capability (struct stream *s, struct peer *peer) u_int32_t restart_time; u_char afi_safi_count = 0; struct utsname names; + int adv_addpath_tx = 0; /* Remember current pointer for Opt Parm Len. */ cp = stream_get_endp (s); @@ -1306,13 +1307,18 @@ bgp_open_capability (struct stream *s, struct peer *peer) local_as = peer->local_as; stream_putl (s, local_as ); - /* AddPath - * For now we will only advertise RX support. TX support will be added later. - */ + /* AddPath */ for (afi = AFI_IP ; afi < AFI_MAX ; afi++) for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++) if (peer->afc[afi][safi]) - afi_safi_count++; + { + afi_safi_count++; + + /* Only advertise addpath TX if a feature that will use it is + * configured */ + if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ADDPATH_TX_ALL_PATHS)) + adv_addpath_tx = 1; + } SET_FLAG (peer->cap, PEER_CAP_ADDPATH_ADV); stream_putc (s, BGP_OPEN_OPT_CAP); @@ -1326,8 +1332,19 @@ bgp_open_capability (struct stream *s, struct peer *peer) { stream_putw (s, afi); stream_putc (s, safi); - stream_putc (s, BGP_ADDPATH_RX); - SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_ADDPATH_AF_RX_ADV); + + if (adv_addpath_tx) + { + stream_putc (s, BGP_ADDPATH_RX|BGP_ADDPATH_TX); + SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_ADDPATH_AF_RX_ADV); + SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_ADDPATH_AF_TX_ADV); + } + else + { + stream_putc (s, BGP_ADDPATH_RX); + SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_ADDPATH_AF_RX_ADV); + UNSET_FLAG (peer->af_cap[afi][safi], PEER_CAP_ADDPATH_AF_TX_ADV); + } } /* ORF capability. */ diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index f7336ea2f..4a99ad4c6 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -50,8 +50,6 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_vty.h" #include "bgpd/bgp_updgrp.h" -int stream_put_prefix (struct stream *, struct prefix *); - /* Set up BGP packet marker and packet type. */ int bgp_packet_set_marker (struct stream *s, u_char type) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 820c3cfd9..6c7a24239 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -1226,6 +1226,15 @@ subgroup_announce_check (struct bgp_info *ri, struct update_subgroup *subgrp, bgp = SUBGRP_INST(subgrp); riattr = bgp_info_mpath_count (ri) ? bgp_info_mpath_attr (ri) : ri->attr; + /* With addpath we may be asked to TX all kinds of paths so make sure + * ri is valid */ + if (!CHECK_FLAG (ri->flags, BGP_INFO_VALID) || + CHECK_FLAG (ri->flags, BGP_INFO_HISTORY) || + CHECK_FLAG (ri->flags, BGP_INFO_REMOVED)) + { + return 0; + } + /* Aggregate-address suppress check. */ if (ri->extra && ri->extra->suppress) if (! UNSUPPRESS_MAP_NAME (filter)) @@ -1917,7 +1926,8 @@ bgp_best_selection (struct bgp *bgp, struct bgp_node *rn, int subgroup_process_announce_selected (struct update_subgroup *subgrp, struct bgp_info *selected, - struct bgp_node *rn) + struct bgp_node *rn, + u_int32_t addpath_tx_id) { struct prefix *p; struct peer *onlypeer; @@ -1945,20 +1955,37 @@ subgroup_process_announce_selected (struct update_subgroup *subgrp, case BGP_TABLE_MAIN: /* Announcement to the subgroup. If the route is filtered, withdraw it. */ - if (selected && subgroup_announce_check(selected, subgrp, p, &attr)) - bgp_adj_out_set_subgroup(rn, subgrp, &attr, selected); - else - bgp_adj_out_unset_subgroup(rn, subgrp, 1); + if (selected) + { + if (subgroup_announce_check(selected, subgrp, p, &attr)) + bgp_adj_out_set_subgroup(rn, subgrp, &attr, selected); + else + bgp_adj_out_unset_subgroup(rn, subgrp, 1, selected->addpath_tx_id); + } + /* If selected is NULL we must withdraw the path using addpath_tx_id */ + else + { + bgp_adj_out_unset_subgroup(rn, subgrp, 1, addpath_tx_id); + } break; + case BGP_TABLE_RSCLIENT: /* Announcement to peer->conf. If the route is filtered, withdraw it. */ - if (selected && - subgroup_announce_check_rsclient (selected, subgrp, p, &attr)) - bgp_adj_out_set_subgroup (rn, subgrp, &attr, selected); + if (selected) + { + if (subgroup_announce_check_rsclient(selected, subgrp, p, &attr)) + bgp_adj_out_set_subgroup(rn, subgrp, &attr, selected); + else + bgp_adj_out_unset_subgroup(rn, subgrp, 1, selected->addpath_tx_id); + } + + /* If selected is NULL we must withdraw the path using addpath_tx_id */ else - bgp_adj_out_unset_subgroup(rn, subgrp, 1); + { + bgp_adj_out_unset_subgroup(rn, subgrp, 1, addpath_tx_id); + } break; } @@ -2033,7 +2060,7 @@ bgp_process_rsclient (struct work_queue *wq, void *data) subgrp = PAF_SUBGRP(paf); if (!subgrp) /* not an established session */ continue; - subgroup_process_announce_selected (subgrp, new_select, rn); + subgroup_process_announce_selected (subgrp, new_select, rn, new_select->addpath_tx_id); } } else @@ -2048,7 +2075,7 @@ bgp_process_rsclient (struct work_queue *wq, void *data) } paf = peer_af_find(rsclient, afi, safi); if (paf && (subgrp = PAF_SUBGRP(paf))) /* if an established session */ - subgroup_process_announce_selected (subgrp, new_select, rn); + subgroup_process_announce_selected (subgrp, new_select, rn, new_select->addpath_tx_id); } if (old_select && CHECK_FLAG (old_select->flags, BGP_INFO_REMOVED)) @@ -2095,18 +2122,18 @@ bgp_process_main (struct work_queue *wq, void *data) new_select = old_and_new.new; /* Nothing to do. */ - if (old_select && old_select == new_select && !CHECK_FLAG(rn->flags, BGP_NODE_USER_CLEAR)) - { - if (! CHECK_FLAG (old_select->flags, BGP_INFO_ATTR_CHANGED)) - { - if (CHECK_FLAG (old_select->flags, BGP_INFO_IGP_CHANGED) || - CHECK_FLAG (old_select->flags, BGP_INFO_MULTIPATH_CHG)) - bgp_zebra_announce (p, old_select, bgp, afi, safi); + if (old_select && old_select == new_select && + !CHECK_FLAG(rn->flags, BGP_NODE_USER_CLEAR) && + !CHECK_FLAG(old_select->flags, BGP_INFO_ATTR_CHANGED) && + !bgp->addpath_tx_used[afi][safi]) + { + if (CHECK_FLAG (old_select->flags, BGP_INFO_IGP_CHANGED) || + CHECK_FLAG (old_select->flags, BGP_INFO_MULTIPATH_CHG)) + bgp_zebra_announce (p, old_select, bgp, afi, safi); - UNSET_FLAG (old_select->flags, BGP_INFO_MULTIPATH_CHG); - UNSET_FLAG (rn->flags, BGP_NODE_PROCESS_SCHEDULED); - return WQ_SUCCESS; - } + UNSET_FLAG (old_select->flags, BGP_INFO_MULTIPATH_CHG); + UNSET_FLAG (rn->flags, BGP_NODE_PROCESS_SCHEDULED); + return WQ_SUCCESS; } /* If the user did "clear ip bgp prefix x.x.x.x" this flag will be set */ @@ -2157,7 +2184,7 @@ bgp_process_main (struct work_queue *wq, void *data) } } - /* Reap old select bgp_info, it it has been removed */ + /* Reap old select bgp_info, if it has been removed */ if (old_select && CHECK_FLAG (old_select->flags, BGP_INFO_REMOVED)) bgp_info_reap (rn, old_select); @@ -2433,6 +2460,7 @@ info_make (int type, int sub_type, u_short instance, struct peer *peer, struct a new->attr = attr; new->uptime = bgp_clock (); new->net = rn; + new->addpath_tx_id = ++peer->bgp->addpath_tx_id; return new; } @@ -3032,7 +3060,6 @@ bgp_update_main (struct peer *peer, struct prefix *p, u_int32_t addpath_id, /* Addpath ID */ new->addpath_rx_id = addpath_id; - new->addpath_tx_id = 0; /* Increment prefix */ bgp_aggregate_increment (bgp, p, new, afi, safi); @@ -3527,17 +3554,6 @@ bgp_clear_route_table (struct peer *peer, afi_t afi, safi_t safi, ain = ain_next; } - /* - * Can't do this anymore. adj-outs are not maintained per peer. - * - for (aout = rn->adj_out; aout; aout = aout->next) - if (aout->peer == peer || purpose == BGP_CLEAR_ROUTE_MY_RSCLIENT) - { - bgp_adj_out_remove (rn, aout, peer, afi, safi); - bgp_unlock_node (rn); - break; - } - */ for (ri = rn->info; ri; ri = ri->next) if (ri->peer == peer || purpose == BGP_CLEAR_ROUTE_MY_RSCLIENT) { @@ -3728,6 +3744,13 @@ bgp_reset (void) prefix_list_reset (); } +static int +bgp_addpath_encode_rx (struct peer *peer, afi_t afi, safi_t safi) +{ + return (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ADDPATH_AF_RX_ADV) && + CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ADDPATH_AF_TX_RCV)); +} + /* Parse NLRI stream. Withdraw NLRI is recognized by NULL attr value. */ int @@ -3740,7 +3763,7 @@ bgp_nlri_parse (struct peer *peer, struct attr *attr, struct bgp_nlri *packet) int ret; afi_t afi; safi_t safi; - u_char addpath_encoded; + int addpath_encoded; u_int32_t addpath_id; /* Check peer status. */ @@ -3752,9 +3775,7 @@ bgp_nlri_parse (struct peer *peer, struct attr *attr, struct bgp_nlri *packet) afi = packet->afi; safi = packet->safi; addpath_id = 0; - - addpath_encoded = (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ADDPATH_AF_RX_ADV) && - CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ADDPATH_AF_TX_RCV)); + addpath_encoded = bgp_addpath_encode_rx (peer, afi, safi); for (; pnt < lim; pnt += psize) { @@ -3856,13 +3877,11 @@ bgp_nlri_sanity_check (struct peer *peer, int afi, safi_t safi, u_char *pnt, u_char *end; u_char prefixlen; int psize; - u_char addpath_encoded; + int addpath_encoded; *numpfx = 0; end = pnt + length; - - addpath_encoded = (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ADDPATH_AF_RX_ADV) && - CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ADDPATH_AF_TX_RCV)); + addpath_encoded = bgp_addpath_encode_rx (peer, afi, safi); /* RFC1771 6.3 The NLRI field in the UPDATE message is checked for syntactic validity. If the field is syntactically incorrect, @@ -7212,6 +7231,57 @@ flap_route_vty_out (struct vty *vty, struct prefix *p, struct bgp_info *binfo, vty_out (vty, "%s", VTY_NEWLINE); } +static void +route_vty_out_advertised_to (struct vty *vty, struct peer *peer, int *first, + const char *header, json_object *json_adv_to) +{ + char buf1[INET6_ADDRSTRLEN]; + json_object *json_peer = NULL; + + if (json_adv_to) + { + /* 'advertised-to' is a dictionary of peers we have advertised this + * prefix too. The key is the peer's IP or swpX, the value is the + * hostname if we know it and "" if not. + */ + json_peer = json_object_new_object(); + + if (peer->hostname) + json_object_string_add(json_peer, "hostname", peer->hostname); + + if (peer->conf_if) + json_object_object_add(json_adv_to, peer->conf_if, json_peer); + else + json_object_object_add(json_adv_to, + sockunion2str (&peer->su, buf1, SU_ADDRSTRLEN), + json_peer); + } + else + { + if (*first) + { + vty_out (vty, "%s", header); + *first = 0; + } + + if (peer->hostname && bgp_flag_check(peer->bgp, BGP_FLAG_SHOW_HOSTNAME)) + { + if (peer->conf_if) + vty_out (vty, " %s(%s)", peer->hostname, peer->conf_if); + else + vty_out (vty, " %s(%s)", peer->hostname, + sockunion2str (&peer->su, buf1, SU_ADDRSTRLEN)); + } + else + { + if (peer->conf_if) + vty_out (vty, " %s", peer->conf_if); + else + vty_out (vty, " %s", sockunion2str (&peer->su, buf1, SU_ADDRSTRLEN)); + } + } +} + static void route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, struct bgp_info *binfo, afi_t afi, safi_t safi, @@ -7235,6 +7305,12 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, json_object *json_path = NULL; json_object *json_peer = NULL; json_object *json_string = NULL; + json_object *json_adv_to = NULL; + int first = 0; + struct listnode *node, *nnode; + struct peer *peer; + int addpath_capable; + int has_adj; if (json_paths) { @@ -7775,6 +7851,45 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, } } + /* If we used addpath to TX a non-bestpath we need to display + * "Advertised to" on a path-by-path basis */ + if (bgp->addpath_tx_used[afi][safi]) + { + first = 1; + + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + addpath_capable = bgp_addpath_encode_tx (peer, afi, safi); + has_adj = bgp_adj_out_lookup (peer, binfo->net, binfo->addpath_tx_id); + + if ((addpath_capable && has_adj) || + (!addpath_capable && has_adj && CHECK_FLAG (binfo->flags, BGP_INFO_SELECTED))) + { + if (json_path && !json_adv_to) + json_adv_to = json_object_new_object(); + + route_vty_out_advertised_to(vty, peer, &first, + " Advertised to:", + json_adv_to); + } + } + + if (json_path) + { + if (json_adv_to) + { + json_object_object_add(json_path, "advertisedTo", json_adv_to); + } + } + else + { + if (!first) + { + vty_out (vty, "%s", VTY_NEWLINE); + } + } + } + /* Line 8 display Uptime */ #ifdef HAVE_CLOCK_MONOTONIC tbuf = time(NULL) - (bgp_clock() - binfo->uptime); @@ -8161,9 +8276,8 @@ route_vty_out_detail_header (struct vty *vty, struct bgp *bgp, int no_export = 0; int no_advertise = 0; int local_as = 0; - int first = 0; + int first = 1; json_object *json_adv_to = NULL; - json_object *json_peer = NULL; p = &rn->p; @@ -8226,70 +8340,38 @@ route_vty_out_detail_header (struct vty *vty, struct bgp *bgp, vty_out (vty, ")%s", VTY_NEWLINE); } - /* advertised peer */ - for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + /* If we are not using addpath then we can display Advertised to and that will + * show what peers we advertised the bestpath to. If we are using addpath + * though then we must display Advertised to on a path-by-path basis. */ + if (!bgp->addpath_tx_used[afi][safi]) { - if (bgp_adj_out_lookup (peer, p, afi, safi, rn)) - { - if (json) + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + if (bgp_adj_out_lookup (peer, rn, 0)) { - /* 'advertised-to' is a dictionary of peers we have advertised this - * prefix too. The key is the peer's IP or swpX, the value is the - * hostname if we know it and "" if not. - */ - json_peer = json_object_new_object(); - - if (peer->hostname) - json_object_string_add(json_peer, "hostname", peer->hostname); - - if (!json_adv_to) + if (json && !json_adv_to) json_adv_to = json_object_new_object(); - if (peer->conf_if) - json_object_object_add(json_adv_to, peer->conf_if, json_peer); - else - json_object_object_add(json_adv_to, - sockunion2str (&peer->su, buf1, SU_ADDRSTRLEN), - json_peer); + route_vty_out_advertised_to(vty, peer, &first, + " Advertised to non peer-group peers:\n ", + json_adv_to); } - else - { - if (! first) - vty_out (vty, " Advertised to non peer-group peers:%s ", VTY_NEWLINE); + } - if (peer->hostname && bgp_flag_check(peer->bgp, BGP_FLAG_SHOW_HOSTNAME)) - { - if (peer->conf_if) - vty_out (vty, " %s(%s)", peer->hostname, peer->conf_if); - else - vty_out (vty, " %s(%s)", peer->hostname, - sockunion2str (&peer->su, buf1, SU_ADDRSTRLEN)); - } - else - { - if (peer->conf_if) - vty_out (vty, " %s", peer->conf_if); - else - vty_out (vty, " %s", sockunion2str (&peer->su, buf1, SU_ADDRSTRLEN)); - } + if (json) + { + if (json_adv_to) + { + json_object_object_add(json, "advertisedTo", json_adv_to); } - first = 1; - } - } - - if (json) - { - if (first) + } + else { - json_object_object_add(json, "advertisedTo", json_adv_to); + if (first) + vty_out (vty, " Not advertised to any peer"); + vty_out (vty, "%s", VTY_NEWLINE); } } - else - { - if (!first) - vty_out (vty, " Not advertised to any peer"); - vty_out (vty, "%s", VTY_NEWLINE); - } } /* Display specified route of BGP table. */ @@ -12093,6 +12175,7 @@ show_adj_route (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi, json_object *json_scode = NULL; json_object *json_ocode = NULL; json_object *json_ar = NULL; + struct peer_af *paf; if (use_json) { @@ -12206,46 +12289,49 @@ show_adj_route (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi, } else { - adj = bgp_adj_peer_lookup(peer, rn); - if (adj) - { - if (header1) + for (adj = rn->adj_out; adj; adj = adj->next) + SUBGRP_FOREACH_PEER(adj->subgroup, paf) + if (paf->peer == peer) { - if (use_json) + if (header1) { - json_object_int_add(json, "bgpTableVersion", table->version); - json_object_string_add(json, "bgpLocalRouterId", inet_ntoa (bgp->router_id)); - json_object_object_add(json, "bgpStatusCodes", json_scode); - json_object_object_add(json, "bgpOriginCodes", json_ocode); + if (use_json) + { + json_object_int_add(json, "bgpTableVersion", table->version); + json_object_string_add(json, "bgpLocalRouterId", inet_ntoa (bgp->router_id)); + json_object_object_add(json, "bgpStatusCodes", json_scode); + json_object_object_add(json, "bgpOriginCodes", json_ocode); + } + else + { + vty_out (vty, "BGP table version is %" PRIu64 ", local router ID is %s%s", table->version, + inet_ntoa (bgp->router_id), VTY_NEWLINE); + vty_out (vty, BGP_SHOW_SCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE); + vty_out (vty, BGP_SHOW_OCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE); + } + header1 = 0; } - else + + if (header2) { - vty_out (vty, "BGP table version is %" PRIu64 ", local router ID is %s%s", table->version, - inet_ntoa (bgp->router_id), VTY_NEWLINE); - vty_out (vty, BGP_SHOW_SCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE); - vty_out (vty, BGP_SHOW_OCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE); + if (!use_json) + vty_out (vty, BGP_SHOW_HEADER, VTY_NEWLINE); + header2 = 0; } - header1 = 0; - } - if (header2) - { - if (!use_json) - vty_out (vty, BGP_SHOW_HEADER, VTY_NEWLINE); - header2 = 0; - } - if (adj->attr) - { - bgp_attr_dup(&attr, adj->attr); - ret = bgp_output_modifier(peer, &rn->p, &attr, afi, safi, rmap_name); - if (ret != RMAP_DENY) + + if (adj->attr) { - route_vty_out_tmp (vty, &rn->p, &attr, safi, use_json, json_ar); - output_count++; + bgp_attr_dup(&attr, adj->attr); + ret = bgp_output_modifier(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); + output_count++; + } + else + filtered_count++; } - else - filtered_count++; } - } } } if (use_json) diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 59003438b..0d2134e99 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -304,7 +304,8 @@ extern void route_vty_out_tmp (struct vty *, struct prefix *, struct attr *, saf extern int subgroup_process_announce_selected (struct update_subgroup *subgrp, struct bgp_info *selected, - struct bgp_node *rn); + struct bgp_node *rn, + u_int32_t addpath_tx_id); extern int subgroup_announce_check(struct bgp_info *ri, struct update_subgroup *subgrp, diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c index c39c68782..c0f0c20b7 100644 --- a/bgpd/bgp_updgrp.c +++ b/bgpd/bgp_updgrp.c @@ -1214,7 +1214,7 @@ update_subgroup_copy_adj_out (struct update_subgroup *source, /* * Copy the adj out. */ - aout_copy = bgp_adj_out_alloc (dest, aout->rn); + aout_copy = bgp_adj_out_alloc (dest, aout->rn, aout->addpath_tx_id); aout_copy->attr = aout->attr ? bgp_attr_refcount (aout->attr) : NULL; } } @@ -1954,3 +1954,10 @@ update_group_clear_update_dbg (struct update_group *updgrp, void *arg) UPDGRP_PEER_DBG_OFF(updgrp); return UPDWALK_CONTINUE; } + +int +bgp_addpath_encode_tx (struct peer *peer, afi_t afi, safi_t safi) +{ + return (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ADDPATH_AF_TX_ADV) && + CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ADDPATH_AF_RX_RCV)); +} diff --git a/bgpd/bgp_updgrp.h b/bgpd/bgp_updgrp.h index 542517f62..1ca62a0a8 100644 --- a/bgpd/bgp_updgrp.h +++ b/bgpd/bgp_updgrp.h @@ -49,6 +49,7 @@ PEER_FLAG_REMOVE_PRIVATE_AS_ALL | \ PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE | \ PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE | \ + PEER_FLAG_ADDPATH_TX_ALL_PATHS | \ PEER_FLAG_AS_OVERRIDE) #define PEER_UPDGRP_CAP_FLAGS (PEER_CAP_AS4_RCV) @@ -454,7 +455,8 @@ extern void update_group_announce (struct bgp *bgp); extern void update_group_announce_rrclients (struct bgp *bgp); extern void peer_af_announce_route (struct peer_af *paf, int combine); extern struct bgp_adj_out *bgp_adj_out_alloc (struct update_subgroup *subgrp, - struct bgp_node *rn); + struct bgp_node *rn, + u_int32_t addpath_tx_id); extern void bgp_adj_out_remove_subgroup (struct bgp_node *rn, struct bgp_adj_out *adj, struct update_subgroup *subgrp); @@ -465,7 +467,8 @@ bgp_adj_out_set_subgroup (struct bgp_node *rn, extern void bgp_adj_out_unset_subgroup (struct bgp_node *rn, struct update_subgroup *subgrp, - char withdraw); + char withdraw, + u_int32_t addpath_tx_id); void subgroup_announce_table (struct update_subgroup *subgrp, struct bgp_table *table, int rsclient); @@ -476,6 +479,7 @@ extern int update_group_clear_update_dbg (struct update_group *updgrp, void *arg); extern void update_bgp_group_free(struct bgp *bgp); +extern int bgp_addpath_encode_tx (struct peer *peer, afi_t afi, safi_t safi); /* * Inline functions diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c index 16ec20e24..493948e79 100644 --- a/bgpd/bgp_updgrp_adv.c +++ b/bgpd/bgp_updgrp_adv.c @@ -56,15 +56,43 @@ ********************/ static inline struct bgp_adj_out * -adj_lookup (struct bgp_node *rn, struct update_subgroup *subgrp) +adj_lookup (struct bgp_node *rn, struct update_subgroup *subgrp, + u_int32_t addpath_tx_id) { struct bgp_adj_out *adj; + struct peer *peer; + afi_t afi; + safi_t safi; + int addpath_capable; if (!rn || !subgrp) return NULL; + + peer = SUBGRP_PEER (subgrp); + afi = SUBGRP_AFI (subgrp); + safi = SUBGRP_SAFI (subgrp); + addpath_capable = bgp_addpath_encode_tx (peer, afi, safi); + + /* update-groups that do not support addpath will pass 0 for + * addpath_tx_id so do not both matching against it */ for (adj = rn->adj_out; adj; adj = adj->next) - if (adj->subgroup == subgrp) - break; + { + if (adj->subgroup == subgrp) + { + if (addpath_capable) + { + if (adj->addpath_tx_id == addpath_tx_id) + { + break; + } + } + else + { + break; + } + } + } + return adj; } @@ -81,6 +109,17 @@ group_announce_route_walkcb (struct update_group *updgrp, void *arg) { struct updwalk_context *ctx = arg; struct update_subgroup *subgrp; + struct bgp_info *ri; + afi_t afi; + safi_t safi; + struct peer *peer; + struct bgp_adj_out *adj; + int addpath_capable; + + afi = UPDGRP_AFI (updgrp); + safi = UPDGRP_SAFI (updgrp); + peer = UPDGRP_PEER (updgrp); + addpath_capable = bgp_addpath_encode_tx (peer, afi, safi); UPDGRP_FOREACH_SUBGRP (updgrp, subgrp) { @@ -91,7 +130,66 @@ group_announce_route_walkcb (struct update_group *updgrp, void *arg) * coalesce timer fires. */ if (!subgrp->t_coalesce) - subgroup_process_announce_selected (subgrp, ctx->ri, ctx->rn); + { + /* An update-group that uses addpath */ + if (addpath_capable) + { + /* Look through all of the paths we have advertised for this rn and + * send a withdraw for the ones that are no longer present */ + for (adj = ctx->rn->adj_out; adj; adj = adj->next) + { + if (adj->subgroup == subgrp) + { + for (ri = ctx->rn->info; ri; ri = ri->next) + { + if (ri->addpath_tx_id == adj->addpath_tx_id) + { + break; + } + } + + if (!ri) + { + subgroup_process_announce_selected (subgrp, NULL, ctx->rn, adj->addpath_tx_id); + } + } + } + + if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ADDPATH_TX_ALL_PATHS)) + { + for (ri = ctx->rn->info; ri; ri = ri->next) + { + if (ri == ctx->ri) + continue; + subgroup_process_announce_selected (subgrp, ri, ctx->rn, ri->addpath_tx_id); + } + } + + if (ctx->ri) + subgroup_process_announce_selected (subgrp, ctx->ri, ctx->rn, ctx->ri->addpath_tx_id); + } + + /* An update-group that does not use addpath */ + else + { + if (ctx->ri) + { + subgroup_process_announce_selected (subgrp, ctx->ri, ctx->rn, ctx->ri->addpath_tx_id); + } + else + { + /* Find the addpath_tx_id of the path we had advertised and + * send a withdraw */ + for (adj = ctx->rn->adj_out; adj; adj = adj->next) + { + if (adj->subgroup == subgrp) + { + subgroup_process_announce_selected (subgrp, NULL, ctx->rn, adj->addpath_tx_id); + } + } + } + } + } } return UPDWALK_CONTINUE; @@ -265,7 +363,8 @@ update_group_announce_rrc_walkcb (struct update_group *updgrp, void *arg) * primarily its association with the subgroup and the prefix. */ struct bgp_adj_out * -bgp_adj_out_alloc (struct update_subgroup *subgrp, struct bgp_node *rn) +bgp_adj_out_alloc (struct update_subgroup *subgrp, struct bgp_node *rn, + u_int32_t addpath_tx_id) { struct bgp_adj_out *adj; @@ -277,6 +376,8 @@ bgp_adj_out_alloc (struct update_subgroup *subgrp, struct bgp_node *rn) bgp_lock_node (rn); adj->rn = rn; } + + adj->addpath_tx_id = addpath_tx_id; TAILQ_INSERT_TAIL (&(subgrp->adjq), adj, subgrp_adj_train); SUBGRP_INCR_STAT (subgrp, adj_count); return adj; @@ -335,11 +436,11 @@ bgp_adj_out_set_subgroup (struct bgp_node *rn, return; /* Look for adjacency information. */ - adj = adj_lookup (rn, subgrp); + adj = adj_lookup (rn, subgrp, binfo->addpath_tx_id); if (!adj) { - adj = bgp_adj_out_alloc (subgrp, rn); + adj = bgp_adj_out_alloc (subgrp, rn, binfo->addpath_tx_id); if (!adj) return; } @@ -388,7 +489,8 @@ bgp_adj_out_set_subgroup (struct bgp_node *rn, void bgp_adj_out_unset_subgroup (struct bgp_node *rn, struct update_subgroup *subgrp, - char withdraw) + char withdraw, + u_int32_t addpath_tx_id) { struct bgp_adj_out *adj; struct bgp_advertise *adv; @@ -397,53 +499,46 @@ bgp_adj_out_unset_subgroup (struct bgp_node *rn, if (DISABLE_BGP_ANNOUNCE) return; - /* Lookup existing adjacency, if it is not there return immediately. */ - adj = adj_lookup (rn, subgrp); + /* Lookup existing adjacency */ + if ((adj = adj_lookup (rn, subgrp, addpath_tx_id)) != NULL) + { + /* Clean up previous advertisement. */ + if (adj->adv) + bgp_advertise_clean_subgroup (subgrp, adj); - if (!adj) - goto done; + if (adj->attr && withdraw) + { + /* We need advertisement structure. */ + adj->adv = bgp_advertise_new (); + adv = adj->adv; + adv->rn = rn; + adv->adj = adj; + + /* Note if we need to trigger a packet write */ + if (BGP_ADV_FIFO_EMPTY (&subgrp->sync->withdraw)) + trigger_write = 1; + else + trigger_write = 0; - /* Clean up previous advertisement. */ - if (adj->adv) - bgp_advertise_clean_subgroup (subgrp, adj); + /* Add to synchronization entry for withdraw announcement. */ + BGP_ADV_FIFO_ADD (&subgrp->sync->withdraw, &adv->fifo); - if (adj->attr && withdraw) - { - /* We need advertisement structure. */ - adj->adv = bgp_advertise_new (); - adv = adj->adv; - adv->rn = rn; - adv->adj = adj; - - /* Note if we need to trigger a packet write */ - if (BGP_ADV_FIFO_EMPTY (&subgrp->sync->withdraw)) - trigger_write = 1; + /* Schedule packet write, if FIFO is getting its first entry. */ + if (trigger_write) + subgroup_trigger_write(subgrp); + } else - trigger_write = 0; - - /* Add to synchronization entry for withdraw announcement. */ - BGP_ADV_FIFO_ADD (&subgrp->sync->withdraw, &adv->fifo); - - /* Schedule packet write, if FIFO is getting its first entry. */ - if (trigger_write) - subgroup_trigger_write(subgrp); - } - else - { - /* Remove myself from adjacency. */ - BGP_ADJ_OUT_DEL (rn, adj); + { + /* Remove myself from adjacency. */ + BGP_ADJ_OUT_DEL (rn, adj); - /* Free allocated information. */ - adj_free (adj); + /* Free allocated information. */ + adj_free (adj); - bgp_unlock_node (rn); + bgp_unlock_node (rn); + } } - /* - * Fall through. - */ - -done: subgrp->version = max (subgrp->version, rn->version); } @@ -492,10 +587,12 @@ subgroup_announce_table (struct update_subgroup *subgrp, struct peer *onlypeer; afi_t afi; safi_t safi; + int addpath_capable; peer = SUBGRP_PEER (subgrp); afi = SUBGRP_AFI (subgrp); safi = SUBGRP_SAFI (subgrp); + addpath_capable = bgp_addpath_encode_tx (peer, afi, safi); onlypeer = ((SUBGRP_PCOUNT (subgrp) == 1) ? (SUBGRP_PFIRST (subgrp))->peer : NULL); @@ -515,13 +612,15 @@ subgroup_announce_table (struct update_subgroup *subgrp, for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) for (ri = rn->info; ri; ri = ri->next) - if (CHECK_FLAG (ri->flags, BGP_INFO_SELECTED)) + if (CHECK_FLAG (ri->flags, BGP_INFO_SELECTED) || + (addpath_capable && + CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ADDPATH_TX_ALL_PATHS))) { if (!rsclient && subgroup_announce_check (ri, subgrp, &rn->p, &attr)) bgp_adj_out_set_subgroup (rn, subgrp, &attr, ri); else - bgp_adj_out_unset_subgroup (rn, subgrp, 1); + bgp_adj_out_unset_subgroup (rn, subgrp, 1, ri->addpath_tx_id); } /* @@ -704,7 +803,7 @@ subgroup_default_originate (struct update_subgroup *subgrp, int withdraw) #endif /* HAVE_IPV6 */ rn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, &p, NULL); - bgp_adj_out_unset_subgroup (rn, subgrp, 0); + bgp_adj_out_unset_subgroup (rn, subgrp, 0, BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE); } } diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c index 43aea264e..320eef21d 100644 --- a/bgpd/bgp_updgrp_packet.c +++ b/bgpd/bgp_updgrp_packet.c @@ -600,6 +600,14 @@ subgroup_packets_to_build (struct update_subgroup *subgrp) return 0; } +static void +bgp_info_addpath_tx_str (int addpath_encode, u_int32_t addpath_tx_id, + char *buf) +{ + if (addpath_encode) + sprintf(buf, " with addpath ID %d", addpath_tx_id); +} + /* Make BGP update packet. */ struct bpacket * subgroup_update_packet (struct update_subgroup *subgrp) @@ -625,7 +633,8 @@ subgroup_update_packet (struct update_subgroup *subgrp) char send_attr_str[BUFSIZ]; int send_attr_printed = 0; int num_pfx = 0; - + int addpath_encode = 0; + u_int32_t addpath_tx_id = 0; if (!subgrp) return NULL; @@ -644,14 +653,16 @@ subgroup_update_packet (struct update_subgroup *subgrp) bpacket_attr_vec_arr_reset (&vecarr); + addpath_encode = bgp_addpath_encode_tx (peer, afi, safi); + adv = BGP_ADV_FIFO_HEAD (&subgrp->sync->update); while (adv) { assert (adv->rn); rn = adv->rn; adj = adv->adj; - if (adv->binfo) - binfo = adv->binfo; + addpath_tx_id = adj->addpath_tx_id; + binfo = adv->binfo; space_remaining = STREAM_CONCAT_REMAIN (s, snlri, STREAM_SIZE(s)) - BGP_MAX_PACKET_SIZE_OVERFLOW; @@ -691,7 +702,7 @@ subgroup_update_packet (struct update_subgroup *subgrp) total_attr_len = bgp_packet_attribute (NULL, peer, s, adv->baa->attr, &vecarr, NULL, afi, safi, - from, NULL, NULL); + from, NULL, NULL, 0, 0); space_remaining = STREAM_CONCAT_REMAIN (s, snlri, STREAM_SIZE(s)) - BGP_MAX_PACKET_SIZE_OVERFLOW; @@ -721,7 +732,7 @@ subgroup_update_packet (struct update_subgroup *subgrp) if ((afi == AFI_IP && safi == SAFI_UNICAST) && !peer_cap_enhe(peer)) - stream_put_prefix (s, &rn->p); + stream_put_prefix_addpath (s, &rn->p, addpath_encode, addpath_tx_id); else { /* Encode the prefix in MP_REACH_NLRI attribute */ @@ -737,14 +748,16 @@ subgroup_update_packet (struct update_subgroup *subgrp) mpattrlen_pos = bgp_packet_mpattr_start (snlri, afi, safi, (peer_cap_enhe(peer) ? AFI_IP6 : afi), &vecarr, adv->baa->attr); - bgp_packet_mpattr_prefix (snlri, afi, safi, &rn->p, prd, tag); + bgp_packet_mpattr_prefix (snlri, afi, safi, &rn->p, prd, tag, + addpath_encode, addpath_tx_id); } num_pfx++; if (bgp_debug_update(NULL, &rn->p, subgrp->update_group, 0)) { - char buf[INET6_BUFSIZ]; + char buf[INET6_BUFSIZ]; + char tx_id_buf[30]; if (!send_attr_printed) { @@ -753,10 +766,11 @@ subgroup_update_packet (struct update_subgroup *subgrp) send_attr_printed = 1; } - zlog_debug ("u%" PRIu64 ":s%" PRIu64 " send UPDATE %s/%d", - subgrp->update_group->id, subgrp->id, - inet_ntop (rn->p.family, &(rn->p.u.prefix), buf, - INET6_BUFSIZ), rn->p.prefixlen); + bgp_info_addpath_tx_str (addpath_encode, addpath_tx_id, tx_id_buf); + zlog_debug ("u%" PRIu64 ":s%" PRIu64 " send UPDATE %s/%d%s", + subgrp->update_group->id, subgrp->id, + inet_ntop (rn->p.family, &(rn->p.u.prefix), buf, INET6_BUFSIZ), + rn->p.prefixlen, tx_id_buf); } /* Synchnorize attribute. */ @@ -831,6 +845,8 @@ subgroup_withdraw_packet (struct update_subgroup *subgrp) int space_remaining = 0; int space_needed = 0; int num_pfx = 0; + int addpath_encode = 0; + u_int32_t addpath_tx_id = 0; if (!subgrp) return NULL; @@ -843,12 +859,14 @@ subgroup_withdraw_packet (struct update_subgroup *subgrp) safi = SUBGRP_SAFI (subgrp); s = subgrp->work; stream_reset (s); + addpath_encode = bgp_addpath_encode_tx (peer, afi, safi); while ((adv = BGP_ADV_FIFO_HEAD (&subgrp->sync->withdraw)) != NULL) { assert (adv->rn); adj = adv->adj; rn = adv->rn; + addpath_tx_id = adj->addpath_tx_id; space_remaining = STREAM_REMAIN (s) - BGP_MAX_PACKET_SIZE_OVERFLOW; @@ -868,7 +886,7 @@ subgroup_withdraw_packet (struct update_subgroup *subgrp) if (afi == AFI_IP && safi == SAFI_UNICAST && !peer_cap_enhe(peer)) - stream_put_prefix (s, &rn->p); + stream_put_prefix_addpath (s, &rn->p, addpath_encode, addpath_tx_id); else { struct prefix_rd *prd = NULL; @@ -886,19 +904,21 @@ subgroup_withdraw_packet (struct update_subgroup *subgrp) mplen_pos = bgp_packet_mpunreach_start (s, afi, safi); } - bgp_packet_mpunreach_prefix (s, &rn->p, afi, safi, prd, NULL); + bgp_packet_mpunreach_prefix (s, &rn->p, afi, safi, prd, NULL, + addpath_encode, addpath_tx_id); } num_pfx++; if (bgp_debug_update(NULL, &rn->p, subgrp->update_group, 0)) { - char buf[INET6_BUFSIZ]; - - zlog_debug ("u%" PRIu64 ":s%" PRIu64 " send UPDATE %s/%d -- unreachable", - subgrp->update_group->id, subgrp->id, - inet_ntop (rn->p.family, &(rn->p.u.prefix), buf, - INET6_BUFSIZ), rn->p.prefixlen); + char buf[INET6_BUFSIZ]; + char tx_id_buf[30]; + bgp_info_addpath_tx_str (addpath_encode, addpath_tx_id, tx_id_buf); + zlog_debug ("u%" PRIu64 ":s%" PRIu64 " send UPDATE %s/%d%s -- unreachable", + subgrp->update_group->id, subgrp->id, + inet_ntop (rn->p.family, &(rn->p.u.prefix), buf, INET6_BUFSIZ), + rn->p.prefixlen, tx_id_buf); } subgrp->scount--; @@ -951,6 +971,7 @@ subgroup_default_update_packet (struct update_subgroup *subgrp, afi_t afi; safi_t safi; struct bpacket_attr_vec_arr vecarr; + int addpath_encode = 0; if (DISABLE_BGP_ANNOUNCE) return; @@ -962,6 +983,7 @@ subgroup_default_update_packet (struct update_subgroup *subgrp, afi = SUBGRP_AFI (subgrp); safi = SUBGRP_SAFI (subgrp); bpacket_attr_vec_arr_reset (&vecarr); + addpath_encode = bgp_addpath_encode_tx (peer, afi, safi); if (afi == AFI_IP) str2prefix ("0.0.0.0/0", &p); @@ -975,13 +997,15 @@ subgroup_default_update_packet (struct update_subgroup *subgrp, { char attrstr[BUFSIZ]; char buf[INET6_BUFSIZ]; + char tx_id_buf[30]; attrstr[0] = '\0'; bgp_dump_attr (peer, attr, attrstr, BUFSIZ); - zlog_debug ("u%" PRIu64 ":s%" PRIu64 " send UPDATE %s/%d %s", - (SUBGRP_UPDGRP (subgrp))->id, subgrp->id, - inet_ntop (p.family, &(p.u.prefix), buf, INET6_BUFSIZ), - p.prefixlen, attrstr); + bgp_info_addpath_tx_str (addpath_encode, BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE, tx_id_buf); + zlog_debug ("u%" PRIu64 ":s%" PRIu64 " send UPDATE %s/%d%s %s", + (SUBGRP_UPDGRP (subgrp))->id, subgrp->id, + inet_ntop (p.family, &(p.u.prefix), buf, INET6_BUFSIZ), + p.prefixlen, tx_id_buf, attrstr); } s = stream_new (BGP_MAX_PACKET_SIZE); @@ -996,7 +1020,9 @@ subgroup_default_update_packet (struct update_subgroup *subgrp, pos = stream_get_endp (s); stream_putw (s, 0); total_attr_len = bgp_packet_attribute (NULL, peer, s, attr, &vecarr, &p, - afi, safi, from, NULL, NULL); + afi, safi, from, NULL, NULL, + addpath_encode, + BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE); /* Set Total Path Attribute Length. */ stream_putw_at (s, pos, total_attr_len); @@ -1004,7 +1030,7 @@ subgroup_default_update_packet (struct update_subgroup *subgrp, /* NLRI set. */ if (p.family == AF_INET && safi == SAFI_UNICAST && !peer_cap_enhe(peer)) - stream_put_prefix (s, &p); + stream_put_prefix_addpath (s, &p, addpath_encode, BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE); /* Set size. */ bgp_packet_set_size (s); @@ -1027,6 +1053,7 @@ subgroup_default_withdraw_packet (struct update_subgroup *subgrp) size_t mplen_pos = 0; afi_t afi; safi_t safi; + int addpath_encode = 0; if (DISABLE_BGP_ANNOUNCE) return; @@ -1034,6 +1061,7 @@ subgroup_default_withdraw_packet (struct update_subgroup *subgrp) peer = SUBGRP_PEER (subgrp); afi = SUBGRP_AFI (subgrp); safi = SUBGRP_SAFI (subgrp); + addpath_encode = bgp_addpath_encode_tx (peer, afi, safi); if (afi == AFI_IP) str2prefix ("0.0.0.0/0", &p); @@ -1047,14 +1075,13 @@ subgroup_default_withdraw_packet (struct update_subgroup *subgrp) if (bgp_debug_update(NULL, &p, subgrp->update_group, 0)) { char buf[INET6_BUFSIZ]; + char tx_id_buf[INET6_BUFSIZ]; - zlog_debug ("u%" PRIu64 ":s%" PRIu64 " send UPDATE %s/%d -- unreachable", - (SUBGRP_UPDGRP (subgrp))->id, subgrp->id, inet_ntop (p.family, - &(p.u. - prefix), - buf, - INET6_BUFSIZ), - p.prefixlen); + bgp_info_addpath_tx_str (addpath_encode, BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE, tx_id_buf); + zlog_debug ("u%" PRIu64 ":s%" PRIu64 " send UPDATE %s/%d%s -- unreachable", + (SUBGRP_UPDGRP (subgrp))->id, subgrp->id, + inet_ntop (p.family, &(p.u.prefix), buf, INET6_BUFSIZ), + p.prefixlen, tx_id_buf); } s = stream_new (BGP_MAX_PACKET_SIZE); @@ -1070,7 +1097,8 @@ subgroup_default_withdraw_packet (struct update_subgroup *subgrp) if (p.family == AF_INET && safi == SAFI_UNICAST && !peer_cap_enhe(peer)) { - stream_put_prefix (s, &p); + stream_put_prefix_addpath (s, &p, addpath_encode, + BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE); unfeasible_len = stream_get_endp (s) - cp - 2; @@ -1086,7 +1114,9 @@ subgroup_default_withdraw_packet (struct update_subgroup *subgrp) stream_putw (s, 0); mp_start = stream_get_endp (s); mplen_pos = bgp_packet_mpunreach_start (s, afi, safi); - bgp_packet_mpunreach_prefix (s, &p, afi, safi, NULL, NULL); + bgp_packet_mpunreach_prefix (s, &p, afi, safi, NULL, NULL, + addpath_encode, + BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE); /* Set the mp_unreach attr's length */ bgp_packet_mpunreach_end (s, mplen_pos); diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index f7d5af112..c021e8297 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -5771,6 +5771,38 @@ DEFUN (no_neighbor_ttl_security, return bgp_vty_return (vty, peer_ttl_security_hops_unset (peer)); } +DEFUN (neighbor_addpath_tx_all_paths, + neighbor_addpath_tx_all_paths_cmd, + NEIGHBOR_CMD2 "addpath-tx-all-paths", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Use addpath to advertise all paths to a neighbor\n") +{ + struct peer *peer; + + + peer = peer_and_group_lookup_vty (vty, argv[0]); + if (! peer) + return CMD_WARNING; + + return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), + PEER_FLAG_ADDPATH_TX_ALL_PATHS); +} + +DEFUN (no_neighbor_addpath_tx_all_paths, + no_neighbor_addpath_tx_all_paths_cmd, + NO_NEIGHBOR_CMD2 "addpath-tx-all-paths", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Use addpath to advertise all paths to a neighbor\n") +{ + return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), + PEER_FLAG_ADDPATH_TX_ALL_PATHS); +} + /* Address family configuration. */ DEFUN (address_family_ipv4, address_family_ipv4_cmd, @@ -8472,7 +8504,7 @@ bgp_adj_out_count (struct peer *peer, int afi, int safi) table = peer->bgp->rib[afi][safi]; if (!table) return(0); for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) - if (bgp_adj_out_lookup(peer, NULL, afi, safi, rn)) + if (bgp_adj_out_lookup(peer, rn, 0)) count++; return (count); } @@ -9247,6 +9279,9 @@ bgp_show_peer_afi (struct vty *vty, struct peer *p, afi_t afi, safi_t safi, else if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_REMOVE_PRIVATE_AS)) json_object_boolean_true_add(json_addr, "privateAsNumsRemovedInUpdatesToNbr"); + if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_ADDPATH_TX_ALL_PATHS)) + json_object_boolean_true_add(json_addr, "addpathTxAllPaths"); + if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_AS_OVERRIDE)) json_object_string_add(json_addr, "overrideASNsInOutboundUpdates", "ifAspathEqualRemoteAs"); @@ -9434,6 +9469,9 @@ bgp_show_peer_afi (struct vty *vty, struct peer *p, afi_t afi, safi_t safi, else if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_REMOVE_PRIVATE_AS)) vty_out (vty, " Private AS numbers removed in updates to this neighbor%s", VTY_NEWLINE); + if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_ADDPATH_TX_ALL_PATHS)) + vty_out (vty, " Advertise all paths via addpath%s", VTY_NEWLINE); + if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_AS_OVERRIDE)) vty_out (vty, " Override ASNs in outbound updates if aspath equals remote-as%s", VTY_NEWLINE); @@ -13171,6 +13209,20 @@ bgp_vty_init (void) install_element (BGP_VPNV4_NODE, &neighbor_route_server_client_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_route_server_client_cmd); + /* "neighbor addpath-tx-all-paths" commands.*/ + install_element (BGP_NODE, &neighbor_addpath_tx_all_paths_cmd); + install_element (BGP_NODE, &no_neighbor_addpath_tx_all_paths_cmd); + install_element (BGP_IPV4_NODE, &neighbor_addpath_tx_all_paths_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_addpath_tx_all_paths_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_addpath_tx_all_paths_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_addpath_tx_all_paths_cmd); + install_element (BGP_IPV6_NODE, &neighbor_addpath_tx_all_paths_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_addpath_tx_all_paths_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_addpath_tx_all_paths_cmd); + install_element (BGP_IPV6M_NODE, &no_neighbor_addpath_tx_all_paths_cmd); + install_element (BGP_VPNV4_NODE, &neighbor_addpath_tx_all_paths_cmd); + install_element (BGP_VPNV4_NODE, &no_neighbor_addpath_tx_all_paths_cmd); + /* "neighbor passive" commands. */ install_element (BGP_NODE, &neighbor_passive_cmd); install_element (BGP_NODE, &no_neighbor_passive_cmd); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index ec5ce9bb9..1edb9229d 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -2817,6 +2817,7 @@ bgp_create (as_t *as, const char *name) bgp_flag_set (bgp, BGP_FLAG_SHOW_HOSTNAME); bgp_flag_set (bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES); bgp_flag_set (bgp, BGP_FLAG_DETERMINISTIC_MED); + bgp->addpath_tx_id = BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE; bgp->as = *as; @@ -3460,25 +3461,29 @@ static const struct peer_flag_action peer_flag_action_list[] = static const struct peer_flag_action peer_af_flag_action_list[] = { - { PEER_FLAG_NEXTHOP_SELF, 1, peer_change_reset_out }, { PEER_FLAG_SEND_COMMUNITY, 1, peer_change_reset_out }, { PEER_FLAG_SEND_EXT_COMMUNITY, 1, peer_change_reset_out }, - { PEER_FLAG_SOFT_RECONFIG, 0, peer_change_reset_in }, + { PEER_FLAG_NEXTHOP_SELF, 1, peer_change_reset_out }, { PEER_FLAG_REFLECTOR_CLIENT, 1, peer_change_reset }, { PEER_FLAG_RSERVER_CLIENT, 1, peer_change_reset }, + { PEER_FLAG_SOFT_RECONFIG, 0, peer_change_reset_in }, { PEER_FLAG_AS_PATH_UNCHANGED, 1, peer_change_reset_out }, { PEER_FLAG_NEXTHOP_UNCHANGED, 1, peer_change_reset_out }, { PEER_FLAG_MED_UNCHANGED, 1, peer_change_reset_out }, + // PEER_FLAG_DEFAULT_ORIGINATE { PEER_FLAG_REMOVE_PRIVATE_AS, 1, peer_change_reset_out }, - { PEER_FLAG_REMOVE_PRIVATE_AS_ALL, 1, peer_change_reset_out }, - { PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE,1, peer_change_reset_out }, { PEER_FLAG_ALLOWAS_IN, 0, peer_change_reset_in }, { PEER_FLAG_ORF_PREFIX_SM, 1, peer_change_reset }, { PEER_FLAG_ORF_PREFIX_RM, 1, peer_change_reset }, + // PEER_FLAG_MAX_PREFIX + // PEER_FLAG_MAX_PREFIX_WARNING { PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED, 0, peer_change_reset_out }, { PEER_FLAG_FORCE_NEXTHOP_SELF, 1, peer_change_reset_out }, + { PEER_FLAG_REMOVE_PRIVATE_AS_ALL, 1, peer_change_reset_out }, + { PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE,1, peer_change_reset_out }, { PEER_FLAG_AS_OVERRIDE, 1, peer_change_reset_out }, { PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE,1, peer_change_reset_out }, + { PEER_FLAG_ADDPATH_TX_ALL_PATHS, 1, peer_change_reset }, { 0, 0, 0 } }; @@ -3680,6 +3685,9 @@ peer_af_flag_modify (struct peer *peer, afi_t afi, safi_t safi, u_int32_t flag, struct listnode *node, *nnode; struct peer_group *group; struct peer_flag_action action; + struct peer *tmp_peer; + struct bgp *bgp; + int addpath_tx_used; memset (&action, 0, sizeof (struct peer_flag_action)); size = sizeof peer_af_flag_action_list / sizeof (struct peer_flag_action); @@ -3746,42 +3754,68 @@ peer_af_flag_modify (struct peer *peer, afi_t afi, safi_t safi, u_int32_t flag, { group = peer->group; - for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, tmp_peer)) { - if (! peer->af_group[afi][safi]) + if (! tmp_peer->af_group[afi][safi]) continue; - if (set && CHECK_FLAG (peer->af_flags[afi][safi], flag) == flag) + if (set && CHECK_FLAG (tmp_peer->af_flags[afi][safi], flag) == flag) continue; - if (! set && ! CHECK_FLAG (peer->af_flags[afi][safi], flag)) + if (! set && ! CHECK_FLAG (tmp_peer->af_flags[afi][safi], flag)) continue; if (set) - SET_FLAG (peer->af_flags[afi][safi], flag); + SET_FLAG (tmp_peer->af_flags[afi][safi], flag); else - UNSET_FLAG (peer->af_flags[afi][safi], flag); + UNSET_FLAG (tmp_peer->af_flags[afi][safi], flag); - if (peer->status == Established) + if (tmp_peer->status == Established) { if (! set && flag == PEER_FLAG_SOFT_RECONFIG) - bgp_clear_adj_in (peer, afi, safi); + bgp_clear_adj_in (tmp_peer, afi, safi); else { if (flag == PEER_FLAG_REFLECTOR_CLIENT) - peer->last_reset = PEER_DOWN_RR_CLIENT_CHANGE; + tmp_peer->last_reset = PEER_DOWN_RR_CLIENT_CHANGE; else if (flag == PEER_FLAG_RSERVER_CLIENT) - peer->last_reset = PEER_DOWN_RS_CLIENT_CHANGE; + tmp_peer->last_reset = PEER_DOWN_RS_CLIENT_CHANGE; else if (flag == PEER_FLAG_ORF_PREFIX_SM) - peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE; + tmp_peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE; else if (flag == PEER_FLAG_ORF_PREFIX_RM) - peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE; + tmp_peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE; - peer_change_action (peer, afi, safi, action.type); + peer_change_action (tmp_peer, afi, safi, action.type); } } } } + + /* Track if addpath TX is in use */ + if (flag & PEER_FLAG_ADDPATH_TX_ALL_PATHS) + { + bgp = peer->bgp; + addpath_tx_used = 0; + + if (set) + { + addpath_tx_used = 1; + } + else + { + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, tmp_peer)) + { + if (CHECK_FLAG (tmp_peer->af_flags[afi][safi], PEER_FLAG_ADDPATH_TX_ALL_PATHS)) + { + addpath_tx_used = 1; + break; + } + } + } + + bgp->addpath_tx_used[afi][safi] = addpath_tx_used; + } + return 0; } @@ -6510,6 +6544,11 @@ bgp_config_write_peer_af (struct vty *vty, struct bgp *bgp, addr, VTY_NEWLINE); } + /* addpath TX knobs */ + if (peergroup_af_flag_check (peer, afi, safi, PEER_FLAG_ADDPATH_TX_ALL_PATHS)) + vty_out (vty, " neighbor %s addpath-tx-all-paths%s", addr, + VTY_NEWLINE); + /* ORF capability. */ if (peergroup_af_flag_check (peer, afi, safi, PEER_FLAG_ORF_PREFIX_SM) || peergroup_af_flag_check (peer, afi, safi, PEER_FLAG_ORF_PREFIX_RM)) diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 41bc5ea81..7ed8176b1 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -315,6 +315,9 @@ struct bgp u_int32_t wpkt_quanta; /* per peer packet quanta to write */ u_int32_t coalesce_time; + + u_int32_t addpath_tx_id; + int addpath_tx_used[AFI_MAX][SAFI_MAX]; }; #define BGP_ROUTE_ADV_HOLD(bgp) \ @@ -364,6 +367,8 @@ struct bgp_nexthop #define BGP_ADDPATH_TX 2 #define BGP_ADDPATH_ID_LEN 4 +#define BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE 1 + /* BGP router distinguisher value. */ #define BGP_RD_SIZE 8 @@ -648,6 +653,7 @@ struct peer #define PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE (1 << 19) /* remove-private-as replace-as */ #define PEER_FLAG_AS_OVERRIDE (1 << 20) /* as-override */ #define PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE (1 << 21) /* remove-private-as all replace-as */ +#define PEER_FLAG_ADDPATH_TX_ALL_PATHS (1 << 22) /* addpath-tx-all-paths */ /* MD5 password */ char *password; diff --git a/lib/stream.c b/lib/stream.c index fcdf56f64..11f35d884 100644 --- a/lib/stream.c +++ b/lib/stream.c @@ -752,20 +752,35 @@ stream_put_in6_addr_at (struct stream *s, size_t putp, struct in6_addr *addr) /* Put prefix by nlri type format. */ int -stream_put_prefix (struct stream *s, struct prefix *p) +stream_put_prefix_addpath (struct stream *s, struct prefix *p, + int addpath_encode, u_int32_t addpath_tx_id) { size_t psize; + size_t psize_with_addpath; STREAM_VERIFY_SANE(s); psize = PSIZE (p->prefixlen); + + if (addpath_encode) + psize_with_addpath = psize + 4; + else + psize_with_addpath = psize; - if (STREAM_WRITEABLE (s) < (psize + sizeof (u_char))) + if (STREAM_WRITEABLE (s) < (psize_with_addpath + sizeof (u_char))) { STREAM_BOUND_WARN (s, "put"); return 0; } + if (addpath_encode) + { + s->data[s->endp++] = (u_char)(addpath_tx_id >> 24); + s->data[s->endp++] = (u_char)(addpath_tx_id >> 16); + s->data[s->endp++] = (u_char)(addpath_tx_id >> 8); + s->data[s->endp++] = (u_char)addpath_tx_id; + } + s->data[s->endp++] = p->prefixlen; memcpy (s->data + s->endp, &p->u.prefix, psize); s->endp += psize; @@ -773,6 +788,13 @@ stream_put_prefix (struct stream *s, struct prefix *p) return psize; } +int +stream_put_prefix (struct stream *s, struct prefix *p) +{ + return stream_put_prefix_addpath (s, p, 0, 0); +} + + /* Read size from fd. */ int stream_read (struct stream *s, int fd, size_t size) diff --git a/lib/stream.h b/lib/stream.h index 3efabe358..32d266831 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -175,6 +175,9 @@ extern int stream_put_ipv4 (struct stream *, u_int32_t); extern int stream_put_in_addr (struct stream *, struct in_addr *); extern int stream_put_in_addr_at (struct stream *, size_t, struct in_addr *); extern int stream_put_in6_addr_at (struct stream *, size_t, struct in6_addr *); +extern int stream_put_prefix_addpath (struct stream *, struct prefix *, + int addpath_encode, + u_int32_t addpath_tx_id); extern int stream_put_prefix (struct stream *, struct prefix *); extern void stream_get (void *, struct stream *, size_t); -- cgit v1.2.3