bgpd: add replace-as modifier for BGP neighbor

Added replace-as modifier for BGP neighbors when using
local-as. If the replace-as modifier is specified, only the
replacement AS as specified by the local-as modifier is
prepended to the AS_PATH, not the process's AS.

In bgp_attr.c, I decided that

if (peer->change_local_as) {
  /* If replace-as is specified, we only use the change_local_as when
     advertising routes. */
  if( ! CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS) ) {
    aspath = aspath_add_seq (aspath, peer->local_as);
  }
  aspath = aspath_add_seq (aspath, peer->change_local_as);
} else {
  aspath = aspath_add_seq (aspath, peer->local_as);
}

was clearer than the alternative that didn't duplicate the prepending of the
process's AS:

/* First, append the process local AS unless we have an alternate local_as
 * and we're replacing it (as opposed to just prepending it). */
if (! (peer->change_local_as
       && CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS) ) ) {
  aspath = aspath_add_seq (aspath, peer->local_as);
}

if (peer->change_local_as)
  aspath = aspath_add_seq (aspath, peer->change_local_as);
}

But I could be convinced otherwise.

Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 69c8c0a..908bdd9 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -962,6 +962,7 @@
     {
       peer->change_local_as = 0;
       UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);
+      UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS);
     }
 }
 
@@ -1830,6 +1831,7 @@
 	{
 	  group->conf->change_local_as = 0;
 	  UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);
+	  UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS);
 	}
     }
 
@@ -3405,7 +3407,7 @@
 }
 
 int
-peer_local_as_set (struct peer *peer, as_t as, int no_prepend)
+peer_local_as_set (struct peer *peer, as_t as, int no_prepend, int replace_as)
 {
   struct bgp *bgp = peer->bgp;
   struct peer_group *group;
@@ -3421,9 +3423,14 @@
   if (peer_group_active (peer))
     return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
 
+  if (peer->as == as)
+    return BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS_REMOTE_AS;
+
   if (peer->change_local_as == as &&
       ((CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND) && no_prepend)
-       || (! CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND) && ! no_prepend)))
+       || (! CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND) && ! no_prepend)) &&
+      ((CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS) && replace_as)
+       || (! CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS) && ! replace_as)))
     return 0;
 
   peer->change_local_as = as;
@@ -3432,6 +3439,11 @@
   else
     UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);
 
+  if (replace_as)
+    SET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS);
+  else
+    UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS);
+
   if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
     {
       if (peer->status == Established)
@@ -3455,6 +3467,11 @@
       else
 	UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);
 
+      if (replace_as)
+        SET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS);
+      else
+        UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS);
+
       if (peer->status == Established)
        {
          peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE;
@@ -3482,6 +3499,7 @@
 
   peer->change_local_as = 0;
   UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);
+  UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS);
 
   if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
     {
@@ -3502,6 +3520,7 @@
     {
       peer->change_local_as = 0;
       UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);
+      UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS);
 
       if (peer->status == Established)
        {
@@ -4770,10 +4789,12 @@
       /* local-as. */
       if (peer->change_local_as)
 	if (! peer_group_active (peer))
-	  vty_out (vty, " neighbor %s local-as %u%s%s", addr,
+	  vty_out (vty, " neighbor %s local-as %u%s%s%s", addr,
 		   peer->change_local_as,
 		   CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND) ?
-		   " no-prepend" : "", VTY_NEWLINE);
+		   " no-prepend" : "",
+		   CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS) ?
+		   " replace-as" : "", VTY_NEWLINE);
 
       /* Description. */
       if (peer->desc)