2004-09-13 Jose Luis Rubio <jrubio@dit.upm.es>
           (at Technical University of Madrid as part of Euro6ix Project)

        Enhanced Route Server functionality and Route-Maps:

        * bgpd/bgpd.h: Modified 'struct peer' and 'struct bgp_filter' to
        support rs-clients. A 'struct bgp_table *rib' has been added to the
        first (to mantain a separated RIB for each rs-client) and two new
        route-maps have been added to the last (for import/export policies).
        Added the following #defines: RMAP_{IN|OUT|IMPORT|EXPORT|MAX},
        PEER_RMAP_TYPE_{IMPORT|EXPORT} and BGP_CLEAR_SOFT_RSCLIENT.

        * bgpd/bgpd.c: Modified the functions that create/delete/etc peers in
        order to consider the new fields included in 'struct peer' for
        supporting rs-clients, i.e. the import/export route-maps and the
        'struct bgp_table'.

        * bgpd/bgp_route.{ch}: Modified several functions related with
        receiving/sending announces in order to support the new Route Server
        capabilities.
        Function 'bgp_process' has been reorganized, creating an auxiliar
        function for best path selection ('bgp_best_selection').
        Modified 'bgp_show' and 'bgp_show_route' for displaying information
        about any RIB (and not only the main bgp RIB).
        Added commands for displaying information about RS-clients RIBs:
        'show bgp rsclient (A.B.C.D|X:X::X:X)', 'show bgp rsclient
        (A.B.C.D|X:X::X:X) X:X::X:X/M', etc

        * bgpd/bgp_table.{ch}: The structure 'struct bgp_table' now has two
        new fields: type (which can take the values BGP_TABLE_{MAIN|RSCLIENT})
        and 'void *owner' which points to 'struct bgp' or 'struct peer' which
        owns the table.
        When creating a new bgp_table by default 'type=BGP_TABLE_MAIN' is set.

        * bgpd/bgp_vty.c: The commands 'neighbor ... route-server-client' and
        'no neighbor ... route-server-client' now not only set/unset the flag
        PEER_FLAG_RSERVER_CLIENT, but they create/destroy the 'struct
        bgp_table' of the peer. Special actions are taken for peer_groups.
        Command 'neighbor ... route-map WORD (in|out)' now also supports two
        new kinds of route-map: 'import' and 'export'.
        Added commands 'clear bgp * rsclient', etc. These commands allow a new
        kind of soft_reconfig which affects only the RIB of the specified
        RS-client.
        Added commands 'show bgp rsclient summary', etc which display a
        summary of the rs-clients configured for the corresponding address
        family.

        * bgpd/bgp_routemap.c: A new match statement is available,
        'match peer (A.B.C.D|X:X::X:X)'. This statement can only be used in
        import/export route-maps, and it matches when the peer who announces
        (when used in an import route-map) or is going to receive (when used
        in an export route-map) the route is the same than the one specified
        in the statement.
        For peer-groups the statement matches if the specified peer is member
        of the peer-group.
        A special version of the command, 'match peer local', matches with
        routes originated by the Route Server (defined with 'network ...',
        redistributed routes and default-originate).

        * lib/routemap.{ch}: Added a new clause 'call NAME' for use in
        route-maps. It jumps into the specified route-map and when it returns
        the first route-map ends if the called RM returns DENY_MATCH, or
        continues in other case.
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 47df3c8..6afbe95 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -531,6 +531,23 @@
   return 0;
 }
 
+/* If peer is RSERVER_CLIENT in at least one address family and is not member
+    of a peer_group for that family, return 1.
+    Used to check wether the peer is included in list bgp->rsclient. */
+int
+peer_rsclient_active (struct peer *peer)
+{
+  int i;
+  int j;
+
+  for (i=AFI_IP; i < AFI_MAX; i++)
+    for (j=SAFI_UNICAST; j < SAFI_MAX; j++)
+      if (CHECK_FLAG(peer->af_flags[i][j], PEER_FLAG_RSERVER_CLIENT)
+            && ! peer->af_group[i][j])
+        return 1;
+  return 0;
+}
+
 /* Peer comparison function for sorting.  */
 static int
 peer_cmp (struct peer *p1, struct peer *p2)
