[Xorp-hackers] [RFC 3/3] static-mcast: Add support for static multicast routes.

greearb at candelatech.com greearb at candelatech.com
Mon Jul 9 12:04:18 PDT 2012


From: Ben Greear <greearb at candelatech.com>

This adds the ability to configure static multicast routes
using the static-route module.

Example config file looks like:

interfaces {
        interface "p3p1" {
                disable: false
                default-system-config
        }
        interface "wlan0" {
                disable: false
                default-system-config
        }
}
fea {
        unicast-forwarding4 {
                disable: false
        }
}

protocols {
  static {
    route 10.2.46.91/16 {
      next-hop: 10.2.46.20
      metric: 1
    }
    mrib-route 10.2.46.0/16 {
      next-hop: 10.2.46.30
      metric: 1
    }
    mcast-route 226.0.0.1 {
      input_if: "p3p1"
      input_ip: 192.168.9.11
      output_ifs: "wlan0"
      distance: 2
    }
  }
}

plumbing {
    mfea4 {
        disable: false
        interface "p3p1" {
            vif "p3p1" {
                disable: false
            }
        }
        interface "wlan0" {
            vif "wlan0" {
                disable: false
            }
        }
        interface "register_vif" {
            vif "register_vif" {
                disable: false
            }
        }
    } /*  mfea4  */
} /* plumbing */

Signed-off-by: Ben Greear <greearb at candelatech.com>
---
 xorp/etc/templates/static_routes.tp          |   39 +++-
 xorp/static_routes/SConscript                |    1 +
 xorp/static_routes/static_routes_node.cc     |  185 +++++++++++
 xorp/static_routes/static_routes_node.hh     |  214 ++++++++-----
 xorp/static_routes/xorp_static_routes.cc     |    5 +-
 xorp/static_routes/xrl_static_routes_node.cc |  459 +++++++++++++++++++++++++-
 xorp/static_routes/xrl_static_routes_node.hh |  131 +++++++-
 xorp/xrl/interfaces/static_routes.xif        |    8 +-
 xorp/xrl/targets/static_routes.tgt           |    3 +-
 9 files changed, 956 insertions(+), 89 deletions(-)

