bgpd: add 'bgp bestpath as-path multipath-relax'

Compute multipath in BGP based on AS_PATH hop count match. If the knob
is turned on, it is not required to have an exact match of AS_PATHs
(provided other multipath conditions are met, of course).

Signed-off-by: Pradosh Mohapatra <pmohapat at cumulusnetworks.com>
Reviewed-by: Dinesh G Dutt <ddutt at cumulusnetworks.com>
Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 335543e..7f68b8d 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -480,7 +480,20 @@
   /* 9. Maximum path check. */
   if (newm == existm)
     {
-      if (new->peer->sort == BGP_PEER_IBGP)
+      if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX))
+        {
+
+	  /*
+	   * For the two paths, all comparison steps till IGP metric
+	   * have succeeded - including AS_PATH hop count. Since 'bgp
+	   * bestpath as-path multipath-relax' knob is on, we don't need
+	   * an exact match of AS_PATH. Thus, mark the paths are equal.
+	   * That will trigger both these paths to get into the multipath
+	   * array.
+	   */
+	  *paths_eq = 1;
+        }
+      else if (new->peer->sort == BGP_PEER_IBGP)
 	{
 	  if (aspath_cmp (new->attr->aspath, exist->attr->aspath))
 	    *paths_eq = 1;
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 0f28894..bfa1f20 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -1160,6 +1160,38 @@
   return CMD_SUCCESS;
 }
 
+/* "bgp bestpath as-path multipath-relax" configuration.  */
+DEFUN (bgp_bestpath_aspath_multipath_relax,
+       bgp_bestpath_aspath_multipath_relax_cmd,
+       "bgp bestpath as-path multipath-relax",
+       "BGP specific commands\n"
+       "Change the default bestpath selection\n"
+       "AS-path attribute\n"
+       "Allow load sharing across routes that have different AS paths (but same length)\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_flag_set (bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_bestpath_aspath_multipath_relax,
+       no_bgp_bestpath_aspath_multipath_relax_cmd,
+       "no bgp bestpath as-path multipath-relax",
+       NO_STR
+       "BGP specific commands\n"
+       "Change the default bestpath selection\n"
+       "AS-path attribute\n"
+       "Allow load sharing across routes that have different AS paths (but same length)\n")
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+  bgp_flag_unset (bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX);
+  return CMD_SUCCESS;
+}
+
 /* "bgp log-neighbor-changes" configuration.  */
 DEFUN (bgp_log_neighbor_changes,
        bgp_log_neighbor_changes_cmd,
@@ -9172,6 +9204,10 @@
   install_element (BGP_NODE, &bgp_bestpath_aspath_confed_cmd);
   install_element (BGP_NODE, &no_bgp_bestpath_aspath_confed_cmd);
 
+  /* "bgp bestpath as-path multipath-relax" commands */
+  install_element (BGP_NODE, &bgp_bestpath_aspath_multipath_relax_cmd);
+  install_element (BGP_NODE, &no_bgp_bestpath_aspath_multipath_relax_cmd);
+
   /* "bgp log-neighbor-changes" commands */
   install_element (BGP_NODE, &bgp_log_neighbor_changes_cmd);
   install_element (BGP_NODE, &no_bgp_log_neighbor_changes_cmd);
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index c9a04ff..4b26993 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -5276,6 +5276,9 @@
 	vty_out (vty, " bgp bestpath as-path ignore%s", VTY_NEWLINE);
       if (bgp_flag_check (bgp, BGP_FLAG_ASPATH_CONFED))
 	vty_out (vty, " bgp bestpath as-path confed%s", VTY_NEWLINE);
+      if (bgp_flag_check (bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX)) {
+	vty_out (vty, " bgp bestpath as-path multipath-relax%s", VTY_NEWLINE);
+      }
       if (bgp_flag_check (bgp, BGP_FLAG_COMPARE_ROUTER_ID))
 	vty_out (vty, " bgp bestpath compare-routerid%s", VTY_NEWLINE);
       if (bgp_flag_check (bgp, BGP_FLAG_MED_CONFED)
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 0746f0d..024fedc 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -120,6 +120,7 @@
 #define BGP_FLAG_LOG_NEIGHBOR_CHANGES     (1 << 11)
 #define BGP_FLAG_GRACEFUL_RESTART         (1 << 12)
 #define BGP_FLAG_ASPATH_CONFED            (1 << 13)
+#define BGP_FLAG_ASPATH_MULTIPATH_RELAX   (1 << 14)
 
   /* BGP Per AF flags */
   u_int16_t af_flags[AFI_MAX][SAFI_MAX];
diff --git a/doc/bgpd.texi b/doc/bgpd.texi
index dd37d3e..24028d6 100644
--- a/doc/bgpd.texi
+++ b/doc/bgpd.texi
@@ -124,6 +124,12 @@
 decision process.
 @end deffn
 
+@deffn {BGP} {bgp bestpath as-path multipath-relax} {}
+This command specifies that BGP decision process should consider paths
+of equal AS_PATH length candidates for multipath computation. Without
+the knob, the entire AS_PATH must match for multipath computation.
+@end deffn
+
 @node BGP route flap dampening
 @subsection BGP route flap dampening