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/lib/routemap.c b/lib/routemap.c
index 9995334..9d6bbcf 100644
--- a/lib/routemap.c
+++ b/lib/routemap.c
@@ -26,6 +26,7 @@
 #include "prefix.h"
 #include "routemap.h"
 #include "command.h"
+#include "log.h"
 
 /* Vector for route match rules. */
 static vector route_match_vec;
@@ -221,9 +222,11 @@
                  rule->cmd->str, rule->rule_str, VTY_NEWLINE);
       
       vty_out (vty, "  Action:%s", VTY_NEWLINE);
-      if (index->exitpolicy == RMAP_GOTO)
+      
+      if (index->nextrm)
+        vty_out (vty, "    Call %s%s", index->nextrm, VTY_NEWLINE);
+      else if (index->exitpolicy == RMAP_GOTO)
         vty_out (vty, "    Goto %d%s", index->nextpref, VTY_NEWLINE);
-        
       else if (index->exitpolicy == RMAP_NEXT)
         {
           vty_out (vty, "    Goto next, (entry ");
@@ -298,6 +301,10 @@
   else
     index->map->head = index->next;
 
+  /* Free 'char *nextrm' if not NULL */
+  if (index->nextrm)
+    free (index->nextrm);
+
     /* Execute event hook. */
   if (route_map_master.event_hook && notify)
     (*route_map_master.event_hook) (RMAP_EVENT_INDEX_DELETED,
@@ -688,18 +695,26 @@
     deny      deny    |     cont
                       |
   
-   action) Apply Set statements, accept route
-      If NEXT is specified, goto NEXT statement
-      If GOTO is specified, goto the first clause where pref > nextpref
-      If nothing is specified, do as Cisco and finish
-   deny)   If NEXT is specified, goto NEXT statement
-      If nothing is specified, finally will be denied by route-map.
-   cont)   Goto Next index
+   action)
+      -Apply Set statements, accept route
+      -If Call statement is present jump to the specified route-map, if it
+         denies the route we finish.
+      -If NEXT is specified, goto NEXT statement
+      -If GOTO is specified, goto the first clause where pref > nextpref
+      -If nothing is specified, do as Cisco and finish
+   deny)
+      -Route is denied by route-map.
+   cont)
+      -Goto Next index
   
    If we get no matches after we've processed all updates, then the route
    is dropped too.
   
-   Some notes on the new "NEXT" and "GOTO"
+   Some notes on the new "CALL", "NEXT" and "GOTO"
+     call WORD        - If this clause is matched, then the set statements
+                        are executed and then we jump to route-map 'WORD'. If
+                        this route-map denies the route, we finish, in other case we
+                        do whatever the exit policy (EXIT, NEXT or GOTO) tells.
      on-match next    - If this clause is matched, then the set statements
                         are executed and then we drop through to the next clause
      on-match goto n  - If this clause is matched, then the set statments