diff --git a/xorp/etc/templates/static_routes.tp b/xorp/etc/templates/static_routes.tp
index f8359d0..9f69c54 100644
--- a/xorp/etc/templates/static_routes.tp
+++ b/xorp/etc/templates/static_routes.tp
@@ -1,4 +1,3 @@
-/* $XORP: xorp/etc/templates/static_routes.tp,v 1.43 2008/08/06 08:23:24 abittau Exp $ */
 
 protocols {
     static {
@@ -26,6 +25,13 @@ protocols {
 	    }
 	}
 
+	mcast-route @: ipv4 {
+	    input_if:		txt;
+	    input_ip:		ipv4;
+	    output_ifs:		txt;
+	    distance:		u32 = 0;
+	}
+
 	route4 @: ipv4net {			/* %deprecated */
 	    next-hop:		ipv4;
 	    nexthop:		ipv4;		/* %deprecated */
@@ -284,6 +290,37 @@ protocols {
 	    }
 	}
 
+	mcast-route @: ipv4 {
+	    %help:	short "Configure a static Multicast route";
+	    %mandatory:	$(@.input_if), $(@.input_ip), $(@.output_ifs);
+
+	    %create:	xrl "$(static.targetname)/static_routes/0.1/add_mcast_route4?mcast_addr:ipv4=$(@)&input_if:txt=$(@.input_if)&input_ip:ipv4=$(@.input_ip)&output_ifs:txt=$(@.output_ifs)&distance:u32=$(@.distance)";
+	    %update:	xrl "$(static.targetname)/static_routes/0.1/replace_mcast_route4?mcast_addr:ipv4=$(@)&input_if:txt=$(@.input_if)&input_ip:ipv4=$(@.input_ip)&output_ifs:txt=$(@.output_ifs))&distance:u32=$(@.distance)";
+	    %delete:	xrl "$(static.targetname)/static_routes/0.1/delete_mcast_route4?mcast_addr:ipv4=$(@)&input_ip:ipv4=$(@.input_ip)";
+
+	    input_if {
+		%help:	short "Configure the input interface";
+		%set:;
+	    }
+
+	    input_ip {
+		%help:	short "Configure the input IP address";
+		%set:;
+	    }
+
+	    output_ifs {
+		%help:	short "Configure the input output interface(s)";
+		%set:;
+	    }
+
+	    distance {
+		%help:	short "Configure the routing distance.  Lower value wins";
+		%allow-range: $(@) "0" "7" %help: "The routing distance";
+		%set:;
+	    }
+	}
+
+
 	route4 @: ipv4net {
 	    %deprecated: "Statement 'route4' is replaced with 'route'";
 	    %help:	short "Configure an IPv4 static route";
diff --git a/xorp/static_routes/SConscript b/xorp/static_routes/SConscript
index 583432a..5b69ef9 100644
--- a/xorp/static_routes/SConscript
+++ b/xorp/static_routes/SConscript
@@ -51,6 +51,7 @@ env.AppendUnique(LIBS = [
     'xst_fea_ifmgr_mirror',
     'xst_static_routes',
     'xif_rib',
+    'xif_mfea',
     'xif_finder_event_notifier',
     'xorp_policy_backend',
     'xorp_policy_common',
diff --git a/xorp/static_routes/static_routes_node.cc b/xorp/static_routes/static_routes_node.cc
index 06a8a9f..f8934ad 100644
--- a/xorp/static_routes/static_routes_node.cc
+++ b/xorp/static_routes/static_routes_node.cc
@@ -121,6 +121,9 @@ StaticRoutesNode::shutdown()
     //
     rib_register_shutdown();
 
+    // De-register with the MFEA
+    rib_register_shutdown();
+
     //
     // De-register with the FEA
     //
@@ -306,6 +309,7 @@ StaticRoutesNode::updates_made()
     StaticRoutesNode::Table::iterator route_iter;
     list<StaticRoute *> add_routes, replace_routes, delete_routes;
     list<StaticRoute *>::iterator pending_iter;
+    list<McastRoute *>::iterator mpending_iter;
 
     for (route_iter = _static_routes.begin();
 	 route_iter != _static_routes.end();
@@ -395,6 +399,80 @@ StaticRoutesNode::updates_made()
 	}
     }
 
+    // Deal with mcast-routes
+    list<McastRoute *> add_mroutes, replace_mroutes, delete_mroutes;
+    map<IPvX, McastRoute>::iterator mroute_iter;
+    for (mroute_iter = _mcast_routes.begin();
+	 mroute_iter != _mcast_routes.end();
+	 ++mroute_iter) {
+	McastRoute& static_route = mroute_iter->second;
+	bool is_old_up = false;
+	bool is_new_up = false;
+	string old_ifname, old_vifname, new_ifname, new_vifname;
+
+	//
+	// Calculate whether the interface was UP before and now.
+	//
+	const IfMgrIfAtom* if_atom;
+	const IfMgrVifAtom* vif_atom;
+
+	if_atom = _iftree.find_interface(static_route.ifname());
+	vif_atom = _iftree.find_vif(static_route.ifname(),
+				    static_route.vifname());
+	if ((if_atom != NULL) && (if_atom->enabled())
+	    && (! if_atom->no_carrier())
+	    && (vif_atom != NULL) && (vif_atom->enabled())) {
+	    is_old_up = true;
+	}
+
+	if_atom = ifmgr_iftree().find_interface(static_route.ifname());
+	vif_atom = ifmgr_iftree().find_vif(static_route.ifname(),
+					   static_route.vifname());
+	if ((if_atom != NULL) && (if_atom->enabled())
+	    && (! if_atom->no_carrier())
+	    && (vif_atom != NULL) && (vif_atom->enabled())) {
+	    is_new_up = true;
+	}
+
+	if ((is_old_up == is_new_up)
+	    && (old_ifname == new_ifname)
+	    && (old_vifname == new_vifname)) {
+	    continue;			// Nothing changed
+	}
+
+	if ((! is_old_up) && (! is_new_up)) {
+	    //
+	    // The interface is still down, so nothing to do
+	    //
+	    continue;
+	}
+	if ((! is_old_up) && (is_new_up)) {
+	    //
+	    // The interface is now up, hence add the route
+	    //
+	    add_mroutes.push_back(&static_route);
+	    continue;
+	}
+	if ((is_old_up) && (! is_new_up)) {
+	    //
+	    // The interface went down, hence cancel all pending requests,
+	    // and withdraw the route.
+	    //
+	    delete_mroutes.push_back(&static_route);
+	    continue;
+	}
+	if (is_old_up && is_new_up) {
+	    //
+	    // The interface remains up, hence probably the interface or
+	    // the vif name has changed.
+	    // Delete the route and then add it again so the information
+	    // in the RIB will be updated.
+	    //
+	    replace_mroutes.push_back(&static_route);
+	    continue;
+	}
+    }
+
     //
     // Update the local copy of the interface tree
     //
@@ -444,6 +522,44 @@ StaticRoutesNode::updates_made()
 	copy_route.set_delete_route();
 	inform_rib(copy_route);
     }
+
+
+    //
+    // Process all pending "add mroute" requests
+    //
+    for (mpending_iter = add_mroutes.begin();
+	 mpending_iter != add_mroutes.end();
+	 ++pending_iter) {
+	McastRoute& orig_route = *(*mpending_iter);
+	McastRoute copy_route = orig_route;
+	copy_route.set_add_route();
+	inform_mfea(copy_route);
+    }
+
+    //
+    // Process all pending "replace mroute" requests
+    //
+    for (mpending_iter = replace_mroutes.begin();
+	 mpending_iter != replace_mroutes.end();
+	 ++mpending_iter) {
+	McastRoute& orig_route = *(*mpending_iter);
+	McastRoute copy_route = orig_route;
+	copy_route.set_replace_route();
+	inform_mfea(copy_route);
+    }
+
+    //
+    // Process all pending "delete mroute" requests
+    //
+    for (mpending_iter = delete_mroutes.begin();
+	 mpending_iter != delete_mroutes.end();
+	 ++mpending_iter) {
+	McastRoute& orig_route = *(*mpending_iter);
+	cancel_mfea_mfc_change(orig_route);
+	McastRoute copy_route = orig_route;
+	copy_route.set_delete_route();
+	inform_mfea(copy_route);
+    }
 }
 
 /**
@@ -670,6 +786,65 @@ StaticRoutesNode::delete_route6(bool unicast, bool multicast,
     return (delete_route(static_route, error_msg));
 }
 
+int StaticRoutesNode::add_mcast_route4(const IPv4& mcast_addr, const string& input_if,
+				       const IPv4& input_ip, const string& output_ifs,
+				       uint32_t distance, string& error_msg) {
+    map<IPvX, McastRoute>::const_iterator iter = _mcast_routes.find(mcast_addr);
+    if (iter == _mcast_routes.end()) {
+	McastRoute mr(mcast_addr, input_if, input_ip, output_ifs, distance);
+	_mcast_routes[mcast_addr] = mr;
+	McastRoute copy_route = mr;
+	copy_route.set_add_route();
+	inform_mfea(copy_route);
+    }
+    else {
+	error_msg.append("Mcast-Route: " + mcast_addr.str() + " already exists!\n");
+	return XORP_ERROR;
+    }
+    return XORP_OK;
+}
+
+int StaticRoutesNode::replace_mcast_route4(const IPv4& mcast_addr, const string& input_if,
+					   const IPv4& input_ip, const string& output_ifs,
+					   uint32_t distance, string& error_msg) {
+    UNUSED(error_msg);
+
+    McastRoute mr(mcast_addr, input_if, input_ip, output_ifs, distance);
+    map<IPvX, McastRoute>::const_iterator iter = _mcast_routes.find(mcast_addr);
+    if (iter == _mcast_routes.end()) {
+	if (iter->second == mr) {
+	    // no changes
+	    return XORP_OK;
+	}
+    }
+    _mcast_routes.erase(mcast_addr);
+    _mcast_routes[mcast_addr] = mr;
+
+    McastRoute copy_route = mr;
+    copy_route.set_replace_route();
+    inform_mfea(copy_route);
+
+    return XORP_OK;
+}
+
+
+int StaticRoutesNode::delete_mcast_route4(const IPv4& mcast_addr, const IPv4& input_ip,
+					  string& error_msg) {
+    UNUSED(error_msg);
+
+    map<IPvX, McastRoute>::const_iterator iter = _mcast_routes.find(mcast_addr);
+    if (iter != _mcast_routes.end()) {
+	_mcast_routes.erase(mcast_addr);
+
+	McastRoute mr(mcast_addr, input_ip);
+	mr.set_delete_route();
+	inform_mfea(mr);
+    }
+
+    return XORP_OK;
+}
+
+
 /**
  * Find a route from the routing table.
  *
@@ -1296,6 +1471,16 @@ StaticRoutesNode::inform_rib(const StaticRoute& route)
 	inform_rib_route_change(modified_route);
 }
 
+
+void
+StaticRoutesNode::inform_mfea(const McastRoute& route)
+{
+    if (! is_enabled())
+	return;
+
+    inform_mfea_mfc_change(route);
+}
+
 /**
  * Update a route received from the user configuration.
  *
diff --git a/xorp/static_routes/static_routes_node.hh b/xorp/static_routes/static_routes_node.hh
index 907dbeb..53106be 100644
--- a/xorp/static_routes/static_routes_node.hh
+++ b/xorp/static_routes/static_routes_node.hh
@@ -18,8 +18,6 @@
 // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA;
 // http://xorp.net
 
-// $XORP: xorp/static_routes/static_routes_node.hh,v 1.32 2008/10/02 21:58:29 bms Exp $
-
 #ifndef __STATIC_ROUTES_STATIC_ROUTES_NODE_HH__
 #define __STATIC_ROUTES_STATIC_ROUTES_NODE_HH__
 
@@ -29,23 +27,91 @@
 //
 
 
-
 #include "libxorp/service.hh"
 #include "libxorp/status_codes.h"
-
 #include "libfeaclient/ifmgr_xrl_mirror.hh"
-
 #include "policy/backend/policytags.hh"
 #include "policy/backend/policy_filters.hh"
 
 class EventLoop;
 
+
+class StaticRouteBase {
+protected:
+    enum RouteType { IDLE_ROUTE, ADD_ROUTE, REPLACE_ROUTE, DELETE_ROUTE };
+    RouteType	_route_type;
+    bool	_is_ignored;	// True if the route is to be ignored
+
+public:
+
+    StaticRouteBase() : _route_type(IDLE_ROUTE), _is_ignored(false) { }
+    virtual ~StaticRouteBase() { }
+
+    /**
+     * Test if this is a route to add.
+     *
+     * @return true if this is a route to add, otherwise false.
+     */
+    bool is_add_route() const { return (_route_type == ADD_ROUTE); }
+
+    /**
+     * Test if this is a replacement route.
+     *
+     * @return true if this is a replacement route, otherwise false.
+     */
+    bool is_replace_route() const { return (_route_type == REPLACE_ROUTE); }
+
+    /**
+     * Test if this is a route to delete.
+     *
+     * @return true if this is a route to delete, otherwise false.
+     */
+    bool is_delete_route() const { return (_route_type == DELETE_ROUTE); }
+
+    /**
+     * Set the type of this route to "a route to add".
+     */
+    void set_add_route() { _route_type = ADD_ROUTE; }
+
+    /**
+     * Set the type of this route to "a replacement route".
+     */
+    void set_replace_route() { _route_type = REPLACE_ROUTE; }
+
+    /**
+     * Set the type of this route to "a route to delete".
+     */
+    void set_delete_route() { _route_type = DELETE_ROUTE; }
+
+
+    /**
+     * Test if the route is to be ignored.
+     *
+     * This method is used only for internal purpose when passing the route
+     * around.
+     *
+     * @return true if the route is to be ignored, otherwise false.
+     */
+    bool is_ignored() const { return _is_ignored; }
+
+    /**
+     * Set whether the route is to be ignored.
+     *
+     * This method is used only for internal purpose when passing the route
+     * around.
+     *
+     * @param v true if the route is to be ignored, otherwise false.
+     */
+    void set_ignored(bool v) { _is_ignored = v; }
+
+};
+
 /**
  * @short A StaticRoute helper class.
  * 
  * This class is used to store a routing entry.
  */