@@ -572,6 +589,9 @@
 	  free (filter->aslist[i].name);
 	  filter->aslist[i].name = NULL;
 	}
+   }
+ for (i = RMAP_IN; i < RMAP_MAX; i++)
+       {
       if (filter->map[i].name)
 	{
 	  free (filter->map[i].name);
@@ -1092,7 +1112,18 @@
 
   /* Delete from all peer list. */
   if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+    {
     listnode_delete (bgp->peer, peer);
+      if (peer_rsclient_active (peer))
+        listnode_delete (bgp->rsclient, peer);
+    }
+
+  /* Free RIB for any family in which peer is RSERVER_CLIENT, and is not
+      member of a peer_group. */
+  for (afi = AFI_IP; afi < AFI_MAX; afi++)
+    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+      if (peer->rib[afi][safi] && ! peer->af_group[afi][safi])
+        bgp_table_finish (peer->rib[afi][safi]);
 
   /* Buffer.  */
   if (peer->ibuf)
@@ -1134,6 +1165,9 @@
 	      free (filter->plist[i].name);
 	    if (filter->aslist[i].name)
 	      free (filter->aslist[i].name);
+     }
+   for (i = RMAP_IN; i < RMAP_MAX; i++)
+      {
 	    if (filter->map[i].name)
 	      free (filter->map[i].name);
 	  }
@@ -1300,6 +1334,34 @@
   /* allowas-in */
   peer->allowas_in[afi][safi] = conf->allowas_in[afi][safi];
 
+  /* route-server-client */
+  if (CHECK_FLAG(conf->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))
+    {
+      /* Make peer's RIB point to group's RIB. */
+      peer->rib[afi][safi] = group->conf->rib[afi][safi];
+
+      /* Import policy. */
+      if (pfilter->map[RMAP_IMPORT].name)
+        free (pfilter->map[RMAP_IMPORT].name);
+      if (gfilter->map[RMAP_IMPORT].name)
+        {
+          pfilter->map[RMAP_IMPORT].name = strdup (gfilter->map[RMAP_IMPORT].name);
+          pfilter->map[RMAP_IMPORT].map = gfilter->map[RMAP_IMPORT].map;
+        }
+      else
+        {
+          pfilter->map[RMAP_IMPORT].name = NULL;
+          pfilter->map[RMAP_IMPORT].map = NULL;
+        }
+
+      /* Export policy. */
+      if (gfilter->map[RMAP_EXPORT].name && ! pfilter->map[RMAP_EXPORT].name)
+        {
+          pfilter->map[RMAP_EXPORT].name = strdup (gfilter->map[RMAP_EXPORT].name);
+          pfilter->map[RMAP_EXPORT].map = gfilter->map[RMAP_EXPORT].map;
+        }
+    }
+
   /* default-originate route-map */
   if (conf->default_rmap[afi][safi].name)
     {
@@ -1355,12 +1417,12 @@
       pfilter->aslist[in].name = strdup (gfilter->aslist[in].name);
       pfilter->aslist[in].aslist = gfilter->aslist[in].aslist;
     }
-  if (gfilter->map[in].name && ! pfilter->map[in].name)
+  if (gfilter->map[RMAP_IN].name && ! pfilter->map[RMAP_IN].name)
     {
-      if (pfilter->map[in].name)
-	free (pfilter->map[in].name);
-      pfilter->map[in].name = strdup (gfilter->map[in].name);
-      pfilter->map[in].map = gfilter->map[in].map;
+      if (pfilter->map[RMAP_IN].name)
+        free (pfilter->map[RMAP_IN].name);
+      pfilter->map[RMAP_IN].name = strdup (gfilter->map[RMAP_IN].name);
+      pfilter->map[RMAP_IN].map = gfilter->map[RMAP_IN].map;
     }
 
   /* outbound filter apply */
@@ -1406,19 +1468,42 @@
       pfilter->aslist[out].name = NULL;
       pfilter->aslist[out].aslist = NULL;
     }
