From: Ben Greear <[email protected]>

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 <[email protected]>
---
 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

_______________________________________________
Xorp-hackers mailing list
[email protected]
http://mailman.ICSI.Berkeley.EDU/mailman/listinfo/xorp-hackers

Reply via email to