@@ -746,10 +761,20 @@
 route_map_apply (struct route_map *map, struct prefix *prefix,
                  route_map_object_t type, void *object)
 {
+  static int recursion = 0;
   int ret = 0;
   struct route_map_index *index;
   struct route_map_rule *set;
 
+  if (recursion > RMAP_RECURSION_LIMIT)
+    {
+      zlog (NULL, LOG_WARNING,
+            "route-map recursion limit (%d) reached, discarding route",
+            RMAP_RECURSION_LIMIT);
+      recursion = 0;
+      return RMAP_DENYMATCH;
+    }
+
   if (map == NULL)
     return RMAP_DENYMATCH;
 
@@ -771,6 +796,25 @@
               for (set = index->set_list.head; set; set = set->next)
                 ret = (*set->cmd->func_apply) (set->value, prefix,
                                                type, object);
+
+              /* Call another route-map if available */
+              if (index->nextrm)
+                {
+                  struct route_map *nextrm =
+                                    route_map_lookup_by_name (index->nextrm);
+
+                  if (nextrm) /* Target route-map found, jump to it */
+                    {
+                      recursion++;
+                      ret = route_map_apply (nextrm, prefix, type, object);
+                      recursion--;
+                    }
+
+                  /* If nextrm returned 'deny', finish. */
+                  if (ret == RMAP_DENYMATCH)
+                    return ret;
+                }
+                
               switch (index->exitpolicy)
                 {
                   case RMAP_EXIT:
@@ -781,8 +825,9 @@
                     {
                       /* Find the next clause to jump to */
                       struct route_map_index *next = index->next;
+                      int nextpref = index->nextpref;
 
-                      while (next && next->pref < index->nextpref)
+                      while (next && next->pref < nextpref)
                         {
                           index = next;
                           next = next->next;
@@ -798,9 +843,6 @@
           else if (index->type == RMAP_DENY)
             /* 'deny' */
             {
-              if (index->exitpolicy == RMAP_NEXT)
-                continue;
-              else
                 return RMAP_DENYMATCH;
             }
         }
@@ -1046,7 +1088,7 @@
        "no on-match goto",
        NO_STR
        "Exit policy on matches\n"
-       "Next clause\n")
+       "Goto Clause number\n")
 {
   struct route_map_index *index;
 
@@ -1103,6 +1145,49 @@
     return vty_show_route_map (vty, argv[0]);
 }
 
+ALIAS (rmap_onmatch_goto,
+      rmap_continue_index_cmd,
+      "continue <1-65536>",
+      "Exit policy on matches\n"
+      "Goto Clause number\n")
+
+DEFUN (rmap_call,
+       rmap_call_cmd,
+       "call WORD",
+       "Jump to another Route-Map after match+set\n"
+       "Target route-map name\n")
+{
+  struct route_map_index *index;
+
+  index = vty->index;
+  if (index)
+    {
+      if (index->nextrm)
+          free (index->nextrm);
+      index->nextrm = strdup (argv[0]);
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_rmap_call,
+       no_rmap_call_cmd,
+       "no call",
+       NO_STR
+       "Jump to another Route-Map after match+set\n")
+{
+  struct route_map_index *index;
+
+  index = vty->index;
+
+  if (index->nextrm)
+    {
+      free (index->nextrm);
+      index->nextrm = NULL;
+    }
+
+  return CMD_SUCCESS;
+}
+
 /* Configuration write function. */
 int
 route_map_config_write (struct vty *vty)
@@ -1135,9 +1220,10 @@
 	  vty_out (vty, " set %s %s%s", rule->cmd->str,
 		   rule->rule_str ? rule->rule_str : "",
 		   VTY_NEWLINE);
+   if (index->nextrm)
+     vty_out (vty, " call %s%s", index->nextrm, VTY_NEWLINE);
 	if (index->exitpolicy == RMAP_GOTO)
-	  vty_out (vty, " on-match goto %d%s", index->nextpref,
-		   VTY_NEWLINE);
+      vty_out (vty, " on-match goto %d%s", index->nextpref, VTY_NEWLINE);
 	if (index->exitpolicy == RMAP_NEXT)
 	  vty_out (vty," on-match next%s", VTY_NEWLINE);
 	
@@ -1173,7 +1259,16 @@
   install_element (RMAP_NODE, &no_rmap_onmatch_next_cmd);
   install_element (RMAP_NODE, &rmap_onmatch_goto_cmd);
   install_element (RMAP_NODE, &no_rmap_onmatch_goto_cmd);
-
+  
+  /* Install the continue stuff (ALIAS of on-match). */
+  install_element (RMAP_NODE, &rmap_continue_cmd);
+  install_element (RMAP_NODE, &no_rmap_continue_cmd);
+  install_element (RMAP_NODE, &rmap_continue_index_cmd);
+  
+  /* Install the call stuff. */
+  install_element (RMAP_NODE, &rmap_call_cmd);
+  install_element (RMAP_NODE, &no_rmap_call_cmd);
+   
   /* Install show command */
   install_element (ENABLE_NODE, &rmap_show_cmd);
   install_element (ENABLE_NODE, &rmap_show_name_cmd);