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/bgp_routemap.c b/bgpd/bgp_routemap.c
index 261f6b8..c49c2e9 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -95,6 +95,103 @@
 
 */ 
 
+ /* 'match peer (A.B.C.D|X:X::X:X)' */
+
+/* Compares the peer specified in the 'match peer' clause with the peer
+    received in bgp_info->peer. If it is the same, or if the peer structure
+    received is a peer_group containing it, returns RMAP_MATCH. */
+route_map_result_t
+route_match_peer (void *rule, struct prefix *prefix, route_map_object_t type,
+      void *object)
+{
+  union sockunion *su;
+  union sockunion *su2;
+  struct peer_group *group;
+  struct peer *peer;
+  struct listnode *nn;
+
+  if (type == RMAP_BGP)
+    {
+      su = rule;
+      peer = ((struct bgp_info *) object)->peer;
+
+      if ( ! CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_IMPORT) &&
+           ! CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_EXPORT) )
+        return RMAP_NOMATCH;
+
+      /* If su='0.0.0.0' (command 'match peer local'), and it's a NETWORK,
+          REDISTRIBUTE or DEFAULT_GENERATED route => return RMAP_MATCH */
+      su2 = sockunion_str2su ("0.0.0.0");
+      if ( sockunion_same (su, su2) )
+        {
+          if ( CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_NETWORK) ||
+               CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_REDISTRIBUTE) ||
+               CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_DEFAULT))
+            {
+              XFREE (MTYPE_SOCKUNION, su2);
+
+              return RMAP_MATCH;
+            }
+          else
+            return RMAP_NOMATCH;
+        }
+      XFREE (MTYPE_SOCKUNION, su2);
+
+      if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+        {
+          if (sockunion_same (su, &peer->su))
+            return RMAP_MATCH;
+
+          return RMAP_NOMATCH;
+        }
+      else
+        {
+          group = peer->group;
+          LIST_LOOP (group->peer, peer, nn)
+            {
+              if (sockunion_same (su, &peer->su))
+                return RMAP_MATCH;
+
+              return RMAP_NOMATCH;
+            }
+        }
+    }
+  return RMAP_NOMATCH;
+}
+
+void *
+route_match_peer_compile (char *arg)
+{
+  union sockunion *su;
+  int ret;
+
+  su = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (union sockunion));
+
+  ret = str2sockunion ( (arg)? arg : "0.0.0.0", su);
+  if (ret < 0) {
+    XFREE (MTYPE_ROUTE_MAP_COMPILED, su);
+    return NULL;
+  }
+
+  return su;
+}
+
+/* Free route map's compiled `ip address' value. */
+void
+route_match_peer_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip address matching. */
+struct route_map_rule_cmd route_match_peer_cmd =
+{
+  "peer",
+  route_match_peer,
+  route_match_peer_compile,
+  route_match_peer_free
+};
+
 /* `match ip address IP_ACCESS_LIST' */
 
 /* Match function should return 1 if match is success else return
@@ -643,7 +740,8 @@
 
       if (rins->peer_address)
 	{
-	  if (CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_IN) 
+         if ((CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_IN) ||
+           CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_IMPORT))
 	      && peer->su_remote 
 	      && sockunion_family (peer->su_remote) == AF_INET)
 	    {
@@ -1974,7 +2072,7 @@
 	      {
 		filter = &peer->filter[afi][safi];
 	  
-		for (direct = FILTER_IN; direct < FILTER_MAX; direct++)
+               for (direct = RMAP_IN; direct < RMAP_MAX; direct++)
 		  {
 		    if (filter->map[direct].name)
 		      filter->map[direct].map = 
@@ -1996,7 +2094,7 @@
 	      {
 		filter = &group->conf->filter[afi][safi];
 	  
-		for (direct = FILTER_IN; direct < FILTER_MAX; direct++)
+               for (direct = RMAP_IN; direct < RMAP_MAX; direct++)
 		  {
 		    if (filter->map[direct].name)
 		      filter->map[direct].map = 
@@ -2064,6 +2162,57 @@
     }
 }
 
+DEFUN (match_peer,
+       match_peer_cmd,
+       "match peer (A.B.C.D|X:X::X:X)",
+       MATCH_STR
+       "Match peer address\n"
+       "IPv6 address of peer\n"
+       "IP address of peer\n")
+{
+  return bgp_route_match_add (vty, vty->index, "peer", argv[0]);
+}
+
+DEFUN (match_peer_local,
+        match_peer_local_cmd,
+        "match peer local",
+        MATCH_STR
+        "Match peer address\n"
+        "Static or Redistributed routes\n")
+{
+  return bgp_route_match_add (vty, vty->index, "peer", NULL);
+}
+
+DEFUN (no_match_peer,
+       no_match_peer_cmd,
+       "no match peer",
+       NO_STR
+       MATCH_STR
+       "Match peer address\n")
+{
+ if (argc == 0)
+   return bgp_route_match_delete (vty, vty->index, "peer", NULL);
+
+  return bgp_route_match_delete (vty, vty->index, "peer", argv[0]);
+}
+
+ALIAS (no_match_peer,
+       no_match_peer_val_cmd,
+       "no match peer (A.B.C.D|X:X::X:X)",
+       NO_STR
+       MATCH_STR
+       "Match peer address\n"
+       "IPv6 address of peer\n"
+       "IP address of peer\n")
+
+ALIAS (no_match_peer,
+       no_match_peer_local_cmd,
+       "no match peer local",
+       NO_STR
+       MATCH_STR
+       "Match peer address\n"
+       "Static or Redistributed routes\n")
+
 DEFUN (match_ip_address, 
        match_ip_address_cmd,
        "match ip address (<1-199>|<1300-2699>|WORD)",
@@ -3239,6 +3388,7 @@
   route_map_add_hook (bgp_route_map_update);
   route_map_delete_hook (bgp_route_map_update);
 
+  route_map_install_match (&route_match_peer_cmd);
   route_map_install_match (&route_match_ip_address_cmd);
   route_map_install_match (&route_match_ip_next_hop_cmd);
   route_map_install_match (&route_match_ip_address_prefix_list_cmd);
@@ -3264,6 +3414,11 @@
   route_map_install_set (&route_set_ecommunity_rt_cmd);
   route_map_install_set (&route_set_ecommunity_soo_cmd);
 
+  install_element (RMAP_NODE, &match_peer_cmd);
+  install_element (RMAP_NODE, &match_peer_local_cmd);
+  install_element (RMAP_NODE, &no_match_peer_cmd);
+  install_element (RMAP_NODE, &no_match_peer_val_cmd);
+  install_element (RMAP_NODE, &no_match_peer_local_cmd);
   install_element (RMAP_NODE, &match_ip_address_cmd);
   install_element (RMAP_NODE, &no_match_ip_address_cmd);
   install_element (RMAP_NODE, &no_match_ip_address_val_cmd);