From e3382f2fa74ab570ff7429ee352ecd41b96731cb Mon Sep 17 00:00:00 2001
From: Soman K S <somanks@gmail.com>
Date: Wed, 8 Apr 2020 15:04:10 +0530
Subject: [PATCH] bgpd: Configuring default-originate withdraws default route

Issue:
Configuring default-originate when static default route is previously
advertised results in withdrawal of the route.

Fix :
Delete the adj-out entry for the previously advertised static
default route without sending explicit withdraw message.

Signed-off-by: kssoman <somanks@gmail.com>
---
 bgpd/bgp_updgrp_adv.c | 102 +++++++++++++++++++++++++++++++++---------
 1 file changed, 81 insertions(+), 21 deletions(-)

diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c
index e40b3320ea46..e83eed6e3fd7 100644
--- a/bgpd/bgp_updgrp_adv.c
+++ b/bgpd/bgp_updgrp_adv.c
@@ -533,6 +533,14 @@ void bgp_adj_out_unset_subgroup(struct bgp_node *rn,
 		if (adj->adv)
 			bgp_advertise_clean_subgroup(subgrp, adj);
 
+		/* If default originate is enabled and the route is default
+		 * route, do not send withdraw. This will prevent deletion of
+		 * the default route at the peer.
+		 */
+		if (CHECK_FLAG(subgrp->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE)
+		    && is_default_prefix(&rn->p))
+			return;
+
 		if (adj->attr && withdraw) {
 			/* We need advertisement structure.  */
 			adj->adv = bgp_advertise_new();
@@ -636,12 +644,25 @@ void subgroup_announce_table(struct update_subgroup *subgrp,
 							    rn_p, &attr))
 					bgp_adj_out_set_subgroup(rn, subgrp,
 								 &attr, ri);
-				else
+				else {
+					/* If default originate is enabled for
+					 * the peer, do not send explicit
+					 * withdraw. This will prevent deletion
+					 * of default route advertised through
+					 * default originate
+					 */
+					if (CHECK_FLAG(
+						    peer->af_flags[afi][safi],
+						    PEER_FLAG_DEFAULT_ORIGINATE)
+					    && is_default_prefix(&rn->p))
+						break;
+
 					bgp_adj_out_unset_subgroup(
 						rn, subgrp, 1,
 						bgp_addpath_id_for_peer(
 							peer, afi, safi,
 							&ri->tx_addpath));
+				}
 			}
 	}
 
@@ -709,7 +730,9 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw)
 	struct prefix p;
 	struct peer *from;
 	struct bgp_node *rn;
+	struct bgp_path_info *pi;
 	struct peer *peer;
+	struct bgp_adj_out *adj;
 	route_map_result_t ret = RMAP_DENYMATCH;
 	afi_t afi;
 	safi_t safi;
@@ -732,10 +755,6 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw)
 
 	attr.local_pref = bgp->default_local_pref;
 
-	memset(&p, 0, sizeof(p));
-	p.family = afi2family(afi);
-	p.prefixlen = 0;
-
 	if ((afi == AFI_IP6) || peer_cap_enhe(peer, afi, safi)) {
 		/* IPv6 global nexthop must be included. */
 		attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL;
@@ -778,21 +797,40 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw)
 		}
 	}
 
+	/* Check if the default route is in local BGP RIB which is
+	 * installed through redistribute or network command
+	 */
+	memset(&p, 0, sizeof(p));
+	p.family = afi2family(afi);
+	p.prefixlen = 0;
+	rn = bgp_afi_node_lookup(bgp->rib[afi][safi], afi, safi, &p, NULL);
+
 	if (withdraw) {
+		/* Withdraw the default route advertised using default
+		 * originate
+		 */
 		if (CHECK_FLAG(subgrp->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE))
 			subgroup_default_withdraw_packet(subgrp);
 		UNSET_FLAG(subgrp->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE);
+
+		/* If default route is present in the local RIB, advertise the
+		 * route
+		 */
+		if (rn != NULL) {
+			for (pi = bgp_node_get_bgp_path_info(rn); pi;
+			     pi = pi->next) {
+				if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED))
+					if (subgroup_announce_check(
+						    rn, pi, subgrp, &rn->p,
+						    &attr))
+						bgp_adj_out_set_subgroup(
+							rn, subgrp, &attr, pi);
+			}
+		}
 	} else {
 		if (!CHECK_FLAG(subgrp->sflags,
 				SUBGRP_STATUS_DEFAULT_ORIGINATE)) {
 
-			if (CHECK_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_SHUTDOWN))
-				bgp_attr_add_gshut_community(new_attr);
-
-			SET_FLAG(subgrp->sflags,
-				 SUBGRP_STATUS_DEFAULT_ORIGINATE);
-			subgroup_default_update_packet(subgrp, new_attr, from);
-
 			/* The 'neighbor x.x.x.x default-originate' default will
 			 * act as an
 			 * implicit withdraw for any previous UPDATEs sent for
@@ -800,15 +838,37 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw)
 			 * clear adj_out for the 0.0.0.0/0 prefix in the BGP
 			 * table.
 			 */
-			memset(&p, 0, sizeof(p));
-			p.family = afi2family(afi);
-			p.prefixlen = 0;
-
-			rn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi,
-					      &p, NULL);
-			bgp_adj_out_unset_subgroup(
-				rn, subgrp, 0,
-				BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE);
+			if (rn != NULL) {
+				/* Remove the adjacency for the previously
+				 * advertised default route
+				 */
+				adj = adj_lookup(
+					rn, subgrp,
+					BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE);
+				if (adj != NULL) {
+					/* Clean up previous advertisement.  */
+					if (adj->adv)
+						bgp_advertise_clean_subgroup(
+							subgrp, adj);
+
+					/* Remove  from adjacency. */
+					RB_REMOVE(bgp_adj_out_rb, &rn->adj_out,
+						  adj);
+
+					/* Free allocated information.  */
+					adj_free(adj);
+
+					bgp_unlock_node(rn);
+				}
+			}
+
+			/* Advertise the default route */
+			if (CHECK_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_SHUTDOWN))
+				bgp_attr_add_gshut_community(new_attr);
+
+			SET_FLAG(subgrp->sflags,
+				 SUBGRP_STATUS_DEFAULT_ORIGINATE);
+			subgroup_default_update_packet(subgrp, new_attr, from);
 		}
 	}