-class StaticRoute {
+class StaticRoute : public StaticRouteBase {
 public:
     /**
      * Constructor for a given IPv4 static route.
@@ -68,12 +134,11 @@ public:
 		const IPv4Net& network, const IPv4& nexthop,
 		const string& ifname, const string& vifname,
 		uint32_t metric, bool is_backup_route)
-	: _unicast(unicast), _multicast(multicast),
-	  _network(network), _nexthop(nexthop),
-	  _ifname(ifname), _vifname(vifname),
-	  _metric(metric), _is_backup_route(is_backup_route),
-	  _route_type(IDLE_ROUTE), _is_ignored(false),
-	  _is_filtered(false), _is_accepted_by_nexthop(false) {}
+	    : _unicast(unicast), _multicast(multicast),
+	      _network(network), _nexthop(nexthop),
+	      _ifname(ifname), _vifname(vifname),
+	      _metric(metric), _is_backup_route(is_backup_route),
+	      _is_filtered(false), _is_accepted_by_nexthop(false) {}
 
 #ifdef XORP_USE_USTL
     StaticRoute() { }
@@ -100,12 +165,11 @@ public:
 		const IPv6Net& network, const IPv6& nexthop,
 		const string& ifname, const string& vifname,
 		uint32_t metric, bool is_backup_route)
-	: _unicast(unicast), _multicast(multicast),
-	  _network(network), _nexthop(nexthop),
-	  _ifname(ifname), _vifname(vifname),
-	  _metric(metric), _is_backup_route(is_backup_route),
-	  _route_type(IDLE_ROUTE), _is_ignored(false),
-	  _is_filtered(false), _is_accepted_by_nexthop(false) {}
+	    : _unicast(unicast), _multicast(multicast),
+	      _network(network), _nexthop(nexthop),
+	      _ifname(ifname), _vifname(vifname),
+	      _metric(metric), _is_backup_route(is_backup_route),
+	      _is_filtered(false), _is_accepted_by_nexthop(false) {}
 
     /**
      * Equality Operator
@@ -231,42 +295,6 @@ public:
     bool is_backup_route() const { return _is_backup_route; }
 
     /**
-     * Test if this is a route to add.
-     * 
-     * @return true if this is a route to add, otherwise false.
-     */
-    bool is_add_route() const { return (_route_type == ADD_ROUTE); }
-
-    /**
-     * Test if this is a replacement route.
-     * 
-     * @return true if this is a replacement route, otherwise false.
-     */
-    bool is_replace_route() const { return (_route_type == REPLACE_ROUTE); }
-
-    /**
-     * Test if this is a route to delete.
-     * 
-     * @return true if this is a route to delete, otherwise false.
-     */
-    bool is_delete_route() const { return (_route_type == DELETE_ROUTE); }
-
-    /**
-     * Set the type of this route to "a route to add".
-     */
-    void set_add_route() { _route_type = ADD_ROUTE; }
-
-    /**
-     * Set the type of this route to "a replacement route".
-     */
-    void set_replace_route() { _route_type = REPLACE_ROUTE; }
-
-    /**
-     * Set the type of this route to "a route to delete".
-     */
-    void set_delete_route() { _route_type = DELETE_ROUTE; }
-
-    /**
      * Test if the route is interface-specific (e.g., if the interface
      * is explicitly specified).
      * 
@@ -284,26 +312,6 @@ public:
     bool is_valid_entry(string& error_msg) const;
 
     /**
-     * Test if the route is to be ignored.
-     * 
-     * This method is used only for internal purpose when passing the route
-     * around.
-     * 
-     * @return true if the route is to be ignored, otherwise false.
-     */
-    bool is_ignored() const { return _is_ignored; }
-
-    /**
-     * Set whether the route is to be ignored.
-     * 
-     * This method is used only for internal purpose when passing the route
-     * around.
-     * 
-     * @param v true if the route is to be ignored, otherwise false.
-     */
-    void set_ignored(bool v) { _is_ignored = v; }
-
-    /**
      * @return policy-tags for this route.
      */
     PolicyTags& policytags() { return _policytags; }
@@ -359,15 +367,48 @@ private:
     string	_vifname;
     uint32_t	_metric;
     bool	_is_backup_route;
-    enum RouteType { IDLE_ROUTE, ADD_ROUTE, REPLACE_ROUTE, DELETE_ROUTE };
-    RouteType	_route_type;
-    bool	_is_ignored;	// True if the route is to be ignored
     bool	_is_filtered;	// True if rejected by a policy filter
     bool	_is_accepted_by_nexthop; // True if the route is accepted based on its next-hop information
     PolicyTags	_policytags;
 };
 
 
+class McastRoute : public StaticRouteBase {
+protected:
+    IPvX _mcast_addr;
+    string _ifname; // assume vifname == ifname
+    IPvX _input_ip;
+    string _output_ifs; // assume vifname == ifname
+    uint32_t _distance;
+
+public:
+    McastRoute() { };
+    McastRoute(const IPvX& addr, const string& ifname, const IPvX& input_ip,
+	       const string& output_ifs, uint32_t distance) :
+	    _mcast_addr(addr), _ifname(ifname), _input_ip(input_ip),
+	    _output_ifs(output_ifs), _distance(distance) { }
+
+    McastRoute(const IPvX& addr, const IPvX& input_ip) :
+	    _mcast_addr(addr), _input_ip(input_ip) { }
+
+    bool operator==(const McastRoute& other) const {
+	if (this == &other)
+	    return true;
+	return (_mcast_addr == other._mcast_addr &&
+		_ifname == other._ifname &&
+		_input_ip == other._input_ip &&
+		_output_ifs == other._output_ifs &&
+		_distance == other._distance);
+    }
+
+    const IPvX& mcast_addr() const { return _mcast_addr; }
+    const string& ifname() const { return _ifname; }
+    const string& vifname() const { return ifname(); }
+    const IPvX& input_ip() const { return _input_ip; }
+    const string& output_ifs() const { return _output_ifs; }
+    const uint32_t& distance() const { return _distance; }
+};
+
 /**
  * @short The StaticRoutes node class.
  * 
@@ -597,6 +638,19 @@ public:
 		      const string& vifname, bool is_backup_route,
 		      string& error_msg);
 
+
+    int add_mcast_route4(const IPv4& mcast_addr, const string& input_if,
+			 const IPv4& input_ip, const string& output_ifs,
+			 uint32_t distance, string& error_msg);
+
+    int replace_mcast_route4(const IPv4& mcast_addr, const string& input_if,
+			     const IPv4& input_ip, const string& output_ifs,
+			     uint32_t distance, string& error_msg);
+
+    int delete_mcast_route4(const IPv4& mcast_addr, const IPv4& input_ip,
+			    string& error_msg);
+
+
     /**
      * Find a route from the routing table.
      *
@@ -814,6 +868,10 @@ private:
      */
     virtual void cancel_rib_route_change(const StaticRoute& static_route) = 0;
 
+    virtual void inform_mfea_mfc_change(const McastRoute& static_route) = 0;
+    virtual void cancel_mfea_mfc_change(const McastRoute& static_route) = 0;
+    void inform_mfea(const McastRoute& route);
+
     /**
      * Update a route received from the user configuration.
      *
@@ -873,6 +931,8 @@ private:
     map<IPvXNet, StaticRoute>	_winning_routes_unicast;
     map<IPvXNet, StaticRoute>	_winning_routes_multicast;
 
+    map<IPvX, McastRoute> _mcast_routes;
+
     //
     // Status-related state
     //
diff --git a/xorp/static_routes/xorp_static_routes.cc b/xorp/static_routes/xorp_static_routes.cc
index dcf3b67..cce5ee4 100644
--- a/xorp/static_routes/xorp_static_routes.cc
+++ b/xorp/static_routes/xorp_static_routes.cc
@@ -31,7 +31,7 @@
 #include "libxorp/callback.hh"
 #include "libxorp/eventloop.hh"
 #include "libxorp/exceptions.hh"
-
+#include "libproto/proto_unit.hh"
 #include "xrl_static_routes_node.hh"
 
 #ifdef HAVE_GETOPT_H
@@ -106,7 +106,8 @@ static_routes_main(const string& finder_hostname, uint16_t finder_port) {
 	finder_port,
 	"finder",
 	"fea",
-	"rib");
+	"rib",
+	xorp_module_name(AF_INET, XORP_MODULE_MFEA));
     wait_until_xrl_router_is_ready(eventloop,
 				   xrl_static_routes_node.xrl_router());
 
diff --git a/xorp/static_routes/xrl_static_routes_node.cc b/xorp/static_routes/xrl_static_routes_node.cc
index a3a4079..c9551a7 100644
--- a/xorp/static_routes/xrl_static_routes_node.cc
+++ b/xorp/static_routes/xrl_static_routes_node.cc
@@ -39,16 +39,19 @@ XrlStaticRoutesNode::XrlStaticRoutesNode(EventLoop&	eventloop,
 					 uint16_t	finder_port,
 					 const string&	finder_target,
 					 const string&	fea_target,
-					 const string&	rib_target)
+					 const string&	rib_target,
+					 const string& mfea_target)
     : StaticRoutesNode(eventloop),
       XrlStdRouter(eventloop, class_name.c_str(), finder_hostname.c_str(),
 		   finder_port),
       XrlStaticRoutesTargetBase(&xrl_router()),
       _eventloop(eventloop),
       _xrl_rib_client(&xrl_router()),
+      _xrl_mfea_client(&xrl_router()),
       _finder_target(finder_target),
       _fea_target(fea_target),
       _rib_target(rib_target),
+      _mfea_target(mfea_target),
       _ifmgr(eventloop, fea_target.c_str(), xrl_router().finder_address(),
 	     xrl_router().finder_port()),
       _xrl_finder_client(&xrl_router()),
@@ -61,7 +64,9 @@ XrlStaticRoutesNode::XrlStaticRoutesNode(EventLoop&	eventloop,
       _is_rib_registered(false),
       _is_rib_registering(false),
       _is_rib_deregistering(false),
-      _is_rib_igp_table4_registered(false)
+      _is_rib_igp_table4_registered(false),
+      _is_mfea_alive(false),
+      _is_mfea_registered(false)
 #ifdef HAVE_IPV6
       , _is_rib_igp_table6_registered(false)
 #endif
@@ -222,6 +227,208 @@ XrlStaticRoutesNode::finder_register_interest_fea_cb(const XrlError& xrl_error)
     }
 }
 
+
+//
+// Register with the MFEA
+//
+void
+XrlStaticRoutesNode::mfea_register_startup()
+{
+    bool success;
+
+    _mfea_register_startup_timer.unschedule();
+    _mfea_register_shutdown_timer.unschedule();
+
+    if (! _is_finder_alive)
+	return;		// The Finder is dead
+
+    if (_is_mfea_registered)
+	return;		// Already registered
+
+    _is_fea_registering = true;
+
+    //
+    // Register interest in the FEA with the Finder
+    //
+    success = _xrl_finder_client.send_register_class_event_interest(
+	_finder_target.c_str(), xrl_router().instance_name(), _mfea_target,
+	callback(this, &XrlStaticRoutesNode::finder_register_interest_mfea_cb));
+
+    if (! success) {
+	//
+	// If an error, then start a timer to try again.
+	//
+	_mfea_register_startup_timer = _eventloop.new_oneoff_after(
+	    RETRY_TIMEVAL,
+	    callback(this, &XrlStaticRoutesNode::mfea_register_startup));
+	return;
+    }
+}
+
+void
+XrlStaticRoutesNode::finder_register_interest_mfea_cb(const XrlError& xrl_error)
+{
+    switch (xrl_error.error_code()) {
+    case OKAY:
+	_is_mfea_registering = false;
+	_is_mfea_registered = true;
+	break;
+
+    case COMMAND_FAILED:
+	//
+	// If a command failed because the other side rejected it, this is
+	// fatal.
+	//
+	XLOG_FATAL("Cannot register interest in Finder events: %s",
+		   xrl_error.str().c_str());
+	break;
+
+    case NO_FINDER:
+    case RESOLVE_FAILED:
+    case SEND_FAILED:
+	//
+	// A communication error that should have been caught elsewhere
+	// (e.g., by tracking the status of the Finder and the other targets).
+	// Probably we caught it here because of event reordering.
+	// In some cases we print an error. In other cases our job is done.
+	//
+	XLOG_ERROR("XRL communication error: %s", xrl_error.str().c_str());
+	break;
+
+    case BAD_ARGS:
+    case NO_SUCH_METHOD:
+    case INTERNAL_ERROR:
+	//
+	// An error that should happen only if there is something unusual:
+	// e.g., there is XRL mismatch, no enough internal resources, etc.
+	// We don't try to recover from such errors, hence this is fatal.
+	//
+	XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str());
+	break;
+
+    case REPLY_TIMED_OUT:
+    case SEND_FAILED_TRANSIENT:
+	//
+	// If a transient error, then start a timer to try again
+	// (unless the timer is already running).
+	//
+	if (! _mfea_register_startup_timer.scheduled()) {
+	    XLOG_ERROR("Failed to register interest in Finder events: %s. "
+		       "Will try again.",
+		       xrl_error.str().c_str());
+	    _mfea_register_startup_timer = _eventloop.new_oneoff_after(
+		RETRY_TIMEVAL,
+		callback(this, &XrlStaticRoutesNode::mfea_register_startup));
+	}
+	break;
+    }
+}
+
+//
+// De-register with the RIB
+//
+void
+XrlStaticRoutesNode::mfea_register_shutdown()
+{
+    bool success;
+
+    _mfea_register_startup_timer.unschedule();
+    _mfea_register_shutdown_timer.unschedule();
+
+    if (! _is_finder_alive)
+	return;		// The Finder is dead
+
+    if (! _is_mfea_alive)
+	return;		// The MFEA is not there anymore
+
+    if (! _is_mfea_registered)
+	return;		// Not registered
+
+    if (! _is_mfea_deregistering) {
+	StaticRoutesNode::incr_shutdown_requests_n();
+	_is_mfea_deregistering = true;
+    }
+
+    success = _xrl_finder_client.send_deregister_class_event_interest(
+	_finder_target.c_str(), xrl_router().instance_name(), _mfea_target,
+	callback(this, &XrlStaticRoutesNode::finder_deregister_interest_mfea_cb));
+
+    if (! success) {
+	//
+	// If an error, then start a timer to try again.
+	//
+	_mfea_register_shutdown_timer = _eventloop.new_oneoff_after(
+	    RETRY_TIMEVAL,
+	    callback(this, &XrlStaticRoutesNode::mfea_register_shutdown));
+	return;
+    }
+}
+
+void
+XrlStaticRoutesNode::finder_deregister_interest_mfea_cb(
+    const XrlError& xrl_error)
+{
+    switch (xrl_error.error_code()) {
+    case OKAY:
+	//
+	// If success, then we are done
+	//
+	_is_mfea_deregistering = false;
+	_is_mfea_registered = false;
+	break;
+
+    case COMMAND_FAILED:
+	//
+	// If a command failed because the other side rejected it, this is
+	// fatal.
+	//
+	XLOG_FATAL("Cannot deregister interest in Finder events: %s",
+		   xrl_error.str().c_str());
+	break;
+
+    case NO_FINDER:
+    case RESOLVE_FAILED:
+    case SEND_FAILED:
+	//
+	// A communication error that should have been caught elsewhere
+	// (e.g., by tracking the status of the Finder and the other targets).
+	// Probably we caught it here because of event reordering.
+	// In some cases we print an error. In other cases our job is done.
+	//
+	_is_mfea_deregistering = false;
+	_is_mfea_registered = false;
+	break;
+
+    case BAD_ARGS:
+    case NO_SUCH_METHOD:
+    case INTERNAL_ERROR:
+	//
+	// An error that should happen only if there is something unusual:
+	// e.g., there is XRL mismatch, no enough internal resources, etc.
+	// We don't try to recover from such errors, hence this is fatal.
+	//
+	XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str());
+	break;
+
+    case REPLY_TIMED_OUT:
+    case SEND_FAILED_TRANSIENT:
+	//
+	// If a transient error, then start a timer to try again
+	// (unless the timer is already running).
+	//
+	if (! _mfea_register_shutdown_timer.scheduled()) {
+	    XLOG_ERROR("Failed to deregister interest in Finder events: %s. "
+		       "Will try again.",
+		       xrl_error.str().c_str());
+	    _mfea_register_shutdown_timer = _eventloop.new_oneoff_after(
+		RETRY_TIMEVAL,
+		callback(this, &XrlStaticRoutesNode::mfea_register_shutdown));
+	}
+	break;
+    }
+}
+
+
 //
 // De-register with the FEA
 //
@@ -445,6 +652,7 @@ XrlStaticRoutesNode::finder_register_interest_rib_cb(const XrlError& xrl_error)
     }
 }
 
+
 //
 // De-register with the RIB
 //
@@ -1036,6 +1244,10 @@ XrlStaticRoutesNode::finder_event_observer_0_1_xrl_target_birth(
 	send_rib_add_tables();
     }
 
+    if (target_class == _mfea_target) {
+	_is_mfea_alive = true;
+    }
+
     return XrlCmdError::OKAY();
     UNUSED(target_instance);
 }
@@ -1070,6 +1282,16 @@ XrlStaticRoutesNode::finder_event_observer_0_1_xrl_target_death(
 	do_shutdown = true;
     }
 
+    if (target_class == _mfea_target) {
+	// If it was never started (by us), then ignore.
+	if (_is_mfea_alive) {
+	    XLOG_ERROR("MFEA (instance %s) has died, shutting down.",
+		       target_instance.c_str());
+	    do_shutdown = true;
+	    _is_mfea_alive = false;
+	}
+    }
+
     if (do_shutdown)
 	StaticRoutesNode::shutdown();
 
@@ -1251,6 +1473,51 @@ XrlStaticRoutesNode::static_routes_0_1_delete_route6(
     return XrlCmdError::OKAY();
 }
 
+XrlCmdError XrlStaticRoutesNode::static_routes_0_1_add_mcast_route4(
+    // Input values,
+    const IPv4&     mcast_addr,
+    const string&   input_if,
+    const IPv4&     input_ip,
+    const string&   output_ifs,
+    const uint32_t& distance)
+{
+    string error_msg;
+    if (StaticRoutesNode::add_mcast_route4(mcast_addr, input_if, input_ip, output_ifs, distance, error_msg) != XORP_OK) {
+	return XrlCmdError::COMMAND_FAILED(error_msg);
+    }
+    return XrlCmdError::OKAY();
+}
+
+XrlCmdError XrlStaticRoutesNode::static_routes_0_1_replace_mcast_route4(
+    // Input values,
+    const IPv4&     mcast_addr,
+    const string&   input_if,
+    const IPv4&     input_ip,
+    const string&   output_ifs,
+    const uint32_t& distance)
+{
+    string error_msg;
+    if (StaticRoutesNode::replace_mcast_route4(mcast_addr, input_if, input_ip, output_ifs, distance, error_msg) != XORP_OK) {
+	return XrlCmdError::COMMAND_FAILED(error_msg);
+    }
+    return XrlCmdError::OKAY();
+}
+
+
+XrlCmdError XrlStaticRoutesNode::static_routes_0_1_delete_mcast_route4(
+        // Input values,
+    const IPv4&     mcast_addr,
+    const IPv4&     input_ip)
+{
+    string error_msg;
+    if (StaticRoutesNode::delete_mcast_route4(mcast_addr, input_ip, error_msg) != XORP_OK) {
+	return XrlCmdError::COMMAND_FAILED(error_msg);
+    }
+    return XrlCmdError::OKAY();
+}
+
+
+
 /**
  *  Add/replace/delete a backup static route.
  *
@@ -1757,6 +2024,33 @@ XrlStaticRoutesNode::inform_rib_route_change(const StaticRoute& static_route)
     }
 }
 
+void
+XrlStaticRoutesNode::inform_mfea_mfc_change(const McastRoute& static_route)
+{
+    // Add the request to the queue
+    _inform_mfea_queue.push_back(static_route);
+
+    // If the queue was empty before, start sending the routes
+    if (_inform_mfea_queue.size() == 1) {
+	send_mfea_mfc_change();
+    }
+}
+
+void
+XrlStaticRoutesNode::cancel_mfea_mfc_change(const McastRoute& static_route)
+{
+    list<McastRoute>::iterator iter;
+
+    for (iter = _inform_mfea_queue.begin();
+	 iter != _inform_mfea_queue.end();
+	 ++iter) {
+	McastRoute& tmp_static_route = *iter;
+	if (tmp_static_route == static_route)
+	    tmp_static_route.set_ignored(true);
+    }
+}
+
+
 /**
  * Cancel a pending request to inform the RIB about a route change.
  *
@@ -1995,6 +2289,167 @@ XrlStaticRoutesNode::send_rib_route_change()
     }
 }
 
+
+void
+XrlStaticRoutesNode::send_mfea_mfc_change()
+{
+    bool success = true;
+
+    if (! _is_finder_alive)
+	return;		// The Finder is dead
+
+    do {
+	// Pop-up all routes that are to be ignored
+	if (_inform_mfea_queue.empty())
+	    return;		// No more route changes to send
+
+	McastRoute& tmp_static_route = _inform_mfea_queue.front();
+	if (tmp_static_route.is_ignored()) {
+	    _inform_mfea_queue.pop_front();
+	    continue;
+	}
+	break;
+    } while (true);
+
+    McastRoute& static_route = _inform_mfea_queue.front();
+
+    //
+    // Check whether we have already registered with the MFEA
+    //
+    if (! _is_mfea_registered) {
+	mfea_register_startup();
+	success = false;
+	goto start_timer_label;
+    }
+
+    //
+    // Send the appropriate XRL
+    //
+    if (static_route.is_add_route() || static_route.is_replace_route()) {
+	XLOG_INFO("sending mfea add-mfc command, input: %s  mcast-addr: %s  ifname: %s  output_ifs: %s\n",
+		  static_route.input_ip().str().c_str(),
+		  static_route.mcast_addr().str().c_str(),
+		  static_route.ifname().c_str(),
+		  static_route.output_ifs().c_str());
+	success = _xrl_mfea_client.send_add_mfc4_str(
+	    _mfea_target.c_str(),
+	    StaticRoutesNode::protocol_name(),
+	    static_route.input_ip().get_ipv4(),
+	    static_route.mcast_addr().get_ipv4(),
+	    static_route.ifname(),
+	    static_route.output_ifs(),
+	    static_route.distance(),
+	    callback(this, &XrlStaticRoutesNode::send_mfea_mfc_change_cb));
+	if (success)
+	    return;
+    }
+
+    if (static_route.is_delete_route()) {
+	success = _xrl_mfea_client.send_delete_mfc4(
+	    _mfea_target.c_str(),
+	    StaticRoutesNode::protocol_name(),
+	    static_route.input_ip().get_ipv4(),
+	    static_route.mcast_addr().get_ipv4(),
+	    callback(this, &XrlStaticRoutesNode::send_mfea_mfc_change_cb));
+	if (success)
+	    return;
+    }
+
+    if (! success) {
+	//
+	// If an error, then start a timer to try again.
+	//
+	XLOG_ERROR("Failed to %s mcast-route for %s with the RIB. "
+		   "Will try again.",
+		   (static_route.is_add_route())? "add"
+		   : (static_route.is_replace_route())? "replace"
+		   : "delete",
+		   static_route.mcast_addr().str().c_str());
+    start_timer_label:
+	_inform_mfea_queue_timer = _eventloop.new_oneoff_after(
+	    RETRY_TIMEVAL,
+	    callback(this, &XrlStaticRoutesNode::send_mfea_mfc_change));
+    }
+}
+
+void
+XrlStaticRoutesNode::send_mfea_mfc_change_cb(const XrlError& xrl_error)
+{
+    switch (xrl_error.error_code()) {
+    case OKAY:
+	//
+	// If success, then send the next route change
+	//
+	_inform_mfea_queue.pop_front();
+	send_mfea_mfc_change();
+	break;
+
+    case COMMAND_FAILED:
+	//
+	// If a command failed because the other side rejected it,
+	// then print an error and send the next one.
+	//
+	XLOG_ERROR("Cannot %s an mcast-routing entry with the MFEA: %s",
+		   (_inform_mfea_queue.front().is_add_route())? "add"
+		   : (_inform_mfea_queue.front().is_replace_route())? "replace"
+		   : "delete",
+		   xrl_error.str().c_str());
+	_inform_mfea_queue.pop_front();
+	send_mfea_mfc_change();
+	break;
+
+    case NO_FINDER:
+    case RESOLVE_FAILED:
+    case SEND_FAILED:
+	//
+	// A communication error that should have been caught elsewhere
+	// (e.g., by tracking the status of the Finder and the other targets).
+	// Probably we caught it here because of event reordering.
+	// In some cases we print an error. In other cases our job is done.
+	//
+	XLOG_ERROR("Cannot %s an mcast-routing entry with the MFEA: %s",
+		   (_inform_mfea_queue.front().is_add_route())? "add"
+		   : (_inform_mfea_queue.front().is_replace_route())? "replace"
+		   : "delete",
+		   xrl_error.str().c_str());
+	_inform_mfea_queue.pop_front();
+	send_mfea_mfc_change();
+	break;
+
+    case BAD_ARGS:
+    case NO_SUCH_METHOD:
+    case INTERNAL_ERROR:
+	//
+	// An error that should happen only if there is something unusual:
+	// e.g., there is XRL mismatch, no enough internal resources, etc.
+	// We don't try to recover from such errors, hence this is fatal.
+	//
+	XLOG_FATAL("Fatal XRL error: %s", xrl_error.str().c_str());
+	break;
+
+    case REPLY_TIMED_OUT:
+    case SEND_FAILED_TRANSIENT:
+	//
+	// If a transient error, then start a timer to try again
+	// (unless the timer is already running).
+	//
+	if (! _inform_mfea_queue_timer.scheduled()) {
+	    XLOG_ERROR("Failed to %s an mcast-routing entry with the RIB: %s. "
+		       "Will try again.",
+		       (_inform_mfea_queue.front().is_add_route())? "add"
+		       : (_inform_mfea_queue.front().is_replace_route())? "replace"
+		       : "delete",
+		       xrl_error.str().c_str());
+	    _inform_mfea_queue_timer = _eventloop.new_oneoff_after(
+		RETRY_TIMEVAL,
+		callback(this, &XrlStaticRoutesNode::send_mfea_mfc_change));
+	}
+	break;
+    }
+}
+
+
+
 void
 XrlStaticRoutesNode::send_rib_route_change_cb(const XrlError& xrl_error)
 {
diff --git a/xorp/static_routes/xrl_static_routes_node.hh b/xorp/static_routes/xrl_static_routes_node.hh
index 905d2ac..aff1585 100644
--- a/xorp/static_routes/xrl_static_routes_node.hh
+++ b/xorp/static_routes/xrl_static_routes_node.hh
@@ -18,7 +18,6 @@
 // XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA;
 // http://xorp.net
 
-// $XORP: xorp/static_routes/xrl_static_routes_node.hh,v 1.26 2008/10/02 21:58:29 bms Exp $
 
 #ifndef __STATIC_ROUTES_XRL_STATIC_ROUTES_NODE_HH__
 #define __STATIC_ROUTES_XRL_STATIC_ROUTES_NODE_HH__
@@ -29,11 +28,10 @@
 //
 
 #include "libxipc/xrl_std_router.hh"
-
 #include "libfeaclient/ifmgr_xrl_mirror.hh"
-
 #include "xrl/interfaces/finder_event_notifier_xif.hh"
 #include "xrl/interfaces/rib_xif.hh"
+#include "xrl/interfaces/mfea_xif.hh"
 #include "xrl/targets/static_routes_base.hh"
 
 #include "static_routes_node.hh"
@@ -52,8 +50,9 @@ public:
 			uint16_t	finder_port,
 			const string&	finder_target,
 			const string&	fea_target,
-			const string&	rib_target);
-    ~XrlStaticRoutesNode();
+			const string&	rib_target,
+			const string&	mfea_target);
+    virtual ~XrlStaticRoutesNode();
 
     /**
      * Startup the node operation.
@@ -210,6 +209,37 @@ protected:
 	const IPv6&	nexthop);
 
     /**
+     * Add/replace/delete multicast routes (not MRIB)
+     *
+     * @param mcast_addr  Multicast-address to be routed.
+     *
+     * @param input_if  Input interface name.
+     *
+     * @param input_ip  Input interface IP address.
+     *
+     * @param output_ifs Output interface name(s).  Space-separated list.
+     */
+    XrlCmdError static_routes_0_1_add_mcast_route4(
+        // Input values,
+        const IPv4&     mcast_addr,
+        const string&   input_if,
+        const IPv4&     input_ip,
+        const string&   output_ifs,
+	const uint32_t& distance);
+
+    XrlCmdError static_routes_0_1_replace_mcast_route4(
+        // Input values,
+        const IPv4&     mcast_addr,
+        const string&   input_if,
+        const IPv4&     input_ip,
+        const string&   output_ifs,
+	const uint32_t& distance);
+    XrlCmdError static_routes_0_1_delete_mcast_route4(
+        // Input values,
+        const IPv4&     mcast_addr,
+        const IPv4&     input_ip);
+
+    /**
      *  Add/replace/delete a backup static route.
      *
      *  @param unicast if true, then the route would be used for unicast
@@ -442,6 +472,73 @@ protected:
 	// Input values,
 	const bool&	enable);
 
+    XrlCmdError mfea_client_0_1_recv_kernel_signal_message4(
+	// Input values,
+	const string&,
+	const uint32_t&,
+	const string&,
+	const uint32_t&,
+	const IPv4&,
+	const IPv4&,
+	const vector<uint8_t>&) {
+	return XrlCmdError::OKAY();
+    }
+
+    XrlCmdError mfea_client_0_1_recv_dataflow_signal4(
+	// Input values,
+	const string&,
+	const IPv4&,
+	const IPv4&,
+	const uint32_t&,
+	const uint32_t&,
+	const uint32_t&,
+	const uint32_t&,
+	const uint32_t&,
+	const uint32_t&,
+	const uint32_t&,
+	const uint32_t&,
+	const bool&,
+	const bool&,
+	const bool&,
+	const bool&) {
+	return XrlCmdError::OKAY();
+    }
+
+
+#ifdef HAVE_IPV6
+    XrlCmdError mfea_client_0_1_recv_kernel_signal_message6(
+	// Input values,
+	const string&,
+	const uint32_t&,
+	const string&,
+	const uint32_t&,
+	const IPv6&,
+	const IPv6&,
+	const vector<uint8_t>&) {
+	return XrlCmdError::OKAY();
+    }
+
+    XrlCmdError mfea_client_0_1_recv_dataflow_signal6(
+	// Input values,
+	const string&,
+	const IPv6&,
+	const IPv6&,
+	const uint32_t&,
+	const uint32_t&,
+	const uint32_t&,
+	const uint32_t&,
+	const uint32_t&,
+	const uint32_t&,
+	const uint32_t&,
+	const uint32_t&,
+	const bool&,
+	const bool&,
+	const bool&,
+	const bool&) {
+	return XrlCmdError::OKAY();
+    }
+#endif
+
     /**
      * Configure a policy filter.
      *
@@ -493,6 +590,11 @@ private:
     void fea_register_shutdown();
     void finder_deregister_interest_fea_cb(const XrlError& xrl_error);
 
+    void mfea_register_startup();
+    void finder_register_interest_mfea_cb(const XrlError& xrl_error);
+    void mfea_register_shutdown();
+    void finder_deregister_interest_mfea_cb(const XrlError& xrl_error);
+
     void rib_register_startup();
     void finder_register_interest_rib_cb(const XrlError& xrl_error);
     void rib_register_shutdown();
@@ -515,6 +617,7 @@ private:
      */
     void inform_rib_route_change(const StaticRoute& static_route);
 
+
     /**
      * Cancel a pending request to inform the RIB about a route change.
      *
@@ -522,18 +625,28 @@ private:
      */
     void cancel_rib_route_change(const StaticRoute& static_route);
 
+    void inform_mfea_mfc_change(const McastRoute& static_route);
+    void cancel_mfea_mfc_change(const McastRoute& static_route);
+
     void send_rib_route_change();
     void send_rib_route_change_cb(const XrlError& xrl_error);
 
+    void send_mfea_mfc_change();
+    void send_mfea_mfc_change_cb(const XrlError& xrl_error);
+
     EventLoop&		_eventloop;
     XrlRibV0p1Client	_xrl_rib_client;
+    XrlMfeaV0p1Client	_xrl_mfea_client;
     const string	_finder_target;
     const string	_fea_target;
     const string	_rib_target;
+    const string	_mfea_target;
 
     IfMgrXrlMirror	_ifmgr;
     list<StaticRoute>	_inform_rib_queue;
     XorpTimer		_inform_rib_queue_timer;
+    list<McastRoute>	_inform_mfea_queue;
+    XorpTimer		_inform_mfea_queue_timer;
     XrlFinderEventNotifierV0p1Client	_xrl_finder_client;
 
     static const TimeVal RETRY_TIMEVAL;
@@ -552,6 +665,14 @@ private:
     bool		_is_rib_registering;
     bool		_is_rib_deregistering;
     bool		_is_rib_igp_table4_registered;
+
+    bool		_is_mfea_alive;
+    bool		_is_mfea_registered;
+    bool		_is_mfea_registering;
+    bool		_is_mfea_deregistering;
+    XorpTimer		_mfea_register_startup_timer;
+    XorpTimer		_mfea_register_shutdown_timer;
+
 #ifdef HAVE_IPV6
     bool		_is_rib_igp_table6_registered;
 #endif
diff --git a/xorp/xrl/interfaces/static_routes.xif b/xorp/xrl/interfaces/static_routes.xif
index f6da4f8..38a1622 100644
--- a/xorp/xrl/interfaces/static_routes.xif
+++ b/xorp/xrl/interfaces/static_routes.xif
@@ -1,4 +1,3 @@
-/* $XORP: xorp/xrl/interfaces/static_routes.xif,v 1.4 2007/01/23 01:57:38 pavlin Exp $ */
 
 /*
  * Static Routes XRL interface.
@@ -48,6 +47,13 @@ interface static_routes/0.1 {
 			& nexthop:ipv6;
 
 	/**
+	 * Add/replace/delete multicast routes
+	 */
+	add_mcast_route4 ? mcast_addr:ipv4 & input_if:txt & input_ip:ipv4 & output_ifs:txt & distance:u32;
+	replace_mcast_route4 ? mcast_addr:ipv4 & input_if:txt & input_ip:ipv4 & output_ifs:txt & distance:u32;
+	delete_mcast_route4 ? mcast_addr:ipv4 & input_ip:ipv4;
+
+	/**
 	 * Add/replace/delete a backup static route.
 	 *
 	 * @param unicast if true, then the route would be used for unicast
diff --git a/xorp/xrl/targets/static_routes.tgt b/xorp/xrl/targets/static_routes.tgt
index 1bf764f..54e6396 100644
--- a/xorp/xrl/targets/static_routes.tgt
+++ b/xorp/xrl/targets/static_routes.tgt
@@ -1,11 +1,12 @@
-/* $XORP: xorp/xrl/targets/static_routes.tgt,v 1.4 2007/05/31 19:02:28 pavlin Exp $ */
 
 #include "common.xif"
 #include "finder_event_observer.xif"
 #include "policy_backend.xif"
 #include "static_routes.xif"
+#include "mfea_client.xif"
 
 target static_routes implements	common/0.1,				\
 				finder_event_observer/0.1,		\
 				policy_backend/0.1,			\
+				mfea_client/0.1,			\
 				static_routes/0.1;
-- 
1.7.7.6



More information about the Xorp-hackers mailing list