-  if (gfilter->map[out].name)
+  if (gfilter->map[RMAP_OUT].name)
     {
-      if (pfilter->map[out].name)
-	free (pfilter->map[out].name);
-      pfilter->map[out].name = strdup (gfilter->map[out].name);
-      pfilter->map[out].map = gfilter->map[out].map;
+      if (pfilter->map[RMAP_OUT].name)
+        free (pfilter->map[RMAP_OUT].name);
+      pfilter->map[RMAP_OUT].name = strdup (gfilter->map[RMAP_OUT].name);
+      pfilter->map[RMAP_OUT].map = gfilter->map[RMAP_OUT].map;
     }
   else
     {
-      if (pfilter->map[out].name)
-	free (pfilter->map[out].name);
-      pfilter->map[out].name = NULL;
-      pfilter->map[out].map = NULL;
+      if (pfilter->map[RMAP_OUT].name)
+        free (pfilter->map[RMAP_OUT].name);
+      pfilter->map[RMAP_OUT].name = NULL;
+      pfilter->map[RMAP_OUT].map = NULL;
+    }
+
+ /* RS-client's import/export route-maps. */
+  if (gfilter->map[RMAP_IMPORT].name)
+    {
+      if (pfilter->map[RMAP_IMPORT].name)
+        free (pfilter->map[RMAP_IMPORT].name);
+      pfilter->map[RMAP_IMPORT].name = strdup (gfilter->map[RMAP_IMPORT].name);
+      pfilter->map[RMAP_IMPORT].map = gfilter->map[RMAP_IMPORT].map;
+    }
+  else
+    {
+      if (pfilter->map[RMAP_IMPORT].name)
+        free (pfilter->map[RMAP_IMPORT].name);
+      pfilter->map[RMAP_IMPORT].name = NULL;
+      pfilter->map[RMAP_IMPORT].map = NULL;
+    }
+  if (gfilter->map[RMAP_EXPORT].name && ! pfilter->map[RMAP_EXPORT].name)
+    {
+      if (pfilter->map[RMAP_EXPORT].name)
+        free (pfilter->map[RMAP_EXPORT].name);
+      pfilter->map[RMAP_EXPORT].name = strdup (gfilter->map[RMAP_EXPORT].name);
+      pfilter->map[RMAP_EXPORT].map = gfilter->map[RMAP_EXPORT].map;
     }
 
   if (gfilter->usmap.name)
@@ -1601,6 +1686,35 @@
 	  UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);
 	}
     }
+
+  if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))
+    {
+      /* If it's not configured as RSERVER_CLIENT in any other address
+          family, without being member of a peer_group, remove it from
+          list bgp->rsclient.*/
+      if (! peer_rsclient_active (peer))
+        listnode_delete (bgp->rsclient, peer);
+
+      bgp_table_finish (peer->rib[afi][safi]);
+
+      /* Import policy. */
+      if (peer->filter[afi][safi].map[RMAP_IMPORT].name)
+        {
+          free (peer->filter[afi][safi].map[RMAP_IMPORT].name);
+          peer->filter[afi][safi].map[RMAP_IMPORT].name = NULL;
+          peer->filter[afi][safi].map[RMAP_IMPORT].map = NULL;
+        }
+
+      /* Export policy. */
+      if (! CHECK_FLAG(group->conf->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)
+              && peer->filter[afi][safi].map[RMAP_EXPORT].name)
+        {
+          free (peer->filter[afi][safi].map[RMAP_EXPORT].name);
+          peer->filter[afi][safi].map[RMAP_EXPORT].name = NULL;
+          peer->filter[afi][safi].map[RMAP_EXPORT].map = NULL;
+        }
+    }
+
   peer_group2peer_config_copy (group, peer, afi, safi);
 
   if (peer->status == Established)
@@ -1629,6 +1743,9 @@
   peer->afc[afi][safi] = 0;
   peer_af_flag_reset (peer, afi, safi);
 
+  if (peer->rib[afi][safi])
+    peer->rib[afi][safi] = NULL;
+
   if (! peer_group_active (peer))
     {
       listnode_delete (group->peer, peer);
@@ -1672,6 +1789,9 @@
   bgp->group = list_new ();
   bgp->group->cmp = (int (*)(void *, void *)) peer_group_cmp;
 
+  bgp->rsclient = list_new ();
+  bgp->rsclient->cmp = (int (*)(void*, void*)) peer_cmp;
+
   for (afi = AFI_IP; afi < AFI_MAX; afi++)
     for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
       {
@@ -1826,6 +1946,9 @@
       peer_delete (peer);
     }
 
+  bgp->rsclient->del = (void (*)(void *)) peer_delete;
+  list_delete (bgp->rsclient);
+
   listnode_delete (bm->bgp, bgp);
 
   if (bgp->name)
@@ -2007,6 +2130,7 @@
     { 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_NEXTHOP_LOCAL_UNCHANGED,  0, peer_change_reset_out },
     { 0, 0, 0 }
   };
 
@@ -3551,10 +3675,12 @@
   if (! peer->afc[afi][safi])
     return BGP_ERR_PEER_INACTIVE;
 
-  if (direct != FILTER_IN && direct != FILTER_OUT)
+  if (direct != RMAP_IN && direct != RMAP_OUT &&
+      direct != RMAP_IMPORT && direct != RMAP_EXPORT)
     return BGP_ERR_INVALID_VALUE;
 
-  if (direct == FILTER_OUT && peer_is_group_member (peer, afi, safi))
+  if ( (direct == RMAP_OUT || direct == RMAP_IMPORT)
+      && peer_is_group_member (peer, afi, safi))
     return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
 
   filter = &peer->filter[afi][safi];
@@ -3662,10 +3788,12 @@
   if (! peer->afc[afi][safi])
     return BGP_ERR_PEER_INACTIVE;
 
-  if (direct != FILTER_IN && direct != FILTER_OUT)
+  if (direct != RMAP_IN && direct != RMAP_OUT &&
+      direct != RMAP_IMPORT && direct != RMAP_EXPORT)
     return BGP_ERR_INVALID_VALUE;
 
-  if (direct == FILTER_OUT && peer_is_group_member (peer, afi, safi))
+  if ( (direct == RMAP_OUT || direct == RMAP_IMPORT)
+      && peer_is_group_member (peer, afi, safi))
     return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
 
   filter = &peer->filter[afi][safi];
@@ -3950,6 +4078,14 @@
   if (! peer->afc[afi][safi])
     return BGP_ERR_AF_UNCONFIGURED;
 
+  if (stype == BGP_CLEAR_SOFT_RSCLIENT)
+    {
+      if (! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))
+        return 0;
+      bgp_check_local_routes_rsclient (peer, afi, safi);
+      bgp_soft_reconfig_rsclient (peer, afi, safi);
+    }
+
   if (stype == BGP_CLEAR_SOFT_OUT || stype == BGP_CLEAR_SOFT_BOTH)
     bgp_announce_route (peer, afi, safi);
 
@@ -4086,14 +4222,23 @@
 	     filter->plist[out].name, VTY_NEWLINE);
 
   /* route-map. */
-  if (filter->map[in].name)
-    if (! gfilter || ! gfilter->map[in].name
-	|| strcmp (filter->map[in].name, gfilter->map[in].name) != 0)
+  if (filter->map[RMAP_IN].name)
+    if (! gfilter || ! gfilter->map[RMAP_IN].name
+       || strcmp (filter->map[RMAP_IN].name, gfilter->map[RMAP_IN].name) != 0)
       vty_out (vty, " neighbor %s route-map %s in%s", addr, 
-	       filter->map[in].name, VTY_NEWLINE);
-  if (filter->map[out].name && ! gfilter)
+              filter->map[RMAP_IN].name, VTY_NEWLINE);
+  if (filter->map[RMAP_OUT].name && ! gfilter)
     vty_out (vty, " neighbor %s route-map %s out%s", addr, 
-	     filter->map[out].name, VTY_NEWLINE);
+            filter->map[RMAP_OUT].name, VTY_NEWLINE);
+  if (filter->map[RMAP_IMPORT].name && ! gfilter)
+    vty_out (vty, " neighbor %s route-map %s import%s", addr,
+        filter->map[RMAP_IMPORT].name, VTY_NEWLINE);
+  if (filter->map[RMAP_EXPORT].name)
+    if (! gfilter || ! gfilter->map[RMAP_EXPORT].name
+    || strcmp (filter->map[RMAP_EXPORT].name,
+                    gfilter->map[RMAP_EXPORT].name) != 0)
+    vty_out (vty, " neighbor %s route-map %s export%s", addr,
+        filter->map[RMAP_EXPORT].name, VTY_NEWLINE);
 
   /* unsuppress-map */
   if (filter->usmap.name && ! gfilter)